mirror of
https://github.com/CCExtractor/ccextractor.git
synced 2026-02-09 05:24:58 +00:00
Compare commits
651 Commits
fix/freebs
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dd2931153e | ||
|
|
10288243b9 | ||
|
|
9762223105 | ||
|
|
cad4d3d62d | ||
|
|
a30b8d7a83 | ||
|
|
f920c16a53 | ||
|
|
5c05173c75 | ||
|
|
2582f628dd | ||
|
|
99f7d1955a | ||
|
|
78e94f85c2 | ||
|
|
36f13922d2 | ||
|
|
3a547fdeab | ||
|
|
528021b577 | ||
|
|
270c89b7f8 | ||
|
|
032cd1c6b1 | ||
|
|
42e4e9a657 | ||
|
|
821e307333 | ||
|
|
ae81f3ba3d | ||
|
|
b190751b2c | ||
|
|
f1bb0f4dce | ||
|
|
f147ac27f8 | ||
|
|
2dfb44d7d4 | ||
|
|
580e721dfe | ||
|
|
d0a82447ff | ||
|
|
5c19c7b932 | ||
|
|
fd7271bae2 | ||
|
|
05c68349d5 | ||
|
|
09f21f64e4 | ||
|
|
c65fb0874e | ||
|
|
9db727d593 | ||
|
|
fe6dad83b7 | ||
|
|
d494286082 | ||
|
|
259e881483 | ||
|
|
197069d3b8 | ||
|
|
7a810d736d | ||
|
|
1413c948c4 | ||
|
|
bb5385913b | ||
|
|
f8981e8e1e | ||
|
|
a1871abf04 | ||
|
|
20b3773bb9 | ||
|
|
8786b4cf75 | ||
|
|
8632ecda5b | ||
|
|
475153a9dd | ||
|
|
df90009f73 | ||
|
|
2352ea21e3 | ||
|
|
dc041a35e8 | ||
|
|
e99ba1d177 | ||
|
|
298665faa4 | ||
|
|
735a01bf04 | ||
|
|
3618c23b5a | ||
|
|
b7c9da75dd | ||
|
|
449d55d5e5 | ||
|
|
60aa370899 | ||
|
|
3d18b38c32 | ||
|
|
2a6d27f9ff | ||
|
|
91d3512bcc | ||
|
|
74e64c0421 | ||
|
|
c175750ebe | ||
|
|
e7dc4d19f7 | ||
|
|
1fbb51056d | ||
|
|
5d9a8cc6f2 | ||
|
|
17abad79f2 | ||
|
|
707e1f01fe | ||
|
|
efc8b791e7 | ||
|
|
a856bbde10 | ||
|
|
9390b876fa | ||
|
|
ead0a4beed | ||
|
|
b2e9cb74c1 | ||
|
|
20b194aac4 | ||
|
|
2d9b480972 | ||
|
|
1447b021cb | ||
|
|
e0ac126cff | ||
|
|
b8019bdb35 | ||
|
|
9d921dec43 | ||
|
|
3ada2b5002 | ||
|
|
50ec9866db | ||
|
|
ce87d01fbd | ||
|
|
fecd24d08e | ||
|
|
482544c5bf | ||
|
|
84a7a1fb41 | ||
|
|
f198bcd2ec | ||
|
|
4b6016ca1c | ||
|
|
9c2ea47eda | ||
|
|
170b466a20 | ||
|
|
2bdcd20115 | ||
|
|
ab18d234d2 | ||
|
|
3ff02617b0 | ||
|
|
c7fad95e24 | ||
|
|
c018f1f43c | ||
|
|
98b50b2a35 | ||
|
|
46cee0893a | ||
|
|
42ad48ca7f | ||
|
|
ed26a595bd | ||
|
|
b1c2aabb22 | ||
|
|
bb2ae1e70f | ||
|
|
6464fa486e | ||
|
|
5aa747ab33 | ||
|
|
39adfa59b0 | ||
|
|
20287548cb | ||
|
|
b7b10419ec | ||
|
|
8fbfd68426 | ||
|
|
7159d0b6d0 | ||
|
|
c515578e37 | ||
|
|
e55b8eb764 | ||
|
|
0228fbcbfa | ||
|
|
0e190e0962 | ||
|
|
13f1b5ab53 | ||
|
|
b39f923c46 | ||
|
|
7e32d6a553 | ||
|
|
3bde3dceec | ||
|
|
d5201b1129 | ||
|
|
a199f4f8af | ||
|
|
eea049923d | ||
|
|
d999c3e0e0 | ||
|
|
aac90d5a5f | ||
|
|
618df184c6 | ||
|
|
5e6aab8972 | ||
|
|
a77c21c06c | ||
|
|
4252703431 | ||
|
|
1af2a29a3c | ||
|
|
8ab474c593 | ||
|
|
1c781c2a38 | ||
|
|
4d718378d5 | ||
|
|
1bd4cd5c0a | ||
|
|
067045ce92 | ||
|
|
2f2904041c | ||
|
|
d837c369e5 | ||
|
|
686ff69fdc | ||
|
|
126835d998 | ||
|
|
6e170cd812 | ||
|
|
fe921626e1 | ||
|
|
6578f0ff34 | ||
|
|
1911068e92 | ||
|
|
493495361d | ||
|
|
643857e98f | ||
|
|
05adb5f47e | ||
|
|
504877b928 | ||
|
|
64ee63a560 | ||
|
|
270c603bd2 | ||
|
|
6d356b4458 | ||
|
|
cfb10d4b91 | ||
|
|
ca2b708023 | ||
|
|
10ac5ca6ce | ||
|
|
333cfb3726 | ||
|
|
c609f66c02 | ||
|
|
91f254017b | ||
|
|
1f5d3df0ae | ||
|
|
e36d81c237 | ||
|
|
8d338dc362 | ||
|
|
c78e01d186 | ||
|
|
401ff6c105 | ||
|
|
83eb51ed6f | ||
|
|
bce0c92fdd | ||
|
|
ea4859fd54 | ||
|
|
8d7890c743 | ||
|
|
477307e438 | ||
|
|
4a4911bcec | ||
|
|
dc946168e7 | ||
|
|
3a60b1268b | ||
|
|
e3d1c56ad0 | ||
|
|
b5bc0e2616 | ||
|
|
600a9a0e75 | ||
|
|
694b61f862 | ||
|
|
86925727e0 | ||
|
|
1c7515681e | ||
|
|
2bcac83761 | ||
|
|
efc28d87d5 | ||
|
|
b4d8e0ffaf | ||
|
|
0b7b7fd031 | ||
|
|
90041554a3 | ||
|
|
6950a7661e | ||
|
|
41fb966f6f | ||
|
|
04ed95f8b5 | ||
|
|
ddf29672fd | ||
|
|
0890e06d84 | ||
|
|
8c33412888 | ||
|
|
f40294cc5c | ||
|
|
22d5d35158 | ||
|
|
51cae1c2f0 | ||
|
|
dfaebd5db8 | ||
|
|
cfa7d912ca | ||
|
|
ad971f0e72 | ||
|
|
8aadbfb5f2 | ||
|
|
44eb665cd8 | ||
|
|
1255b318ae | ||
|
|
1b0e66bc67 | ||
|
|
f5dc1cf467 | ||
|
|
aaf937a135 | ||
|
|
317c66f14e | ||
|
|
946c5859d4 | ||
|
|
7166e48698 | ||
|
|
d31ea87c03 | ||
|
|
028ce9d0b5 | ||
|
|
cc7a43b5e2 | ||
|
|
3e1424cda8 | ||
|
|
82109e6cd9 | ||
|
|
5dc8292dd2 | ||
|
|
a5b8bc8bf6 | ||
|
|
29158b2c38 | ||
|
|
ad2ee70743 | ||
|
|
562de8893b | ||
|
|
12adb5e92b | ||
|
|
203eb23030 | ||
|
|
774c3a0d3a | ||
|
|
07f1ddc3fe | ||
|
|
303bec8d5d | ||
|
|
e43a6b5ced | ||
|
|
64484af49e | ||
|
|
7526da884c | ||
|
|
3529bb29b4 | ||
|
|
925560f773 | ||
|
|
200eb1750a | ||
|
|
6dcdb4b2d8 | ||
|
|
a2d2c4f063 | ||
|
|
4ab6c83c27 | ||
|
|
e66a0183c3 | ||
|
|
a8ec28630a | ||
|
|
432d4237ec | ||
|
|
e9519c4a67 | ||
|
|
fef005ddaf | ||
|
|
546c776e57 | ||
|
|
daeed5df71 | ||
|
|
b56ab005a8 | ||
|
|
f1681ee929 | ||
|
|
031f463b5c | ||
|
|
b23866f5a8 | ||
|
|
2ec93c3d3d | ||
|
|
5564aa8a54 | ||
|
|
868fac5423 | ||
|
|
9ca26171d6 | ||
|
|
ead4cbb278 | ||
|
|
dfd7101f54 | ||
|
|
9659d3cf4c | ||
|
|
34c7cd6d2e | ||
|
|
7448a260c7 | ||
|
|
54236f840c | ||
|
|
f2aeef167b | ||
|
|
6a4a1c97ec | ||
|
|
f369959096 | ||
|
|
1c2bcb5088 | ||
|
|
da79ee44d9 | ||
|
|
26434a7f89 | ||
|
|
718eb1a37f | ||
|
|
ace6361bfb | ||
|
|
7041441d39 | ||
|
|
1589c31774 | ||
|
|
c96d3ff3f1 | ||
|
|
598a48e260 | ||
|
|
0cc3626261 | ||
|
|
e0e66bd0ba | ||
|
|
2642ca8805 | ||
|
|
a108302dc0 | ||
|
|
ce90b61923 | ||
|
|
18566f2213 | ||
|
|
125c5e8821 | ||
|
|
64ce4ac84f | ||
|
|
674b859284 | ||
|
|
9a761331f8 | ||
|
|
046ee71eda | ||
|
|
b5fc3e63c4 | ||
|
|
5eaf805d27 | ||
|
|
0ba941e8c0 | ||
|
|
a9413a2312 | ||
|
|
a2eb03cb73 | ||
|
|
06063f26a4 | ||
|
|
82daa7fb2b | ||
|
|
a71687e19f | ||
|
|
25162fe40a | ||
|
|
3365a715a6 | ||
|
|
26e0f64720 | ||
|
|
a1ed940c8b | ||
|
|
f5f4768503 | ||
|
|
e4374204bd | ||
|
|
7f55ae5c1d | ||
|
|
8bf1bc16de | ||
|
|
5352a8b877 | ||
|
|
fd155285d2 | ||
|
|
a6fd8d468a | ||
|
|
5b05ce5073 | ||
|
|
d28bc4e114 | ||
|
|
285e81f9a7 | ||
|
|
730156f33b | ||
|
|
152bbd308c | ||
|
|
8c586bccbd | ||
|
|
434cd3959a | ||
|
|
3cb0f61b0c | ||
|
|
a18eaa2c96 | ||
|
|
69b7f9f4c3 | ||
|
|
63dde6f3b2 | ||
|
|
8f64eeb54f | ||
|
|
02d91c4a03 | ||
|
|
463a4a85a1 | ||
|
|
ba2833b819 | ||
|
|
635a305c37 | ||
|
|
6fe612db3e | ||
|
|
2930c61420 | ||
|
|
173db88dcf | ||
|
|
29c3f4e684 | ||
|
|
d4a7b1d6ed | ||
|
|
9d14766b0d | ||
|
|
6f2a73d706 | ||
|
|
1fccb783f2 | ||
|
|
ec30a79be9 | ||
|
|
5beb4389f6 | ||
|
|
a6ccf29630 | ||
|
|
b6d7c7e778 | ||
|
|
117c2fce69 | ||
|
|
ffd6a34c30 | ||
|
|
70af627078 | ||
|
|
b0a5c069ed | ||
|
|
53ee63894c | ||
|
|
50ece42e0a | ||
|
|
3d00e718f6 | ||
|
|
021b788461 | ||
|
|
86e5d47141 | ||
|
|
5b36356456 | ||
|
|
ba04aedae1 | ||
|
|
5001df0d6c | ||
|
|
28506fee7b | ||
|
|
47d8aaddb9 | ||
|
|
1b2254f911 | ||
|
|
dc34b26afb | ||
|
|
c06102678e | ||
|
|
b0800a112c | ||
|
|
2b0d9ed427 | ||
|
|
fd4db0e7bf | ||
|
|
00d8c9cb0a | ||
|
|
7829c14c60 | ||
|
|
d3602ec938 | ||
|
|
f9b5e081a7 | ||
|
|
bdc3eaa81b | ||
|
|
2820042c1d | ||
|
|
d4d228125a | ||
|
|
43d5ba2f34 | ||
|
|
557774b202 | ||
|
|
4e0472bddf | ||
|
|
9a2fe6221e | ||
|
|
182b23a283 | ||
|
|
77f3fd35f4 | ||
|
|
14e6919f2e | ||
|
|
353a37010d | ||
|
|
921cbe0c57 | ||
|
|
f0523ceaa3 | ||
|
|
7284430fc6 | ||
|
|
68d0d4094e | ||
|
|
7075f6291d | ||
|
|
170d769476 | ||
|
|
1ff3457744 | ||
|
|
dc352a2202 | ||
|
|
c8750e42d1 | ||
|
|
20448bfeb2 | ||
|
|
807df0339e | ||
|
|
6642973c63 | ||
|
|
f08fd658e6 | ||
|
|
5ae3116a6c | ||
|
|
826afcd991 | ||
|
|
46af5ce9bb | ||
|
|
123b35ae69 | ||
|
|
f6e9d55838 | ||
|
|
6f7d3f6169 | ||
|
|
07cc78c2f1 | ||
|
|
affa34848c | ||
|
|
45ee03aecc | ||
|
|
c6e27ca809 | ||
|
|
a8f25ce25e | ||
|
|
2781a7f7d6 | ||
|
|
903ccc1442 | ||
|
|
857a3bc9c6 | ||
|
|
c2c589d6f6 | ||
|
|
941604b33c | ||
|
|
1950f096b6 | ||
|
|
1fc5ec00d4 | ||
|
|
c0deae4b0c | ||
|
|
84692b5658 | ||
|
|
4a51ad114e | ||
|
|
6789376b92 | ||
|
|
ea5125f030 | ||
|
|
000b39775c | ||
|
|
23fe02f0d2 | ||
|
|
394fb39a9c | ||
|
|
294bf5bc18 | ||
|
|
4e52e61c91 | ||
|
|
faaaabf63c | ||
|
|
f5a9018ef0 | ||
|
|
e01720c05e | ||
|
|
f80b1f26ca | ||
|
|
e42bc2b9f9 | ||
|
|
f9ebfd2a32 | ||
|
|
bf9841a255 | ||
|
|
9f670de8ed | ||
|
|
fc4a14e7d6 | ||
|
|
4f13b861cd | ||
|
|
df692f296d | ||
|
|
419fc4694d | ||
|
|
fc230fc217 | ||
|
|
825e160e72 | ||
|
|
8e24c17c1e | ||
|
|
4e21fae053 | ||
|
|
be239a5c46 | ||
|
|
1d9f32239e | ||
|
|
cbb5f0b0a8 | ||
|
|
fd063931ea | ||
|
|
7a9acb7bd2 | ||
|
|
cbf180eb39 | ||
|
|
614e6c42b5 | ||
|
|
38bcb7ed85 | ||
|
|
d57354830e | ||
|
|
7b43201ce1 | ||
|
|
ea1c82ac17 | ||
|
|
b3f1e27f5c | ||
|
|
82c92d3910 | ||
|
|
5bf8e7de0d | ||
|
|
5b8a9709df | ||
|
|
063786c4b7 | ||
|
|
6ed09ea397 | ||
|
|
44363c0acd | ||
|
|
701271ec82 | ||
|
|
7c74ea4112 | ||
|
|
ed42525f44 | ||
|
|
b88d1ebab2 | ||
|
|
ec11b00f9f | ||
|
|
8c0fe08781 | ||
|
|
3304c1b094 | ||
|
|
5bad3732c3 | ||
|
|
e3b0defb49 | ||
|
|
2065c5509d | ||
|
|
5458370346 | ||
|
|
9e19c58edf | ||
|
|
0bb56d508a | ||
|
|
2c67381d2b | ||
|
|
2b708c4a31 | ||
|
|
94a43928ad | ||
|
|
25d68b75bd | ||
|
|
73cd19f5d0 | ||
|
|
d0caf23a82 | ||
|
|
da3dc52b45 | ||
|
|
0fdfb751ba | ||
|
|
0b5f13e2c4 | ||
|
|
60cec9e6de | ||
|
|
d758f3156a | ||
|
|
da802a0a39 | ||
|
|
8f78a8bbb2 | ||
|
|
e87807ec27 | ||
|
|
d097ec881c | ||
|
|
87c898497a | ||
|
|
49b698259d | ||
|
|
5715d6d315 | ||
|
|
9fddaab3b0 | ||
|
|
6fdfde0838 | ||
|
|
8db7fc7a6d | ||
|
|
d8504f80bd | ||
|
|
70404c29ca | ||
|
|
feb2a61c1d | ||
|
|
6503502624 | ||
|
|
bf271de52c | ||
|
|
67e560d288 | ||
|
|
54bc97a3f8 | ||
|
|
3d7c534824 | ||
|
|
eda489265d | ||
|
|
0ac093e4b2 | ||
|
|
6838666b79 | ||
|
|
08d59ecb5f | ||
|
|
2ce3e0c0de | ||
|
|
3f45a4e136 | ||
|
|
d0d46fc176 | ||
|
|
3e9ed3043b | ||
|
|
1bdd9abd35 | ||
|
|
9e970fd788 | ||
|
|
87bc1d9613 | ||
|
|
440cd5527f | ||
|
|
0fbbc06bcf | ||
|
|
5f0c6728bf | ||
|
|
b9aabcd60d | ||
|
|
d0243237db | ||
|
|
a86a4ca7ce | ||
|
|
77624ec678 | ||
|
|
73db3a2c39 | ||
|
|
dd3dab7d52 | ||
|
|
ebfa31c333 | ||
|
|
d52d26baf8 | ||
|
|
3a852b7915 | ||
|
|
c3f637a10e | ||
|
|
f3768625c6 | ||
|
|
c733902473 | ||
|
|
6c44100f97 | ||
|
|
a0593c60e3 | ||
|
|
300f8ca65a | ||
|
|
8988152fa5 | ||
|
|
78642bcf02 | ||
|
|
609a53f373 | ||
|
|
0c0e44472d | ||
|
|
2060db99c8 | ||
|
|
a299d06d97 | ||
|
|
50b51e4234 | ||
|
|
0b74c9226a | ||
|
|
80957d645b | ||
|
|
80a117e643 | ||
|
|
63999369b7 | ||
|
|
0e815c6e2d | ||
|
|
0ef7227d7e | ||
|
|
2fa023b9fe | ||
|
|
2f0770d45f | ||
|
|
ee36ac1d4d | ||
|
|
e160a533b0 | ||
|
|
083c12698f | ||
|
|
88fbe9190a | ||
|
|
ac49bb5978 | ||
|
|
138ccd01c2 | ||
|
|
9fe2dab6d4 | ||
|
|
a28561ad0d | ||
|
|
c8f6b565fd | ||
|
|
442ce1015d | ||
|
|
e2dfdaa6a8 | ||
|
|
a0809caa94 | ||
|
|
859741a22c | ||
|
|
4429067965 | ||
|
|
d72646ac85 | ||
|
|
4a304346c9 | ||
|
|
627e0855ce | ||
|
|
7b1a169b8f | ||
|
|
3d5d8e2a0a | ||
|
|
683468e233 | ||
|
|
89849d321f | ||
|
|
588ad5260a | ||
|
|
ebd8148cad | ||
|
|
ba33f7572d | ||
|
|
9cf96b1899 | ||
|
|
0b3ad40377 | ||
|
|
ac72625030 | ||
|
|
f6cb862dcb | ||
|
|
53c0f56b6f | ||
|
|
62272e7be6 | ||
|
|
a7e05c265c | ||
|
|
9ce13cf45f | ||
|
|
e0ac99a241 | ||
|
|
6ebf98ea4a | ||
|
|
9372e15024 | ||
|
|
7e1a01447a | ||
|
|
b728ddadfa | ||
|
|
300541b873 | ||
|
|
2f1c1bf227 | ||
|
|
0bcb532428 | ||
|
|
d8698dc9cb | ||
|
|
4cc9231fc8 | ||
|
|
d202a66fd0 | ||
|
|
d8048bc95a | ||
|
|
af3ab5acd4 | ||
|
|
90519e2296 | ||
|
|
494b14b651 | ||
|
|
5b286c5b8d | ||
|
|
ea4f884b9d | ||
|
|
3b0a63d9c6 | ||
|
|
390c96f00d | ||
|
|
95f6f09659 | ||
|
|
42885caedd | ||
|
|
8d95ad0e7b | ||
|
|
1f0980185f | ||
|
|
6c764aa56c | ||
|
|
a0129df16c | ||
|
|
d2ab31fe38 | ||
|
|
3f6656176e | ||
|
|
f2f63ed65f | ||
|
|
3738540804 | ||
|
|
31c6e94e25 | ||
|
|
33f41f6045 | ||
|
|
137719ebea | ||
|
|
ecb0780af5 | ||
|
|
abce0864a5 | ||
|
|
9ff46656be | ||
|
|
446923c79d | ||
|
|
cde9e1f842 | ||
|
|
6c75b26484 | ||
|
|
9c4d5a8a58 | ||
|
|
a49ebf4230 | ||
|
|
7b8533a2dc | ||
|
|
134cd75d3b | ||
|
|
80e21171b1 | ||
|
|
0b262d0e17 | ||
|
|
f579cbe45d | ||
|
|
1a83913540 | ||
|
|
075ae04f1d | ||
|
|
d4949ccfa3 | ||
|
|
588c981184 | ||
|
|
941b88f3f9 | ||
|
|
071d017b27 | ||
|
|
65d9a7ed1a | ||
|
|
54df50f4fe | ||
|
|
bc5d605543 | ||
|
|
a1a0094167 | ||
|
|
5b8d8a72d8 | ||
|
|
621871eb7c | ||
|
|
ffcb5fe149 | ||
|
|
1b0808b4f3 | ||
|
|
68da0a044d | ||
|
|
87b0d22057 | ||
|
|
af5e36cdab | ||
|
|
8329257b99 | ||
|
|
1869c4c713 | ||
|
|
b3c3bdcdac | ||
|
|
6e295ac374 | ||
|
|
468bd2c156 | ||
|
|
bcf7eb2a50 | ||
|
|
54c7dfa45f | ||
|
|
984123521d | ||
|
|
a2cb65f181 | ||
|
|
fe7a4b3f45 | ||
|
|
d4ec0fe49b | ||
|
|
4a98bf5290 | ||
|
|
249cac359f | ||
|
|
69e521b320 | ||
|
|
8af19df556 | ||
|
|
bff08bec9e | ||
|
|
a66fb8c661 | ||
|
|
042716adde | ||
|
|
1342e4edee | ||
|
|
4d1d874243 | ||
|
|
155f56ede7 | ||
|
|
fb49d9460d | ||
|
|
37fed5e5b5 | ||
|
|
7113036719 | ||
|
|
d93d6731ba | ||
|
|
77e1dff779 | ||
|
|
58dedba93f | ||
|
|
9eb266914a | ||
|
|
1510396aa0 | ||
|
|
a7dfaea559 | ||
|
|
e8383c84ee | ||
|
|
810c869bc5 | ||
|
|
b32c120e89 | ||
|
|
3d7553349f | ||
|
|
d524a0247f | ||
|
|
f30f276456 | ||
|
|
17a8e1ec7b | ||
|
|
ebe25af476 | ||
|
|
1f7120f32f | ||
|
|
9e9023c258 | ||
|
|
b2930178be | ||
|
|
759c3f5d41 | ||
|
|
3c51fb6536 | ||
|
|
494df3edae | ||
|
|
810e02f7fa | ||
|
|
2720448e87 | ||
|
|
5fceac5e90 | ||
|
|
60ae6fb760 | ||
|
|
c9d80e12b8 | ||
|
|
a0aa9e4616 | ||
|
|
1515f5c1be | ||
|
|
42d750950a | ||
|
|
5338c15f8d | ||
|
|
ee232b5ded | ||
|
|
5a016d09b1 |
37
.dockerignore
Normal file
37
.dockerignore
Normal file
@@ -0,0 +1,37 @@
|
||||
# Build artifacts
|
||||
linux/ccextractor
|
||||
linux/rust/
|
||||
linux/*.o
|
||||
linux/*.a
|
||||
mac/ccextractor
|
||||
mac/rust/
|
||||
build/
|
||||
build_*/
|
||||
|
||||
# Git
|
||||
.git/
|
||||
.github/
|
||||
|
||||
# IDE
|
||||
.vscode/
|
||||
.idea/
|
||||
*.swp
|
||||
*.swo
|
||||
|
||||
# Docker
|
||||
docker/
|
||||
|
||||
# Documentation (not needed for build)
|
||||
docs/
|
||||
*.md
|
||||
!README.md
|
||||
|
||||
# Test files
|
||||
*.ts
|
||||
*.mp4
|
||||
*.mkv
|
||||
*.srt
|
||||
*.vtt
|
||||
|
||||
# Plans
|
||||
plans/
|
||||
157
.github/workflows/build_appimage.yml
vendored
Normal file
157
.github/workflows/build_appimage.yml
vendored
Normal file
@@ -0,0 +1,157 @@
|
||||
name: Build Linux AppImage
|
||||
|
||||
on:
|
||||
# Build on releases
|
||||
release:
|
||||
types: [published]
|
||||
# Allow manual trigger
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
build_type:
|
||||
description: 'Build type (all, minimal, ocr, hardsubx)'
|
||||
required: false
|
||||
default: 'all'
|
||||
# Build on pushes to workflow file for testing
|
||||
push:
|
||||
paths:
|
||||
- '.github/workflows/build_appimage.yml'
|
||||
- 'linux/build_appimage.sh'
|
||||
|
||||
jobs:
|
||||
build-appimage:
|
||||
runs-on: ubuntu-22.04
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
build_type: [minimal, ocr, hardsubx]
|
||||
|
||||
steps:
|
||||
- name: Check if should build this variant
|
||||
id: should_build
|
||||
run: |
|
||||
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
|
||||
INPUT_TYPE="${{ github.event.inputs.build_type }}"
|
||||
if [ "$INPUT_TYPE" = "all" ] || [ "$INPUT_TYPE" = "${{ matrix.build_type }}" ]; then
|
||||
echo "should_build=true" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "should_build=false" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
else
|
||||
echo "should_build=true" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
- name: Checkout repository
|
||||
if: steps.should_build.outputs.should_build == 'true'
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Install base dependencies
|
||||
if: steps.should_build.outputs.should_build == 'true'
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y --no-install-recommends \
|
||||
build-essential \
|
||||
cmake \
|
||||
pkg-config \
|
||||
wget \
|
||||
file \
|
||||
libfuse2 \
|
||||
zlib1g-dev \
|
||||
libpng-dev \
|
||||
libjpeg-dev \
|
||||
libfreetype-dev \
|
||||
libxml2-dev \
|
||||
libcurl4-gnutls-dev \
|
||||
libssl-dev \
|
||||
clang \
|
||||
libclang-dev
|
||||
|
||||
- name: Install OCR dependencies
|
||||
if: steps.should_build.outputs.should_build == 'true' && (matrix.build_type == 'ocr' || matrix.build_type == 'hardsubx')
|
||||
run: |
|
||||
sudo apt-get install -y --no-install-recommends \
|
||||
tesseract-ocr \
|
||||
libtesseract-dev \
|
||||
libleptonica-dev \
|
||||
tesseract-ocr-eng
|
||||
|
||||
- name: Install FFmpeg dependencies (HardSubX)
|
||||
if: steps.should_build.outputs.should_build == 'true' && matrix.build_type == 'hardsubx'
|
||||
run: |
|
||||
sudo apt-get install -y --no-install-recommends \
|
||||
libavcodec-dev \
|
||||
libavformat-dev \
|
||||
libavutil-dev \
|
||||
libswscale-dev \
|
||||
libswresample-dev \
|
||||
libavfilter-dev \
|
||||
libavdevice-dev
|
||||
|
||||
- name: Install Rust toolchain
|
||||
if: steps.should_build.outputs.should_build == 'true'
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
|
||||
- name: Cache GPAC build
|
||||
if: steps.should_build.outputs.should_build == 'true'
|
||||
id: cache-gpac
|
||||
uses: actions/cache@v5
|
||||
with:
|
||||
path: /usr/local/lib/libgpac*
|
||||
key: gpac-v2.4.0-ubuntu22
|
||||
|
||||
- name: Build and install GPAC
|
||||
if: steps.should_build.outputs.should_build == 'true' && steps.cache-gpac.outputs.cache-hit != 'true'
|
||||
run: |
|
||||
git clone -b v2.4.0 --depth 1 https://github.com/gpac/gpac
|
||||
cd gpac
|
||||
./configure
|
||||
make -j$(nproc) lib
|
||||
sudo make install-lib
|
||||
sudo ldconfig
|
||||
|
||||
- name: Update library cache
|
||||
if: steps.should_build.outputs.should_build == 'true'
|
||||
run: sudo ldconfig
|
||||
|
||||
- name: Build AppImage
|
||||
if: steps.should_build.outputs.should_build == 'true'
|
||||
run: |
|
||||
cd linux
|
||||
chmod +x build_appimage.sh
|
||||
BUILD_TYPE=${{ matrix.build_type }} ./build_appimage.sh
|
||||
|
||||
- name: Get AppImage name
|
||||
if: steps.should_build.outputs.should_build == 'true'
|
||||
id: appimage_name
|
||||
run: |
|
||||
case "${{ matrix.build_type }}" in
|
||||
minimal)
|
||||
echo "name=ccextractor-minimal-x86_64.AppImage" >> $GITHUB_OUTPUT
|
||||
;;
|
||||
ocr)
|
||||
echo "name=ccextractor-x86_64.AppImage" >> $GITHUB_OUTPUT
|
||||
;;
|
||||
hardsubx)
|
||||
echo "name=ccextractor-hardsubx-x86_64.AppImage" >> $GITHUB_OUTPUT
|
||||
;;
|
||||
esac
|
||||
|
||||
- name: Test AppImage
|
||||
if: steps.should_build.outputs.should_build == 'true'
|
||||
run: |
|
||||
chmod +x linux/${{ steps.appimage_name.outputs.name }}
|
||||
linux/${{ steps.appimage_name.outputs.name }} --version
|
||||
|
||||
- name: Upload AppImage artifact
|
||||
if: steps.should_build.outputs.should_build == 'true'
|
||||
uses: actions/upload-artifact@v6
|
||||
with:
|
||||
name: ${{ steps.appimage_name.outputs.name }}
|
||||
path: linux/${{ steps.appimage_name.outputs.name }}
|
||||
|
||||
- name: Upload to Release
|
||||
if: steps.should_build.outputs.should_build == 'true' && github.event_name == 'release'
|
||||
uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
files: linux/${{ steps.appimage_name.outputs.name }}
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
283
.github/workflows/build_deb.yml
vendored
Normal file
283
.github/workflows/build_deb.yml
vendored
Normal file
@@ -0,0 +1,283 @@
|
||||
name: Build Linux .deb Package
|
||||
|
||||
on:
|
||||
# Build on releases
|
||||
release:
|
||||
types: [published]
|
||||
# Allow manual trigger
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
build_type:
|
||||
description: 'Build type (all, basic, hardsubx)'
|
||||
required: false
|
||||
default: 'all'
|
||||
# Build on pushes to workflow file for testing
|
||||
push:
|
||||
paths:
|
||||
- '.github/workflows/build_deb.yml'
|
||||
|
||||
jobs:
|
||||
build-deb:
|
||||
runs-on: ubuntu-24.04
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
build_type: [basic, hardsubx]
|
||||
|
||||
steps:
|
||||
- name: Check if should build this variant
|
||||
id: should_build
|
||||
run: |
|
||||
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
|
||||
INPUT_TYPE="${{ github.event.inputs.build_type }}"
|
||||
if [ "$INPUT_TYPE" = "all" ] || [ "$INPUT_TYPE" = "${{ matrix.build_type }}" ]; then
|
||||
echo "should_build=true" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "should_build=false" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
else
|
||||
echo "should_build=true" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
- name: Checkout repository
|
||||
if: steps.should_build.outputs.should_build == 'true'
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Get version
|
||||
if: steps.should_build.outputs.should_build == 'true'
|
||||
id: version
|
||||
run: |
|
||||
# Extract version from source or use tag
|
||||
if [ "${{ github.event_name }}" = "release" ]; then
|
||||
VERSION="${{ github.event.release.tag_name }}"
|
||||
VERSION="${VERSION#v}" # Remove 'v' prefix if present
|
||||
else
|
||||
# Extract version from lib_ccx.h (e.g., #define VERSION "0.96.5")
|
||||
VERSION=$(grep -oP '#define VERSION "\K[^"]+' src/lib_ccx/lib_ccx.h || echo "0.96")
|
||||
fi
|
||||
echo "version=$VERSION" >> $GITHUB_OUTPUT
|
||||
echo "Building version: $VERSION"
|
||||
|
||||
- name: Install base dependencies
|
||||
if: steps.should_build.outputs.should_build == 'true'
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y --no-install-recommends \
|
||||
build-essential \
|
||||
cmake \
|
||||
pkg-config \
|
||||
zlib1g-dev \
|
||||
libpng-dev \
|
||||
libjpeg-dev \
|
||||
libfreetype-dev \
|
||||
libxml2-dev \
|
||||
libcurl4-gnutls-dev \
|
||||
libssl-dev \
|
||||
clang \
|
||||
libclang-dev \
|
||||
tesseract-ocr \
|
||||
libtesseract-dev \
|
||||
libleptonica-dev \
|
||||
patchelf
|
||||
|
||||
- name: Install FFmpeg dependencies (HardSubX)
|
||||
if: steps.should_build.outputs.should_build == 'true' && matrix.build_type == 'hardsubx'
|
||||
run: |
|
||||
sudo apt-get install -y --no-install-recommends \
|
||||
libavcodec-dev \
|
||||
libavformat-dev \
|
||||
libavutil-dev \
|
||||
libswscale-dev \
|
||||
libswresample-dev \
|
||||
libavfilter-dev \
|
||||
libavdevice-dev
|
||||
|
||||
- name: Install Rust toolchain
|
||||
if: steps.should_build.outputs.should_build == 'true'
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
|
||||
- name: Cache GPAC build
|
||||
if: steps.should_build.outputs.should_build == 'true'
|
||||
id: cache-gpac
|
||||
uses: actions/cache@v5
|
||||
with:
|
||||
path: ~/gpac-install
|
||||
key: gpac-abi-16.4-ubuntu24-deb
|
||||
|
||||
- name: Build GPAC
|
||||
if: steps.should_build.outputs.should_build == 'true' && steps.cache-gpac.outputs.cache-hit != 'true'
|
||||
run: |
|
||||
git clone -b abi-16.4 --depth 1 https://github.com/gpac/gpac
|
||||
cd gpac
|
||||
./configure --prefix=/usr
|
||||
make -j$(nproc)
|
||||
make DESTDIR=$HOME/gpac-install install-lib
|
||||
|
||||
- name: Install GPAC to system
|
||||
if: steps.should_build.outputs.should_build == 'true'
|
||||
run: |
|
||||
sudo cp -r $HOME/gpac-install/usr/lib/* /usr/lib/
|
||||
sudo cp -r $HOME/gpac-install/usr/include/* /usr/include/
|
||||
sudo ldconfig
|
||||
|
||||
- name: Build CCExtractor
|
||||
if: steps.should_build.outputs.should_build == 'true'
|
||||
run: |
|
||||
mkdir build && cd build
|
||||
if [ "${{ matrix.build_type }}" = "hardsubx" ]; then
|
||||
cmake ../src -DCMAKE_BUILD_TYPE=Release -DWITH_OCR=ON -DWITH_HARDSUBX=ON
|
||||
else
|
||||
cmake ../src -DCMAKE_BUILD_TYPE=Release -DWITH_OCR=ON
|
||||
fi
|
||||
make -j$(nproc)
|
||||
|
||||
- name: Test build
|
||||
if: steps.should_build.outputs.should_build == 'true'
|
||||
run: ./build/ccextractor --version
|
||||
|
||||
- name: Create .deb package structure
|
||||
if: steps.should_build.outputs.should_build == 'true'
|
||||
run: |
|
||||
VERSION="${{ steps.version.outputs.version }}"
|
||||
VARIANT="${{ matrix.build_type }}"
|
||||
|
||||
if [ "$VARIANT" = "basic" ]; then
|
||||
PKG_NAME="ccextractor_${VERSION}_amd64"
|
||||
else
|
||||
PKG_NAME="ccextractor-${VARIANT}_${VERSION}_amd64"
|
||||
fi
|
||||
|
||||
mkdir -p ${PKG_NAME}/DEBIAN
|
||||
mkdir -p ${PKG_NAME}/usr/bin
|
||||
mkdir -p ${PKG_NAME}/usr/lib/ccextractor
|
||||
mkdir -p ${PKG_NAME}/usr/share/doc/ccextractor
|
||||
mkdir -p ${PKG_NAME}/usr/share/man/man1
|
||||
|
||||
# Copy binary
|
||||
cp build/ccextractor ${PKG_NAME}/usr/bin/
|
||||
|
||||
# Copy GPAC library
|
||||
cp $HOME/gpac-install/usr/lib/libgpac.so* ${PKG_NAME}/usr/lib/ccextractor/
|
||||
|
||||
# Set rpath so ccextractor finds bundled libgpac
|
||||
patchelf --set-rpath '/usr/lib/ccextractor:$ORIGIN/../lib/ccextractor' ${PKG_NAME}/usr/bin/ccextractor
|
||||
|
||||
# Copy documentation
|
||||
cp docs/CHANGES.TXT ${PKG_NAME}/usr/share/doc/ccextractor/changelog
|
||||
cp LICENSE.txt ${PKG_NAME}/usr/share/doc/ccextractor/copyright
|
||||
gzip -9 -n ${PKG_NAME}/usr/share/doc/ccextractor/changelog
|
||||
|
||||
# Generate man page
|
||||
help2man --no-info --name="closed captions and teletext subtitle extractor" \
|
||||
./build/ccextractor > ${PKG_NAME}/usr/share/man/man1/ccextractor.1 2>/dev/null || true
|
||||
if [ -f ${PKG_NAME}/usr/share/man/man1/ccextractor.1 ]; then
|
||||
gzip -9 -n ${PKG_NAME}/usr/share/man/man1/ccextractor.1
|
||||
fi
|
||||
|
||||
# Create control file
|
||||
if [ "$VARIANT" = "basic" ]; then
|
||||
PKG_DESCRIPTION="CCExtractor - closed captions and teletext subtitle extractor"
|
||||
else
|
||||
PKG_DESCRIPTION="CCExtractor (with HardSubX) - closed captions and teletext subtitle extractor"
|
||||
fi
|
||||
|
||||
INSTALLED_SIZE=$(du -sk ${PKG_NAME}/usr | cut -f1)
|
||||
|
||||
# Determine dependencies based on build variant (Ubuntu 24.04)
|
||||
if [ "$VARIANT" = "hardsubx" ]; then
|
||||
DEPENDS="libc6, libtesseract5, liblept5, libcurl3t64-gnutls, libavcodec60, libavformat60, libavutil58, libswscale7, libavdevice60, libswresample4, libavfilter9"
|
||||
else
|
||||
DEPENDS="libc6, libtesseract5, liblept5, libcurl3t64-gnutls"
|
||||
fi
|
||||
|
||||
cat > ${PKG_NAME}/DEBIAN/control << CTRL
|
||||
Package: ccextractor
|
||||
Version: ${VERSION}
|
||||
Section: utils
|
||||
Priority: optional
|
||||
Architecture: amd64
|
||||
Installed-Size: ${INSTALLED_SIZE}
|
||||
Depends: ${DEPENDS}
|
||||
Maintainer: CCExtractor Development Team <carlos@ccextractor.org>
|
||||
Homepage: https://www.ccextractor.org
|
||||
Description: ${PKG_DESCRIPTION}
|
||||
CCExtractor is a tool that extracts closed captions and teletext subtitles
|
||||
from video files and streams. It supports a wide variety of input formats
|
||||
including MPEG, H.264/AVC, H.265/HEVC, MP4, MKV, WTV, and transport streams.
|
||||
.
|
||||
This package includes a bundled GPAC library for MP4 support.
|
||||
CTRL
|
||||
|
||||
# Remove leading spaces from control file
|
||||
sed -i 's/^ //' ${PKG_NAME}/DEBIAN/control
|
||||
|
||||
# Create postinst to update library cache
|
||||
cat > ${PKG_NAME}/DEBIAN/postinst << 'POSTINST'
|
||||
#!/bin/sh
|
||||
set -e
|
||||
ldconfig
|
||||
POSTINST
|
||||
chmod 755 ${PKG_NAME}/DEBIAN/postinst
|
||||
|
||||
# Create postrm to update library cache
|
||||
cat > ${PKG_NAME}/DEBIAN/postrm << 'POSTRM'
|
||||
#!/bin/sh
|
||||
set -e
|
||||
ldconfig
|
||||
POSTRM
|
||||
chmod 755 ${PKG_NAME}/DEBIAN/postrm
|
||||
|
||||
# Set permissions
|
||||
chmod 755 ${PKG_NAME}/usr/bin/ccextractor
|
||||
chmod 755 ${PKG_NAME}/usr/lib/ccextractor
|
||||
find ${PKG_NAME}/usr/lib/ccextractor -name "*.so*" -exec chmod 644 {} \;
|
||||
|
||||
# Build the .deb
|
||||
dpkg-deb --build --root-owner-group ${PKG_NAME}
|
||||
|
||||
echo "deb_name=${PKG_NAME}.deb" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Test .deb package
|
||||
if: steps.should_build.outputs.should_build == 'true'
|
||||
run: |
|
||||
VERSION="${{ steps.version.outputs.version }}"
|
||||
VARIANT="${{ matrix.build_type }}"
|
||||
|
||||
if [ "$VARIANT" = "basic" ]; then
|
||||
PKG_NAME="ccextractor_${VERSION}_amd64"
|
||||
else
|
||||
PKG_NAME="ccextractor-${VARIANT}_${VERSION}_amd64"
|
||||
fi
|
||||
|
||||
# Install and test (apt handles dependencies automatically)
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y ./${PKG_NAME}.deb
|
||||
ccextractor --version
|
||||
|
||||
- name: Get .deb filename
|
||||
if: steps.should_build.outputs.should_build == 'true'
|
||||
id: deb_name
|
||||
run: |
|
||||
VERSION="${{ steps.version.outputs.version }}"
|
||||
VARIANT="${{ matrix.build_type }}"
|
||||
|
||||
if [ "$VARIANT" = "basic" ]; then
|
||||
echo "name=ccextractor_${VERSION}_amd64.deb" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "name=ccextractor-${VARIANT}_${VERSION}_amd64.deb" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
- name: Upload .deb artifact
|
||||
if: steps.should_build.outputs.should_build == 'true'
|
||||
uses: actions/upload-artifact@v6
|
||||
with:
|
||||
name: ${{ steps.deb_name.outputs.name }}
|
||||
path: ${{ steps.deb_name.outputs.name }}
|
||||
|
||||
- name: Upload to Release
|
||||
if: steps.should_build.outputs.should_build == 'true' && github.event_name == 'release'
|
||||
uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
files: ${{ steps.deb_name.outputs.name }}
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
275
.github/workflows/build_deb_debian13.yml
vendored
Normal file
275
.github/workflows/build_deb_debian13.yml
vendored
Normal file
@@ -0,0 +1,275 @@
|
||||
name: Build Debian 13 .deb Package
|
||||
|
||||
on:
|
||||
# Build on releases
|
||||
release:
|
||||
types: [published]
|
||||
# Allow manual trigger
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
build_type:
|
||||
description: 'Build type (all, basic, hardsubx)'
|
||||
required: false
|
||||
default: 'all'
|
||||
# Build on pushes to workflow file for testing
|
||||
push:
|
||||
paths:
|
||||
- '.github/workflows/build_deb_debian13.yml'
|
||||
|
||||
jobs:
|
||||
build-deb:
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: debian:trixie
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
build_type: [basic, hardsubx]
|
||||
|
||||
steps:
|
||||
- name: Check if should build this variant
|
||||
id: should_build
|
||||
run: |
|
||||
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
|
||||
INPUT_TYPE="${{ github.event.inputs.build_type }}"
|
||||
if [ "$INPUT_TYPE" = "all" ] || [ "$INPUT_TYPE" = "${{ matrix.build_type }}" ]; then
|
||||
echo "should_build=true" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "should_build=false" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
else
|
||||
echo "should_build=true" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
- name: Install git and dependencies for checkout
|
||||
if: steps.should_build.outputs.should_build == 'true'
|
||||
run: |
|
||||
apt-get update
|
||||
apt-get install -y git ca-certificates
|
||||
|
||||
- name: Checkout repository
|
||||
if: steps.should_build.outputs.should_build == 'true'
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Get version
|
||||
if: steps.should_build.outputs.should_build == 'true'
|
||||
id: version
|
||||
run: |
|
||||
# Extract version from source or use tag
|
||||
if [ "${{ github.event_name }}" = "release" ]; then
|
||||
VERSION="${{ github.event.release.tag_name }}"
|
||||
VERSION="${VERSION#v}" # Remove 'v' prefix if present
|
||||
else
|
||||
# Extract version from lib_ccx.h (e.g., #define VERSION "0.96.5")
|
||||
VERSION=$(grep -oP '#define VERSION "\K[^"]+' src/lib_ccx/lib_ccx.h || echo "0.96")
|
||||
fi
|
||||
echo "version=$VERSION" >> $GITHUB_OUTPUT
|
||||
echo "Building version: $VERSION"
|
||||
|
||||
- name: Install base dependencies
|
||||
if: steps.should_build.outputs.should_build == 'true'
|
||||
run: |
|
||||
apt-get install -y --no-install-recommends \
|
||||
build-essential \
|
||||
cmake \
|
||||
pkg-config \
|
||||
zlib1g-dev \
|
||||
libpng-dev \
|
||||
libjpeg-dev \
|
||||
libfreetype-dev \
|
||||
libxml2-dev \
|
||||
libcurl4-gnutls-dev \
|
||||
libssl-dev \
|
||||
clang \
|
||||
libclang-dev \
|
||||
tesseract-ocr \
|
||||
libtesseract-dev \
|
||||
libleptonica-dev \
|
||||
patchelf \
|
||||
curl
|
||||
|
||||
- name: Install FFmpeg dependencies (HardSubX)
|
||||
if: steps.should_build.outputs.should_build == 'true' && matrix.build_type == 'hardsubx'
|
||||
run: |
|
||||
apt-get install -y --no-install-recommends \
|
||||
libavcodec-dev \
|
||||
libavformat-dev \
|
||||
libavutil-dev \
|
||||
libswscale-dev \
|
||||
libswresample-dev \
|
||||
libavfilter-dev \
|
||||
libavdevice-dev
|
||||
|
||||
- name: Install Rust toolchain
|
||||
if: steps.should_build.outputs.should_build == 'true'
|
||||
run: |
|
||||
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
|
||||
echo "$HOME/.cargo/bin" >> $GITHUB_PATH
|
||||
|
||||
- name: Build GPAC
|
||||
if: steps.should_build.outputs.should_build == 'true'
|
||||
run: |
|
||||
git clone -b abi-16.4 --depth 1 https://github.com/gpac/gpac
|
||||
cd gpac
|
||||
./configure --prefix=/usr
|
||||
make -j$(nproc)
|
||||
make install-lib
|
||||
ldconfig
|
||||
|
||||
- name: Build CCExtractor
|
||||
if: steps.should_build.outputs.should_build == 'true'
|
||||
run: |
|
||||
export PATH="$HOME/.cargo/bin:$PATH"
|
||||
mkdir build && cd build
|
||||
if [ "${{ matrix.build_type }}" = "hardsubx" ]; then
|
||||
cmake ../src -DCMAKE_BUILD_TYPE=Release -DWITH_OCR=ON -DWITH_HARDSUBX=ON
|
||||
else
|
||||
cmake ../src -DCMAKE_BUILD_TYPE=Release -DWITH_OCR=ON
|
||||
fi
|
||||
make -j$(nproc)
|
||||
|
||||
- name: Test build
|
||||
if: steps.should_build.outputs.should_build == 'true'
|
||||
run: ./build/ccextractor --version
|
||||
|
||||
- name: Create .deb package structure
|
||||
if: steps.should_build.outputs.should_build == 'true'
|
||||
id: create_deb
|
||||
run: |
|
||||
VERSION="${{ steps.version.outputs.version }}"
|
||||
VARIANT="${{ matrix.build_type }}"
|
||||
|
||||
if [ "$VARIANT" = "basic" ]; then
|
||||
PKG_NAME="ccextractor_${VERSION}_debian13_amd64"
|
||||
else
|
||||
PKG_NAME="ccextractor-${VARIANT}_${VERSION}_debian13_amd64"
|
||||
fi
|
||||
|
||||
mkdir -p ${PKG_NAME}/DEBIAN
|
||||
mkdir -p ${PKG_NAME}/usr/bin
|
||||
mkdir -p ${PKG_NAME}/usr/lib/ccextractor
|
||||
mkdir -p ${PKG_NAME}/usr/share/doc/ccextractor
|
||||
mkdir -p ${PKG_NAME}/usr/share/man/man1
|
||||
|
||||
# Copy binary
|
||||
cp build/ccextractor ${PKG_NAME}/usr/bin/
|
||||
|
||||
# Copy GPAC library
|
||||
cp /usr/lib/libgpac.so* ${PKG_NAME}/usr/lib/ccextractor/
|
||||
|
||||
# Set rpath so ccextractor finds bundled libgpac
|
||||
patchelf --set-rpath '/usr/lib/ccextractor:$ORIGIN/../lib/ccextractor' ${PKG_NAME}/usr/bin/ccextractor
|
||||
|
||||
# Copy documentation
|
||||
cp docs/CHANGES.TXT ${PKG_NAME}/usr/share/doc/ccextractor/changelog
|
||||
cp LICENSE.txt ${PKG_NAME}/usr/share/doc/ccextractor/copyright
|
||||
gzip -9 -n ${PKG_NAME}/usr/share/doc/ccextractor/changelog
|
||||
|
||||
# Create control file
|
||||
if [ "$VARIANT" = "basic" ]; then
|
||||
PKG_DESCRIPTION="CCExtractor - closed captions and teletext subtitle extractor"
|
||||
else
|
||||
PKG_DESCRIPTION="CCExtractor (with HardSubX) - closed captions and teletext subtitle extractor"
|
||||
fi
|
||||
|
||||
INSTALLED_SIZE=$(du -sk ${PKG_NAME}/usr | cut -f1)
|
||||
|
||||
# Determine dependencies based on build variant (Debian 13 Trixie)
|
||||
if [ "$VARIANT" = "hardsubx" ]; then
|
||||
DEPENDS="libc6, libtesseract5, libleptonica6, libcurl3t64-gnutls, libavcodec61, libavformat61, libavutil59, libswscale8, libavdevice61, libswresample5, libavfilter10"
|
||||
else
|
||||
DEPENDS="libc6, libtesseract5, libleptonica6, libcurl3t64-gnutls"
|
||||
fi
|
||||
|
||||
cat > ${PKG_NAME}/DEBIAN/control << CTRL
|
||||
Package: ccextractor
|
||||
Version: ${VERSION}
|
||||
Section: utils
|
||||
Priority: optional
|
||||
Architecture: amd64
|
||||
Installed-Size: ${INSTALLED_SIZE}
|
||||
Depends: ${DEPENDS}
|
||||
Maintainer: CCExtractor Development Team <carlos@ccextractor.org>
|
||||
Homepage: https://www.ccextractor.org
|
||||
Description: ${PKG_DESCRIPTION}
|
||||
CCExtractor is a tool that extracts closed captions and teletext subtitles
|
||||
from video files and streams. It supports a wide variety of input formats
|
||||
including MPEG, H.264/AVC, H.265/HEVC, MP4, MKV, WTV, and transport streams.
|
||||
.
|
||||
This package includes a bundled GPAC library for MP4 support.
|
||||
Built for Debian 13 (Trixie).
|
||||
CTRL
|
||||
|
||||
# Remove leading spaces from control file
|
||||
sed -i 's/^ //' ${PKG_NAME}/DEBIAN/control
|
||||
|
||||
# Create postinst to update library cache
|
||||
cat > ${PKG_NAME}/DEBIAN/postinst << 'POSTINST'
|
||||
#!/bin/sh
|
||||
set -e
|
||||
ldconfig
|
||||
POSTINST
|
||||
chmod 755 ${PKG_NAME}/DEBIAN/postinst
|
||||
|
||||
# Create postrm to update library cache
|
||||
cat > ${PKG_NAME}/DEBIAN/postrm << 'POSTRM'
|
||||
#!/bin/sh
|
||||
set -e
|
||||
ldconfig
|
||||
POSTRM
|
||||
chmod 755 ${PKG_NAME}/DEBIAN/postrm
|
||||
|
||||
# Set permissions
|
||||
chmod 755 ${PKG_NAME}/usr/bin/ccextractor
|
||||
chmod 755 ${PKG_NAME}/usr/lib/ccextractor
|
||||
find ${PKG_NAME}/usr/lib/ccextractor -name "*.so*" -exec chmod 644 {} \;
|
||||
|
||||
# Build the .deb
|
||||
dpkg-deb --build --root-owner-group ${PKG_NAME}
|
||||
|
||||
echo "deb_name=${PKG_NAME}.deb" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Test .deb package
|
||||
if: steps.should_build.outputs.should_build == 'true'
|
||||
run: |
|
||||
VERSION="${{ steps.version.outputs.version }}"
|
||||
VARIANT="${{ matrix.build_type }}"
|
||||
|
||||
if [ "$VARIANT" = "basic" ]; then
|
||||
PKG_NAME="ccextractor_${VERSION}_debian13_amd64"
|
||||
else
|
||||
PKG_NAME="ccextractor-${VARIANT}_${VERSION}_debian13_amd64"
|
||||
fi
|
||||
|
||||
# Install and test (apt handles dependencies automatically)
|
||||
apt-get update
|
||||
apt-get install -y ./${PKG_NAME}.deb
|
||||
ccextractor --version
|
||||
|
||||
- name: Get .deb filename
|
||||
if: steps.should_build.outputs.should_build == 'true'
|
||||
id: deb_name
|
||||
run: |
|
||||
VERSION="${{ steps.version.outputs.version }}"
|
||||
VARIANT="${{ matrix.build_type }}"
|
||||
|
||||
if [ "$VARIANT" = "basic" ]; then
|
||||
echo "name=ccextractor_${VERSION}_debian13_amd64.deb" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "name=ccextractor-${VARIANT}_${VERSION}_debian13_amd64.deb" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
- name: Upload .deb artifact
|
||||
if: steps.should_build.outputs.should_build == 'true'
|
||||
uses: actions/upload-artifact@v6
|
||||
with:
|
||||
name: ${{ steps.deb_name.outputs.name }}
|
||||
path: ${{ steps.deb_name.outputs.name }}
|
||||
|
||||
- name: Upload to Release
|
||||
if: steps.should_build.outputs.should_build == 'true' && github.event_name == 'release'
|
||||
uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
files: ${{ steps.deb_name.outputs.name }}
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
96
.github/workflows/build_docker.yml
vendored
Normal file
96
.github/workflows/build_docker.yml
vendored
Normal file
@@ -0,0 +1,96 @@
|
||||
name: Build CCExtractor Docker Images
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
paths:
|
||||
- '.github/workflows/build_docker.yml'
|
||||
- 'docker/**'
|
||||
- '**.c'
|
||||
- '**.h'
|
||||
- '**CMakeLists.txt'
|
||||
- '**.cmake'
|
||||
- 'src/rust/**'
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened]
|
||||
paths:
|
||||
- '.github/workflows/build_docker.yml'
|
||||
- 'docker/**'
|
||||
- '**.c'
|
||||
- '**.h'
|
||||
- '**CMakeLists.txt'
|
||||
- '**.cmake'
|
||||
- 'src/rust/**'
|
||||
|
||||
jobs:
|
||||
build_minimal:
|
||||
name: Docker build (minimal)
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
- name: Build minimal image
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: .
|
||||
file: docker/Dockerfile
|
||||
build-args: |
|
||||
BUILD_TYPE=minimal
|
||||
USE_LOCAL_SOURCE=1
|
||||
tags: ccextractor:minimal
|
||||
load: true
|
||||
cache-from: type=gha,scope=docker-minimal
|
||||
cache-to: type=gha,mode=max,scope=docker-minimal
|
||||
- name: Test minimal image
|
||||
run: |
|
||||
docker run --rm ccextractor:minimal --version
|
||||
echo "Minimal build successful"
|
||||
|
||||
build_ocr:
|
||||
name: Docker build (ocr)
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
- name: Build OCR image
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: .
|
||||
file: docker/Dockerfile
|
||||
build-args: |
|
||||
BUILD_TYPE=ocr
|
||||
USE_LOCAL_SOURCE=1
|
||||
tags: ccextractor:ocr
|
||||
load: true
|
||||
cache-from: type=gha,scope=docker-ocr
|
||||
cache-to: type=gha,mode=max,scope=docker-ocr
|
||||
- name: Test OCR image
|
||||
run: |
|
||||
docker run --rm ccextractor:ocr --version
|
||||
echo "OCR build successful"
|
||||
|
||||
build_hardsubx:
|
||||
name: Docker build (hardsubx)
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
- name: Build HardSubX image
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: .
|
||||
file: docker/Dockerfile
|
||||
build-args: |
|
||||
BUILD_TYPE=hardsubx
|
||||
USE_LOCAL_SOURCE=1
|
||||
tags: ccextractor:hardsubx
|
||||
load: true
|
||||
cache-from: type=gha,scope=docker-hardsubx
|
||||
cache-to: type=gha,mode=max,scope=docker-hardsubx
|
||||
- name: Test HardSubX image
|
||||
run: |
|
||||
docker run --rm ccextractor:hardsubx --version
|
||||
echo "HardSubX build successful"
|
||||
18
.github/workflows/build_linux.yml
vendored
18
.github/workflows/build_linux.yml
vendored
@@ -7,6 +7,8 @@ on:
|
||||
- '.github/workflows/build_linux.yml'
|
||||
- '**.c'
|
||||
- '**.h'
|
||||
- '**CMakeLists.txt'
|
||||
- '**.cmake'
|
||||
- '**Makefile**'
|
||||
- 'linux/**'
|
||||
- 'package_creators/**'
|
||||
@@ -17,6 +19,8 @@ on:
|
||||
- '.github/workflows/build_linux.yml'
|
||||
- '**.c'
|
||||
- '**.h'
|
||||
- '**CMakeLists.txt'
|
||||
- '**.cmake'
|
||||
- '**Makefile**'
|
||||
- 'linux/**'
|
||||
- 'package_creators/**'
|
||||
@@ -27,7 +31,7 @@ jobs:
|
||||
steps:
|
||||
- name: Install dependencies
|
||||
run: sudo apt update && sudo apt-get install libgpac-dev libtesseract-dev libavcodec-dev libavdevice-dev libx11-dev libxcb1-dev libxcb-shm0-dev
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
- name: build
|
||||
run: ./build -hardsubx
|
||||
working-directory: ./linux
|
||||
@@ -38,7 +42,7 @@ jobs:
|
||||
run: mkdir ./linux/artifacts
|
||||
- name: Copy release artifact
|
||||
run: cp ./linux/ccextractor ./linux/artifacts/
|
||||
- uses: actions/upload-artifact@v4
|
||||
- uses: actions/upload-artifact@v6
|
||||
with:
|
||||
name: CCExtractor Linux build
|
||||
path: ./linux/artifacts
|
||||
@@ -47,7 +51,7 @@ jobs:
|
||||
steps:
|
||||
- name: Install dependencies
|
||||
run: sudo apt update && sudo apt-get install libgpac-dev
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
- name: run autogen
|
||||
run: ./autogen.sh
|
||||
working-directory: ./linux
|
||||
@@ -65,7 +69,7 @@ jobs:
|
||||
steps:
|
||||
- name: Install dependencies
|
||||
run: sudo apt update && sudo apt-get install libgpac-dev
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
- name: cmake
|
||||
run: mkdir build && cd build && cmake ../src
|
||||
- name: build
|
||||
@@ -76,7 +80,7 @@ jobs:
|
||||
cmake_ocr_hardsubx:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
- name: Install dependencies
|
||||
run: sudo apt update && sudo apt install libgpac-dev libtesseract-dev libavformat-dev libavdevice-dev libswscale-dev yasm
|
||||
- name: cmake
|
||||
@@ -94,9 +98,9 @@ jobs:
|
||||
steps:
|
||||
- name: Install dependencies
|
||||
run: sudo apt update && sudo apt-get install libgpac-dev
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
- name: cache
|
||||
uses: actions/cache@v4
|
||||
uses: actions/cache@v5
|
||||
with:
|
||||
path: |
|
||||
src/rust/.cargo/registry
|
||||
|
||||
154
.github/workflows/build_linux_systemlibs.yml
vendored
Normal file
154
.github/workflows/build_linux_systemlibs.yml
vendored
Normal file
@@ -0,0 +1,154 @@
|
||||
name: Build Linux (System Libs)
|
||||
|
||||
on:
|
||||
# Build on releases
|
||||
release:
|
||||
types: [published]
|
||||
# Allow manual trigger
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
build_type:
|
||||
description: 'Build type (all, basic, hardsubx)'
|
||||
required: false
|
||||
default: 'all'
|
||||
# Build on pushes to workflow file for testing
|
||||
push:
|
||||
paths:
|
||||
- '.github/workflows/build_linux_systemlibs.yml'
|
||||
- 'linux/build'
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
jobs:
|
||||
build-systemlibs:
|
||||
runs-on: ubuntu-22.04
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
build_type: [basic, hardsubx]
|
||||
|
||||
steps:
|
||||
- name: Check if should build this variant
|
||||
id: should_build
|
||||
run: |
|
||||
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
|
||||
INPUT_TYPE="${{ github.event.inputs.build_type }}"
|
||||
if [ "$INPUT_TYPE" = "all" ] || [ "$INPUT_TYPE" = "${{ matrix.build_type }}" ]; then
|
||||
echo "should_build=true" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "should_build=false" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
else
|
||||
echo "should_build=true" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
- name: Checkout repository
|
||||
if: steps.should_build.outputs.should_build == 'true'
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Install base dependencies
|
||||
if: steps.should_build.outputs.should_build == 'true'
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y --no-install-recommends \
|
||||
build-essential \
|
||||
pkg-config \
|
||||
zlib1g-dev \
|
||||
libpng-dev \
|
||||
libfreetype-dev \
|
||||
libutf8proc-dev \
|
||||
libgpac-dev \
|
||||
libtesseract-dev \
|
||||
libleptonica-dev \
|
||||
tesseract-ocr-eng \
|
||||
clang \
|
||||
libclang-dev
|
||||
|
||||
- name: Install FFmpeg dependencies (HardSubX)
|
||||
if: steps.should_build.outputs.should_build == 'true' && matrix.build_type == 'hardsubx'
|
||||
run: |
|
||||
sudo apt-get install -y --no-install-recommends \
|
||||
libavcodec-dev \
|
||||
libavformat-dev \
|
||||
libavutil-dev \
|
||||
libswscale-dev \
|
||||
libswresample-dev \
|
||||
libavfilter-dev \
|
||||
libavdevice-dev \
|
||||
libxcb1-dev \
|
||||
libxcb-shm0-dev \
|
||||
libx11-dev \
|
||||
liblzma-dev
|
||||
|
||||
- name: Install Rust toolchain
|
||||
if: steps.should_build.outputs.should_build == 'true'
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
|
||||
- name: Build with system libraries
|
||||
if: steps.should_build.outputs.should_build == 'true'
|
||||
run: |
|
||||
cd linux
|
||||
if [ "${{ matrix.build_type }}" = "hardsubx" ]; then
|
||||
./build -system-libs -hardsubx
|
||||
else
|
||||
./build -system-libs
|
||||
fi
|
||||
|
||||
- name: Verify build
|
||||
if: steps.should_build.outputs.should_build == 'true'
|
||||
run: |
|
||||
./linux/ccextractor --version
|
||||
echo "=== Library dependencies ==="
|
||||
ldd ./linux/ccextractor | grep -E 'freetype|png|utf8proc|tesseract|leptonica' || true
|
||||
|
||||
- name: Get output name
|
||||
if: steps.should_build.outputs.should_build == 'true'
|
||||
id: output_name
|
||||
run: |
|
||||
case "${{ matrix.build_type }}" in
|
||||
basic)
|
||||
echo "name=ccextractor-linux-systemlibs-x86_64" >> $GITHUB_OUTPUT
|
||||
;;
|
||||
hardsubx)
|
||||
echo "name=ccextractor-linux-systemlibs-hardsubx-x86_64" >> $GITHUB_OUTPUT
|
||||
;;
|
||||
esac
|
||||
|
||||
- name: Package binary
|
||||
if: steps.should_build.outputs.should_build == 'true'
|
||||
run: |
|
||||
mkdir -p package
|
||||
cp linux/ccextractor package/
|
||||
# Create a simple README for the package
|
||||
cat > package/README.txt << 'EOF'
|
||||
CCExtractor - System Libraries Build
|
||||
=====================================
|
||||
|
||||
This build uses system libraries (dynamic linking).
|
||||
|
||||
Required system packages (Debian/Ubuntu):
|
||||
sudo apt install libgpac12 libtesseract5 libleptonica6 \
|
||||
libpng16-16 libfreetype6 libutf8proc3
|
||||
|
||||
For HardSubX builds, also install:
|
||||
sudo apt install libavcodec60 libavformat60 libswscale7 libavfilter9
|
||||
|
||||
Run with: ./ccextractor --help
|
||||
EOF
|
||||
tar -czvf ${{ steps.output_name.outputs.name }}.tar.gz -C package .
|
||||
|
||||
- name: Upload artifact
|
||||
if: steps.should_build.outputs.should_build == 'true'
|
||||
uses: actions/upload-artifact@v6
|
||||
with:
|
||||
name: ${{ steps.output_name.outputs.name }}
|
||||
path: ${{ steps.output_name.outputs.name }}.tar.gz
|
||||
|
||||
- name: Upload to Release
|
||||
if: steps.should_build.outputs.should_build == 'true' && github.event_name == 'release'
|
||||
uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
files: ${{ steps.output_name.outputs.name }}.tar.gz
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
99
.github/workflows/build_mac.yml
vendored
99
.github/workflows/build_mac.yml
vendored
@@ -7,6 +7,8 @@ on:
|
||||
- '.github/workflows/build_mac.yml'
|
||||
- '**.c'
|
||||
- '**.h'
|
||||
- '**CMakeLists.txt'
|
||||
- '**.cmake'
|
||||
- '**Makefile**'
|
||||
- 'mac/**'
|
||||
- 'package_creators/**'
|
||||
@@ -17,6 +19,8 @@ on:
|
||||
- '.github/workflows/build_mac.yml'
|
||||
- '**.c'
|
||||
- '**.h'
|
||||
- '**CMakeLists.txt'
|
||||
- '**.cmake'
|
||||
- '**Makefile**'
|
||||
- 'mac/**'
|
||||
- 'package_creators/**'
|
||||
@@ -27,7 +31,7 @@ jobs:
|
||||
steps:
|
||||
- name: Install dependencies
|
||||
run: brew install pkg-config autoconf automake libtool tesseract leptonica gpac
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
- name: build
|
||||
run: ./build.command
|
||||
working-directory: ./mac
|
||||
@@ -38,14 +42,27 @@ jobs:
|
||||
run: mkdir ./mac/artifacts
|
||||
- name: Copy release artifact
|
||||
run: cp ./mac/ccextractor ./mac/artifacts/
|
||||
- uses: actions/upload-artifact@v4
|
||||
- uses: actions/upload-artifact@v6
|
||||
with:
|
||||
name: CCExtractor mac build
|
||||
path: ./mac/artifacts
|
||||
build_shell_system_libs:
|
||||
# Test building with system libraries via pkg-config (for Homebrew formula compatibility)
|
||||
runs-on: macos-latest
|
||||
steps:
|
||||
- name: Install dependencies
|
||||
run: brew install pkg-config autoconf automake libtool tesseract leptonica gpac freetype libpng protobuf-c utf8proc zlib
|
||||
- uses: actions/checkout@v6
|
||||
- name: build with system libs
|
||||
run: ./build.command -system-libs
|
||||
working-directory: ./mac
|
||||
- name: Display version information
|
||||
run: ./ccextractor --version
|
||||
working-directory: ./mac
|
||||
build_autoconf:
|
||||
runs-on: macos-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
- name: Install dependencies
|
||||
run: brew install pkg-config autoconf automake libtool gpac
|
||||
- name: run autogen
|
||||
@@ -63,10 +80,10 @@ jobs:
|
||||
cmake:
|
||||
runs-on: macos-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
- name: dependencies
|
||||
run: brew install gpac
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
- name: cmake
|
||||
run: mkdir build && cd build && cmake ../src
|
||||
- name: build
|
||||
@@ -74,12 +91,76 @@ jobs:
|
||||
working-directory: build
|
||||
- name: Display version information
|
||||
run: ./build/ccextractor --version
|
||||
cmake_ocr_hardsubx:
|
||||
runs-on: macos-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- name: Install dependencies
|
||||
run: brew install pkg-config autoconf automake libtool tesseract leptonica gpac ffmpeg
|
||||
- name: cmake
|
||||
run: |
|
||||
mkdir build && cd build
|
||||
cmake -DWITH_OCR=ON -DWITH_HARDSUBX=ON ../src
|
||||
- name: build
|
||||
run: |
|
||||
make -j$(nproc)
|
||||
working-directory: build
|
||||
- name: Display version information
|
||||
run: ./build/ccextractor --version
|
||||
build_shell_hardsubx:
|
||||
# Test build.command with -hardsubx flag (burned-in subtitle extraction)
|
||||
runs-on: macos-latest
|
||||
steps:
|
||||
- name: Install dependencies
|
||||
run: brew install pkg-config autoconf automake libtool tesseract leptonica gpac ffmpeg
|
||||
- uses: actions/checkout@v6
|
||||
- name: build with hardsubx
|
||||
run: ./build.command -hardsubx
|
||||
working-directory: ./mac
|
||||
- name: Display version information
|
||||
run: ./ccextractor --version
|
||||
working-directory: ./mac
|
||||
- name: Verify hardsubx support
|
||||
run: |
|
||||
# Check that -hardsubx is recognized (will fail if not compiled in)
|
||||
./ccextractor -hardsubx --help 2>&1 | head -20 || true
|
||||
working-directory: ./mac
|
||||
build_autoconf_hardsubx:
|
||||
# Test autoconf build with HARDSUBX enabled (fixes issue #1173)
|
||||
runs-on: macos-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- name: Install dependencies
|
||||
run: brew install pkg-config autoconf automake libtool tesseract leptonica gpac ffmpeg
|
||||
- name: run autogen
|
||||
run: ./autogen.sh
|
||||
working-directory: ./mac
|
||||
- name: configure with hardsubx
|
||||
run: |
|
||||
# Set Homebrew paths for configure to find libraries
|
||||
export HOMEBREW_PREFIX="$(brew --prefix)"
|
||||
export LDFLAGS="-L${HOMEBREW_PREFIX}/lib"
|
||||
export CPPFLAGS="-I${HOMEBREW_PREFIX}/include"
|
||||
export PKG_CONFIG_PATH="${HOMEBREW_PREFIX}/lib/pkgconfig"
|
||||
./configure --enable-hardsubx --enable-ocr
|
||||
working-directory: ./mac
|
||||
- name: make
|
||||
run: make
|
||||
working-directory: ./mac
|
||||
- name: Display version information
|
||||
run: ./ccextractor --version
|
||||
working-directory: ./mac
|
||||
- name: Verify hardsubx support
|
||||
run: |
|
||||
# Check that -hardsubx is recognized
|
||||
./ccextractor -hardsubx --help 2>&1 | head -20 || true
|
||||
working-directory: ./mac
|
||||
build_rust:
|
||||
runs-on: macos-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: cache
|
||||
uses: actions/cache@v4
|
||||
- uses: actions/checkout@v6
|
||||
- name: cache
|
||||
uses: actions/cache@v5
|
||||
with:
|
||||
path: |
|
||||
src/rust/.cargo/registry
|
||||
@@ -92,5 +173,5 @@ jobs:
|
||||
toolchain: stable
|
||||
override: true
|
||||
- name: build
|
||||
run: cargo build
|
||||
run: cargo build
|
||||
working-directory: ./src/rust
|
||||
51
.github/workflows/build_snap.yml
vendored
Normal file
51
.github/workflows/build_snap.yml
vendored
Normal file
@@ -0,0 +1,51 @@
|
||||
name: Build CCExtractor Snap
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
release:
|
||||
types: [published]
|
||||
|
||||
jobs:
|
||||
build_snap:
|
||||
name: Build Snap package
|
||||
runs-on: ubuntu-22.04
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Install snapd
|
||||
run: |
|
||||
sudo apt update
|
||||
sudo apt install -y snapd
|
||||
|
||||
- name: Start snapd
|
||||
run: |
|
||||
sudo systemctl start snapd.socket
|
||||
sudo systemctl start snapd
|
||||
|
||||
- name: Install Snapcraft
|
||||
run: |
|
||||
sudo snap install core22
|
||||
sudo snap install snapcraft --classic
|
||||
|
||||
- name: Show Snapcraft version
|
||||
run: snapcraft --version
|
||||
|
||||
- name: Build snap
|
||||
run: sudo snapcraft --destructive-mode
|
||||
|
||||
- name: List generated snap
|
||||
run: ls -lh *.snap
|
||||
|
||||
- name: Upload snap as workflow artifact
|
||||
uses: actions/upload-artifact@v6
|
||||
with:
|
||||
name: CCExtractor Snap
|
||||
path: "*.snap"
|
||||
|
||||
- name: Upload snap to GitHub Release
|
||||
if: github.event_name == 'release'
|
||||
uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
files: "*.snap"
|
||||
133
.github/workflows/build_windows.yml
vendored
133
.github/workflows/build_windows.yml
vendored
@@ -3,8 +3,7 @@ name: Build CCExtractor on Windows
|
||||
env:
|
||||
RUSTFLAGS: -Ctarget-feature=+crt-static
|
||||
VCPKG_DEFAULT_TRIPLET: x64-windows-static
|
||||
VCPKG_DEFAULT_BINARY_CACHE: C:\vcpkg\.cache
|
||||
VCPKG_COMMIT: fba75d09065fcc76a25dcf386b1d00d33f5175af
|
||||
VCPKG_COMMIT: ab2977be50c702126336e5088f4836060733c899
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
@@ -13,6 +12,8 @@ on:
|
||||
- ".github/workflows/build_windows.yml"
|
||||
- "**.c"
|
||||
- "**.h"
|
||||
- "**CMakeLists.txt"
|
||||
- "**.cmake"
|
||||
- "windows/**"
|
||||
- "src/rust/**"
|
||||
pull_request:
|
||||
@@ -21,108 +22,118 @@ on:
|
||||
- ".github/workflows/build_windows.yml"
|
||||
- "**.c"
|
||||
- "**.h"
|
||||
- "**CMakeLists.txt"
|
||||
- "**.cmake"
|
||||
- "windows/**"
|
||||
- "src/rust/**"
|
||||
|
||||
jobs:
|
||||
build_release:
|
||||
build:
|
||||
runs-on: windows-2022
|
||||
steps:
|
||||
- name: Check out repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Setup MSBuild.exe
|
||||
uses: microsoft/setup-msbuild@v2.0.0
|
||||
with:
|
||||
msbuild-architecture: x64
|
||||
|
||||
# Install GPAC (fast, ~30s, not worth caching complexity)
|
||||
- name: Install gpac
|
||||
run: choco install gpac --version 2.4.0
|
||||
run: choco install gpac --version 2.4.0 --no-progress
|
||||
|
||||
# Use lukka/run-vcpkg for better caching
|
||||
- name: Setup vcpkg
|
||||
run: mkdir C:\vcpkg\.cache
|
||||
- name: Cache vcpkg
|
||||
id: cache
|
||||
uses: actions/cache@v4
|
||||
uses: lukka/run-vcpkg@v11
|
||||
id: runvcpkg
|
||||
with:
|
||||
path: |
|
||||
C:\vcpkg\.cache
|
||||
key: vcpkg-${{ runner.os }}-${{ env.VCPKG_COMMIT }}
|
||||
- name: Build vcpkg
|
||||
run: |
|
||||
git clone https://github.com/microsoft/vcpkg
|
||||
./vcpkg/bootstrap-vcpkg.bat
|
||||
- name: Install dependencies
|
||||
vcpkgGitCommitId: ${{ env.VCPKG_COMMIT }}
|
||||
vcpkgDirectory: ${{ github.workspace }}/vcpkg
|
||||
vcpkgJsonGlob: 'windows/vcpkg.json'
|
||||
|
||||
# Cache vcpkg installed packages separately for faster restores
|
||||
- name: Cache vcpkg installed packages
|
||||
id: vcpkg-installed-cache
|
||||
uses: actions/cache@v5
|
||||
with:
|
||||
path: ${{ github.workspace }}/vcpkg/installed
|
||||
key: vcpkg-installed-${{ runner.os }}-${{ env.VCPKG_COMMIT }}-${{ hashFiles('windows/vcpkg.json') }}
|
||||
restore-keys: |
|
||||
vcpkg-installed-${{ runner.os }}-${{ env.VCPKG_COMMIT }}-
|
||||
|
||||
- name: Install vcpkg dependencies
|
||||
if: steps.vcpkg-installed-cache.outputs.cache-hit != 'true'
|
||||
run: ${{ github.workspace }}/vcpkg/vcpkg.exe install --x-install-root ${{ github.workspace }}/vcpkg/installed/
|
||||
working-directory: windows
|
||||
- uses: actions-rs/toolchain@v1
|
||||
|
||||
# Cache Rust/Cargo artifacts
|
||||
- name: Cache Cargo registry
|
||||
uses: actions/cache@v5
|
||||
with:
|
||||
toolchain: stable
|
||||
override: true
|
||||
path: |
|
||||
~/.cargo/registry
|
||||
~/.cargo/git
|
||||
key: ${{ runner.os }}-cargo-registry-${{ hashFiles('**/Cargo.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-cargo-registry-
|
||||
|
||||
# Cache Cargo build artifacts - rust.bat sets CARGO_TARGET_DIR to windows/
|
||||
# which results in artifacts at windows/x86_64-pc-windows-msvc/
|
||||
- name: Cache Cargo build artifacts
|
||||
uses: actions/cache@v5
|
||||
with:
|
||||
path: ${{ github.workspace }}/windows/x86_64-pc-windows-msvc
|
||||
key: ${{ runner.os }}-cargo-build-${{ hashFiles('**/Cargo.lock') }}-${{ hashFiles('src/rust/**/*.rs') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-cargo-build-${{ hashFiles('**/Cargo.lock') }}-
|
||||
${{ runner.os }}-cargo-build-
|
||||
|
||||
- name: Setup Rust toolchain
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
|
||||
- name: Install Win 10 SDK
|
||||
uses: ilammy/msvc-dev-cmd@v1
|
||||
- name: build Release-Full
|
||||
|
||||
# Build Release-Full
|
||||
- name: Build Release-Full
|
||||
env:
|
||||
LIBCLANG_PATH: "C:\\Program Files\\LLVM\\lib"
|
||||
LLVM_CONFIG_PATH: "C:\\Program Files\\LLVM\\bin\\llvm-config"
|
||||
CARGO_TARGET_DIR: "..\\..\\windows"
|
||||
BINDGEN_EXTRA_CLANG_ARGS: -fmsc-version=0
|
||||
VCPKG_ROOT: ${{ github.workspace }}/vcpkg
|
||||
run: msbuild ccextractor.sln /p:Configuration=Release-Full /p:Platform=x64
|
||||
working-directory: ./windows
|
||||
- name: Display version information
|
||||
|
||||
- name: Display Release version information
|
||||
run: ./ccextractorwinfull.exe --version
|
||||
working-directory: ./windows/x64/Release-Full
|
||||
- uses: actions/upload-artifact@v4
|
||||
|
||||
- name: Upload Release artifact
|
||||
uses: actions/upload-artifact@v6
|
||||
with:
|
||||
name: CCExtractor Windows Release build
|
||||
path: |
|
||||
./windows/x64/Release-Full/ccextractorwinfull.exe
|
||||
./windows/x64/Release-Full/*.dll
|
||||
build_debug:
|
||||
runs-on: windows-2022
|
||||
steps:
|
||||
- name: Check out repository
|
||||
uses: actions/checkout@v4
|
||||
- name: Setup MSBuild.exe
|
||||
uses: microsoft/setup-msbuild@v2.0.0
|
||||
with:
|
||||
msbuild-architecture: x64
|
||||
- name: Install gpac
|
||||
run: choco install gpac --version 2.4.0
|
||||
- name: Setup vcpkg
|
||||
run: mkdir C:\vcpkg\.cache
|
||||
- name: Cache vcpkg
|
||||
id: cache
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
C:\vcpkg\.cache
|
||||
key: vcpkg-${{ runner.os }}-${{ env.VCPKG_COMMIT }}
|
||||
- name: Build vcpkg
|
||||
run: |
|
||||
git clone https://github.com/microsoft/vcpkg
|
||||
./vcpkg/bootstrap-vcpkg.bat
|
||||
- name: Install dependencies
|
||||
run: ${{ github.workspace }}/vcpkg/vcpkg.exe install --x-install-root ${{ github.workspace }}/vcpkg/installed/
|
||||
working-directory: windows
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: stable
|
||||
override: true
|
||||
- name: Install Win 10 SDK
|
||||
uses: ilammy/msvc-dev-cmd@v1
|
||||
- name: build Debug-Full
|
||||
|
||||
# Build Debug-Full (reuses cached Cargo artifacts)
|
||||
- name: Build Debug-Full
|
||||
env:
|
||||
LIBCLANG_PATH: "C:\\Program Files\\LLVM\\lib"
|
||||
LLVM_CONFIG_PATH: "C:\\Program Files\\LLVM\\bin\\llvm-config"
|
||||
CARGO_TARGET_DIR: "..\\..\\windows"
|
||||
BINDGEN_EXTRA_CLANG_ARGS: -fmsc-version=0
|
||||
VCPKG_ROOT: ${{ github.workspace }}/vcpkg
|
||||
run: msbuild ccextractor.sln /p:Configuration=Debug-Full /p:Platform=x64
|
||||
working-directory: ./windows
|
||||
- name: Display version information
|
||||
|
||||
- name: Display Debug version information
|
||||
continue-on-error: true
|
||||
run: ./ccextractorwinfull.exe --version
|
||||
working-directory: ./windows/x64/Debug-Full
|
||||
- uses: actions/upload-artifact@v4
|
||||
|
||||
- name: Upload Debug artifact
|
||||
uses: actions/upload-artifact@v6
|
||||
with:
|
||||
name: CCExtractor Windows Debug build
|
||||
path: |
|
||||
|
||||
6
.github/workflows/format.yml
vendored
6
.github/workflows/format.yml
vendored
@@ -19,7 +19,7 @@ jobs:
|
||||
format:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
- name: Format code
|
||||
run: |
|
||||
find src/ -type f -not -path "src/thirdparty/*" -not -path "src/lib_ccx/zvbi/*" -name '*.c' -not -path "src/GUI/icon_data.c" | xargs clang-format -i
|
||||
@@ -33,9 +33,9 @@ jobs:
|
||||
run:
|
||||
working-directory: ${{ matrix.workdir }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
- name: cache
|
||||
uses: actions/cache@v4
|
||||
uses: actions/cache@v5
|
||||
with:
|
||||
path: |
|
||||
${{ matrix.workdir }}/.cargo/registry
|
||||
|
||||
15
.github/workflows/homebrew.yml
vendored
Normal file
15
.github/workflows/homebrew.yml
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
name: Bump Homebrew Formula
|
||||
|
||||
on:
|
||||
release:
|
||||
types: [published]
|
||||
|
||||
jobs:
|
||||
homebrew:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Update Homebrew formula
|
||||
uses: dawidd6/action-homebrew-bump-formula@v7
|
||||
with:
|
||||
token: ${{ secrets.HOMEBREW_GITHUB_API_TOKEN }}
|
||||
formula: ccextractor
|
||||
136
.github/workflows/publish_chocolatey.yml
vendored
Normal file
136
.github/workflows/publish_chocolatey.yml
vendored
Normal file
@@ -0,0 +1,136 @@
|
||||
# Publish to Chocolatey Community Repository
|
||||
#
|
||||
# PREREQUISITES:
|
||||
# 1. Create a Chocolatey account at https://community.chocolatey.org/account/Register
|
||||
# 2. Get your API key from https://community.chocolatey.org/account
|
||||
# 3. Add the API key as repository secret: CHOCOLATEY_API_KEY
|
||||
#
|
||||
# Reference: https://docs.chocolatey.org/en-us/create/create-packages-quick-start
|
||||
|
||||
name: Publish to Chocolatey
|
||||
|
||||
on:
|
||||
release:
|
||||
types: [released]
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
release_tag:
|
||||
description: 'Release tag to publish (e.g., v0.96.1)'
|
||||
required: true
|
||||
type: string
|
||||
|
||||
jobs:
|
||||
publish:
|
||||
runs-on: windows-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Get version from tag
|
||||
id: version
|
||||
shell: bash
|
||||
run: |
|
||||
TAG="${{ github.event.inputs.release_tag || github.event.release.tag_name }}"
|
||||
# Strip 'v' prefix if present
|
||||
VERSION="${TAG#v}"
|
||||
echo "version=$VERSION" >> $GITHUB_OUTPUT
|
||||
echo "tag=$TAG" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Download MSI from release
|
||||
shell: pwsh
|
||||
run: |
|
||||
$version = "${{ steps.version.outputs.version }}"
|
||||
$tag = "${{ steps.version.outputs.tag }}"
|
||||
$msiUrl = "https://github.com/CCExtractor/ccextractor/releases/download/$tag/CCExtractor.$version.msi"
|
||||
|
||||
Write-Host "Downloading MSI from: $msiUrl"
|
||||
Invoke-WebRequest -Uri $msiUrl -OutFile "CCExtractor.msi"
|
||||
|
||||
# Calculate SHA256 checksum
|
||||
$hash = (Get-FileHash -Path "CCExtractor.msi" -Algorithm SHA256).Hash
|
||||
Write-Host "SHA256: $hash"
|
||||
echo "MSI_CHECKSUM=$hash" >> $env:GITHUB_ENV
|
||||
|
||||
- name: Update nuspec version
|
||||
shell: pwsh
|
||||
run: |
|
||||
$version = "${{ steps.version.outputs.version }}"
|
||||
$nuspecPath = "packaging/chocolatey/ccextractor.nuspec"
|
||||
|
||||
$content = Get-Content $nuspecPath -Raw
|
||||
$content = $content -replace '<version>.*</version>', "<version>$version</version>"
|
||||
Set-Content -Path $nuspecPath -Value $content
|
||||
|
||||
Write-Host "Updated nuspec to version $version"
|
||||
|
||||
- name: Update install script
|
||||
shell: pwsh
|
||||
run: |
|
||||
$version = "${{ steps.version.outputs.version }}"
|
||||
$tag = "${{ steps.version.outputs.tag }}"
|
||||
$checksum = $env:MSI_CHECKSUM
|
||||
$installScript = "packaging/chocolatey/tools/chocolateyInstall.ps1"
|
||||
|
||||
$content = Get-Content $installScript -Raw
|
||||
|
||||
# Update URL
|
||||
$newUrl = "https://github.com/CCExtractor/ccextractor/releases/download/$tag/CCExtractor.$version.msi"
|
||||
$content = $content -replace "url64bit\s*=\s*'[^']*'", "url64bit = '$newUrl'"
|
||||
|
||||
# Update checksum
|
||||
$content = $content -replace "checksum64\s*=\s*'[^']*'", "checksum64 = '$checksum'"
|
||||
|
||||
Set-Content -Path $installScript -Value $content
|
||||
|
||||
Write-Host "Updated install script with URL and checksum"
|
||||
|
||||
- name: Build Chocolatey package
|
||||
shell: pwsh
|
||||
run: |
|
||||
cd packaging/chocolatey
|
||||
choco pack ccextractor.nuspec
|
||||
|
||||
# List the generated package
|
||||
Get-ChildItem *.nupkg
|
||||
|
||||
- name: Test package locally
|
||||
shell: pwsh
|
||||
run: |
|
||||
cd packaging/chocolatey
|
||||
$nupkg = Get-ChildItem *.nupkg | Select-Object -First 1
|
||||
Write-Host "Testing package: $($nupkg.Name)"
|
||||
|
||||
# Install from local package
|
||||
choco install ccextractor --source="'.;https://community.chocolatey.org/api/v2/'" --yes --force
|
||||
|
||||
# Verify installation
|
||||
$ccx = Get-Command ccextractor -ErrorAction SilentlyContinue
|
||||
if ($ccx) {
|
||||
Write-Host "CCExtractor found at: $($ccx.Source)"
|
||||
& ccextractor --version
|
||||
} else {
|
||||
Write-Host "CCExtractor not found in PATH, checking Program Files..."
|
||||
$exePath = Join-Path $env:ProgramFiles "CCExtractor\ccextractor.exe"
|
||||
if (Test-Path $exePath) {
|
||||
& $exePath --version
|
||||
}
|
||||
}
|
||||
|
||||
- name: Push to Chocolatey
|
||||
shell: pwsh
|
||||
env:
|
||||
CHOCOLATEY_API_KEY: ${{ secrets.CHOCOLATEY_API_KEY }}
|
||||
run: |
|
||||
cd packaging/chocolatey
|
||||
$nupkg = Get-ChildItem *.nupkg | Select-Object -First 1
|
||||
|
||||
Write-Host "Pushing $($nupkg.Name) to Chocolatey..."
|
||||
choco push $nupkg.Name --source="https://push.chocolatey.org/" --api-key="$env:CHOCOLATEY_API_KEY"
|
||||
|
||||
Write-Host "Package submitted to Chocolatey! It may take some time to be moderated and published."
|
||||
|
||||
- name: Upload package artifact
|
||||
uses: actions/upload-artifact@v6
|
||||
with:
|
||||
name: chocolatey-package
|
||||
path: packaging/chocolatey/*.nupkg
|
||||
38
.github/workflows/publish_winget.yml
vendored
Normal file
38
.github/workflows/publish_winget.yml
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
# Publish to Windows Package Manager (winget)
|
||||
#
|
||||
# PREREQUISITES:
|
||||
# 1. CCExtractor must already have ONE version in winget-pkgs before this works
|
||||
# - Submit the initial manifest manually from packaging/winget/
|
||||
# - PR to: https://github.com/microsoft/winget-pkgs
|
||||
#
|
||||
# 2. Create a fork of microsoft/winget-pkgs under the CCExtractor organization
|
||||
# - https://github.com/CCExtractor/winget-pkgs (needs to be created)
|
||||
#
|
||||
# 3. Create a GitHub Personal Access Token (classic) with 'public_repo' scope
|
||||
# - Add as repository secret: WINGET_TOKEN
|
||||
#
|
||||
# Reference: https://github.com/vedantmgoyal9/winget-releaser
|
||||
|
||||
name: Publish to WinGet
|
||||
|
||||
on:
|
||||
release:
|
||||
types: [released]
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
release_tag:
|
||||
description: 'Release tag to publish (e.g., v0.96.1)'
|
||||
required: true
|
||||
type: string
|
||||
|
||||
jobs:
|
||||
publish:
|
||||
runs-on: windows-latest
|
||||
steps:
|
||||
- name: Publish to WinGet
|
||||
uses: vedantmgoyal9/winget-releaser@v2
|
||||
with:
|
||||
identifier: CCExtractor.CCExtractor
|
||||
installers-regex: '\.msi$' # Only use the MSI installer
|
||||
token: ${{ secrets.WINGET_TOKEN }}
|
||||
release-tag: ${{ github.event.inputs.release_tag || github.event.release.tag_name }}
|
||||
94
.github/workflows/release.yml
vendored
94
.github/workflows/release.yml
vendored
@@ -5,23 +5,64 @@ on:
|
||||
types:
|
||||
- created
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
env:
|
||||
RUSTFLAGS: -Ctarget-feature=+crt-static
|
||||
VCPKG_DEFAULT_TRIPLET: x64-windows-static
|
||||
VCPKG_DEFAULT_BINARY_CACHE: C:\vcpkg\.cache
|
||||
VCPKG_COMMIT: ab2977be50c702126336e5088f4836060733c899
|
||||
|
||||
jobs:
|
||||
build_windows:
|
||||
runs-on: windows-latest
|
||||
runs-on: windows-2022
|
||||
steps:
|
||||
- name: Check out repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v6
|
||||
- name: Get the version
|
||||
id: get_version
|
||||
run: echo ::set-output name=VERSION::${GITHUB_REF/refs\/tags\/v/}
|
||||
run: |
|
||||
# Extract version from tag, strip 'v' prefix and everything after first dash
|
||||
VERSION=${GITHUB_REF/refs\/tags\/v/}
|
||||
VERSION=${VERSION%%-*}
|
||||
# Save display version for filenames (e.g., 0.96.1)
|
||||
echo ::set-output name=DISPLAY_VERSION::$VERSION
|
||||
# Count dots to determine version format
|
||||
DOTS="${VERSION//[^.]}"
|
||||
PART_COUNT=$((${#DOTS} + 1))
|
||||
# MSI requires 4-part version (major.minor.build.revision)
|
||||
if [ "$PART_COUNT" -eq 2 ]; then
|
||||
MSI_VERSION="${VERSION}.0.0"
|
||||
elif [ "$PART_COUNT" -eq 3 ]; then
|
||||
MSI_VERSION="${VERSION}.0"
|
||||
else
|
||||
MSI_VERSION="${VERSION}"
|
||||
fi
|
||||
echo ::set-output name=VERSION::$MSI_VERSION
|
||||
shell: bash
|
||||
- name: Setup MSBuild.exe
|
||||
uses: microsoft/setup-msbuild@v2.0.0
|
||||
- name: Install llvm and clang
|
||||
uses: egor-tensin/setup-clang@v1
|
||||
with:
|
||||
version: latest
|
||||
platform: x64
|
||||
msbuild-architecture: x64
|
||||
- name: Install gpac
|
||||
run: choco install gpac --version 2.4.0
|
||||
- name: Setup vcpkg
|
||||
run: mkdir C:\vcpkg\.cache
|
||||
- name: Cache vcpkg
|
||||
id: cache
|
||||
uses: actions/cache@v5
|
||||
with:
|
||||
path: |
|
||||
C:\vcpkg\.cache
|
||||
key: vcpkg-${{ runner.os }}-${{ env.VCPKG_COMMIT }}
|
||||
- name: Build vcpkg
|
||||
run: |
|
||||
git clone https://github.com/microsoft/vcpkg
|
||||
./vcpkg/bootstrap-vcpkg.bat
|
||||
- name: Install dependencies
|
||||
run: ${{ github.workspace }}/vcpkg/vcpkg.exe install --x-install-root ${{ github.workspace }}/vcpkg/installed/
|
||||
working-directory: windows
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: stable
|
||||
@@ -34,15 +75,24 @@ jobs:
|
||||
LLVM_CONFIG_PATH: "C:\\Program Files\\LLVM\\bin\\llvm-config"
|
||||
CARGO_TARGET_DIR: "..\\..\\windows"
|
||||
BINDGEN_EXTRA_CLANG_ARGS: -fmsc-version=0
|
||||
run: msbuild ccextractor.sln /p:Configuration=Release-Full /p:Platform=Win32
|
||||
VCPKG_ROOT: ${{ github.workspace }}/vcpkg
|
||||
run: msbuild ccextractor.sln /p:Configuration=Release-Full /p:Platform=x64
|
||||
working-directory: ./windows
|
||||
- name: Copy files to directory for installer
|
||||
run: mkdir installer; cp ./Release-Full/ccextractorwinfull.exe ./installer; cp ./Release-Full/*.dll ./installer
|
||||
run: mkdir installer; cp ./x64/Release-Full/ccextractorwinfull.exe ./installer; cp ./x64/Release-Full/*.dll ./installer
|
||||
working-directory: ./windows
|
||||
- name: Download tessdata for OCR support
|
||||
run: |
|
||||
mkdir -p ./installer/tessdata
|
||||
# Download English traineddata from tessdata_fast (smaller, faster, good for most use cases)
|
||||
Invoke-WebRequest -Uri "https://github.com/tesseract-ocr/tessdata_fast/raw/main/eng.traineddata" -OutFile "./installer/tessdata/eng.traineddata"
|
||||
# Download OSD (Orientation and Script Detection) for automatic script detection
|
||||
Invoke-WebRequest -Uri "https://github.com/tesseract-ocr/tessdata_fast/raw/main/osd.traineddata" -OutFile "./installer/tessdata/osd.traineddata"
|
||||
working-directory: ./windows
|
||||
- name: install WiX
|
||||
run: dotnet tool install --global wix --version 4.0.0-preview.0 && wix extension -g add WixToolset.UI.wixext
|
||||
run: dotnet tool uninstall --global wix; dotnet tool install --global wix --version 6.0.2 && wix extension add -g WixToolset.UI.wixext/6.0.2
|
||||
- name: Make sure WiX works
|
||||
run: wix --version && wix extension -g list
|
||||
run: wix --version && wix extension list -g
|
||||
- name: Download Flutter GUI
|
||||
run: ((Invoke-WebRequest -UseBasicParsing https://api.github.com/repos/CCExtractor/ccextractorfluttergui/releases/latest).Content | ConvertFrom-Json).assets | ForEach-Object {if ($_.name -eq "windows.zip") { Invoke-WebRequest -UseBasicParsing -Uri $_.browser_download_url -OutFile windows.zip}}
|
||||
working-directory: ./windows
|
||||
@@ -50,32 +100,38 @@ jobs:
|
||||
run: ls
|
||||
working-directory: ./windows
|
||||
- name: Unzip Flutter GUI
|
||||
run: Expand-Archive -Path ./windows.zip -DestinationPath ./installer
|
||||
run: Expand-Archive -Path ./windows.zip -DestinationPath ./installer -Force
|
||||
working-directory: ./windows
|
||||
- name: Display installer folder contents
|
||||
run: Get-ChildItem -Recurse ./installer
|
||||
working-directory: ./windows
|
||||
- name: Create portable zip
|
||||
run: Compress-Archive -Path ./installer/* -DestinationPath ./CCExtractor_win_portable.zip
|
||||
run: Compress-Archive -Path ./installer/* -DestinationPath ./CCExtractor.${{ steps.get_version.outputs.DISPLAY_VERSION }}_win_portable.zip
|
||||
working-directory: ./windows
|
||||
- name: Build installer
|
||||
run: wix build -ext "$HOME\.wix\extensions\WixToolset.UI.wixext\4.0.0-preview.0\tools\WixToolset.UI.wixext.dll" -d "AppVersion=${{ steps.get_version.outputs.VERSION }}.0.0" -o CCExtractor.msi installer.wxs
|
||||
run: wix build -arch x64 -ext WixToolset.UI.wixext -d "AppVersion=${{ steps.get_version.outputs.VERSION }}" -o CCExtractor.${{ steps.get_version.outputs.DISPLAY_VERSION }}.msi installer.wxs CustomUI.wxs
|
||||
working-directory: ./windows
|
||||
- name: Upload as asset
|
||||
uses: AButler/upload-release-assets@v3.0
|
||||
with:
|
||||
files: './windows/CCExtractor.msi;./windows/CCExtractor_win_portable.zip'
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
files: './windows/CCExtractor.${{ steps.get_version.outputs.DISPLAY_VERSION }}.msi;./windows/CCExtractor.${{ steps.get_version.outputs.DISPLAY_VERSION }}_win_portable.zip'
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
create_linux_package:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
path: ./ccextractor
|
||||
- name: Get the version
|
||||
id: get_version
|
||||
run: |
|
||||
VERSION=${GITHUB_REF/refs\/tags\/v/}
|
||||
VERSION=${VERSION%%-*}
|
||||
echo ::set-output name=DISPLAY_VERSION::$VERSION
|
||||
- name: Create .tar.gz without git and windows folders
|
||||
run: tar -pczf ./ccextractor_minimal.tar.gz --exclude "ccextractor/windows" --exclude "ccextractor/.git" ccextractor
|
||||
run: tar -pczf ./ccextractor.${{ steps.get_version.outputs.DISPLAY_VERSION }}.tar.gz --exclude "ccextractor/windows" --exclude "ccextractor/.git" ccextractor
|
||||
- name: Upload as asset
|
||||
uses: AButler/upload-release-assets@v3.0
|
||||
with:
|
||||
files: './ccextractor_minimal.tar.gz'
|
||||
files: './ccextractor.${{ steps.get_version.outputs.DISPLAY_VERSION }}.tar.gz'
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
4
.github/workflows/test_rust.yml
vendored
4
.github/workflows/test_rust.yml
vendored
@@ -18,9 +18,9 @@ jobs:
|
||||
run:
|
||||
working-directory: ./src/rust
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
- name: cache
|
||||
uses: actions/cache@v4
|
||||
uses: actions/cache@v5
|
||||
with:
|
||||
path: |
|
||||
src/rust/.cargo/registry
|
||||
|
||||
11
.gitignore
vendored
11
.gitignore
vendored
@@ -17,6 +17,7 @@ CVS
|
||||
mac/ccextractor
|
||||
linux/ccextractor
|
||||
linux/depend
|
||||
linux/build_scan/
|
||||
windows/x86_64-pc-windows-msvc/**
|
||||
windows/Debug/**
|
||||
windows/Debug-OCR/**
|
||||
@@ -28,6 +29,7 @@ windows/Debug-Full/**
|
||||
windows/x64/**
|
||||
windows/ccextractor.VC.db
|
||||
build/
|
||||
build_*/
|
||||
|
||||
####
|
||||
# Python
|
||||
@@ -143,6 +145,9 @@ bazel*
|
||||
#Intellij IDEs
|
||||
.idea/
|
||||
|
||||
# Plans (local only)
|
||||
plans/
|
||||
|
||||
# Rust build and MakeFiles (and CMake files)
|
||||
src/rust/CMakeFiles/
|
||||
src/rust/CMakeCache.txt
|
||||
@@ -155,3 +160,9 @@ windows/*/debug/*
|
||||
windows/*/CACHEDIR.TAG
|
||||
windows/.rustc_info.json
|
||||
linux/configure~
|
||||
|
||||
# Plans and temporary files
|
||||
plans/
|
||||
tess.log
|
||||
**/tess.log
|
||||
ut=srt*
|
||||
|
||||
@@ -4,7 +4,7 @@ MAINTAINER = Marc Espie <espie@openbsd.org>
|
||||
CATEGORIES = multimedia
|
||||
COMMENT = closed caption subtitles extractor
|
||||
HOMEPAGE = https://ccextractor.org
|
||||
V = 0.94
|
||||
V = 0.96.5
|
||||
DISTFILES = ccextractor.${V:S/.//}-src.zip
|
||||
MASTER_SITES = ${MASTER_SITE_SOURCEFORGE:=ccextractor/}
|
||||
DISTNAME = ccextractor-$V
|
||||
|
||||
48
README.md
48
README.md
@@ -2,7 +2,6 @@
|
||||
|
||||
# CCExtractor
|
||||
|
||||
<a href="https://travis-ci.org/CCExtractor/ccextractor"><img src="https://raw.githubusercontent.com/CCExtractor/ccextractor-org-media/master/static/macOS-build-badge-logo.png" width="20"></a> [](https://travis-ci.org/CCExtractor/ccextractor)
|
||||
[](https://sampleplatform.ccextractor.org/test/master/windows)
|
||||
[](https://sampleplatform.ccextractor.org/test/master/linux)
|
||||
[](https://sourceforge.net/projects/ccextractor/)
|
||||
@@ -29,6 +28,25 @@ The core functionality is written in C. Other languages used include C++ and Pyt
|
||||
|
||||
Downloads for precompiled binaries and source code can be found [on our website](https://ccextractor.org/public/general/downloads/).
|
||||
|
||||
|
||||
### Windows Package Managers
|
||||
|
||||
**WinGet:**
|
||||
```powershell
|
||||
winget install CCExtractor.CCExtractor
|
||||
```
|
||||
|
||||
**Chocolatey:**
|
||||
```powershell
|
||||
choco install ccextractor
|
||||
```
|
||||
|
||||
**Scoop:**
|
||||
```powershell
|
||||
scoop bucket add extras
|
||||
scoop install ccextractor
|
||||
```
|
||||
|
||||
Extracting subtitles is relatively simple. Just run the following command:
|
||||
|
||||
`ccextractor <input>`
|
||||
@@ -44,6 +62,34 @@ You can also find the list of parameters and their brief description by running
|
||||
|
||||
You can find sample files on [our website](https://ccextractor.org/public/general/tvsamples/) to test the software.
|
||||
|
||||
### Building from Source
|
||||
|
||||
- [Building on Windows using WSL](docs/build-wsl.md)
|
||||
|
||||
#### Linux (Autotools) build notes
|
||||
|
||||
CCExtractor also supports an autotools-based build system under the `linux/`
|
||||
directory.
|
||||
|
||||
Important notes:
|
||||
- The autotools workflow lives inside `linux/`. The `configure` script is
|
||||
generated there and should be run from that directory.
|
||||
- Typical build steps are:
|
||||
```
|
||||
cd linux
|
||||
./autogen.sh
|
||||
./configure
|
||||
make
|
||||
```
|
||||
- Rust support is enabled automatically if `cargo` and `rustc` are available
|
||||
on the system. In that case, Rust components are built and linked during
|
||||
`make`.
|
||||
- If you encounter unexpected build or linking issues, a clean rebuild
|
||||
(`make clean` or a fresh clone) is recommended, especially when Rust is
|
||||
involved.
|
||||
|
||||
This build flow has been tested on Linux and WSL.
|
||||
|
||||
## Compiling CCExtractor
|
||||
|
||||
To learn more about how to compile and build CCExtractor for your platform check the [compilation guide](https://github.com/CCExtractor/ccextractor/blob/master/docs/COMPILATION.MD).
|
||||
|
||||
239
docker/Dockerfile
Normal file
239
docker/Dockerfile
Normal file
@@ -0,0 +1,239 @@
|
||||
# CCExtractor Docker Build
|
||||
#
|
||||
# Build variants via BUILD_TYPE argument:
|
||||
# - minimal: Basic CCExtractor without OCR
|
||||
# - ocr: CCExtractor with OCR support (default)
|
||||
# - hardsubx: CCExtractor with burned-in subtitle extraction (requires FFmpeg)
|
||||
#
|
||||
# Source options via USE_LOCAL_SOURCE argument:
|
||||
# - 0 (default): Clone from GitHub (standalone Dockerfile usage)
|
||||
# - 1: Use local source (when building from cloned repo)
|
||||
#
|
||||
# Build examples:
|
||||
#
|
||||
# # Standalone (just the Dockerfile, clones from GitHub):
|
||||
# docker build -t ccextractor docker/
|
||||
# docker build --build-arg BUILD_TYPE=hardsubx -t ccextractor docker/
|
||||
#
|
||||
# # From cloned repository (faster, uses local source):
|
||||
# docker build --build-arg USE_LOCAL_SOURCE=1 -f docker/Dockerfile -t ccextractor .
|
||||
# docker build --build-arg USE_LOCAL_SOURCE=1 --build-arg BUILD_TYPE=minimal -f docker/Dockerfile -t ccextractor .
|
||||
|
||||
ARG DEBIAN_VERSION=bookworm-slim
|
||||
|
||||
FROM debian:${DEBIAN_VERSION} AS base
|
||||
|
||||
FROM base AS builder
|
||||
|
||||
# Build arguments
|
||||
ARG BUILD_TYPE=ocr
|
||||
ARG USE_LOCAL_SOURCE=0
|
||||
# BUILD_TYPE: minimal, ocr, hardsubx
|
||||
# USE_LOCAL_SOURCE: 0 = git clone, 1 = copy local source
|
||||
|
||||
# Avoid interactive prompts during package installation
|
||||
ENV DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
# Install base build dependencies
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
git \
|
||||
curl \
|
||||
ca-certificates \
|
||||
gcc \
|
||||
g++ \
|
||||
cmake \
|
||||
make \
|
||||
pkg-config \
|
||||
bash \
|
||||
zlib1g-dev \
|
||||
libpng-dev \
|
||||
libjpeg-dev \
|
||||
libssl-dev \
|
||||
libfreetype-dev \
|
||||
libxml2-dev \
|
||||
libcurl4-gnutls-dev \
|
||||
clang \
|
||||
libclang-dev \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Install Rust toolchain
|
||||
RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain stable
|
||||
ENV PATH="/root/.cargo/bin:${PATH}"
|
||||
|
||||
# Install OCR dependencies (for ocr and hardsubx builds)
|
||||
RUN if [ "$BUILD_TYPE" = "ocr" ] || [ "$BUILD_TYPE" = "hardsubx" ]; then \
|
||||
apt-get update && apt-get install -y --no-install-recommends \
|
||||
tesseract-ocr \
|
||||
libtesseract-dev \
|
||||
libleptonica-dev \
|
||||
&& rm -rf /var/lib/apt/lists/*; \
|
||||
fi
|
||||
|
||||
# Install FFmpeg dependencies (for hardsubx build)
|
||||
RUN if [ "$BUILD_TYPE" = "hardsubx" ]; then \
|
||||
apt-get update && apt-get install -y --no-install-recommends \
|
||||
libavcodec-dev \
|
||||
libavformat-dev \
|
||||
libavutil-dev \
|
||||
libswscale-dev \
|
||||
libswresample-dev \
|
||||
libavfilter-dev \
|
||||
libavdevice-dev \
|
||||
&& rm -rf /var/lib/apt/lists/*; \
|
||||
fi
|
||||
|
||||
# Build and install GPAC library
|
||||
WORKDIR /root
|
||||
RUN git clone -b v2.4.0 --depth 1 https://github.com/gpac/gpac
|
||||
WORKDIR /root/gpac
|
||||
RUN ./configure && make -j$(nproc) lib && make install-lib && ldconfig
|
||||
WORKDIR /root
|
||||
RUN rm -rf /root/gpac
|
||||
|
||||
# Get CCExtractor source (either clone or copy based on USE_LOCAL_SOURCE)
|
||||
WORKDIR /root
|
||||
# First, copy local source if provided (will be empty dir if building standalone)
|
||||
COPY . /root/ccextractor-local/
|
||||
|
||||
# Then get source: use local copy if USE_LOCAL_SOURCE=1 and source exists,
|
||||
# otherwise clone from GitHub
|
||||
RUN if [ "$USE_LOCAL_SOURCE" = "1" ] && [ -f /root/ccextractor-local/src/ccextractor.c ]; then \
|
||||
echo "Using local source"; \
|
||||
mv /root/ccextractor-local /root/ccextractor; \
|
||||
else \
|
||||
echo "Cloning from GitHub"; \
|
||||
rm -rf /root/ccextractor-local; \
|
||||
git clone --depth 1 https://github.com/CCExtractor/ccextractor.git /root/ccextractor; \
|
||||
fi
|
||||
|
||||
WORKDIR /root/ccextractor/linux
|
||||
|
||||
# Generate build info
|
||||
RUN ./pre-build.sh
|
||||
|
||||
# Build Rust library with appropriate features
|
||||
RUN if [ "$BUILD_TYPE" = "hardsubx" ]; then \
|
||||
cd ../src/rust && \
|
||||
CARGO_TARGET_DIR=../../linux/rust cargo build --release --features hardsubx_ocr; \
|
||||
else \
|
||||
cd ../src/rust && \
|
||||
CARGO_TARGET_DIR=../../linux/rust cargo build --release; \
|
||||
fi
|
||||
|
||||
RUN cp rust/release/libccx_rust.a ./libccx_rust.a
|
||||
|
||||
# Compile CCExtractor
|
||||
RUN if [ "$BUILD_TYPE" = "minimal" ]; then \
|
||||
BLD_FLAGS="-std=gnu99 -Wno-write-strings -Wno-pointer-sign -D_FILE_OFFSET_BITS=64 -DVERSION_FILE_PRESENT -DFT2_BUILD_LIBRARY -DGPAC_DISABLE_VTT -DGPAC_DISABLE_OD_DUMP -DGPAC_DISABLE_REMOTERY -DNO_GZIP -DGPAC_64_BITS"; \
|
||||
BLD_INCLUDE="-I../src -I../src/lib_ccx/ -I /usr/include/gpac/ -I../src/thirdparty/libpng -I../src/thirdparty/zlib -I../src/lib_ccx/zvbi -I../src/thirdparty/lib_hash -I../src/thirdparty -I../src/thirdparty/freetype/include"; \
|
||||
BLD_LINKER="-lm -Wl,--allow-multiple-definition -lpthread -ldl -lgpac ./libccx_rust.a"; \
|
||||
elif [ "$BUILD_TYPE" = "hardsubx" ]; then \
|
||||
BLD_FLAGS="-std=gnu99 -Wno-write-strings -Wno-pointer-sign -D_FILE_OFFSET_BITS=64 -DVERSION_FILE_PRESENT -DENABLE_OCR -DENABLE_HARDSUBX -DFT2_BUILD_LIBRARY -DGPAC_DISABLE_VTT -DGPAC_DISABLE_OD_DUMP -DGPAC_DISABLE_REMOTERY -DNO_GZIP -DGPAC_64_BITS"; \
|
||||
BLD_INCLUDE="-I../src -I /usr/include/leptonica/ -I /usr/include/tesseract/ -I../src/lib_ccx/ -I /usr/include/gpac/ -I../src/thirdparty/libpng -I../src/thirdparty/zlib -I../src/lib_ccx/zvbi -I../src/thirdparty/lib_hash -I../src/thirdparty -I../src/thirdparty/freetype/include"; \
|
||||
BLD_LINKER="-lm -Wl,--allow-multiple-definition -ltesseract -lleptonica -lpthread -ldl -lgpac -lswscale -lavutil -lavformat -lavcodec -lavfilter -lswresample ./libccx_rust.a"; \
|
||||
else \
|
||||
BLD_FLAGS="-std=gnu99 -Wno-write-strings -Wno-pointer-sign -D_FILE_OFFSET_BITS=64 -DVERSION_FILE_PRESENT -DENABLE_OCR -DFT2_BUILD_LIBRARY -DGPAC_DISABLE_VTT -DGPAC_DISABLE_OD_DUMP -DGPAC_DISABLE_REMOTERY -DNO_GZIP -DGPAC_64_BITS"; \
|
||||
BLD_INCLUDE="-I../src -I /usr/include/leptonica/ -I /usr/include/tesseract/ -I../src/lib_ccx/ -I /usr/include/gpac/ -I../src/thirdparty/libpng -I../src/thirdparty/zlib -I../src/lib_ccx/zvbi -I../src/thirdparty/lib_hash -I../src/thirdparty -I../src/thirdparty/freetype/include"; \
|
||||
BLD_LINKER="-lm -Wl,--allow-multiple-definition -ltesseract -lleptonica -lpthread -ldl -lgpac ./libccx_rust.a"; \
|
||||
fi && \
|
||||
SRC_LIBPNG="$(find ../src/thirdparty/libpng/ -name '*.c')" && \
|
||||
SRC_ZLIB="$(find ../src/thirdparty/zlib/ -name '*.c')" && \
|
||||
SRC_CCX="$(find ../src/lib_ccx/ -name '*.c')" && \
|
||||
SRC_GPAC="$(find /usr/include/gpac/ -name '*.c' 2>/dev/null || true)" && \
|
||||
SRC_HASH="$(find ../src/thirdparty/lib_hash/ -name '*.c')" && \
|
||||
SRC_UTF8PROC="../src/thirdparty/utf8proc/utf8proc.c" && \
|
||||
SRC_FREETYPE="../src/thirdparty/freetype/autofit/autofit.c \
|
||||
../src/thirdparty/freetype/base/ftbase.c \
|
||||
../src/thirdparty/freetype/base/ftbbox.c \
|
||||
../src/thirdparty/freetype/base/ftbdf.c \
|
||||
../src/thirdparty/freetype/base/ftbitmap.c \
|
||||
../src/thirdparty/freetype/base/ftcid.c \
|
||||
../src/thirdparty/freetype/base/ftfntfmt.c \
|
||||
../src/thirdparty/freetype/base/ftfstype.c \
|
||||
../src/thirdparty/freetype/base/ftgasp.c \
|
||||
../src/thirdparty/freetype/base/ftglyph.c \
|
||||
../src/thirdparty/freetype/base/ftgxval.c \
|
||||
../src/thirdparty/freetype/base/ftinit.c \
|
||||
../src/thirdparty/freetype/base/ftlcdfil.c \
|
||||
../src/thirdparty/freetype/base/ftmm.c \
|
||||
../src/thirdparty/freetype/base/ftotval.c \
|
||||
../src/thirdparty/freetype/base/ftpatent.c \
|
||||
../src/thirdparty/freetype/base/ftpfr.c \
|
||||
../src/thirdparty/freetype/base/ftstroke.c \
|
||||
../src/thirdparty/freetype/base/ftsynth.c \
|
||||
../src/thirdparty/freetype/base/ftsystem.c \
|
||||
../src/thirdparty/freetype/base/fttype1.c \
|
||||
../src/thirdparty/freetype/base/ftwinfnt.c \
|
||||
../src/thirdparty/freetype/bdf/bdf.c \
|
||||
../src/thirdparty/freetype/bzip2/ftbzip2.c \
|
||||
../src/thirdparty/freetype/cache/ftcache.c \
|
||||
../src/thirdparty/freetype/cff/cff.c \
|
||||
../src/thirdparty/freetype/cid/type1cid.c \
|
||||
../src/thirdparty/freetype/gzip/ftgzip.c \
|
||||
../src/thirdparty/freetype/lzw/ftlzw.c \
|
||||
../src/thirdparty/freetype/pcf/pcf.c \
|
||||
../src/thirdparty/freetype/pfr/pfr.c \
|
||||
../src/thirdparty/freetype/psaux/psaux.c \
|
||||
../src/thirdparty/freetype/pshinter/pshinter.c \
|
||||
../src/thirdparty/freetype/psnames/psnames.c \
|
||||
../src/thirdparty/freetype/raster/raster.c \
|
||||
../src/thirdparty/freetype/sfnt/sfnt.c \
|
||||
../src/thirdparty/freetype/smooth/smooth.c \
|
||||
../src/thirdparty/freetype/truetype/truetype.c \
|
||||
../src/thirdparty/freetype/type1/type1.c \
|
||||
../src/thirdparty/freetype/type42/type42.c \
|
||||
../src/thirdparty/freetype/winfonts/winfnt.c" && \
|
||||
BLD_SOURCES="../src/ccextractor.c $SRC_CCX $SRC_GPAC $SRC_ZLIB $SRC_LIBPNG $SRC_HASH $SRC_UTF8PROC $SRC_FREETYPE" && \
|
||||
gcc $BLD_FLAGS $BLD_INCLUDE -o ccextractor $BLD_SOURCES $BLD_LINKER
|
||||
|
||||
# Copy binary to known location
|
||||
RUN cp /root/ccextractor/linux/ccextractor /ccextractor
|
||||
|
||||
# Final minimal image
|
||||
FROM base AS final
|
||||
|
||||
ARG BUILD_TYPE=ocr
|
||||
|
||||
# Avoid interactive prompts
|
||||
ENV DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
# Install runtime dependencies based on build type
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
libpng16-16 \
|
||||
libjpeg62-turbo \
|
||||
zlib1g \
|
||||
libssl3 \
|
||||
libcurl4 \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# OCR runtime dependencies
|
||||
RUN if [ "$BUILD_TYPE" = "ocr" ] || [ "$BUILD_TYPE" = "hardsubx" ]; then \
|
||||
apt-get update && apt-get install -y --no-install-recommends \
|
||||
tesseract-ocr \
|
||||
liblept5 \
|
||||
&& rm -rf /var/lib/apt/lists/*; \
|
||||
fi
|
||||
|
||||
# HardSubX runtime dependencies
|
||||
RUN if [ "$BUILD_TYPE" = "hardsubx" ]; then \
|
||||
apt-get update && apt-get install -y --no-install-recommends \
|
||||
libavcodec59 \
|
||||
libavformat59 \
|
||||
libavutil57 \
|
||||
libswscale6 \
|
||||
libswresample4 \
|
||||
libavfilter8 \
|
||||
libavdevice59 \
|
||||
&& rm -rf /var/lib/apt/lists/*; \
|
||||
fi
|
||||
|
||||
# Copy GPAC library from builder
|
||||
COPY --from=builder /usr/local/lib/libgpac.so* /usr/local/lib/
|
||||
|
||||
# Update library cache
|
||||
RUN ldconfig
|
||||
|
||||
# Copy CCExtractor binary
|
||||
COPY --from=builder /ccextractor /ccextractor
|
||||
|
||||
ENTRYPOINT ["/ccextractor"]
|
||||
102
docker/README.md
102
docker/README.md
@@ -1,61 +1,91 @@
|
||||
# CCExtractor Docker image
|
||||
# CCExtractor Docker Image
|
||||
|
||||
This dockerfile prepares a minimalist Docker image with CCExtractor. It compiles CCExtractor from sources following instructions from the [Compilation Guide](https://github.com/CCExtractor/ccextractor/blob/master/docs/COMPILATION.MD).
|
||||
This Dockerfile builds CCExtractor with support for multiple build variants.
|
||||
|
||||
You can install the latest build of this image by running `docker pull CCExtractor/ccextractor`
|
||||
## Build Variants
|
||||
|
||||
## Build
|
||||
| Variant | Description | Features |
|
||||
|---------|-------------|----------|
|
||||
| `minimal` | Basic CCExtractor | No OCR support |
|
||||
| `ocr` | With OCR support (default) | Tesseract OCR for bitmap subtitles |
|
||||
| `hardsubx` | With burned-in subtitle extraction | OCR + FFmpeg for hardcoded subtitles |
|
||||
|
||||
You can build the Docker image directly from the Dockerfile provided in [docker](https://github.com/CCExtractor/ccextractor/tree/master/docker) directory of CCExtractor source
|
||||
## Building
|
||||
|
||||
### Standalone Build (from Dockerfile only)
|
||||
|
||||
You can build CCExtractor using just the Dockerfile - it will clone the source from GitHub:
|
||||
|
||||
```bash
|
||||
$ git clone https://github.com/CCExtractor/ccextractor.git && cd ccextractor
|
||||
$ cd docker/
|
||||
$ docker build -t ccextractor .
|
||||
# Default build (OCR enabled)
|
||||
docker build -t ccextractor docker/
|
||||
|
||||
# Minimal build (no OCR)
|
||||
docker build --build-arg BUILD_TYPE=minimal -t ccextractor docker/
|
||||
|
||||
# HardSubX build (OCR + FFmpeg for burned-in subtitles)
|
||||
docker build --build-arg BUILD_TYPE=hardsubx -t ccextractor docker/
|
||||
```
|
||||
|
||||
### Build from Cloned Repository (faster)
|
||||
|
||||
If you have already cloned the repository, you can use local source for faster builds:
|
||||
|
||||
```bash
|
||||
git clone https://github.com/CCExtractor/ccextractor.git
|
||||
cd ccextractor
|
||||
|
||||
# Default build (OCR enabled)
|
||||
docker build --build-arg USE_LOCAL_SOURCE=1 -f docker/Dockerfile -t ccextractor .
|
||||
|
||||
# Minimal build
|
||||
docker build --build-arg USE_LOCAL_SOURCE=1 --build-arg BUILD_TYPE=minimal -f docker/Dockerfile -t ccextractor .
|
||||
|
||||
# HardSubX build
|
||||
docker build --build-arg USE_LOCAL_SOURCE=1 --build-arg BUILD_TYPE=hardsubx -f docker/Dockerfile -t ccextractor .
|
||||
```
|
||||
|
||||
## Build Arguments
|
||||
|
||||
| Argument | Default | Description |
|
||||
|----------|---------|-------------|
|
||||
| `BUILD_TYPE` | `ocr` | Build variant: `minimal`, `ocr`, or `hardsubx` |
|
||||
| `USE_LOCAL_SOURCE` | `0` | Set to `1` to use local source instead of cloning |
|
||||
| `DEBIAN_VERSION` | `bookworm-slim` | Debian version to use as base |
|
||||
|
||||
## Usage
|
||||
|
||||
The CCExtractor Docker image can be used in several ways, depending on your needs.
|
||||
### Basic Usage
|
||||
|
||||
```bash
|
||||
# General usage
|
||||
$ docker run ccextractor:latest <features>
|
||||
# Show version
|
||||
docker run --rm ccextractor --version
|
||||
|
||||
# Show help
|
||||
docker run --rm ccextractor --help
|
||||
```
|
||||
|
||||
1. Process a local file & use `-o` flag
|
||||
### Processing Local Files
|
||||
|
||||
To process a local video file, mount a directory containing the input file inside the container:
|
||||
Mount your local directory to process files:
|
||||
|
||||
```bash
|
||||
# Use `-o` to specifying output file
|
||||
$ docker run --rm -v $(pwd):$(pwd) -w $(pwd) ccextractor:latest input.mp4 -o output.srt
|
||||
# Process a video file with output file
|
||||
docker run --rm -v $(pwd):$(pwd) -w $(pwd) ccextractor input.mp4 -o output.srt
|
||||
|
||||
# Alternatively use `--stdout` feature
|
||||
$ docker run --rm -v $(pwd):$(pwd) -w $(pwd) ccextractor:latest input.mp4 --stdout > output.srt
|
||||
# Process using stdout
|
||||
docker run --rm -v $(pwd):$(pwd) -w $(pwd) ccextractor input.mp4 --stdout > output.srt
|
||||
```
|
||||
|
||||
Run this command from where your input video file is present, and change `input.mp4` & `output.srt` with the actual name of files.
|
||||
|
||||
2. Enter an interactive environment
|
||||
|
||||
If you need to run CCExtractor with additional options or perform other tasks within the container, you can enter an interactive environment:
|
||||
bash
|
||||
### Interactive Mode
|
||||
|
||||
```bash
|
||||
$ docker run --rm -it --entrypoint='sh' ccextractor:latest
|
||||
docker run --rm -it --entrypoint=/bin/bash ccextractor
|
||||
```
|
||||
|
||||
This will start a Bash shell inside the container, allowing you to run CCExtractor commands manually or perform other operations.
|
||||
## Image Size
|
||||
|
||||
### Example
|
||||
|
||||
I run help command in image built from `dockerfile`
|
||||
|
||||
```bash
|
||||
$ docker build -t ccextractor .
|
||||
$ docker run --rm ccextractor:latest --help
|
||||
```
|
||||
|
||||
This will show the `--help` message of CCExtractor tool
|
||||
From there you can see all the features and flags which can be used.
|
||||
The multi-stage build produces runtime images:
|
||||
- `minimal`: ~130MB
|
||||
- `ocr`: ~215MB (includes Tesseract)
|
||||
- `hardsubx`: ~610MB (includes Tesseract + FFmpeg)
|
||||
|
||||
@@ -1,46 +0,0 @@
|
||||
FROM alpine:latest as base
|
||||
|
||||
FROM base as builder
|
||||
|
||||
RUN apk add --no-cache --update git curl gcc cmake glew glfw \
|
||||
tesseract-ocr-dev leptonica-dev clang-dev llvm-dev make pkgconfig \
|
||||
zlib-dev libpng-dev libjpeg-turbo-dev openssl-dev freetype-dev libxml2-dev bash cargo
|
||||
|
||||
WORKDIR /root
|
||||
RUN git clone -b v2.4.0 https://github.com/gpac/gpac
|
||||
WORKDIR /root/gpac/
|
||||
RUN ./configure && make -j$(nproc) && make install-lib
|
||||
WORKDIR /root
|
||||
RUN rm -rf /root/gpac
|
||||
|
||||
RUN git clone https://github.com/CCExtractor/ccextractor.git
|
||||
WORKDIR /root/ccextractor/linux
|
||||
RUN ./pre-build.sh && ./build
|
||||
|
||||
RUN cp /root/ccextractor/linux/ccextractor /ccextractor && rm -rf ~/ccextractor
|
||||
|
||||
FROM base as final
|
||||
|
||||
COPY --from=builder /lib/ld-musl-x86_64.so.1 /lib/
|
||||
COPY --from=builder /usr/lib/libtesseract.so.5 /usr/lib/
|
||||
COPY --from=builder /usr/lib/libleptonica.so.6 /usr/lib/
|
||||
COPY --from=builder /usr/local/lib/libgpac.so.12 /usr/local/lib/
|
||||
COPY --from=builder /usr/lib/libstdc++.so.6 /usr/lib/
|
||||
COPY --from=builder /usr/lib/libgcc_s.so.1 /usr/lib/
|
||||
COPY --from=builder /usr/lib/libgomp.so.1 /usr/lib/
|
||||
COPY --from=builder /usr/lib/libpng16.so.16 /usr/lib/
|
||||
COPY --from=builder /usr/lib/libjpeg.so.8 /usr/lib/
|
||||
COPY --from=builder /usr/lib/libgif.so.7 /usr/lib/
|
||||
COPY --from=builder /usr/lib/libtiff.so.6 /usr/lib/
|
||||
COPY --from=builder /usr/lib/libwebp.so.7 /usr/lib/
|
||||
COPY --from=builder /usr/lib/libwebpmux.so.3 /usr/lib/
|
||||
COPY --from=builder /usr/lib/libz.so.1 /lib/
|
||||
COPY --from=builder /usr/lib/libssl.so.3 /lib/
|
||||
COPY --from=builder /usr/lib/libcrypto.so.3 /lib/
|
||||
COPY --from=builder /usr/lib/liblzma.so.5 /usr/lib/
|
||||
COPY --from=builder /usr/lib/libzstd.so.1 /usr/lib/
|
||||
COPY --from=builder /usr/lib/libsharpyuv.so.0 /usr/lib/
|
||||
|
||||
COPY --from=builder /ccextractor /
|
||||
|
||||
ENTRYPOINT [ "/ccextractor" ]
|
||||
157
docs/Building_macos_system_libs.md
Normal file
157
docs/Building_macos_system_libs.md
Normal file
@@ -0,0 +1,157 @@
|
||||
# Building CCExtractor on macOS using System Libraries (-system-libs)
|
||||
|
||||
## Overview
|
||||
|
||||
This document explains how to build CCExtractor on macOS using system-installed libraries instead of bundled third-party libraries.
|
||||
|
||||
This build mode is required for Homebrew compatibility and is enabled via the `-system-libs` flag introduced in PR #1862.
|
||||
|
||||
## Why is -system-libs needed?
|
||||
|
||||
### Background
|
||||
|
||||
CCExtractor was removed from Homebrew (homebrew-core) because:
|
||||
|
||||
- Homebrew does not allow bundling third-party libraries
|
||||
- The default CCExtractor build compiles libraries from `src/thirdparty/`
|
||||
- This violates Homebrew packaging policies
|
||||
|
||||
### What -system-libs fixes
|
||||
|
||||
The `-system-libs` flag allows CCExtractor to:
|
||||
|
||||
- Use system-installed libraries via Homebrew
|
||||
- Resolve headers and linker flags using `pkg-config`
|
||||
- Skip compiling bundled copies of common libraries
|
||||
|
||||
This makes CCExtractor acceptable for Homebrew packaging.
|
||||
|
||||
## Build Modes Explained
|
||||
|
||||
### 1️⃣ Default Build (Bundled Libraries)
|
||||
|
||||
**Command:**
|
||||
|
||||
```bash
|
||||
./mac/build.command
|
||||
```
|
||||
|
||||
**Behavior:**
|
||||
|
||||
- Compiles bundled libraries:
|
||||
- `freetype`
|
||||
- `libpng`
|
||||
- `zlib`
|
||||
- `utf8proc`
|
||||
- Self-contained binary
|
||||
- Larger size
|
||||
- Suitable for standalone builds
|
||||
|
||||
### 2️⃣ System Libraries Build (Homebrew-compatible)
|
||||
|
||||
**Command:**
|
||||
|
||||
```bash
|
||||
./mac/build.command -system-libs
|
||||
```
|
||||
|
||||
**Behavior:**
|
||||
|
||||
- Uses system libraries via `pkg-config`
|
||||
- Does not compile bundled libraries
|
||||
- Smaller binary
|
||||
- Faster build
|
||||
- Required for Homebrew
|
||||
|
||||
## Required Homebrew Dependencies
|
||||
|
||||
Install required dependencies:
|
||||
|
||||
```bash
|
||||
brew install pkg-config autoconf automake libtool \
|
||||
gpac freetype libpng protobuf-c utf8proc zlib
|
||||
```
|
||||
|
||||
**Optional** (OCR / HARDSUBX support):
|
||||
|
||||
```bash
|
||||
brew install tesseract leptonica ffmpeg
|
||||
```
|
||||
|
||||
## How to Build
|
||||
|
||||
```bash
|
||||
cd mac
|
||||
./build.command -system-libs
|
||||
```
|
||||
|
||||
**Verify:**
|
||||
|
||||
```bash
|
||||
./ccextractor --version
|
||||
```
|
||||
|
||||
## What Changes Internally with -system-libs
|
||||
|
||||
### Libraries NOT compiled (system-provided)
|
||||
|
||||
- **FreeType**
|
||||
- **libpng**
|
||||
- **zlib**
|
||||
- **utf8proc**
|
||||
|
||||
### Libraries STILL bundled
|
||||
|
||||
- **lib_hash** (Custom SHA-256 implementation, no system equivalent)
|
||||
|
||||
## CI Coverage
|
||||
|
||||
A new CI job was added:
|
||||
|
||||
- `build_shell_system_libs`
|
||||
|
||||
**What it does:**
|
||||
|
||||
- Installs Homebrew dependencies
|
||||
- Runs `./build.command -system-libs`
|
||||
- Verifies the binary runs correctly
|
||||
|
||||
This ensures Homebrew-compatible builds stay working.
|
||||
|
||||
## Verification (Local)
|
||||
|
||||
You can confirm system libraries are used:
|
||||
|
||||
```bash
|
||||
otool -L mac/ccextractor
|
||||
```
|
||||
|
||||
**Expected output includes paths like:**
|
||||
|
||||
```
|
||||
/opt/homebrew/opt/gpac/lib/libgpac.dylib
|
||||
```
|
||||
|
||||
## Homebrew Formula Usage (Future)
|
||||
|
||||
Example formula snippet:
|
||||
|
||||
```ruby
|
||||
def install
|
||||
system "./mac/build.command", "-system-libs"
|
||||
bin.install "mac/ccextractor"
|
||||
end
|
||||
```
|
||||
|
||||
## Summary
|
||||
|
||||
- `-system-libs` is opt-in
|
||||
- Default build remains unchanged
|
||||
- Enables CCExtractor to return to Homebrew
|
||||
- Fully tested in CI and locally
|
||||
|
||||
## Related
|
||||
|
||||
- **PR #1862** — Add `-system-libs` flag
|
||||
- **Issue #1580** — Homebrew compatibility
|
||||
- **Issue #1534** — System library support
|
||||
130
docs/CHANGES.TXT
130
docs/CHANGES.TXT
@@ -1,58 +1,98 @@
|
||||
1.0 (to be released)
|
||||
0.96.6 (unreleased)
|
||||
-------------------
|
||||
- Fix: DVB EIT start time BCD decoding in XMLTV output causing invalid timestamps (#1835)
|
||||
- New: Add Snap packaging support with Snapcraft configuration and GitHub Actions CI workflow.
|
||||
- Fix: Clear status line output on Linux/WSL to prevent text artifacts (#2017)
|
||||
- Fix: Prevent infinite loop on truncated MKV files
|
||||
- Fix: Various memory safety and stability fixes in demuxers (MP4, PS, MKV, DVB)
|
||||
- Fix: Delete empty output files instead of leaving 0-byte files (#1282)
|
||||
- Fix: --mkvlang now supports BCP 47 language tags (e.g., en-US, zh-Hans-CN) and multiple codes
|
||||
- Fix: segmentation fault when using --multiprogram
|
||||
|
||||
0.96.5 (2026-01-05)
|
||||
-------------------
|
||||
- New: CCExtractor is available again via Homebrew on macOS and Linux.
|
||||
- New: Add support for raw CDP (Caption Distribution Packet) files (#1406)
|
||||
- New: Add --scc-accurate-timing option for bandwidth-aware SCC output (#1120)
|
||||
- Fix: MXF files containing CEA-708 captions not being detected/extracted (#1647)
|
||||
- Docs: Add Windows WSL build instructions
|
||||
- Fix: Security fixes (out-of-bounds read/write) in a few places in the legacy C code.
|
||||
|
||||
0.96.4 (2026-01-01)
|
||||
-------------------
|
||||
- New: Persistent CEA-708 decoder context - maintains state across multiple calls for proper subtitle continuity
|
||||
- New: OCR character blacklist options (--ocr-blacklist, --ocr-blacklist-file) for improved accuracy
|
||||
- New: OCR line-split option (--ocr-splitontimechange) for better subtitle segmentation
|
||||
- Fix: 32-bit build failures on i686 and armv7l architectures
|
||||
- Fix: Legacy command-line argument compatibility (-1, -2, -12, --sc, --svc)
|
||||
- Fix: Prevent heap buffer overflow in Teletext processing (security fix)
|
||||
- Fix: Prevent integer overflow leading to heap buffer overflow in Transport Stream handling (security fix)
|
||||
- Fix: Lazy OCR initialization - only initialize when first DVB subtitle is encountered
|
||||
- Build: Optimized Windows CI workflow for faster builds
|
||||
- Fix: Updated GUI with version 0.7.1. A blind attempt to fix a hang on start on some Windows.
|
||||
|
||||
0.96.3 (2025-12-29)
|
||||
-------------------
|
||||
- New: VOBSUB subtitle extraction with OCR support for MP4 files
|
||||
- New: VOBSUB subtitle extraction support for MKV/Matroska files
|
||||
- New: Native SCC (Scenarist Closed Caption) input file support - CCExtractor can now read SCC files
|
||||
- New: Configurable frame rate (--scc-framerate) and styled PAC codes for SCC output
|
||||
- Fix: Apply --delay option to DVB/bitmap subtitles (previously only worked with text-based subtitles)
|
||||
- Fix: 200ms timing offset in MOV/MP4 caption extraction
|
||||
- Fix: utf8proc include path for system library builds
|
||||
- Fix: Use fixed-width integer types in MP4 bswap functions for better portability
|
||||
- Fix: Guard ocr_text access with ENABLE_OCR preprocessor check
|
||||
- Fix: Preserve FFmpeg libs when building with -system-libs -hardsubx
|
||||
- Build: Add vobsub_decoder to Windows and autoconf build systems
|
||||
- Build: Add winget and Chocolatey packaging workflows for Windows distribution
|
||||
- Docs: Add VOBSUB extraction documentation and subtile-ocr Dockerfile
|
||||
|
||||
0.96.2 (2025-12-26)
|
||||
-------------------
|
||||
- Fix: Resolve utf8proc header include path when building against system libraries on Linux.
|
||||
- Rebundle Windows version to include required runtime files to process hardcoded subtitles
|
||||
(hardcodex mode).
|
||||
- New: Add optional -system-libs flag to Linux build script for package manager compatibility
|
||||
|
||||
0.96.1 (2025-12-25)
|
||||
-------------------
|
||||
- Rebundle Windows version to include an updated GUI. No changes in CCExtractor itself.
|
||||
|
||||
0.96 (2025-12-23)
|
||||
-----------------
|
||||
- New: Multi-page teletext extraction support (#665)
|
||||
- Extract multiple teletext pages simultaneously with separate output files
|
||||
- Use --tpage multiple times (e.g., --tpage 100 --tpage 200)
|
||||
- Output files are named with page suffix (e.g., output_p100.srt, output_p200.srt)
|
||||
- Fix: SPUPNG subtitle offset calculation to center based on actual image dimensions
|
||||
|
||||
- New: Added --list-tracks (-L) option to list all tracks in media files without processing
|
||||
New: Chinese, Korean, Japanese support - proper encoding and OCR.
|
||||
New: Correct McPoodle DVD raw format support
|
||||
Fix: Timing is now frame perfect (using FFMpeg timing dump as reference) in all formats.
|
||||
Fix: Solved garbling in all the pending issues we had on GitHub.
|
||||
Fix: All causes of "premature end of file" messages due to bugs and not actual file cuts.
|
||||
Fix: All memory leaks, double frees and usual C nastyness that valgrind could find.
|
||||
- Fix Include ATSC VCT virtual channel numbers and call signs in XMLTV output
|
||||
- Fix: Restore ATSC XMLTV generation with ETT parsing for extended descriptions, multi-segment handling, extended table ID's (EIT/VCT), corrected <programme> XMLTV formatting, buffer bounds fixes
|
||||
- Fix: Add HEVC/H.265 stream type recognition to prevent crashes on ATSC 3.0 streams.
|
||||
Fix: Tolerance to damaged streams - recover where possible instead of terminating.
|
||||
Issues closed: Over 40! Too many to list here, but each of them was either a bug squashed or a feature implemented.
|
||||
|
||||
0.95 (2025-09-15 - never formally packaged)
|
||||
-----------------
|
||||
- Fix: ARM64/aarch64 build failure due to c_char type mismatch in nal.rs
|
||||
- Fix: HardSubX OCR on Rust
|
||||
- Removed the Share Module
|
||||
- Fix: Regression failures on DVD files
|
||||
- Fix: Segmentation faults on MP4 files with CEA-708 captions
|
||||
- Refactor: Remove API structures from ccextractor
|
||||
- New: Add Encoder Module to Rust
|
||||
- Fix: Elementary stream regressions
|
||||
- Fix: Segmentation faults on XDS files
|
||||
- Fix: Clippy Errors Based on Rust 1.88
|
||||
- IMPROVEMENT: Refactor and optimize Dockerfile
|
||||
- Fix: Improved handling of IETF language tags in Matroska files (#1665)
|
||||
- New: Create unit test for rust code (#1615)
|
||||
- Breaking: Major argument flags revamp for CCExtractor (#1564 & #1619)
|
||||
- New: Create a Docker image to simplify the CCExtractor usage without any environmental hustle (#1611)
|
||||
- New: Add time units module in lib_ccxr (#1623)
|
||||
- New: Add bits and levenshtein module in lib_ccxr (#1627)
|
||||
- New: Add constants module in lib_ccxr (#1624)
|
||||
- New: Add log module in lib_ccxr (#1622)
|
||||
- New: Create `lib_ccxr` and `libccxr_exports` (#1621)
|
||||
- Fix: Unexpected behavior of get_write_interval (#1609)
|
||||
- Update: Bump rsmpeg to latest version for ffmpeg bindings (#1600)
|
||||
- New: Add SCC support for CEA-708 decoder (#1595)
|
||||
- Fix: respect `-stdout` even if multiple CC tracks are present in a Matroska input file (#1453)
|
||||
- Fix: crash in Rust decoder on ATSC1.0 TS Files (#1407)
|
||||
- Removed the --with-gui flag for linux/configure and mac/configure (use the Flutter GUI instead)
|
||||
Refactor: Lots of code ported to Rust.
|
||||
- Fix: Improved handling of IETF language tags in Matroska files (#1665)
|
||||
- Breaking: Major argument flags revamp for CCExtractor (#1564 & #1619)
|
||||
- Fix: segmentation fault in using hardsubx
|
||||
- New: Add function (and command) that extracts closed caption subtitles as well as burnt-in subtitles from a file in a single pass. (As proposed in issue 726)
|
||||
- Refactored: the `general_loop` function has some code moved to a new function
|
||||
- Fix: WebVTT X-TIMESTAMP-MAP placement (#1463)
|
||||
- Disable X-TIMESTAMP-MAP by default (changed option --no-timestamp-map to --timestamp-map)
|
||||
- Fix: missing `#` in color attribute of font tag
|
||||
- Fix: ffmpeg 5.0, tesseract 5.0 compatibility and remove deprecated methods
|
||||
- Fix: tesseract 5.x traineddata location in ocr
|
||||
- Fix: fix autoconf tesseract detection problem (#1503)
|
||||
- Fix: add missing compile_info_real.h source to Autotools build
|
||||
- Fix: add missing `-lavfilter` for hardsubx linking
|
||||
- Fix: make webvtt-full work correctly with multi-byte utf-8 characters
|
||||
- Fix: encoding of solid block in latin-1 and unicode
|
||||
- Fix: McPoodle Broadcast Raw format for field 1
|
||||
- Fix: Incorrect skipping of packets
|
||||
- Fix: Repeated values for enums
|
||||
- Cleanup: Remove the (unmaintained) Nuklear GUI code
|
||||
- Cleanup: Reduce the amount of Windows build options in the project file
|
||||
- Fix: infinite loop in MP4 file type detector.
|
||||
- Improvement: Use Corrosion to build Rust code
|
||||
- Improvement: Ignore MXF Caption Essence Container version byte to enhance SRT subtitle extraction compatibility
|
||||
- New: Add tesseract page segmentation modes control with `--psm` flag
|
||||
- Fix: Resolve compile-time error about implicit declarations (#1646)
|
||||
- Fix: fatal out of memory error extracting from a VOB PS
|
||||
- Fix: Unit Test Rust failing due to changes in Rust Version 1.86.0
|
||||
- Fix: Support for MINGW-w64 cross compiling
|
||||
- Fix: Build with ENABLE_FFMPEG to support ffmpeg 5
|
||||
|
||||
0.94 (2021-12-14)
|
||||
-----------------
|
||||
|
||||
@@ -1,3 +1,16 @@
|
||||
# Installation
|
||||
|
||||
## Homebrew
|
||||
The easiest way to install CCExtractor for Mac and Linux is through Homebrew:
|
||||
|
||||
```bash
|
||||
brew install ccextractor
|
||||
```
|
||||
Note: If you don't have Homebrew installed, see [brew.sh](https://brew.sh/)
|
||||
for installation instructions.
|
||||
|
||||
---
|
||||
|
||||
# Compiling CCExtractor
|
||||
|
||||
You may compile CCExtractor across all major platforms using `CMakeLists.txt` stored under `ccextractor/src/` directory. Autoconf and custom build scripts are also available. See platform specific instructions in the below sections.
|
||||
@@ -10,6 +23,16 @@ Clone the latest repository from Github
|
||||
git clone https://github.com/CCExtractor/ccextractor.git
|
||||
```
|
||||
|
||||
### Hardsubx (Burned-in Subtitles) and FFmpeg Versions
|
||||
|
||||
CCExtractor's hardsubx feature extracts burned-in subtitles from videos using OCR. It requires FFmpeg libraries. The build system automatically selects appropriate FFmpeg versions for each platform:
|
||||
|
||||
- **Linux**: FFmpeg 6.x (default)
|
||||
- **Windows**: FFmpeg 6.x (default)
|
||||
- **macOS**: FFmpeg 8.x (default)
|
||||
|
||||
You can override the default by setting the `FFMPEG_VERSION` environment variable to `ffmpeg6`, `ffmpeg7`, or `ffmpeg8` before building. This flexibility ensures compatibility with different FFmpeg installations across platforms.
|
||||
|
||||
## Docker
|
||||
You can now use docker image to build latest source of CCExtractor without any environmental hustle. Follow these [instructions](https://github.com/CCExtractor/ccextractor/tree/master/docker/README.md) for building docker image & usage of it.
|
||||
|
||||
@@ -33,6 +56,10 @@ Arch:
|
||||
```bash
|
||||
sudo paru -S glew glfw curl tesseract leptonica cmake gcc clang gpac
|
||||
```
|
||||
or
|
||||
```bash
|
||||
sudo pacman -S glew glfw curl tesseract leptonica cmake gcc clang gpac
|
||||
```
|
||||
|
||||
Rust 1.54 or above is also required. [Install Rust](https://www.rust-lang.org/tools/install). Check specific compilation methods below, on how to compile without rust.
|
||||
|
||||
@@ -56,21 +83,26 @@ cd ccextractor/linux
|
||||
# compile without debug flags
|
||||
./build
|
||||
|
||||
# compile without rust
|
||||
./build -without-rust
|
||||
|
||||
# compile with debug info
|
||||
./build -debug # same as ./builddebug
|
||||
|
||||
# compile with hardsubx
|
||||
[Optional] You need to set these environment variables correctly according to your machine,
|
||||
FFMPEG_INCLUDE_DIR=/usr/include
|
||||
FFMPEG_PKG_CONFIG_PATH=/usr/lib/pkgconfig
|
||||
# compile with hardsubx (burned-in subtitle extraction)
|
||||
# Hardsubx requires FFmpeg libraries. Different FFmpeg versions are used by default:
|
||||
# - Linux: FFmpeg 6.x (automatic)
|
||||
# - Windows: FFmpeg 6.x (automatic)
|
||||
# - macOS: FFmpeg 8.x (automatic)
|
||||
|
||||
./build -hardsubx # same as ./build_hardsubx
|
||||
./build -hardsubx # uses platform-specific FFmpeg version
|
||||
|
||||
# To override the default FFmpeg version, set FFMPEG_VERSION:
|
||||
FFMPEG_VERSION=ffmpeg8 ./build -hardsubx # force FFmpeg 8 on any platform
|
||||
FFMPEG_VERSION=ffmpeg6 ./build -hardsubx # force FFmpeg 6 on any platform
|
||||
FFMPEG_VERSION=ffmpeg7 ./build -hardsubx # force FFmpeg 7 on any platform
|
||||
|
||||
# [Optional] For custom FFmpeg installations, set these environment variables:
|
||||
FFMPEG_INCLUDE_DIR=/usr/include
|
||||
FFMPEG_PKG_CONFIG_PATH=/usr/lib/pkgconfig
|
||||
|
||||
# compile in debug mode without rust
|
||||
./build -debug -without-rust
|
||||
|
||||
# test your build
|
||||
./ccextractor
|
||||
@@ -82,7 +114,7 @@ cd ccextractor/linux
|
||||
sudo apt-get install autoconf # dependency to generate configuration script
|
||||
cd ccextractor/linux
|
||||
./autogen.sh
|
||||
./configure # OR ./configure --without-rust
|
||||
./configure
|
||||
make
|
||||
|
||||
# test your build
|
||||
@@ -113,9 +145,15 @@ sudo make install
|
||||
|
||||
`cmake` also accepts the options:
|
||||
`-DWITH_OCR=ON` to enable OCR
|
||||
`-DWITH_HARDSUBX=ON` to enable burned-in subtitles
|
||||
`-DWITH_HARDSUBX=ON` to enable burned-in subtitles (requires FFmpeg)
|
||||
|
||||
([OPTIONAL] For hardsubx, you also need to set these environment variables correctly according to your machine)
|
||||
For hardsubx with specific FFmpeg versions:
|
||||
Set `FFMPEG_VERSION=ffmpeg6` for FFmpeg 6.x (default on Linux and Windows)
|
||||
Set `FFMPEG_VERSION=ffmpeg7` for FFmpeg 7.x
|
||||
Set `FFMPEG_VERSION=ffmpeg8` for FFmpeg 8.x
|
||||
(Defaults: Linux=FFmpeg 6, Windows=FFmpeg 6, macOS=FFmpeg 8)
|
||||
|
||||
([OPTIONAL] For custom FFmpeg installations, set these environment variables)
|
||||
|
||||
FFMPEG_INCLUDE_DIR=/usr/include
|
||||
FFMPEG_PKG_CONFIG_PATH=/usr/lib/pkgconfig
|
||||
@@ -136,6 +174,8 @@ brew install cmake gpac
|
||||
# optional if you want OCR:
|
||||
brew install tesseract
|
||||
brew install leptonica
|
||||
# optional if you want hardsubx (burned-in subtitle extraction):
|
||||
brew install ffmpeg
|
||||
```
|
||||
|
||||
If configuring OCR, use pkg-config to verify tesseract and leptonica dependencies, e.g.
|
||||
@@ -151,7 +191,12 @@ pkg-config --exists --print-errors lept
|
||||
|
||||
```bash
|
||||
cd ccextractor/mac
|
||||
./build.command # OR ./build.command OCR
|
||||
./build.command # basic build
|
||||
./build.command -ocr # build with OCR support
|
||||
./build.command -hardsubx # build with hardsubx (uses FFmpeg 8 by default on macOS)
|
||||
|
||||
# Override FFmpeg version if needed:
|
||||
FFMPEG_VERSION=ffmpeg7 ./build.command -hardsubx
|
||||
|
||||
# test your build
|
||||
./ccextractor
|
||||
@@ -182,7 +227,7 @@ make
|
||||
```bash
|
||||
cd ccextractor/mac
|
||||
./autogen.sh
|
||||
./configure # OR ./configure --without-rust
|
||||
./configure
|
||||
make
|
||||
|
||||
# test your build
|
||||
@@ -220,6 +265,12 @@ Other dependencies are required through vcpkg, so you can follow below steps:
|
||||
```
|
||||
vcpkg install ffmpeg leptonica tesseract --triplet x64-windows-static
|
||||
```
|
||||
Note: Windows builds use FFmpeg 6 by default. To override:
|
||||
```
|
||||
set FFMPEG_VERSION=ffmpeg8
|
||||
msbuild ccextractor.sln /p:Configuration=Debug-Full /p:Platform=x64
|
||||
```
|
||||
|
||||
otherwise if you have Debug, Release
|
||||
```
|
||||
vcpkg install libpng --triplet x64-windows-static
|
||||
|
||||
@@ -54,6 +54,32 @@ To build the program with hardsubx support,
|
||||
|
||||
NOTE: The build has been tested with FFMpeg version 3.1.0, and Tesseract 3.04.
|
||||
|
||||
macOS
|
||||
-----
|
||||
|
||||
Install the required dependencies using Homebrew:
|
||||
brew install tesseract leptonica ffmpeg
|
||||
|
||||
To build the program with hardsubx support, use one of these methods:
|
||||
|
||||
== Using build.command (Recommended):
|
||||
cd ccextractor/mac
|
||||
./build.command -hardsubx
|
||||
|
||||
== Using autoconf:
|
||||
cd ccextractor/mac
|
||||
./autogen.sh
|
||||
./configure --enable-hardsubx --enable-ocr
|
||||
make
|
||||
|
||||
== Using cmake:
|
||||
cd ccextractor
|
||||
mkdir build && cd build
|
||||
cmake -DWITH_OCR=ON -DWITH_HARDSUBX=ON ../src/
|
||||
make
|
||||
|
||||
NOTE: The -hardsubx parameter uses a single dash (not --hardsubx).
|
||||
|
||||
Windows
|
||||
-------
|
||||
|
||||
|
||||
@@ -26,6 +26,14 @@ Running ccextractor without parameters shows the help screen. Usage is
|
||||
trivial - you just need to pass the input file and (optionally) some
|
||||
details about the input and output files.
|
||||
|
||||
Example:
|
||||
|
||||
ccextractor input_video.ts
|
||||
|
||||
This command extracts subtitles from the input video file and generates a subtitle output file
|
||||
(such as .srt) in the same directory.
|
||||
|
||||
|
||||
|
||||
## Languages
|
||||
Usually English captions are transmitted in line 21 field 1 data,
|
||||
|
||||
129
docs/VOBSUB.md
Normal file
129
docs/VOBSUB.md
Normal file
@@ -0,0 +1,129 @@
|
||||
# VOBSUB Subtitle Extraction from MKV Files
|
||||
|
||||
CCExtractor supports extracting VOBSUB (S_VOBSUB) subtitles from Matroska (MKV) containers. VOBSUB is an image-based subtitle format originally from DVD video.
|
||||
|
||||
## Overview
|
||||
|
||||
VOBSUB subtitles consist of two files:
|
||||
- `.idx` - Index file containing metadata, palette, and timestamp/position entries
|
||||
- `.sub` - Binary file containing the actual subtitle bitmap data in MPEG Program Stream format
|
||||
|
||||
## Basic Usage
|
||||
|
||||
```bash
|
||||
ccextractor movie.mkv
|
||||
```
|
||||
|
||||
This will extract all VOBSUB tracks and create paired `.idx` and `.sub` files:
|
||||
- `movie_eng.idx` + `movie_eng.sub` (first English track)
|
||||
- `movie_eng_1.idx` + `movie_eng_1.sub` (second English track, if present)
|
||||
- etc.
|
||||
|
||||
## Converting VOBSUB to SRT (Text)
|
||||
|
||||
Since VOBSUB subtitles are images, you need OCR (Optical Character Recognition) to convert them to text-based formats like SRT.
|
||||
|
||||
### Using subtile-ocr (Recommended)
|
||||
|
||||
[subtile-ocr](https://github.com/gwen-lg/subtile-ocr) is an actively maintained Rust tool that provides accurate OCR conversion.
|
||||
|
||||
#### Option 1: Docker (Easiest)
|
||||
|
||||
We provide a Dockerfile that builds subtile-ocr with all dependencies:
|
||||
|
||||
```bash
|
||||
# Build the Docker image (one-time)
|
||||
cd tools/vobsubocr
|
||||
docker build -t subtile-ocr .
|
||||
|
||||
# Extract VOBSUB from MKV
|
||||
ccextractor movie.mkv
|
||||
|
||||
# Convert to SRT using OCR
|
||||
docker run --rm -v $(pwd):/data subtile-ocr -l eng -o /data/movie_eng.srt /data/movie_eng.idx
|
||||
```
|
||||
|
||||
#### Option 2: Install subtile-ocr Natively
|
||||
|
||||
If you have Rust and Tesseract development libraries installed:
|
||||
|
||||
```bash
|
||||
# Install dependencies (Ubuntu/Debian)
|
||||
sudo apt-get install libleptonica-dev libtesseract-dev tesseract-ocr tesseract-ocr-eng
|
||||
|
||||
# Install subtile-ocr
|
||||
cargo install --git https://github.com/gwen-lg/subtile-ocr
|
||||
|
||||
# Convert
|
||||
subtile-ocr -l eng -o movie_eng.srt movie_eng.idx
|
||||
```
|
||||
|
||||
### subtile-ocr Options
|
||||
|
||||
| Option | Description |
|
||||
|--------|-------------|
|
||||
| `-l, --lang <LANG>` | Tesseract language code (required). Examples: `eng`, `fra`, `deu`, `chi_sim` |
|
||||
| `-o, --output <FILE>` | Output SRT file (stdout if not specified) |
|
||||
| `-t, --threshold <0.0-1.0>` | Binarization threshold (default: 0.6) |
|
||||
| `-d, --dpi <DPI>` | Image DPI for OCR (default: 150) |
|
||||
| `--dump` | Save processed subtitle images as PNG files |
|
||||
|
||||
### Language Codes
|
||||
|
||||
Install additional Tesseract language packs as needed:
|
||||
|
||||
```bash
|
||||
# Examples
|
||||
sudo apt-get install tesseract-ocr-fra # French
|
||||
sudo apt-get install tesseract-ocr-deu # German
|
||||
sudo apt-get install tesseract-ocr-spa # Spanish
|
||||
sudo apt-get install tesseract-ocr-chi-sim # Simplified Chinese
|
||||
```
|
||||
|
||||
## Technical Details
|
||||
|
||||
### .idx File Format
|
||||
|
||||
The index file contains:
|
||||
1. Header with metadata (size, palette, alignment settings)
|
||||
2. Language identifier line
|
||||
3. Timestamp entries with file positions
|
||||
|
||||
Example:
|
||||
```
|
||||
# VobSub index file, v7 (do not modify this line!)
|
||||
size: 720x576
|
||||
palette: 000000, 828282, ...
|
||||
|
||||
id: eng, index: 0
|
||||
timestamp: 00:01:12:920, filepos: 000000000
|
||||
timestamp: 00:01:18:640, filepos: 000000800
|
||||
...
|
||||
```
|
||||
|
||||
### .sub File Format
|
||||
|
||||
The binary file contains MPEG Program Stream packets:
|
||||
- Each subtitle is wrapped in a PS Pack header (14 bytes) + PES header (15 bytes)
|
||||
- Subtitles are aligned to 2048-byte boundaries
|
||||
- Contains raw SPU (SubPicture Unit) bitmap data
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Empty output files
|
||||
- Ensure the MKV file actually contains VOBSUB tracks (check with `mediainfo` or `ffprobe`)
|
||||
- CCExtractor will report "No VOBSUB subtitles to write" if the track is empty
|
||||
|
||||
### OCR quality issues
|
||||
- Try adjusting the `-t` threshold parameter
|
||||
- Ensure the correct language pack is installed
|
||||
- Use `--dump` to inspect the processed images
|
||||
|
||||
### Docker permission issues
|
||||
- The output files may be owned by root; use `sudo chown` to fix ownership
|
||||
- Or run Docker with `--user $(id -u):$(id -g)`
|
||||
|
||||
## See Also
|
||||
|
||||
- [OCR.md](OCR.md) - General OCR support in CCExtractor
|
||||
- [subtile-ocr GitHub](https://github.com/gwen-lg/subtile-ocr) - OCR tool documentation
|
||||
137
docs/build-wsl.md
Normal file
137
docs/build-wsl.md
Normal file
@@ -0,0 +1,137 @@
|
||||
# Building CCExtractor on Windows using WSL
|
||||
|
||||
This guide explains how to build CCExtractor on Windows using WSL (Ubuntu).
|
||||
It is based on a fresh setup and includes all required dependencies and
|
||||
common build issues encountered during compilation.
|
||||
|
||||
---
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Windows 10 or Windows 11
|
||||
- WSL enabled
|
||||
- Ubuntu installed via Microsoft Store
|
||||
|
||||
---
|
||||
|
||||
## Install WSL and Ubuntu
|
||||
|
||||
From PowerShell (run as Administrator):
|
||||
|
||||
```powershell
|
||||
wsl --install -d Ubuntu
|
||||
```
|
||||
|
||||
Restart the system if prompted, then launch Ubuntu from the Start menu.
|
||||
|
||||
---
|
||||
|
||||
## Update system packages
|
||||
|
||||
```bash
|
||||
sudo apt update
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Install basic build tools
|
||||
|
||||
```bash
|
||||
sudo apt install -y build-essential git pkg-config
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Install Rust (required)
|
||||
|
||||
CCExtractor includes Rust components, so Rust and Cargo are required.
|
||||
|
||||
```bash
|
||||
curl https://sh.rustup.rs -sSf | sh
|
||||
source ~/.cargo/env
|
||||
```
|
||||
|
||||
Verify installation:
|
||||
|
||||
```bash
|
||||
cargo --version
|
||||
rustc --version
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Install required libraries
|
||||
|
||||
```bash
|
||||
sudo apt install -y \
|
||||
libclang-dev clang \
|
||||
libtesseract-dev tesseract-ocr \
|
||||
libgpac-dev
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Clone the repository
|
||||
|
||||
```bash
|
||||
git clone https://github.com/CCExtractor/ccextractor.git
|
||||
cd ccextractor
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Build CCExtractor
|
||||
|
||||
```bash
|
||||
cd linux
|
||||
./build
|
||||
```
|
||||
|
||||
After a successful build, verify by running:
|
||||
|
||||
```bash
|
||||
./ccextractor
|
||||
```
|
||||
|
||||
You should see the help/usage output.
|
||||
|
||||
---
|
||||
|
||||
## Common build issues
|
||||
|
||||
### cargo: command not found
|
||||
|
||||
```bash
|
||||
source ~/.cargo/env
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Unable to find libclang
|
||||
|
||||
```bash
|
||||
sudo apt install libclang-dev clang
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### gpac/isomedia.h: No such file or directory
|
||||
|
||||
```bash
|
||||
sudo apt install libgpac-dev
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### please install tesseract development library
|
||||
|
||||
```bash
|
||||
sudo apt install libtesseract-dev tesseract-ocr
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Notes
|
||||
|
||||
- Compiler warnings during the build process are expected and do not indicate failure.
|
||||
- This guide was tested on Ubuntu (WSL) running on Windows 11.
|
||||
@@ -151,6 +151,8 @@ ccextractor_SOURCES = \
|
||||
../src/lib_ccx/list.h \
|
||||
../src/lib_ccx/matroska.c \
|
||||
../src/lib_ccx/matroska.h \
|
||||
../src/lib_ccx/vobsub_decoder.c \
|
||||
../src/lib_ccx/vobsub_decoder.h \
|
||||
../src/lib_ccx/mp4.c \
|
||||
../src/lib_ccx/myth.c \
|
||||
../src/lib_ccx/networking.c \
|
||||
@@ -294,12 +296,18 @@ ccextractor_CPPFLAGS+= ${libavformat_CFLAGS}
|
||||
ccextractor_CPPFLAGS+= ${libavfilter_CFLAGS}
|
||||
ccextractor_CPPFLAGS+= ${libavutil_CFALGS}
|
||||
ccextractor_CPPFLAGS+= ${libswscale_CFLAGS}
|
||||
# HARDSUBX requires tesseract/leptonica for OCR (same as OCR feature)
|
||||
ccextractor_CPPFLAGS+= ${tesseract_CFLAGS}
|
||||
ccextractor_CPPFLAGS+= ${lept_CFLAGS}
|
||||
AV_LIB = ${libavcodec_LIBS}
|
||||
AV_LIB += ${libavformat_LIBS}
|
||||
AV_LIB += ${libavfilter_LIBS}
|
||||
AV_LIB += ${libavutil_LIBS}
|
||||
AV_LIB += ${libswscale_LIBS}
|
||||
ccextractor_LDADD += $(AV_LIB)
|
||||
# HARDSUBX requires tesseract/leptonica libs for OCR
|
||||
ccextractor_LDADD += ${tesseract_LIBS}
|
||||
ccextractor_LDADD += ${lept_LIBS}
|
||||
HARDSUBX_FEATURE_RUST += --features "hardsubx_ocr"
|
||||
endif
|
||||
|
||||
|
||||
75
linux/build
75
linux/build
@@ -2,6 +2,7 @@
|
||||
|
||||
RUST_LIB="rust/release/libccx_rust.a"
|
||||
RUST_PROFILE="--release"
|
||||
USE_SYSTEM_LIBS=false
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case $1 in
|
||||
-debug)
|
||||
@@ -13,11 +14,20 @@ while [[ $# -gt 0 ]]; do
|
||||
;;
|
||||
-hardsubx)
|
||||
HARDSUBX=true
|
||||
RUST_FEATURES="--features hardsubx_ocr"
|
||||
# Allow overriding FFmpeg version via environment variable
|
||||
if [ -n "$FFMPEG_VERSION" ]; then
|
||||
RUST_FEATURES="--features hardsubx_ocr,$FFMPEG_VERSION"
|
||||
else
|
||||
RUST_FEATURES="--features hardsubx_ocr"
|
||||
fi
|
||||
BLD_FLAGS="$BLD_FLAGS -DENABLE_HARDSUBX"
|
||||
BLD_LINKER="$BLD_LINKER -lswscale -lavutil -pthread -lavformat -lavcodec -lavfilter -lxcb-shm -lxcb -lX11 -llzma -lswresample"
|
||||
shift
|
||||
;;
|
||||
-system-libs)
|
||||
USE_SYSTEM_LIBS=true
|
||||
shift
|
||||
;;
|
||||
-*)
|
||||
echo "Unknown option $1"
|
||||
exit 1
|
||||
@@ -25,7 +35,42 @@ while [[ $# -gt 0 ]]; do
|
||||
esac
|
||||
done
|
||||
|
||||
BLD_FLAGS="$BLD_FLAGS -std=gnu99 -Wno-write-strings -Wno-pointer-sign -D_FILE_OFFSET_BITS=64 -DVERSION_FILE_PRESENT -DENABLE_OCR -DFT2_BUILD_LIBRARY -DGPAC_DISABLE_VTT -DGPAC_DISABLE_OD_DUMP -DGPAC_DISABLE_REMOTERY -DNO_GZIP"
|
||||
if [ "$USE_SYSTEM_LIBS" = true ]; then
|
||||
command -v pkg-config >/dev/null || {
|
||||
echo "Error: pkg-config is required for -system-libs mode"
|
||||
exit 1
|
||||
}
|
||||
|
||||
MISSING=""
|
||||
for lib in libpng zlib freetype2 libutf8proc; do
|
||||
if ! pkg-config --exists "$lib" 2>/dev/null; then
|
||||
MISSING="$MISSING $lib"
|
||||
fi
|
||||
done
|
||||
|
||||
if [ -n "$MISSING" ]; then
|
||||
echo "Error: Missing required system libraries:$MISSING"
|
||||
echo ""
|
||||
echo "On Debian/Ubuntu: sudo apt install libpng-dev zlib1g-dev libfreetype-dev libutf8proc-dev"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
for hdr in leptonica/allheaders.h tesseract/capi.h; do
|
||||
if ! echo "#include <$hdr>" | gcc -E - >/dev/null 2>&1; then
|
||||
echo "Error: Missing headers for <$hdr>"
|
||||
echo "On Debian/Ubuntu: sudo apt install libleptonica-dev libtesseract-dev"
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
PKG_CFLAGS="$(pkg-config --cflags libpng zlib freetype2 libutf8proc)"
|
||||
PKG_LIBS="$(pkg-config --libs libpng zlib freetype2 libutf8proc)"
|
||||
fi
|
||||
|
||||
BLD_FLAGS="$BLD_FLAGS -std=gnu99 -Wno-write-strings -Wno-pointer-sign -D_FILE_OFFSET_BITS=64 -DVERSION_FILE_PRESENT -DENABLE_OCR -DGPAC_DISABLE_VTT -DGPAC_DISABLE_OD_DUMP -DGPAC_DISABLE_REMOTERY -DNO_GZIP"
|
||||
if [ "$USE_SYSTEM_LIBS" != true ]; then
|
||||
BLD_FLAGS="$BLD_FLAGS -DFT2_BUILD_LIBRARY"
|
||||
fi
|
||||
bit_os=$(getconf LONG_BIT)
|
||||
if [ "$bit_os" == "64" ]
|
||||
then
|
||||
@@ -35,7 +80,7 @@ BLD_INCLUDE="-I../src -I /usr/include/leptonica/ -I /usr/include/tesseract/ -I..
|
||||
SRC_LIBPNG="$(find ../src/thirdparty/libpng/ -name '*.c')"
|
||||
SRC_ZLIB="$(find ../src/thirdparty/zlib/ -name '*.c')"
|
||||
SRC_CCX="$(find ../src/lib_ccx/ -name '*.c')"
|
||||
SRC_GPAC="$(find /usr/include/gpac/ -name '*.c')"
|
||||
SRC_GPAC="$(find /usr/include/gpac/ -name '*.c' 2>/dev/null)"
|
||||
SRC_HASH="$(find ../src/thirdparty/lib_hash/ -name '*.c')"
|
||||
SRC_UTF8PROC="../src/thirdparty/utf8proc/utf8proc.c"
|
||||
SRC_FREETYPE="../src/thirdparty/freetype/autofit/autofit.c
|
||||
@@ -82,6 +127,24 @@ SRC_FREETYPE="../src/thirdparty/freetype/autofit/autofit.c
|
||||
BLD_SOURCES="../src/ccextractor.c $SRC_CCX $SRC_GPAC $SRC_ZLIB $SRC_LIBPNG $SRC_HASH $SRC_UTF8PROC $SRC_FREETYPE"
|
||||
BLD_LINKER="$BLD_LINKER -lm -zmuldefs -l tesseract -l leptonica -lpthread -ldl -lgpac"
|
||||
|
||||
if [ "$USE_SYSTEM_LIBS" = true ]; then
|
||||
LEPTONICA_CFLAGS="$(pkg-config --cflags --silence-errors lept)"
|
||||
TESSERACT_CFLAGS="$(pkg-config --cflags --silence-errors tesseract)"
|
||||
GPAC_CFLAGS="$(pkg-config --cflags --silence-errors gpac)"
|
||||
|
||||
BLD_INCLUDE="-I../src -I../src/lib_ccx -I../src/lib_ccx/zvbi -I../src/thirdparty/lib_hash \
|
||||
$PKG_CFLAGS $LEPTONICA_CFLAGS $TESSERACT_CFLAGS $GPAC_CFLAGS"
|
||||
|
||||
BLD_SOURCES="../src/ccextractor.c $SRC_CCX $SRC_HASH"
|
||||
# Preserve FFmpeg libraries if -hardsubx was specified
|
||||
FFMPEG_LIBS=""
|
||||
if [ "$HARDSUBX" = true ]; then
|
||||
FFMPEG_LIBS="-lswscale -lavutil -pthread -lavformat -lavcodec -lavfilter -lxcb-shm -lxcb -lX11 -llzma -lswresample"
|
||||
fi
|
||||
BLD_LINKER="$PKG_LIBS -ltesseract -lleptonica -lgpac -lpthread -ldl -lm $FFMPEG_LIBS"
|
||||
fi
|
||||
|
||||
|
||||
echo "Running pre-build script..."
|
||||
./pre-build.sh
|
||||
echo "Trying to compile..."
|
||||
@@ -96,7 +159,7 @@ fi
|
||||
rustc_version="$(rustc --version)"
|
||||
semver=( ${rustc_version//./ } )
|
||||
version="${semver[1]}.${semver[2]}.${semver[3]}"
|
||||
MSRV="1.54.0"
|
||||
MSRV="1.87.0"
|
||||
if [ "$(printf '%s\n' "$MSRV" "$version" | sort -V | head -n1)" = "$MSRV" ]; then
|
||||
echo "rustc >= MSRV(${MSRV})"
|
||||
else
|
||||
@@ -144,3 +207,7 @@ if [[ "$out" != "" ]] ; then
|
||||
else
|
||||
echo "Compilation successful, no compiler messages."
|
||||
fi
|
||||
|
||||
if [ -d ./utf8proc_compat ]; then
|
||||
rm -rf ./utf8proc_compat
|
||||
fi
|
||||
|
||||
@@ -1,63 +1,230 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# CCExtractor AppImage Build Script
|
||||
#
|
||||
# Build variants via BUILD_TYPE environment variable:
|
||||
# - minimal: Basic CCExtractor without OCR (smallest size)
|
||||
# - ocr: CCExtractor with OCR support (default)
|
||||
# - hardsubx: CCExtractor with burned-in subtitle extraction (requires FFmpeg)
|
||||
#
|
||||
# Usage:
|
||||
# ./build_appimage.sh # Builds 'ocr' variant (default)
|
||||
# BUILD_TYPE=minimal ./build_appimage.sh
|
||||
# BUILD_TYPE=hardsubx ./build_appimage.sh
|
||||
#
|
||||
# Requirements:
|
||||
# - CMake, GCC, pkg-config, Rust toolchain
|
||||
# - For OCR: tesseract-ocr, libtesseract-dev, libleptonica-dev
|
||||
# - For HardSubX: libavcodec-dev, libavformat-dev, libswscale-dev, etc.
|
||||
# - wget for downloading linuxdeploy
|
||||
#
|
||||
|
||||
set -x
|
||||
set -e
|
||||
|
||||
# store the path of where the script is
|
||||
OLD_CWD=$(readlink -f .)
|
||||
# Build type: minimal, ocr, hardsubx (default: ocr)
|
||||
BUILD_TYPE="${BUILD_TYPE:-ocr}"
|
||||
|
||||
# store repo root as variable
|
||||
REPO_ROOT=$(dirname $OLD_CWD)
|
||||
echo "=========================================="
|
||||
echo "CCExtractor AppImage Builder"
|
||||
echo "Build type: $BUILD_TYPE"
|
||||
echo "=========================================="
|
||||
|
||||
# Make a temp directory for building stuff which will be cleaned automatically
|
||||
BUILD_DIR="$OLD_CWD/temp"
|
||||
# Validate build type
|
||||
case "$BUILD_TYPE" in
|
||||
minimal|ocr|hardsubx)
|
||||
;;
|
||||
*)
|
||||
echo "Error: Invalid BUILD_TYPE '$BUILD_TYPE'"
|
||||
echo "Valid options: minimal, ocr, hardsubx"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
# Check if temp directory exist, and if so then remove contents from it
|
||||
# if not then create temp directory
|
||||
if [ -d "$BUILD_DIR" ]; then
|
||||
rm -r "$BUILD_DIR/*" | true
|
||||
else
|
||||
mkdir -p "$BUILD_DIR"
|
||||
fi
|
||||
# Store paths
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
REPO_ROOT="$(dirname "$SCRIPT_DIR")"
|
||||
BUILD_DIR="$SCRIPT_DIR/appimage_build"
|
||||
|
||||
# make sure to clean up build dir, even if errors occur
|
||||
# Clean up function
|
||||
cleanup() {
|
||||
if [ -d "$BUILD_DIR" ]; then
|
||||
rm -rf "$BUILD_DIR"
|
||||
fi
|
||||
if [ -d "$BUILD_DIR" ]; then
|
||||
echo "Cleaning up build directory..."
|
||||
rm -rf "$BUILD_DIR"
|
||||
fi
|
||||
}
|
||||
|
||||
# Automatically trigger Cleanup function
|
||||
# Cleanup on exit (comment out for debugging)
|
||||
trap cleanup EXIT
|
||||
|
||||
# switch to build dir
|
||||
pushd "$BUILD_DIR"
|
||||
# Create fresh build directory
|
||||
rm -rf "$BUILD_DIR" 2>/dev/null || true
|
||||
mkdir -p "$BUILD_DIR"
|
||||
|
||||
# configure build files with CMake
|
||||
# we need to explicitly set the install prefix, as CMake's default is /usr/local for some reason...
|
||||
cmake "$REPO_ROOT/src"
|
||||
cd "$BUILD_DIR"
|
||||
|
||||
# build project and install files into AppDir
|
||||
make -j$(nproc) ENABLE_OCR=yes
|
||||
# Determine CMake options based on build type
|
||||
CMAKE_OPTIONS=""
|
||||
case "$BUILD_TYPE" in
|
||||
minimal)
|
||||
CMAKE_OPTIONS=""
|
||||
;;
|
||||
ocr)
|
||||
CMAKE_OPTIONS="-DWITH_OCR=ON"
|
||||
;;
|
||||
hardsubx)
|
||||
CMAKE_OPTIONS="-DWITH_OCR=ON -DWITH_HARDSUBX=ON -DWITH_FFMPEG=ON"
|
||||
;;
|
||||
esac
|
||||
|
||||
# download linuxdeploy tool
|
||||
wget https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-x86_64.AppImage
|
||||
echo "CMake options: $CMAKE_OPTIONS"
|
||||
|
||||
# make them executable
|
||||
chmod +x linuxdeploy*.AppImage
|
||||
# Configure with CMake
|
||||
echo "Configuring with CMake..."
|
||||
cmake $CMAKE_OPTIONS "$REPO_ROOT/src"
|
||||
|
||||
# Create AppDir
|
||||
mkdir -p "$BUILD_DIR/AppDir"
|
||||
# Build
|
||||
echo "Building CCExtractor..."
|
||||
make -j$(nproc)
|
||||
|
||||
# Link of CCExtractor image of any of these resolution(8x8, 16x16, 20x20, 22x22, 24x24, 28x28, 32x32, 36x36, 42x42,
|
||||
# 48x48, 64x64, 72x72, 96x96, 128x128, 160x160, 192x192, 256x256, 384x384, 480x480, 512x512) in png extension
|
||||
PNG_LINK="https://ccextractor.org/images/ccextractor.png"
|
||||
# Verify binary was built
|
||||
if [ ! -f "$BUILD_DIR/ccextractor" ]; then
|
||||
echo "Error: ccextractor binary not found after build"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Download the image and put it in AppDir
|
||||
wget "$PNG_LINK" -P AppDir
|
||||
echo "Build successful!"
|
||||
"$BUILD_DIR/ccextractor" --version
|
||||
|
||||
# now, build AppImage using linuxdeploy
|
||||
./linuxdeploy-x86_64.AppImage --appdir=AppDir -e ccextractor --create-desktop-file --output appimage -i AppDir/ccextractor.png
|
||||
# Download linuxdeploy
|
||||
echo "Downloading linuxdeploy..."
|
||||
LINUXDEPLOY_URL="https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-x86_64.AppImage"
|
||||
wget -q --show-progress "$LINUXDEPLOY_URL" -O linuxdeploy-x86_64.AppImage
|
||||
chmod +x linuxdeploy-x86_64.AppImage
|
||||
|
||||
# Move resulted AppImage binary to base directory
|
||||
mv ccextractor*.AppImage "$OLD_CWD"
|
||||
# Create AppDir structure
|
||||
echo "Creating AppDir structure..."
|
||||
mkdir -p AppDir/usr/bin
|
||||
mkdir -p AppDir/usr/share/icons/hicolor/256x256/apps
|
||||
mkdir -p AppDir/usr/share/applications
|
||||
mkdir -p AppDir/usr/share/tessdata
|
||||
|
||||
# Copy binary
|
||||
cp "$BUILD_DIR/ccextractor" AppDir/usr/bin/
|
||||
|
||||
# Download icon
|
||||
echo "Downloading icon..."
|
||||
PNG_URL="https://ccextractor.org/images/ccextractor.png"
|
||||
if wget -q "$PNG_URL" -O AppDir/usr/share/icons/hicolor/256x256/apps/ccextractor.png 2>/dev/null; then
|
||||
echo "Icon downloaded successfully"
|
||||
else
|
||||
# Create a simple placeholder icon if download fails
|
||||
echo "Warning: Could not download icon, creating placeholder"
|
||||
convert -size 256x256 xc:navy -fill white -gravity center -pointsize 40 -annotate 0 "CCX" \
|
||||
AppDir/usr/share/icons/hicolor/256x256/apps/ccextractor.png 2>/dev/null || \
|
||||
echo "P3 256 256 255" > AppDir/usr/share/icons/hicolor/256x256/apps/ccextractor.ppm
|
||||
fi
|
||||
|
||||
# Create desktop file
|
||||
cat > AppDir/usr/share/applications/ccextractor.desktop << 'EOF'
|
||||
[Desktop Entry]
|
||||
Type=Application
|
||||
Name=CCExtractor
|
||||
Comment=Extract closed captions and subtitles from video files
|
||||
Exec=ccextractor
|
||||
Icon=ccextractor
|
||||
Categories=AudioVideo;Video;
|
||||
Terminal=true
|
||||
NoDisplay=true
|
||||
EOF
|
||||
|
||||
# Copy desktop file to AppDir root (required by linuxdeploy)
|
||||
cp AppDir/usr/share/applications/ccextractor.desktop AppDir/
|
||||
|
||||
# Copy icon to AppDir root
|
||||
cp AppDir/usr/share/icons/hicolor/256x256/apps/ccextractor.png AppDir/ 2>/dev/null || true
|
||||
|
||||
# For OCR builds, bundle tessdata
|
||||
if [ "$BUILD_TYPE" = "ocr" ] || [ "$BUILD_TYPE" = "hardsubx" ]; then
|
||||
echo "Bundling tessdata for OCR support..."
|
||||
|
||||
# Try to find system tessdata
|
||||
TESSDATA_PATHS=(
|
||||
"/usr/share/tesseract-ocr/5/tessdata"
|
||||
"/usr/share/tesseract-ocr/4.00/tessdata"
|
||||
"/usr/share/tessdata"
|
||||
"/usr/local/share/tessdata"
|
||||
)
|
||||
|
||||
TESSDATA_SRC=""
|
||||
for path in "${TESSDATA_PATHS[@]}"; do
|
||||
if [ -d "$path" ] && [ -f "$path/eng.traineddata" ]; then
|
||||
TESSDATA_SRC="$path"
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
if [ -n "$TESSDATA_SRC" ]; then
|
||||
echo "Found tessdata at: $TESSDATA_SRC"
|
||||
# Copy English language data (most common)
|
||||
cp "$TESSDATA_SRC/eng.traineddata" AppDir/usr/share/tessdata/ 2>/dev/null || true
|
||||
# Copy OSD (orientation and script detection) if available
|
||||
cp "$TESSDATA_SRC/osd.traineddata" AppDir/usr/share/tessdata/ 2>/dev/null || true
|
||||
else
|
||||
echo "Warning: tessdata not found, downloading English language data..."
|
||||
wget -q "https://github.com/tesseract-ocr/tessdata/raw/main/eng.traineddata" \
|
||||
-O AppDir/usr/share/tessdata/eng.traineddata || true
|
||||
fi
|
||||
|
||||
# Create wrapper script that sets TESSDATA_PREFIX
|
||||
mv AppDir/usr/bin/ccextractor AppDir/usr/bin/ccextractor.bin
|
||||
cat > AppDir/usr/bin/ccextractor << 'WRAPPER'
|
||||
#!/bin/bash
|
||||
SELF_DIR="$(dirname "$(readlink -f "$0")")"
|
||||
export TESSDATA_PREFIX="${SELF_DIR}/../share/tessdata"
|
||||
exec "${SELF_DIR}/ccextractor.bin" "$@"
|
||||
WRAPPER
|
||||
chmod +x AppDir/usr/bin/ccextractor
|
||||
fi
|
||||
|
||||
# Determine output name based on build type
|
||||
ARCH="x86_64"
|
||||
case "$BUILD_TYPE" in
|
||||
minimal)
|
||||
OUTPUT_NAME="ccextractor-minimal-${ARCH}.AppImage"
|
||||
;;
|
||||
ocr)
|
||||
OUTPUT_NAME="ccextractor-${ARCH}.AppImage"
|
||||
;;
|
||||
hardsubx)
|
||||
OUTPUT_NAME="ccextractor-hardsubx-${ARCH}.AppImage"
|
||||
;;
|
||||
esac
|
||||
|
||||
# Build AppImage
|
||||
echo "Building AppImage..."
|
||||
export OUTPUT="$OUTPUT_NAME"
|
||||
|
||||
# Determine which executable to pass to linuxdeploy
|
||||
# For OCR builds, we have a wrapper script, so pass the actual binary (.bin)
|
||||
if [ -f "AppDir/usr/bin/ccextractor.bin" ]; then
|
||||
LINUXDEPLOY_EXEC="AppDir/usr/bin/ccextractor.bin"
|
||||
else
|
||||
LINUXDEPLOY_EXEC="AppDir/usr/bin/ccextractor"
|
||||
fi
|
||||
|
||||
./linuxdeploy-x86_64.AppImage \
|
||||
--appdir=AppDir \
|
||||
--executable="$LINUXDEPLOY_EXEC" \
|
||||
--desktop-file=AppDir/ccextractor.desktop \
|
||||
--icon-file=AppDir/ccextractor.png \
|
||||
--output=appimage
|
||||
|
||||
# Move to output directory
|
||||
mv "$OUTPUT_NAME" "$SCRIPT_DIR/"
|
||||
|
||||
echo "=========================================="
|
||||
echo "AppImage built successfully!"
|
||||
echo "Output: $SCRIPT_DIR/$OUTPUT_NAME"
|
||||
echo ""
|
||||
echo "Test with: $SCRIPT_DIR/$OUTPUT_NAME --version"
|
||||
echo "=========================================="
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
# Process this file with autoconf to produce a configure script.
|
||||
|
||||
AC_PREREQ([2.71])
|
||||
AC_INIT([CCExtractor], [0.94], [carlos@ccextractor.org])
|
||||
AC_INIT([CCExtractor], [0.96.5], [carlos@ccextractor.org])
|
||||
AC_CONFIG_AUX_DIR([build-conf])
|
||||
AC_CONFIG_SRCDIR([../src/ccextractor.c])
|
||||
AM_INIT_AUTOMAKE([foreign subdir-objects])
|
||||
@@ -32,6 +32,11 @@ AC_CHECK_LIB([avformat], [avformat_version], [HAS_AVFORMAT=1 && PKG_CHECK_MODULE
|
||||
AC_CHECK_LIB([avutil], [avutil_version], [HAS_AVUTIL=1 && PKG_CHECK_MODULES([libavutil], [libavutil])], [HAS_AVUTIL=0])
|
||||
AC_CHECK_LIB([swscale], [swscale_version], [HAS_SWSCALE=1 && PKG_CHECK_MODULES([libswscale], [libswscale])], [HAS_SWSCALE=0])
|
||||
|
||||
# Check for GPAC library (required for MP4 support)
|
||||
PKG_CHECK_MODULES([gpac], [gpac], [HAS_GPAC=1], [HAS_GPAC=0])
|
||||
AS_IF([test $HAS_GPAC -eq 0],
|
||||
[AC_MSG_ERROR([GPAC library not found. Install gpac-devel (Fedora/RHEL), libgpac-dev (Debian/Ubuntu), or gpac (Arch) before proceeding.])])
|
||||
|
||||
# Checks for header files.
|
||||
AC_CHECK_HEADERS([arpa/inet.h fcntl.h float.h inttypes.h limits.h locale.h malloc.h netdb.h netinet/in.h stddef.h stdint.h stdlib.h string.h sys/socket.h sys/time.h sys/timeb.h termios.h unistd.h wchar.h])
|
||||
|
||||
@@ -104,7 +109,7 @@ if test "x$with_rust" = "xyes" ; then
|
||||
AS_IF([test "$RUSTC" = "notfound"], [AC_MSG_ERROR([rustc is required])])
|
||||
|
||||
rustc_version=$(rustc --version)
|
||||
MSRV="1.54.0"
|
||||
MSRV="1.87.0"
|
||||
AX_COMPARE_VERSION($rustc_version, [ge], [$MSRV],
|
||||
[AC_MSG_RESULT(rustc >= $MSRV)],
|
||||
[AC_MSG_ERROR([Minimum supported rust version(MSRV) is $MSRV, please upgrade rust])])
|
||||
@@ -149,7 +154,7 @@ AS_IF([ (test x$ocr = xtrue || test x$hardsubx = xtrue) && test ! $HAS_LEPT -gt
|
||||
AM_CONDITIONAL(HARDSUBX_IS_ENABLED, [ test x$hardsubx = xtrue ])
|
||||
AM_CONDITIONAL(OCR_IS_ENABLED, [ test x$ocr = xtrue || test x$hardsubx = xtrue ])
|
||||
AM_CONDITIONAL(FFMPEG_IS_ENABLED, [ test x$ffmpeg = xtrue ])
|
||||
AM_CONDITIONAL(TESSERACT_PRESENT, [ test ! -z $(pkg-config --libs-only-l --silence-errors tesseract) ])
|
||||
AM_CONDITIONAL(TESSERACT_PRESENT, [ test ! -z "$(pkg-config --libs-only-l --silence-errors tesseract)" ])
|
||||
AM_CONDITIONAL(TESSERACT_PRESENT_RPI, [ test -d "/usr/include/tesseract" && test $(ls -A /usr/include/tesseract | wc -l) -gt 0 ])
|
||||
AM_CONDITIONAL(SYS_IS_LINUX, [ test $(uname -s) = "Linux"])
|
||||
AM_CONDITIONAL(SYS_IS_MAC, [ test $(uname -s) = "Darwin"])
|
||||
|
||||
@@ -123,6 +123,8 @@ ccextractor_SOURCES = \
|
||||
../src/lib_ccx/list.h \
|
||||
../src/lib_ccx/matroska.c \
|
||||
../src/lib_ccx/matroska.h \
|
||||
../src/lib_ccx/vobsub_decoder.c \
|
||||
../src/lib_ccx/vobsub_decoder.h \
|
||||
../src/lib_ccx/mp4.c \
|
||||
../src/lib_ccx/myth.c \
|
||||
../src/lib_ccx/networking.c \
|
||||
|
||||
@@ -20,7 +20,19 @@ while [[ $# -gt 0 ]]; do
|
||||
;;
|
||||
-hardsubx)
|
||||
HARDSUBX=true
|
||||
RUST_FEATURES="--features hardsubx_ocr"
|
||||
ENABLE_OCR=true
|
||||
# Allow overriding FFmpeg version via environment variable
|
||||
if [ -n "$FFMPEG_VERSION" ]; then
|
||||
RUST_FEATURES="--features hardsubx_ocr,$FFMPEG_VERSION"
|
||||
else
|
||||
RUST_FEATURES="--features hardsubx_ocr"
|
||||
fi
|
||||
shift
|
||||
;;
|
||||
-system-libs)
|
||||
# Use system-installed libraries via pkg-config instead of bundled ones
|
||||
# This is required for Homebrew formula compatibility
|
||||
USE_SYSTEM_LIBS=true
|
||||
shift
|
||||
;;
|
||||
-*)
|
||||
@@ -30,7 +42,21 @@ while [[ $# -gt 0 ]]; do
|
||||
esac
|
||||
done
|
||||
|
||||
BLD_FLAGS="-std=gnu99 -Wno-write-strings -Wno-pointer-sign -D_FILE_OFFSET_BITS=64 -DVERSION_FILE_PRESENT -Dfopen64=fopen -Dopen64=open -Dlseek64=lseek -DFT2_BUILD_LIBRARY -DGPAC_DISABLE_VTT -DGPAC_DISABLE_OD_DUMP -DGPAC_DISABLE_REMOTERY -DNO_GZIP"
|
||||
# Determine architecture based on cargo (to ensure consistency with Rust part)
|
||||
CARGO_ARCH=$(file $(which cargo) | grep -o 'x86_64\|arm64')
|
||||
if [[ "$CARGO_ARCH" == "x86_64" ]]; then
|
||||
echo "Detected Intel (x86_64) Cargo. Forcing x86_64 build to match Rust and libraries..."
|
||||
BLD_ARCH="-arch x86_64"
|
||||
else
|
||||
BLD_ARCH="-arch arm64"
|
||||
fi
|
||||
|
||||
BLD_FLAGS="$BLD_ARCH -std=gnu99 -Wno-write-strings -Wno-pointer-sign -D_FILE_OFFSET_BITS=64 -DVERSION_FILE_PRESENT -Dfopen64=fopen -Dopen64=open -Dlseek64=lseek"
|
||||
|
||||
# Add flags for bundled libraries (not needed when using system libs)
|
||||
if [[ "$USE_SYSTEM_LIBS" != "true" ]]; then
|
||||
BLD_FLAGS="$BLD_FLAGS -DFT2_BUILD_LIBRARY -DGPAC_DISABLE_VTT -DGPAC_DISABLE_OD_DUMP -DGPAC_DISABLE_REMOTERY -DNO_GZIP"
|
||||
fi
|
||||
|
||||
# Add debug flags if needed
|
||||
if [[ "$DEBUG" == "true" ]]; then
|
||||
@@ -47,7 +73,68 @@ if [[ "$HARDSUBX" == "true" ]]; then
|
||||
BLD_FLAGS="$BLD_FLAGS -DENABLE_HARDSUBX"
|
||||
fi
|
||||
|
||||
BLD_INCLUDE="-I../src/ -I../src/lib_ccx -I../src/lib_hash -I../src/thirdparty/libpng -I../src/thirdparty -I../src/thirdparty/zlib -I../src/thirdparty/freetype/include `pkg-config --cflags --silence-errors gpac`"
|
||||
# Set up include paths based on whether we're using system libs or bundled
|
||||
if [[ "$USE_SYSTEM_LIBS" == "true" ]]; then
|
||||
# Use system libraries via pkg-config (for Homebrew compatibility)
|
||||
# Note: -I../src/thirdparty/lib_hash is needed so that "../lib_hash/sha2.h" resolves correctly
|
||||
# (the .. goes up from lib_hash to thirdparty, then lib_hash/sha2.h finds the file)
|
||||
BLD_INCLUDE="-I../src/ -I../src/lib_ccx -I../src/thirdparty/lib_hash -I../src/thirdparty"
|
||||
BLD_INCLUDE="$BLD_INCLUDE $(pkg-config --cflags --silence-errors freetype2)"
|
||||
BLD_INCLUDE="$BLD_INCLUDE $(pkg-config --cflags --silence-errors gpac)"
|
||||
BLD_INCLUDE="$BLD_INCLUDE $(pkg-config --cflags --silence-errors libpng)"
|
||||
BLD_INCLUDE="$BLD_INCLUDE $(pkg-config --cflags --silence-errors libprotobuf-c)"
|
||||
BLD_INCLUDE="$BLD_INCLUDE $(pkg-config --cflags --silence-errors libutf8proc)"
|
||||
else
|
||||
# Use bundled libraries (default for standalone builds)
|
||||
BLD_INCLUDE="-I../src/ -I../src/lib_ccx -I../src/thirdparty/lib_hash -I../src/thirdparty/libpng -I../src/thirdparty -I../src/thirdparty/zlib -I../src/thirdparty/freetype/include $(pkg-config --cflags --silence-errors gpac)"
|
||||
fi
|
||||
|
||||
# Add FFmpeg include path for Mac
|
||||
if [[ -d "/opt/homebrew/Cellar/ffmpeg" ]]; then
|
||||
FFMPEG_VERSION=$(ls -1 /opt/homebrew/Cellar/ffmpeg | head -1)
|
||||
if [[ -n "$FFMPEG_VERSION" ]]; then
|
||||
BLD_INCLUDE="$BLD_INCLUDE -I/opt/homebrew/Cellar/ffmpeg/$FFMPEG_VERSION/include"
|
||||
fi
|
||||
elif [[ -d "/usr/local/Cellar/ffmpeg" ]]; then
|
||||
FFMPEG_VERSION=$(ls -1 /usr/local/Cellar/ffmpeg | head -1)
|
||||
if [[ -n "$FFMPEG_VERSION" ]]; then
|
||||
BLD_INCLUDE="$BLD_INCLUDE -I/usr/local/Cellar/ffmpeg/$FFMPEG_VERSION/include"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Add Leptonica include path for Mac
|
||||
if [[ -d "/opt/homebrew/Cellar/leptonica" ]]; then
|
||||
LEPT_VERSION=$(ls -1 /opt/homebrew/Cellar/leptonica | head -1)
|
||||
if [[ -n "$LEPT_VERSION" ]]; then
|
||||
BLD_INCLUDE="$BLD_INCLUDE -I/opt/homebrew/Cellar/leptonica/$LEPT_VERSION/include"
|
||||
fi
|
||||
elif [[ -d "/usr/local/Cellar/leptonica" ]]; then
|
||||
LEPT_VERSION=$(ls -1 /usr/local/Cellar/leptonica | head -1)
|
||||
if [[ -n "$LEPT_VERSION" ]]; then
|
||||
BLD_INCLUDE="$BLD_INCLUDE -I/usr/local/Cellar/leptonica/$LEPT_VERSION/include"
|
||||
fi
|
||||
elif [[ -d "/opt/homebrew/include/leptonica" ]]; then
|
||||
BLD_INCLUDE="$BLD_INCLUDE -I/opt/homebrew/include"
|
||||
elif [[ -d "/usr/local/include/leptonica" ]]; then
|
||||
BLD_INCLUDE="$BLD_INCLUDE -I/usr/local/include"
|
||||
fi
|
||||
|
||||
# Add Tesseract include path for Mac
|
||||
if [[ -d "/opt/homebrew/Cellar/tesseract" ]]; then
|
||||
TESS_VERSION=$(ls -1 /opt/homebrew/Cellar/tesseract | head -1)
|
||||
if [[ -n "$TESS_VERSION" ]]; then
|
||||
BLD_INCLUDE="$BLD_INCLUDE -I/opt/homebrew/Cellar/tesseract/$TESS_VERSION/include"
|
||||
fi
|
||||
elif [[ -d "/usr/local/Cellar/tesseract" ]]; then
|
||||
TESS_VERSION=$(ls -1 /usr/local/Cellar/tesseract | head -1)
|
||||
if [[ -n "$TESS_VERSION" ]]; then
|
||||
BLD_INCLUDE="$BLD_INCLUDE -I/usr/local/Cellar/tesseract/$TESS_VERSION/include"
|
||||
fi
|
||||
elif [[ -d "/opt/homebrew/include/tesseract" ]]; then
|
||||
BLD_INCLUDE="$BLD_INCLUDE -I/opt/homebrew/include"
|
||||
elif [[ -d "/usr/local/include/tesseract" ]]; then
|
||||
BLD_INCLUDE="$BLD_INCLUDE -I/usr/local/include"
|
||||
fi
|
||||
|
||||
if [[ "$ENABLE_OCR" == "true" ]]; then
|
||||
BLD_INCLUDE="$BLD_INCLUDE `pkg-config --cflags --silence-errors tesseract`"
|
||||
@@ -55,61 +142,111 @@ fi
|
||||
|
||||
SRC_CCX="$(find ../src/lib_ccx -name '*.c')"
|
||||
SRC_LIB_HASH="$(find ../src/thirdparty/lib_hash -name '*.c')"
|
||||
SRC_LIBPNG="$(find ../src/thirdparty/libpng -name '*.c')"
|
||||
SRC_UTF8="../src/thirdparty/utf8proc/utf8proc.c"
|
||||
SRC_ZLIB="$(find ../src/thirdparty/zlib -name '*.c')"
|
||||
SRC_FREETYPE="../src/thirdparty/freetype/autofit/autofit.c \
|
||||
../src/thirdparty/freetype/base/ftbase.c \
|
||||
../src/thirdparty/freetype/base/ftbbox.c \
|
||||
../src/thirdparty/freetype/base/ftbdf.c \
|
||||
../src/thirdparty/freetype/base/ftbitmap.c \
|
||||
../src/thirdparty/freetype/base/ftcid.c \
|
||||
../src/thirdparty/freetype/base/ftfntfmt.c \
|
||||
../src/thirdparty/freetype/base/ftfstype.c \
|
||||
../src/thirdparty/freetype/base/ftgasp.c \
|
||||
../src/thirdparty/freetype/base/ftglyph.c \
|
||||
../src/thirdparty/freetype/base/ftgxval.c \
|
||||
../src/thirdparty/freetype/base/ftinit.c \
|
||||
../src/thirdparty/freetype/base/ftlcdfil.c \
|
||||
../src/thirdparty/freetype/base/ftmm.c \
|
||||
../src/thirdparty/freetype/base/ftotval.c \
|
||||
../src/thirdparty/freetype/base/ftpatent.c \
|
||||
../src/thirdparty/freetype/base/ftpfr.c \
|
||||
../src/thirdparty/freetype/base/ftstroke.c \
|
||||
../src/thirdparty/freetype/base/ftsynth.c \
|
||||
../src/thirdparty/freetype/base/ftsystem.c \
|
||||
../src/thirdparty/freetype/base/fttype1.c \
|
||||
../src/thirdparty/freetype/base/ftwinfnt.c \
|
||||
../src/thirdparty/freetype/bdf/bdf.c \
|
||||
../src/thirdparty/freetype/bzip2/ftbzip2.c \
|
||||
../src/thirdparty/freetype/cache/ftcache.c \
|
||||
../src/thirdparty/freetype/cff/cff.c \
|
||||
../src/thirdparty/freetype/cid/type1cid.c \
|
||||
../src/thirdparty/freetype/gzip/ftgzip.c \
|
||||
../src/thirdparty/freetype/lzw/ftlzw.c \
|
||||
../src/thirdparty/freetype/pcf/pcf.c \
|
||||
../src/thirdparty/freetype/pfr/pfr.c \
|
||||
../src/thirdparty/freetype/psaux/psaux.c \
|
||||
../src/thirdparty/freetype/pshinter/pshinter.c \
|
||||
../src/thirdparty/freetype/psnames/psnames.c \
|
||||
../src/thirdparty/freetype/raster/raster.c \
|
||||
../src/thirdparty/freetype/sfnt/sfnt.c \
|
||||
../src/thirdparty/freetype/smooth/smooth.c \
|
||||
../src/thirdparty/freetype/truetype/truetype.c \
|
||||
../src/thirdparty/freetype/type1/type1.c \
|
||||
../src/thirdparty/freetype/type42/type42.c \
|
||||
../src/thirdparty/freetype/winfonts/winfnt.c"
|
||||
|
||||
BLD_SOURCES="../src/ccextractor.c $SRC_CCX $SRC_LIB_HASH $SRC_LIBPNG $SRC_UTF8 $SRC_ZLIB $SRC_FREETYPE"
|
||||
# Set up sources and linker based on whether we're using system libs or bundled
|
||||
if [[ "$USE_SYSTEM_LIBS" == "true" ]]; then
|
||||
# Use system libraries - don't compile bundled sources
|
||||
BLD_SOURCES="../src/ccextractor.c $SRC_CCX $SRC_LIB_HASH"
|
||||
|
||||
BLD_LINKER="-lm -liconv -lpthread -ldl `pkg-config --libs --silence-errors gpac`"
|
||||
BLD_LINKER="-lm -liconv -lpthread -ldl"
|
||||
BLD_LINKER="$BLD_LINKER $(pkg-config --libs --silence-errors freetype2)"
|
||||
BLD_LINKER="$BLD_LINKER $(pkg-config --libs --silence-errors gpac)"
|
||||
BLD_LINKER="$BLD_LINKER $(pkg-config --libs --silence-errors libpng)"
|
||||
BLD_LINKER="$BLD_LINKER $(pkg-config --libs --silence-errors libprotobuf-c)"
|
||||
BLD_LINKER="$BLD_LINKER $(pkg-config --libs --silence-errors libutf8proc)"
|
||||
BLD_LINKER="$BLD_LINKER $(pkg-config --libs --silence-errors zlib)"
|
||||
else
|
||||
# Use bundled libraries (default)
|
||||
SRC_LIBPNG="$(find ../src/thirdparty/libpng -name '*.c')"
|
||||
SRC_UTF8="../src/thirdparty/utf8proc/utf8proc.c"
|
||||
SRC_ZLIB="$(find ../src/thirdparty/zlib -name '*.c')"
|
||||
SRC_FREETYPE="../src/thirdparty/freetype/autofit/autofit.c \
|
||||
../src/thirdparty/freetype/base/ftbase.c \
|
||||
../src/thirdparty/freetype/base/ftbbox.c \
|
||||
../src/thirdparty/freetype/base/ftbdf.c \
|
||||
../src/thirdparty/freetype/base/ftbitmap.c \
|
||||
../src/thirdparty/freetype/base/ftcid.c \
|
||||
../src/thirdparty/freetype/base/ftfntfmt.c \
|
||||
../src/thirdparty/freetype/base/ftfstype.c \
|
||||
../src/thirdparty/freetype/base/ftgasp.c \
|
||||
../src/thirdparty/freetype/base/ftglyph.c \
|
||||
../src/thirdparty/freetype/base/ftgxval.c \
|
||||
../src/thirdparty/freetype/base/ftinit.c \
|
||||
../src/thirdparty/freetype/base/ftlcdfil.c \
|
||||
../src/thirdparty/freetype/base/ftmm.c \
|
||||
../src/thirdparty/freetype/base/ftotval.c \
|
||||
../src/thirdparty/freetype/base/ftpatent.c \
|
||||
../src/thirdparty/freetype/base/ftpfr.c \
|
||||
../src/thirdparty/freetype/base/ftstroke.c \
|
||||
../src/thirdparty/freetype/base/ftsynth.c \
|
||||
../src/thirdparty/freetype/base/ftsystem.c \
|
||||
../src/thirdparty/freetype/base/fttype1.c \
|
||||
../src/thirdparty/freetype/base/ftwinfnt.c \
|
||||
../src/thirdparty/freetype/bdf/bdf.c \
|
||||
../src/thirdparty/freetype/bzip2/ftbzip2.c \
|
||||
../src/thirdparty/freetype/cache/ftcache.c \
|
||||
../src/thirdparty/freetype/cff/cff.c \
|
||||
../src/thirdparty/freetype/cid/type1cid.c \
|
||||
../src/thirdparty/freetype/gzip/ftgzip.c \
|
||||
../src/thirdparty/freetype/lzw/ftlzw.c \
|
||||
../src/thirdparty/freetype/pcf/pcf.c \
|
||||
../src/thirdparty/freetype/pfr/pfr.c \
|
||||
../src/thirdparty/freetype/psaux/psaux.c \
|
||||
../src/thirdparty/freetype/pshinter/pshinter.c \
|
||||
../src/thirdparty/freetype/psnames/psnames.c \
|
||||
../src/thirdparty/freetype/raster/raster.c \
|
||||
../src/thirdparty/freetype/sfnt/sfnt.c \
|
||||
../src/thirdparty/freetype/smooth/smooth.c \
|
||||
../src/thirdparty/freetype/truetype/truetype.c \
|
||||
../src/thirdparty/freetype/type1/type1.c \
|
||||
../src/thirdparty/freetype/type42/type42.c \
|
||||
../src/thirdparty/freetype/winfonts/winfnt.c"
|
||||
|
||||
BLD_SOURCES="../src/ccextractor.c $SRC_CCX $SRC_LIB_HASH $SRC_LIBPNG $SRC_UTF8 $SRC_ZLIB $SRC_FREETYPE"
|
||||
BLD_LINKER="-lm -liconv -lpthread -ldl $(pkg-config --libs --silence-errors gpac)"
|
||||
fi
|
||||
|
||||
if [[ "$ENABLE_OCR" == "true" ]]; then
|
||||
BLD_LINKER="$BLD_LINKER `pkg-config --libs --silence-errors tesseract` `pkg-config --libs --silence-errors lept`"
|
||||
fi
|
||||
|
||||
if [[ "$HARDSUBX" == "true" ]]; then
|
||||
BLD_LINKER="$BLD_LINKER -lswscale -lavutil -pthread -lavformat -lavcodec -lavfilter"
|
||||
# Add FFmpeg library path for Mac
|
||||
if [[ -d "/opt/homebrew/Cellar/ffmpeg" ]]; then
|
||||
FFMPEG_VERSION=$(ls -1 /opt/homebrew/Cellar/ffmpeg | head -1)
|
||||
if [[ -n "$FFMPEG_VERSION" ]]; then
|
||||
BLD_LINKER="$BLD_LINKER -L/opt/homebrew/Cellar/ffmpeg/$FFMPEG_VERSION/lib"
|
||||
fi
|
||||
elif [[ -d "/usr/local/Cellar/ffmpeg" ]]; then
|
||||
FFMPEG_VERSION=$(ls -1 /usr/local/Cellar/ffmpeg | head -1)
|
||||
if [[ -n "$FFMPEG_VERSION" ]]; then
|
||||
BLD_LINKER="$BLD_LINKER -L/usr/local/Cellar/ffmpeg/$FFMPEG_VERSION/lib"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Add library paths for Leptonica and Tesseract from Cellar
|
||||
if [[ -d "/opt/homebrew/Cellar/leptonica" ]]; then
|
||||
LEPT_VERSION=$(ls -1 /opt/homebrew/Cellar/leptonica | head -1)
|
||||
if [[ -n "$LEPT_VERSION" ]]; then
|
||||
BLD_LINKER="$BLD_LINKER -L/opt/homebrew/Cellar/leptonica/$LEPT_VERSION/lib"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ -d "/opt/homebrew/Cellar/tesseract" ]]; then
|
||||
TESS_VERSION=$(ls -1 /opt/homebrew/Cellar/tesseract | head -1)
|
||||
if [[ -n "$TESS_VERSION" ]]; then
|
||||
BLD_LINKER="$BLD_LINKER -L/opt/homebrew/Cellar/tesseract/$TESS_VERSION/lib"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Also add homebrew lib path as fallback
|
||||
if [[ -d "/opt/homebrew/lib" ]]; then
|
||||
BLD_LINKER="$BLD_LINKER -L/opt/homebrew/lib"
|
||||
elif [[ -d "/usr/local/lib" ]]; then
|
||||
BLD_LINKER="$BLD_LINKER -L/usr/local/lib"
|
||||
fi
|
||||
|
||||
BLD_LINKER="$BLD_LINKER -lswscale -lavutil -pthread -lavformat -lavcodec -lavfilter -lleptonica -ltesseract"
|
||||
fi
|
||||
|
||||
echo "Running pre-build script..."
|
||||
@@ -127,7 +264,7 @@ fi
|
||||
rustc_version="$(rustc --version)"
|
||||
semver=( ${rustc_version//./ } )
|
||||
version="${semver[1]}.${semver[2]}.${semver[3]}"
|
||||
MSRV="1.54.0"
|
||||
MSRV="1.87.0"
|
||||
if [ "$(printf '%s\n' "$MSRV" "$version" | sort -V | head -n1)" = "$MSRV" ]; then
|
||||
echo "rustc >= MSRV(${MSRV})"
|
||||
else
|
||||
@@ -180,4 +317,4 @@ if [[ "$out" != "" ]]; then
|
||||
echo "Compilation successful, compiler message shown in previous lines"
|
||||
else
|
||||
echo "Compilation successful, no compiler messages."
|
||||
fi
|
||||
fi
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
# Process this file with autoconf to produce a configure script.
|
||||
|
||||
AC_PREREQ([2.71])
|
||||
AC_INIT([CCExtractor],[0.94],[carlos@ccextractor.org])
|
||||
AC_INIT([CCExtractor],[0.96.5],[carlos@ccextractor.org])
|
||||
AC_CONFIG_AUX_DIR([build-conf])
|
||||
AC_CONFIG_SRCDIR([../src/ccextractor.c])
|
||||
AM_INIT_AUTOMAKE([foreign subdir-objects])
|
||||
@@ -25,7 +25,7 @@ fi
|
||||
|
||||
# Checks for libraries.
|
||||
AC_CHECK_LIB([m], [sin], [], [AC_MSG_ERROR(Math library not installed. Install it before proceeding.)])
|
||||
AC_CHECK_LIB([lept], [getLeptonicaVersion], [HAS_LEPT=1 && PKG_CHECK_MODULES([lept], [lept])], [HAS_LEPT=0])
|
||||
AC_CHECK_LIB([leptonica], [getLeptonicaVersion], [HAS_LEPT=1 && PKG_CHECK_MODULES([lept], [lept])], [HAS_LEPT=0])
|
||||
AC_CHECK_LIB([tesseract], [TessVersion], [HAS_TESSERACT=1 && PKG_CHECK_MODULES([tesseract], [tesseract])], [HAS_TESSERACT=0])
|
||||
AC_CHECK_LIB([avcodec], [avcodec_version], [HAS_AVCODEC=1 && PKG_CHECK_MODULES([libavcodec], [libavcodec])], [HAS_AVCODEC=0])
|
||||
AC_CHECK_LIB([avformat], [avformat_version], [HAS_AVFORMAT=1 && PKG_CHECK_MODULES([libavformat], [libavformat])], [HAS_AVFORMAT=0])
|
||||
@@ -104,7 +104,7 @@ if test "x$with_rust" = "xyes" ; then
|
||||
AS_IF([test "$RUSTC" = "notfound"], [AC_MSG_ERROR([rustc is required])])
|
||||
|
||||
rustc_version=$(rustc --version)
|
||||
MSRV="1.54.0"
|
||||
MSRV="1.87.0"
|
||||
AX_COMPARE_VERSION($rustc_version, [ge], [$MSRV],
|
||||
[AC_MSG_RESULT(rustc >= $MSRV)],
|
||||
[AC_MSG_ERROR([Minimum supported rust version(MSRV) is $MSRV, please upgrade rust])])
|
||||
@@ -148,7 +148,7 @@ AS_IF([ (test x$ocr = xtrue || test x$hardsubx = xtrue) && test ! $HAS_LEPT -gt
|
||||
AM_CONDITIONAL(HARDSUBX_IS_ENABLED, [ test x$hardsubx = xtrue ])
|
||||
AM_CONDITIONAL(OCR_IS_ENABLED, [ test x$ocr = xtrue || test x$hardsubx = xtrue ])
|
||||
AM_CONDITIONAL(FFMPEG_IS_ENABLED, [ test x$ffmpeg = xtrue ])
|
||||
AM_CONDITIONAL(TESSERACT_PRESENT, [ test ! -z $(pkg-config --libs-only-l --silence-errors tesseract) ])
|
||||
AM_CONDITIONAL(TESSERACT_PRESENT, [ test ! -z "$(pkg-config --libs-only-l --silence-errors tesseract)" ])
|
||||
AM_CONDITIONAL(TESSERACT_PRESENT_RPI, [ test -d "/usr/include/tesseract" && test $(ls -A /usr/include/tesseract | wc -l) -gt 0 ])
|
||||
AM_CONDITIONAL(SYS_IS_LINUX, [ test $(uname -s) = "Linux"])
|
||||
AM_CONDITIONAL(SYS_IS_MAC, [ test $(uname -s) = "Darwin"])
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
pkgname=ccextractor
|
||||
pkgver=0.94
|
||||
pkgver=0.96.5
|
||||
pkgrel=1
|
||||
pkgdesc="A closed captions and teletext subtitles extractor for video streams."
|
||||
arch=('i686' 'x86_64')
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
Name: ccextractor
|
||||
Version: 0.94
|
||||
Version: 0.96.5
|
||||
Release: 1
|
||||
Summary: A closed captions and teletext subtitles extractor for video streams.
|
||||
Group: Applications/Internet
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/bin/bash
|
||||
TYPE="debian" # can be one of 'slackware', 'debian', 'rpm'
|
||||
PROGRAM_NAME="ccextractor"
|
||||
VERSION="0.94"
|
||||
VERSION="0.96.5"
|
||||
RELEASE="1"
|
||||
LICENSE="GPL-2.0"
|
||||
MAINTAINER="carlos@ccextractor.org"
|
||||
|
||||
96
packaging/README.md
Normal file
96
packaging/README.md
Normal file
@@ -0,0 +1,96 @@
|
||||
# CCExtractor Packaging
|
||||
|
||||
This directory contains packaging configurations for Windows package managers.
|
||||
|
||||
## Windows Package Manager (winget)
|
||||
|
||||
### Initial Setup (One-time)
|
||||
|
||||
1. **Calculate MSI hash** for the current release:
|
||||
```powershell
|
||||
certutil -hashfile CCExtractor.0.96.1.msi SHA256
|
||||
```
|
||||
|
||||
2. **Update the manifest files** in `winget/` with the SHA256 hash
|
||||
|
||||
3. **Fork microsoft/winget-pkgs** to the CCExtractor organization:
|
||||
- Go to https://github.com/microsoft/winget-pkgs
|
||||
- Fork to https://github.com/CCExtractor/winget-pkgs
|
||||
|
||||
4. **Submit initial manifest** via PR:
|
||||
- Clone your fork
|
||||
- Create directory: `manifests/c/CCExtractor/CCExtractor/0.96.1/`
|
||||
- Copy the three YAML files from `winget/`
|
||||
- Submit PR to microsoft/winget-pkgs
|
||||
|
||||
5. **Create GitHub token** for automation:
|
||||
- Go to GitHub Settings > Developer settings > Personal access tokens > Tokens (classic)
|
||||
- Create token with `public_repo` scope
|
||||
- Add as secret `WINGET_TOKEN` in CCExtractor/ccextractor repository
|
||||
|
||||
### Automated Updates
|
||||
|
||||
After the initial submission is merged, the `publish_winget.yml` workflow will automatically submit PRs for new releases.
|
||||
|
||||
## Chocolatey
|
||||
|
||||
### Initial Setup (One-time)
|
||||
|
||||
1. **Create Chocolatey account**:
|
||||
- Register at https://community.chocolatey.org/account/Register
|
||||
|
||||
2. **Get API key**:
|
||||
- Go to https://community.chocolatey.org/account
|
||||
- Copy your API key
|
||||
|
||||
3. **Add secret**:
|
||||
- Add `CHOCOLATEY_API_KEY` secret to CCExtractor/ccextractor repository
|
||||
|
||||
### Package Structure
|
||||
|
||||
```
|
||||
chocolatey/
|
||||
├── ccextractor.nuspec # Package metadata
|
||||
└── tools/
|
||||
├── chocolateyInstall.ps1 # Installation script
|
||||
└── chocolateyUninstall.ps1 # Uninstallation script
|
||||
```
|
||||
|
||||
### Manual Testing
|
||||
|
||||
```powershell
|
||||
cd packaging/chocolatey
|
||||
|
||||
# Update version and checksum in files first, then:
|
||||
choco pack ccextractor.nuspec
|
||||
|
||||
# Test locally
|
||||
choco install ccextractor --source="'.'" --yes --force
|
||||
|
||||
# Verify
|
||||
ccextractor --version
|
||||
```
|
||||
|
||||
### Automated Updates
|
||||
|
||||
The `publish_chocolatey.yml` workflow automatically:
|
||||
1. Downloads the MSI from the release
|
||||
2. Calculates the SHA256 checksum
|
||||
3. Updates the nuspec and install script
|
||||
4. Builds and tests the package
|
||||
5. Pushes to Chocolatey
|
||||
|
||||
Note: Chocolatey packages go through moderation before being publicly available.
|
||||
|
||||
## Workflow Triggers
|
||||
|
||||
Both workflows trigger on:
|
||||
- **Release published**: Automatic publishing when a new release is created
|
||||
- **Manual dispatch**: Can be triggered manually with a specific tag
|
||||
|
||||
## Secrets Required
|
||||
|
||||
| Secret | Purpose |
|
||||
|--------|---------|
|
||||
| `WINGET_TOKEN` | GitHub PAT with `public_repo` scope for winget PRs |
|
||||
| `CHOCOLATEY_API_KEY` | Chocolatey API key for package uploads |
|
||||
43
packaging/chocolatey/ccextractor.nuspec
Normal file
43
packaging/chocolatey/ccextractor.nuspec
Normal file
@@ -0,0 +1,43 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<package xmlns="http://schemas.microsoft.com/packaging/2015/06/nuspec.xsd">
|
||||
<metadata>
|
||||
<id>ccextractor</id>
|
||||
<version>0.96.5</version>
|
||||
<title>CCExtractor</title>
|
||||
<authors>CCExtractor Development Team</authors>
|
||||
<owners>CCExtractor</owners>
|
||||
<licenseUrl>https://github.com/CCExtractor/ccextractor/blob/master/LICENSE.txt</licenseUrl>
|
||||
<projectUrl>https://ccextractor.org</projectUrl>
|
||||
<iconUrl>https://raw.githubusercontent.com/CCExtractor/ccextractor/master/windows/CCX.ico</iconUrl>
|
||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||
<description>CCExtractor is a tool that analyzes video files and produces independent subtitle files from the closed captions data.
|
||||
|
||||
### Features
|
||||
- Extracts closed captions from various video formats (MPEG, H.264, MKV, MP4, etc.)
|
||||
- Supports multiple input sources including DVDs, DVRs, and live TV captures
|
||||
- Outputs to multiple formats (SRT, WebVTT, SAMI, transcript, etc.)
|
||||
- OCR support for bitmap-based subtitles (DVB, teletext)
|
||||
- Includes a graphical user interface
|
||||
|
||||
### Usage
|
||||
After installation, run `ccextractor` from the command line or use the GUI.
|
||||
|
||||
```
|
||||
ccextractor video.ts -o output.srt
|
||||
```
|
||||
|
||||
For more options: `ccextractor --help`
|
||||
</description>
|
||||
<summary>Extract closed captions and subtitles from video files</summary>
|
||||
<releaseNotes>https://github.com/CCExtractor/ccextractor/releases</releaseNotes>
|
||||
<copyright>Copyright (c) CCExtractor Development</copyright>
|
||||
<tags>subtitles closed-captions video extraction accessibility srt dvb teletext ocr media cli</tags>
|
||||
<projectSourceUrl>https://github.com/CCExtractor/ccextractor</projectSourceUrl>
|
||||
<packageSourceUrl>https://github.com/CCExtractor/ccextractor/tree/master/packaging/chocolatey</packageSourceUrl>
|
||||
<docsUrl>https://github.com/CCExtractor/ccextractor/wiki</docsUrl>
|
||||
<bugTrackerUrl>https://github.com/CCExtractor/ccextractor/issues</bugTrackerUrl>
|
||||
</metadata>
|
||||
<files>
|
||||
<file src="tools\**" target="tools" />
|
||||
</files>
|
||||
</package>
|
||||
24
packaging/chocolatey/tools/chocolateyInstall.ps1
Normal file
24
packaging/chocolatey/tools/chocolateyInstall.ps1
Normal file
@@ -0,0 +1,24 @@
|
||||
$ErrorActionPreference = 'Stop'
|
||||
|
||||
$packageName = 'ccextractor'
|
||||
$toolsDir = "$(Split-Path -parent $MyInvocation.MyCommand.Definition)"
|
||||
|
||||
# Package parameters
|
||||
$packageArgs = @{
|
||||
packageName = $packageName
|
||||
fileType = 'MSI'
|
||||
url64bit = 'https://github.com/CCExtractor/ccextractor/releases/download/v0.96.5/CCExtractor.0.96.5.msi'
|
||||
checksum64 = 'FFCAB0D766180AFC2832277397CDEC885D15270DECE33A9A51947B790F1F095B'
|
||||
checksumType64 = 'sha256'
|
||||
silentArgs = '/quiet /norestart'
|
||||
validExitCodes = @(0, 3010, 1641)
|
||||
}
|
||||
|
||||
Install-ChocolateyPackage @packageArgs
|
||||
|
||||
# Add to PATH if not already there
|
||||
$installPath = Join-Path $env:ProgramFiles 'CCExtractor'
|
||||
if (Test-Path $installPath) {
|
||||
Install-ChocolateyPath -PathToInstall $installPath -PathType 'Machine'
|
||||
Write-Host "CCExtractor installed to: $installPath"
|
||||
}
|
||||
23
packaging/chocolatey/tools/chocolateyUninstall.ps1
Normal file
23
packaging/chocolatey/tools/chocolateyUninstall.ps1
Normal file
@@ -0,0 +1,23 @@
|
||||
$ErrorActionPreference = 'Stop'
|
||||
|
||||
$packageName = 'ccextractor'
|
||||
|
||||
# Get the uninstall registry key
|
||||
$regKey = Get-UninstallRegistryKey -SoftwareName 'CCExtractor*'
|
||||
|
||||
if ($regKey) {
|
||||
$silentArgs = '/quiet /norestart'
|
||||
$file = $regKey.UninstallString -replace 'msiexec.exe','msiexec.exe ' -replace '/I','/X'
|
||||
|
||||
$packageArgs = @{
|
||||
packageName = $packageName
|
||||
fileType = 'MSI'
|
||||
silentArgs = "$($regKey.PSChildName) $silentArgs"
|
||||
file = ''
|
||||
validExitCodes = @(0, 3010, 1605, 1614, 1641)
|
||||
}
|
||||
|
||||
Uninstall-ChocolateyPackage @packageArgs
|
||||
} else {
|
||||
Write-Warning "CCExtractor was not found in the registry. It may have been uninstalled already."
|
||||
}
|
||||
21
packaging/winget/CCExtractor.CCExtractor.installer.yaml
Normal file
21
packaging/winget/CCExtractor.CCExtractor.installer.yaml
Normal file
@@ -0,0 +1,21 @@
|
||||
# yaml-language-server: $schema=https://aka.ms/winget-manifest.installer.1.9.0.schema.json
|
||||
PackageIdentifier: CCExtractor.CCExtractor
|
||||
PackageVersion: 0.96.5
|
||||
Platform:
|
||||
- Windows.Desktop
|
||||
MinimumOSVersion: 10.0.0.0
|
||||
InstallModes:
|
||||
- interactive
|
||||
- silent
|
||||
- silentWithProgress
|
||||
InstallerSwitches:
|
||||
Silent: /quiet
|
||||
SilentWithProgress: /passive
|
||||
UpgradeBehavior: install
|
||||
Installers:
|
||||
- Architecture: x64
|
||||
InstallerType: msi
|
||||
InstallerUrl: https://github.com/CCExtractor/ccextractor/releases/download/v0.96.5/CCExtractor.0.96.5.msi
|
||||
InstallerSha256: FFCAB0D766180AFC2832277397CDEC885D15270DECE33A9A51947B790F1F095B
|
||||
ManifestType: installer
|
||||
ManifestVersion: 1.9.0
|
||||
39
packaging/winget/CCExtractor.CCExtractor.locale.en-US.yaml
Normal file
39
packaging/winget/CCExtractor.CCExtractor.locale.en-US.yaml
Normal file
@@ -0,0 +1,39 @@
|
||||
# yaml-language-server: $schema=https://aka.ms/winget-manifest.defaultLocale.1.9.0.schema.json
|
||||
PackageIdentifier: CCExtractor.CCExtractor
|
||||
PackageVersion: 0.96.5
|
||||
PackageLocale: en-US
|
||||
Publisher: CCExtractor Development
|
||||
PublisherUrl: https://ccextractor.org
|
||||
PublisherSupportUrl: https://github.com/CCExtractor/ccextractor/issues
|
||||
Author: CCExtractor Development Team
|
||||
PackageName: CCExtractor
|
||||
PackageUrl: https://ccextractor.org
|
||||
License: GPL-2.0
|
||||
LicenseUrl: https://github.com/CCExtractor/ccextractor/blob/master/LICENSE.txt
|
||||
Copyright: Copyright (c) CCExtractor Development
|
||||
ShortDescription: A tool to extract subtitles from video files
|
||||
Description: |-
|
||||
CCExtractor is a tool that analyzes video files and produces independent subtitle files from the closed captions data.
|
||||
|
||||
Key features:
|
||||
- Extracts closed captions from various video formats (MPEG, H.264, MKV, MP4, etc.)
|
||||
- Supports multiple input sources including DVDs, DVRs, and live TV captures
|
||||
- Outputs to multiple formats (SRT, WebVTT, SAMI, transcript, etc.)
|
||||
- OCR support for bitmap-based subtitles (DVB, teletext)
|
||||
- Cross-platform (Windows, Linux, macOS)
|
||||
- Includes a GUI for easy operation
|
||||
Moniker: ccextractor
|
||||
Tags:
|
||||
- subtitles
|
||||
- closed-captions
|
||||
- video
|
||||
- extraction
|
||||
- accessibility
|
||||
- srt
|
||||
- dvb
|
||||
- teletext
|
||||
- ocr
|
||||
- media
|
||||
ReleaseNotesUrl: https://github.com/CCExtractor/ccextractor/releases
|
||||
ManifestType: defaultLocale
|
||||
ManifestVersion: 1.9.0
|
||||
6
packaging/winget/CCExtractor.CCExtractor.yaml
Normal file
6
packaging/winget/CCExtractor.CCExtractor.yaml
Normal file
@@ -0,0 +1,6 @@
|
||||
# yaml-language-server: $schema=https://aka.ms/winget-manifest.version.1.9.0.schema.json
|
||||
PackageIdentifier: CCExtractor.CCExtractor
|
||||
PackageVersion: 0.96.5
|
||||
DefaultLocale: en-US
|
||||
ManifestType: version
|
||||
ManifestVersion: 1.9.0
|
||||
19
snap/local/run-ccextractor.sh
Executable file
19
snap/local/run-ccextractor.sh
Executable file
@@ -0,0 +1,19 @@
|
||||
#!/bin/sh
|
||||
set -e
|
||||
# Default fallback
|
||||
LIB_TRIPLET="x86_64-linux-gnu"
|
||||
# Detect multiarch directory if present
|
||||
for d in "$SNAP/usr/lib/"*-linux-gnu; do
|
||||
if [ -d "$d" ]; then
|
||||
LIB_TRIPLET=$(basename "$d")
|
||||
break
|
||||
fi
|
||||
done
|
||||
export LD_LIBRARY_PATH="$SNAP/usr/lib:\
|
||||
$SNAP/usr/lib/$LIB_TRIPLET:\
|
||||
$SNAP/usr/lib/$LIB_TRIPLET/blas:\
|
||||
$SNAP/usr/lib/$LIB_TRIPLET/lapack:\
|
||||
$SNAP/usr/lib/$LIB_TRIPLET/pulseaudio:\
|
||||
${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}"
|
||||
shift
|
||||
exec "$SNAP/usr/local/bin/ccextractor" "$@"
|
||||
104
snap/snapcraft.yaml
Normal file
104
snap/snapcraft.yaml
Normal file
@@ -0,0 +1,104 @@
|
||||
name: ccextractor
|
||||
base: core22
|
||||
version: '0.96.5'
|
||||
summary: Closed Caption Extractor
|
||||
description: |
|
||||
CCExtractor is a tool for extracting closed captions from video files.
|
||||
website: https://www.ccextractor.org
|
||||
source-code: https://github.com/CCExtractor/ccextractor
|
||||
confinement: classic
|
||||
|
||||
apps:
|
||||
ccextractor:
|
||||
command: usr/local/bin/ccextractor
|
||||
command-chain:
|
||||
- local/run-ccextractor.sh
|
||||
plugs:
|
||||
- home
|
||||
|
||||
parts:
|
||||
gpac:
|
||||
plugin: make
|
||||
source: https://github.com/gpac/gpac.git
|
||||
source-tag: abi-16.4
|
||||
build-packages:
|
||||
- build-essential
|
||||
- pkg-config
|
||||
- zlib1g-dev
|
||||
- libssl-dev
|
||||
- libfreetype6-dev
|
||||
- libjpeg-dev
|
||||
- libpng-dev
|
||||
override-build: |
|
||||
set -eux
|
||||
./configure --prefix=/usr
|
||||
make -j$(nproc)
|
||||
make DESTDIR=$SNAPCRAFT_PART_INSTALL install-lib
|
||||
sed -i "s|^prefix=.*|prefix=$SNAPCRAFT_STAGE/usr|" $SNAPCRAFT_PART_INSTALL/usr/lib/pkgconfig/gpac.pc
|
||||
stage:
|
||||
- usr/lib/libgpac*
|
||||
- usr/lib/pkgconfig/gpac.pc
|
||||
- usr/include/gpac
|
||||
|
||||
ccextractor:
|
||||
after: [gpac]
|
||||
plugin: cmake
|
||||
source: .
|
||||
source-subdir: src
|
||||
build-environment:
|
||||
- PKG_CONFIG_PATH: "$SNAPCRAFT_STAGE/usr/lib/pkgconfig:$PKG_CONFIG_PATH"
|
||||
build-snaps:
|
||||
- cmake/latest/stable
|
||||
- rustup/latest/stable
|
||||
build-packages:
|
||||
- build-essential
|
||||
- pkg-config
|
||||
- clang
|
||||
- llvm-dev
|
||||
- libclang-dev
|
||||
- libzvbi-dev
|
||||
- libtesseract-dev
|
||||
- libavcodec-dev
|
||||
- libavformat-dev
|
||||
- libavdevice-dev
|
||||
- libavfilter-dev
|
||||
- libswscale-dev
|
||||
- libx11-dev
|
||||
- libxcb1-dev
|
||||
- libxcb-shm0-dev
|
||||
- libpng-dev
|
||||
- zlib1g-dev
|
||||
- libblas3
|
||||
- liblapack3
|
||||
stage-packages:
|
||||
- libzvbi0
|
||||
- libfreetype6
|
||||
- libpng16-16
|
||||
- libprotobuf-c1
|
||||
- libutf8proc2
|
||||
- libgl1
|
||||
- libglu1-mesa
|
||||
- libavcodec58
|
||||
- libavformat58
|
||||
- libavutil56
|
||||
- libavdevice58
|
||||
- libavfilter7
|
||||
- libswscale5
|
||||
- libjpeg-turbo8
|
||||
- libvorbis0a
|
||||
- libtheora0
|
||||
- libxvidcore4
|
||||
- libfaad2
|
||||
- libmad0
|
||||
- liba52-0.7.4
|
||||
- libpulse0
|
||||
- pulseaudio-utils
|
||||
override-build: |
|
||||
set -eux
|
||||
rustup toolchain install stable
|
||||
rustup default stable
|
||||
export PATH="$HOME/.cargo/bin:$PATH"
|
||||
snapcraftctl build
|
||||
install -D -m 0755 \
|
||||
$SNAPCRAFT_PROJECT_DIR/snap/local/run-ccextractor.sh \
|
||||
$SNAPCRAFT_PART_INSTALL/local/run-ccextractor.sh
|
||||
@@ -9,7 +9,7 @@ option (WITH_HARDSUBX "Build with support for burned-in subtitles" OFF)
|
||||
|
||||
# Version number
|
||||
set (CCEXTRACTOR_VERSION_MAJOR 0)
|
||||
set (CCEXTRACTOR_VERSION_MINOR 89)
|
||||
set (CCEXTRACTOR_VERSION_MINOR 96)
|
||||
|
||||
# Get project directory
|
||||
get_filename_component(BASE_PROJ_DIR ../ ABSOLUTE)
|
||||
@@ -230,6 +230,14 @@ if (PKG_CONFIG_FOUND AND WITH_HARDSUBX)
|
||||
set (EXTRA_INCLUDES ${EXTRA_INCLUDES} ${SWSCALE_INCLUDE_DIRS})
|
||||
|
||||
set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DENABLE_HARDSUBX")
|
||||
pkg_check_modules (TESSERACT REQUIRED tesseract)
|
||||
pkg_check_modules (LEPTONICA REQUIRED lept)
|
||||
|
||||
set (EXTRA_LIBS ${EXTRA_LIBS} ${TESSERACT_LIBRARIES})
|
||||
set (EXTRA_LIBS ${EXTRA_LIBS} ${LEPTONICA_LIBRARIES})
|
||||
|
||||
set (EXTRA_INCLUDES ${EXTRA_INCLUDES} ${TESSERACT_INCLUDE_DIRS})
|
||||
set (EXTRA_INCLUDES ${EXTRA_INCLUDES} ${LEPTONICA_INCLUDE_DIRS})
|
||||
endif (PKG_CONFIG_FOUND AND WITH_HARDSUBX)
|
||||
|
||||
add_executable (ccextractor ${SOURCEFILE} ${FREETYPE_SOURCE} ${UTF8PROC_SOURCE})
|
||||
@@ -247,4 +255,13 @@ endif (PKG_CONFIG_FOUND)
|
||||
target_link_libraries (ccextractor ${EXTRA_LIBS})
|
||||
target_include_directories (ccextractor PUBLIC ${EXTRA_INCLUDES})
|
||||
|
||||
# ccx_rust (Rust) calls C functions from ccx (like decode_vbi).
|
||||
# Force the linker to pull these symbols from ccx before processing ccx_rust.
|
||||
if (NOT WIN32 AND NOT APPLE)
|
||||
target_link_options (ccextractor PRIVATE
|
||||
-Wl,--undefined=decode_vbi
|
||||
-Wl,--undefined=do_cb
|
||||
-Wl,--undefined=store_hdcc)
|
||||
endif()
|
||||
|
||||
install (TARGETS ccextractor DESTINATION bin)
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
/* CCExtractor, originally by carlos at ccextractor.org, now a lot of people.
|
||||
Credits: See AUTHORS.TXT
|
||||
License: GPL 2.0
|
||||
|
||||
CI verification run: 2025-12-19T08:30 - Testing merged fixes from PRs #1847 and #1848
|
||||
*/
|
||||
#include "ccextractor.h"
|
||||
#include <stdio.h>
|
||||
@@ -184,6 +186,11 @@ int start_ccx()
|
||||
ccx_options.use_gop_as_pts = 0;
|
||||
if (ccx_options.ignore_pts_jumps)
|
||||
ccx_common_timing_settings.disable_sync_check = 1;
|
||||
// When using GOP timing (--goptime), disable sync check because
|
||||
// GOP time (wall-clock) and PES PTS (stream-relative) are in
|
||||
// different time bases and will always appear as huge jumps.
|
||||
if (ccx_options.use_gop_as_pts == 1)
|
||||
ccx_common_timing_settings.disable_sync_check = 1;
|
||||
mprint("\rAnalyzing data in general mode\n");
|
||||
tmp = general_loop(ctx);
|
||||
if (!ret)
|
||||
@@ -195,6 +202,12 @@ int start_ccx()
|
||||
if (!ret)
|
||||
ret = tmp;
|
||||
break;
|
||||
case CCX_SM_SCC:
|
||||
mprint("\rAnalyzing data in SCC (Scenarist Closed Caption) mode\n");
|
||||
tmp = raw_loop(ctx);
|
||||
if (!ret)
|
||||
ret = tmp;
|
||||
break;
|
||||
case CCX_SM_RCWT:
|
||||
mprint("\rAnalyzing data in CCExtractor's binary format\n");
|
||||
tmp = rcwt_loop(ctx);
|
||||
@@ -422,6 +435,9 @@ int main(int argc, char *argv[])
|
||||
|
||||
int compile_ret = ccxr_parse_parameters(argc, argv);
|
||||
|
||||
// Update the Rust logger target after parsing so --quiet is respected
|
||||
ccxr_update_logger_target();
|
||||
|
||||
if (compile_ret == EXIT_NO_INPUT_FILES)
|
||||
{
|
||||
print_usage();
|
||||
|
||||
@@ -29,17 +29,16 @@ CURLcode res;
|
||||
|
||||
extern struct ccx_s_options ccx_options;
|
||||
extern struct lib_ccx_ctx *signal_ctx;
|
||||
//volatile int terminate_asap = 0;
|
||||
// volatile int terminate_asap = 0;
|
||||
|
||||
struct ccx_s_options* api_init_options();
|
||||
struct ccx_s_options *api_init_options();
|
||||
|
||||
int api_start(struct ccx_s_options api_options);
|
||||
|
||||
|
||||
void sigterm_handler(int sig);
|
||||
void sigint_handler(int sig);
|
||||
void print_end_msg(void);
|
||||
|
||||
int main(int argc, char *argv[]);
|
||||
|
||||
#endif //CCEXTRACTOR_H
|
||||
#endif // CCEXTRACTOR_H
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
cmake_policy (SET CMP0037 NEW)
|
||||
|
||||
if(MSVC)
|
||||
set (CMAKE_C_FLAGS "-W3 /wd4005 /wd4996")
|
||||
set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -W3 /wd4005 /wd4996")
|
||||
else (MSVC)
|
||||
set (CMAKE_C_FLAGS "-Wall -Wno-pointer-sign -g -std=gnu99")
|
||||
set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wno-pointer-sign -g -std=gnu99")
|
||||
endif(MSVC)
|
||||
|
||||
if(WIN32)
|
||||
|
||||
@@ -2,16 +2,16 @@
|
||||
#define ACTIVITY_H
|
||||
|
||||
extern unsigned long net_activity_gui;
|
||||
void activity_header (void);
|
||||
void activity_progress (int percentaje, int cur_min, int cur_sec);
|
||||
void activity_report_version (void);
|
||||
void activity_input_file_closed (void);
|
||||
void activity_input_file_open (const char *filename);
|
||||
void activity_message (const char *fmt, ...);
|
||||
void activity_video_info (int hor_size,int vert_size,
|
||||
const char *aspect_ratio, const char *framerate);
|
||||
void activity_program_number (unsigned program_number);
|
||||
void activity_header(void);
|
||||
void activity_progress(int percentaje, int cur_min, int cur_sec);
|
||||
void activity_report_version(void);
|
||||
void activity_input_file_closed(void);
|
||||
void activity_input_file_open(const char *filename);
|
||||
void activity_message(const char *fmt, ...);
|
||||
void activity_video_info(int hor_size, int vert_size,
|
||||
const char *aspect_ratio, const char *framerate);
|
||||
void activity_program_number(unsigned program_number);
|
||||
void activity_library_process(enum ccx_common_logging_gui message_type, ...);
|
||||
void activity_report_data_read (void);
|
||||
void activity_report_data_read(void);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -30,28 +30,30 @@
|
||||
// 10.13 - Undocumented DVR-MS properties
|
||||
#define DVRMS_PTS "\x2A\xC0\x3C\xFD\xDB\x06\xFA\x4C\x80\x1C\x72\x12\xD3\x87\x45\xE4"
|
||||
|
||||
typedef struct {
|
||||
typedef struct
|
||||
{
|
||||
int VideoStreamNumber;
|
||||
int AudioStreamNumber;
|
||||
int CaptionStreamNumber;
|
||||
int CaptionStreamStyle; // 1 = NTSC, 2 = ATSC
|
||||
int DecodeStreamNumber; // The stream that is chosen to be decoded
|
||||
int DecodeStreamPTS; // This will be used for the next returned block
|
||||
int CaptionStreamStyle; // 1 = NTSC, 2 = ATSC
|
||||
int DecodeStreamNumber; // The stream that is chosen to be decoded
|
||||
int DecodeStreamPTS; // This will be used for the next returned block
|
||||
int currDecodeStreamPTS; // Time of the data returned by the function
|
||||
int prevDecodeStreamPTS; // Previous time
|
||||
int VideoStreamMS; // See ableve, just for video
|
||||
int VideoStreamMS; // See ableve, just for video
|
||||
int currVideoStreamMS;
|
||||
int prevVideoStreamMS;
|
||||
int VideoJump; // Remember a jump in the video timeline
|
||||
int VideoJump; // Remember a jump in the video timeline
|
||||
} asf_data_stream_properties;
|
||||
|
||||
#define STREAMNUM 10
|
||||
#define STREAMNUM 10
|
||||
#define PAYEXTNUM 10
|
||||
|
||||
typedef struct {
|
||||
typedef struct
|
||||
{
|
||||
// Generic buffer to hold data
|
||||
unsigned char *parsebuf;
|
||||
long parsebufsize;
|
||||
int64_t parsebufsize;
|
||||
// Header Object variables
|
||||
int64_t HeaderObjectSize;
|
||||
int64_t FileSize;
|
||||
@@ -72,23 +74,23 @@ typedef struct {
|
||||
uint32_t TotalDataPackets;
|
||||
int VideoClosedCaptioningFlag;
|
||||
// Payload data
|
||||
int PayloadLType; // ASF - Payload Length Type. <>0 for multiple payloads
|
||||
uint32_t PayloadLength; // ASF - Payload Length
|
||||
int NumberOfPayloads; // ASF - Number of payloads.
|
||||
int payloadcur; // local
|
||||
int PayloadLType; // ASF - Payload Length Type. <>0 for multiple payloads
|
||||
uint32_t PayloadLength; // ASF - Payload Length
|
||||
int NumberOfPayloads; // ASF - Number of payloads.
|
||||
int payloadcur; // local
|
||||
int PayloadStreamNumber; // ASF
|
||||
int KeyFrame; // ASF
|
||||
int KeyFrame; // ASF
|
||||
uint32_t PayloadMediaNumber; // ASF
|
||||
// Data Object Loop
|
||||
uint32_t datapacketcur; // Current packet number
|
||||
int64_t dobjectread; // Bytes read in Data Object
|
||||
uint32_t datapacketcur; // Current packet number
|
||||
int64_t dobjectread; // Bytes read in Data Object
|
||||
// Payload parsing information
|
||||
int MultiplePayloads; // ASF
|
||||
int PacketLType; // ASF
|
||||
int ReplicatedLType; // ASF
|
||||
int OffsetMediaLType; // ASF
|
||||
int MediaNumberLType; // ASF
|
||||
int StreamNumberLType; // ASF
|
||||
int MultiplePayloads; // ASF
|
||||
int PacketLType; // ASF
|
||||
int ReplicatedLType; // ASF
|
||||
int OffsetMediaLType; // ASF
|
||||
int MediaNumberLType; // ASF
|
||||
int StreamNumberLType; // ASF
|
||||
uint32_t PacketLength;
|
||||
uint32_t PaddingLength;
|
||||
} asf_data;
|
||||
|
||||
@@ -42,14 +42,14 @@ char *gui_data_string(void *val)
|
||||
{
|
||||
static char sbuf[40];
|
||||
|
||||
sprintf(sbuf, "%08lX-%04X-%04X-",
|
||||
(long)*((uint32_t *)((char *)val + 0)),
|
||||
(int)*((uint16_t *)((char *)val + 4)),
|
||||
(int)*((uint16_t *)((char *)val + 6)));
|
||||
snprintf(sbuf, sizeof(sbuf), "%08lX-%04X-%04X-",
|
||||
(long)*((uint32_t *)((char *)val + 0)),
|
||||
(int)*((uint16_t *)((char *)val + 4)),
|
||||
(int)*((uint16_t *)((char *)val + 6)));
|
||||
for (int ii = 0; ii < 2; ii++)
|
||||
sprintf(sbuf + 19 + ii * 2, "%02X-", *((unsigned char *)val + 8 + ii));
|
||||
snprintf(sbuf + 19 + ii * 2, sizeof(sbuf) - 19 - ii * 2, "%02X-", *((unsigned char *)val + 8 + ii));
|
||||
for (int ii = 0; ii < 6; ii++)
|
||||
sprintf(sbuf + 24 + ii * 2, "%02X", *((unsigned char *)val + 10 + ii));
|
||||
snprintf(sbuf + 24 + ii * 2, sizeof(sbuf) - 24 - ii * 2, "%02X", *((unsigned char *)val + 10 + ii));
|
||||
|
||||
return sbuf;
|
||||
}
|
||||
@@ -150,6 +150,10 @@ int asf_get_more_data(struct lib_ccx_ctx *ctx, struct demuxer_data **ppdata)
|
||||
.StreamNumberLType = 0,
|
||||
.PacketLength = 0,
|
||||
.PaddingLength = 0};
|
||||
// Check for allocation failure
|
||||
if (!asf_data_container.parsebuf)
|
||||
fatal(EXIT_NOT_ENOUGH_MEMORY, "In asf_getmoredata: Out of memory allocating initial parse buffer.");
|
||||
|
||||
// Initialize the Payload Extension System
|
||||
for (int stream = 0; stream < STREAMNUM; stream++)
|
||||
{
|
||||
@@ -185,9 +189,13 @@ int asf_get_more_data(struct lib_ccx_ctx *ctx, struct demuxer_data **ppdata)
|
||||
|
||||
if (asf_data_container.HeaderObjectSize > asf_data_container.parsebufsize)
|
||||
{
|
||||
asf_data_container.parsebuf = (unsigned char *)realloc(asf_data_container.parsebuf, (size_t)asf_data_container.HeaderObjectSize);
|
||||
if (!asf_data_container.parsebuf)
|
||||
unsigned char *tmp = (unsigned char *)realloc(asf_data_container.parsebuf, (size_t)asf_data_container.HeaderObjectSize);
|
||||
if (!tmp)
|
||||
{
|
||||
free(asf_data_container.parsebuf);
|
||||
fatal(EXIT_NOT_ENOUGH_MEMORY, "In asf_getmoredata: Out of memory requesting buffer for data container.");
|
||||
}
|
||||
asf_data_container.parsebuf = tmp;
|
||||
asf_data_container.parsebufsize = (long)asf_data_container.HeaderObjectSize;
|
||||
}
|
||||
|
||||
@@ -751,9 +759,13 @@ int asf_get_more_data(struct lib_ccx_ctx *ctx, struct demuxer_data **ppdata)
|
||||
|
||||
if ((long)replicated_length > asf_data_container.parsebufsize)
|
||||
{
|
||||
asf_data_container.parsebuf = (unsigned char *)realloc(asf_data_container.parsebuf, replicated_length);
|
||||
if (!asf_data_container.parsebuf)
|
||||
unsigned char *tmp = (unsigned char *)realloc(asf_data_container.parsebuf, replicated_length);
|
||||
if (!tmp)
|
||||
{
|
||||
free(asf_data_container.parsebuf);
|
||||
fatal(EXIT_NOT_ENOUGH_MEMORY, "In asf_getmoredata: Not enough memory for buffer, unable to continue.\n");
|
||||
}
|
||||
asf_data_container.parsebuf = tmp;
|
||||
asf_data_container.parsebufsize = replicated_length;
|
||||
}
|
||||
result = buffered_read(ctx->demux_ctx, asf_data_container.parsebuf, (long)replicated_length);
|
||||
|
||||
@@ -48,6 +48,7 @@ struct avc_ctx *init_avc(void)
|
||||
ctx->cc_databufsize = 1024;
|
||||
ctx->cc_buffer_saved = CCX_TRUE; // Was the CC buffer saved after it was last updated?
|
||||
|
||||
ctx->is_hevc = 0;
|
||||
ctx->got_seq_para = 0;
|
||||
ctx->nal_ref_idc = 0;
|
||||
ctx->seq_parameter_set_id = 0;
|
||||
@@ -87,16 +88,43 @@ struct avc_ctx *init_avc(void)
|
||||
return ctx;
|
||||
}
|
||||
|
||||
// HEVC NAL unit types for SEI messages
|
||||
#define HEVC_NAL_PREFIX_SEI 39
|
||||
#define HEVC_NAL_SUFFIX_SEI 40
|
||||
#define HEVC_NAL_VPS 32
|
||||
#define HEVC_NAL_SPS 33
|
||||
#define HEVC_NAL_PPS 34
|
||||
|
||||
void do_NAL(struct encoder_ctx *enc_ctx, struct lib_cc_decode *dec_ctx, unsigned char *NAL_start, LLONG NAL_length, struct cc_subtitle *sub)
|
||||
{
|
||||
unsigned char *NAL_stop;
|
||||
enum ccx_avc_nal_types nal_unit_type = *NAL_start & 0x1F;
|
||||
int nal_unit_type;
|
||||
int nal_header_size;
|
||||
unsigned char *payload_start;
|
||||
|
||||
// Determine if this is HEVC or H.264 based on NAL header
|
||||
// H.264 NAL header: 1 byte, type in bits [4:0]
|
||||
// HEVC NAL header: 2 bytes, type in bits [6:1] of first byte
|
||||
if (dec_ctx->avc_ctx->is_hevc)
|
||||
{
|
||||
// HEVC: NAL type is in bits [6:1] of byte 0
|
||||
nal_unit_type = (NAL_start[0] >> 1) & 0x3F;
|
||||
nal_header_size = 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
// H.264: NAL type is in bits [4:0] of byte 0
|
||||
nal_unit_type = NAL_start[0] & 0x1F;
|
||||
nal_header_size = 1;
|
||||
}
|
||||
|
||||
NAL_stop = NAL_length + NAL_start;
|
||||
NAL_stop = remove_03emu(NAL_start + 1, NAL_stop); // Add +1 to NAL_stop for TS, without it for MP4. Still don't know why
|
||||
NAL_stop = remove_03emu(NAL_start + nal_header_size, NAL_stop);
|
||||
payload_start = NAL_start + nal_header_size;
|
||||
|
||||
dvprint("BEGIN NAL unit type: %d length %d ref_idc: %d - Buffered captions before: %d\n",
|
||||
nal_unit_type, NAL_stop - NAL_start - 1, dec_ctx->avc_ctx->nal_ref_idc, !dec_ctx->avc_ctx->cc_buffer_saved);
|
||||
dvprint("BEGIN NAL unit type: %d length %d ref_idc: %d - Buffered captions before: %d (HEVC: %d)\n",
|
||||
nal_unit_type, NAL_stop - NAL_start - nal_header_size, dec_ctx->avc_ctx->nal_ref_idc,
|
||||
!dec_ctx->avc_ctx->cc_buffer_saved, dec_ctx->avc_ctx->is_hevc);
|
||||
|
||||
if (NAL_stop == NULL) // remove_03emu failed.
|
||||
{
|
||||
@@ -104,45 +132,64 @@ void do_NAL(struct encoder_ctx *enc_ctx, struct lib_cc_decode *dec_ctx, unsigned
|
||||
return;
|
||||
}
|
||||
|
||||
if (nal_unit_type == CCX_NAL_TYPE_ACCESS_UNIT_DELIMITER_9)
|
||||
if (dec_ctx->avc_ctx->is_hevc)
|
||||
{
|
||||
// Found Access Unit Delimiter
|
||||
// HEVC NAL unit processing
|
||||
if (nal_unit_type == HEVC_NAL_VPS || nal_unit_type == HEVC_NAL_SPS || nal_unit_type == HEVC_NAL_PPS)
|
||||
{
|
||||
// Found HEVC parameter set - mark as having sequence params
|
||||
// We don't parse HEVC SPS fully, but we need to enable SEI processing
|
||||
dec_ctx->avc_ctx->got_seq_para = 1;
|
||||
}
|
||||
else if (nal_unit_type == HEVC_NAL_PREFIX_SEI || nal_unit_type == HEVC_NAL_SUFFIX_SEI)
|
||||
{
|
||||
// Found HEVC SEI (used for subtitles)
|
||||
// SEI payload format is similar to H.264
|
||||
sei_rbsp(dec_ctx->avc_ctx, payload_start, NAL_stop);
|
||||
}
|
||||
}
|
||||
else if (nal_unit_type == CCX_NAL_TYPE_SEQUENCE_PARAMETER_SET_7)
|
||||
else
|
||||
{
|
||||
// Found sequence parameter set
|
||||
// We need this to parse NAL type 1 (CCX_NAL_TYPE_CODED_SLICE_NON_IDR_PICTURE_1)
|
||||
dec_ctx->avc_ctx->num_nal_unit_type_7++;
|
||||
seq_parameter_set_rbsp(dec_ctx->avc_ctx, NAL_start + 1, NAL_stop);
|
||||
dec_ctx->avc_ctx->got_seq_para = 1;
|
||||
}
|
||||
else if (dec_ctx->avc_ctx->got_seq_para && (nal_unit_type == CCX_NAL_TYPE_CODED_SLICE_NON_IDR_PICTURE_1 ||
|
||||
nal_unit_type == CCX_NAL_TYPE_CODED_SLICE_IDR_PICTURE)) // Only if nal_unit_type=1
|
||||
{
|
||||
// Found coded slice of a non-IDR picture
|
||||
// We only need the slice header data, no need to implement
|
||||
// slice_layer_without_partitioning_rbsp( );
|
||||
slice_header(enc_ctx, dec_ctx, NAL_start + 1, NAL_stop, nal_unit_type, sub);
|
||||
}
|
||||
else if (dec_ctx->avc_ctx->got_seq_para && nal_unit_type == CCX_NAL_TYPE_SEI)
|
||||
{
|
||||
// Found SEI (used for subtitles)
|
||||
// set_fts(ctx->timing); // FIXME - check this!!!
|
||||
sei_rbsp(dec_ctx->avc_ctx, NAL_start + 1, NAL_stop);
|
||||
}
|
||||
else if (dec_ctx->avc_ctx->got_seq_para && nal_unit_type == CCX_NAL_TYPE_PICTURE_PARAMETER_SET)
|
||||
{
|
||||
// Found Picture parameter set
|
||||
// H.264 NAL unit processing (original code)
|
||||
if (nal_unit_type == CCX_NAL_TYPE_ACCESS_UNIT_DELIMITER_9)
|
||||
{
|
||||
// Found Access Unit Delimiter
|
||||
}
|
||||
else if (nal_unit_type == CCX_NAL_TYPE_SEQUENCE_PARAMETER_SET_7)
|
||||
{
|
||||
// Found sequence parameter set
|
||||
// We need this to parse NAL type 1 (CCX_NAL_TYPE_CODED_SLICE_NON_IDR_PICTURE_1)
|
||||
dec_ctx->avc_ctx->num_nal_unit_type_7++;
|
||||
seq_parameter_set_rbsp(dec_ctx->avc_ctx, payload_start, NAL_stop);
|
||||
dec_ctx->avc_ctx->got_seq_para = 1;
|
||||
}
|
||||
else if (dec_ctx->avc_ctx->got_seq_para && (nal_unit_type == CCX_NAL_TYPE_CODED_SLICE_NON_IDR_PICTURE_1 ||
|
||||
nal_unit_type == CCX_NAL_TYPE_CODED_SLICE_IDR_PICTURE))
|
||||
{
|
||||
// Found coded slice of a non-IDR picture
|
||||
// We only need the slice header data
|
||||
slice_header(enc_ctx, dec_ctx, payload_start, NAL_stop, nal_unit_type, sub);
|
||||
}
|
||||
else if (dec_ctx->avc_ctx->got_seq_para && nal_unit_type == CCX_NAL_TYPE_SEI)
|
||||
{
|
||||
// Found SEI (used for subtitles)
|
||||
sei_rbsp(dec_ctx->avc_ctx, payload_start, NAL_stop);
|
||||
}
|
||||
else if (dec_ctx->avc_ctx->got_seq_para && nal_unit_type == CCX_NAL_TYPE_PICTURE_PARAMETER_SET)
|
||||
{
|
||||
// Found Picture parameter set
|
||||
}
|
||||
}
|
||||
|
||||
if (temp_debug)
|
||||
{
|
||||
int len = NAL_stop - (NAL_start + 1);
|
||||
int len = NAL_stop - payload_start;
|
||||
dbg_print(CCX_DMT_VIDES, "\n After decoding, the actual thing was (length =%d)\n", len);
|
||||
dump(CCX_DMT_VIDES, NAL_start + 1, len > 160 ? 160 : len, 0, 0);
|
||||
dump(CCX_DMT_VIDES, payload_start, len > 160 ? 160 : len, 0, 0);
|
||||
}
|
||||
|
||||
dvprint("END NAL unit type: %d length %d ref_idc: %d - Buffered captions after: %d\n",
|
||||
nal_unit_type, NAL_stop - NAL_start - 1, dec_ctx->avc_ctx->nal_ref_idc, !dec_ctx->avc_ctx->cc_buffer_saved);
|
||||
nal_unit_type, NAL_stop - NAL_start - nal_header_size, dec_ctx->avc_ctx->nal_ref_idc, !dec_ctx->avc_ctx->cc_buffer_saved);
|
||||
}
|
||||
|
||||
// Process inbuf bytes in buffer holding and AVC (H.264) video stream.
|
||||
@@ -332,11 +379,10 @@ void sei_rbsp(struct avc_ctx *ctx, unsigned char *seibuf, unsigned char *seiend)
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO: This really really looks bad
|
||||
mprint("WARNING: Unexpected SEI unit length...trying to continue.");
|
||||
temp_debug = 1;
|
||||
mprint("\n Failed block (at sei_rbsp) was:\n");
|
||||
dump(CCX_DMT_GENERIC_NOTICES, (unsigned char *)seibuf, seiend - seibuf, 0, 0);
|
||||
// Unexpected SEI length - common with malformed streams, don't spam output
|
||||
dbg_print(CCX_DMT_VERBOSE, "WARNING: Unexpected SEI unit length (parsed to %p, expected %p)...trying to continue.\n",
|
||||
(void *)tbuf, (void *)(seiend - 1));
|
||||
dump(CCX_DMT_VERBOSE, (unsigned char *)seibuf, seiend - seibuf, 0, 0);
|
||||
|
||||
ctx->num_unexpected_sei_length++;
|
||||
}
|
||||
@@ -346,20 +392,24 @@ void sei_rbsp(struct avc_ctx *ctx, unsigned char *seibuf, unsigned char *seiend)
|
||||
unsigned char *sei_message(struct avc_ctx *ctx, unsigned char *seibuf, unsigned char *seiend)
|
||||
{
|
||||
int payload_type = 0;
|
||||
while (*seibuf == 0xff)
|
||||
while (seibuf < seiend && *seibuf == 0xff)
|
||||
{
|
||||
payload_type += 255;
|
||||
seibuf++;
|
||||
}
|
||||
if (seibuf >= seiend)
|
||||
return NULL;
|
||||
payload_type += *seibuf;
|
||||
seibuf++;
|
||||
|
||||
int payload_size = 0;
|
||||
while (*seibuf == 0xff)
|
||||
while (seibuf < seiend && *seibuf == 0xff)
|
||||
{
|
||||
payload_size += 255;
|
||||
seibuf++;
|
||||
}
|
||||
if (seibuf >= seiend)
|
||||
return NULL;
|
||||
payload_size += *seibuf;
|
||||
seibuf++;
|
||||
|
||||
@@ -510,9 +560,13 @@ void user_data_registered_itu_t_t35(struct avc_ctx *ctx, unsigned char *userbuf,
|
||||
// Save the data and process once we know the sequence number
|
||||
if (((ctx->cc_count + local_cc_count) * 3) + 1 > ctx->cc_databufsize)
|
||||
{
|
||||
ctx->cc_data = (unsigned char *)realloc(ctx->cc_data, (size_t)((ctx->cc_count + local_cc_count) * 6) + 1);
|
||||
if (!ctx->cc_data)
|
||||
unsigned char *tmp = (unsigned char *)realloc(ctx->cc_data, (size_t)((ctx->cc_count + local_cc_count) * 6) + 1);
|
||||
if (!tmp)
|
||||
{
|
||||
free(ctx->cc_data);
|
||||
fatal(EXIT_NOT_ENOUGH_MEMORY, "In user_data_registered_itu_t_t35: Out of memory to allocate buffer for CC data.");
|
||||
}
|
||||
ctx->cc_data = tmp;
|
||||
ctx->cc_databufsize = (long)((ctx->cc_count + local_cc_count) * 6) + 1;
|
||||
}
|
||||
// Copy new cc data into cc_data
|
||||
@@ -581,9 +635,13 @@ void user_data_registered_itu_t_t35(struct avc_ctx *ctx, unsigned char *userbuf,
|
||||
// Save the data and process once we know the sequence number
|
||||
if ((((local_cc_count + ctx->cc_count) * 3) + 1) > ctx->cc_databufsize)
|
||||
{
|
||||
ctx->cc_data = (unsigned char *)realloc(ctx->cc_data, (size_t)(((local_cc_count + ctx->cc_count) * 6) + 1));
|
||||
if (!ctx->cc_data)
|
||||
unsigned char *tmp = (unsigned char *)realloc(ctx->cc_data, (size_t)(((local_cc_count + ctx->cc_count) * 6) + 1));
|
||||
if (!tmp)
|
||||
{
|
||||
free(ctx->cc_data);
|
||||
fatal(EXIT_NOT_ENOUGH_MEMORY, "In user_data_registered_itu_t_t35: Not enough memory trying to allocate buffer for CC data.");
|
||||
}
|
||||
ctx->cc_data = tmp;
|
||||
ctx->cc_databufsize = (long)(((local_cc_count + ctx->cc_count) * 6) + 1);
|
||||
}
|
||||
// Copy new cc data into cc_data - replace command below.
|
||||
@@ -849,10 +907,10 @@ void seq_parameter_set_rbsp(struct avc_ctx *ctx, unsigned char *seqbuf, unsigned
|
||||
dvprint("vcl_hrd_parameters_present_flag= %llX\n", tmp1);
|
||||
if (tmp)
|
||||
{
|
||||
// TODO.
|
||||
mprint("vcl_hrd. Not implemented for now. Hopefully not needed. Skipping rest of NAL\n");
|
||||
// VCL HRD parameters are for video buffering compliance, not needed for caption extraction.
|
||||
// Just skip and continue - this doesn't affect our ability to extract captions.
|
||||
mprint("Skipping VCL HRD parameters (not needed for caption extraction)\n");
|
||||
ctx->num_vcl_hrd++;
|
||||
// exit(1);
|
||||
}
|
||||
if (tmp || tmp1)
|
||||
{
|
||||
@@ -899,6 +957,15 @@ void slice_header(struct encoder_ctx *enc_ctx, struct lib_cc_decode *dec_ctx, un
|
||||
dvprint("first_mb_in_slice= % 4lld (%#llX)\n", tmp, tmp);
|
||||
slice_type = read_exp_golomb_unsigned(&q1);
|
||||
dvprint("slice_type= % 4llX\n", slice_type);
|
||||
|
||||
// Validate slice_type to prevent buffer overflow in slice_types[] array
|
||||
// Valid H.264 slice_type values are 0-9 (H.264 spec Table 7-6)
|
||||
if (slice_type >= 10)
|
||||
{
|
||||
mprint("Invalid slice_type %lld in slice header, skipping.\n", slice_type);
|
||||
return;
|
||||
}
|
||||
|
||||
tmp = read_exp_golomb_unsigned(&q1);
|
||||
dvprint("pic_parameter_set_id= % 4lld (%#llX)\n", tmp, tmp);
|
||||
|
||||
@@ -929,9 +996,9 @@ void slice_header(struct encoder_ctx *enc_ctx, struct lib_cc_decode *dec_ctx, un
|
||||
|
||||
if (nal_unit_type == 5)
|
||||
{
|
||||
// idr_pic_id: Read to advance bitstream position; value not needed for caption extraction
|
||||
tmp = read_exp_golomb_unsigned(&q1);
|
||||
dvprint("idr_pic_id= % 4lld (%#llX)\n", tmp, tmp);
|
||||
// TODO
|
||||
}
|
||||
if (dec_ctx->avc_ctx->pic_order_cnt_type == 0)
|
||||
{
|
||||
@@ -1038,7 +1105,12 @@ void slice_header(struct encoder_ctx *enc_ctx, struct lib_cc_decode *dec_ctx, un
|
||||
}
|
||||
|
||||
// if slices are buffered - flush
|
||||
if (isref)
|
||||
// For I/P-only streams (like HDHomeRun recordings), flushing on every
|
||||
// reference frame defeats reordering since all frames are reference frames.
|
||||
// Only flush and reset on IDR frames (nal_unit_type==5), not P-frames.
|
||||
// This allows P-frames to accumulate in the buffer and be sorted by PTS.
|
||||
int is_idr = (nal_unit_type == CCX_NAL_TYPE_CODED_SLICE_IDR_PICTURE);
|
||||
if (isref && is_idr)
|
||||
{
|
||||
dvprint("\nReference pic! [%s]\n", slice_types[slice_type]);
|
||||
dbg_print(CCX_DMT_TIME, "\nReference pic! [%s] maxrefcnt: %3d\n",
|
||||
@@ -1133,8 +1205,32 @@ void slice_header(struct encoder_ctx *enc_ctx, struct lib_cc_decode *dec_ctx, un
|
||||
|
||||
if (abs(current_index) >= MAXBFRAMES)
|
||||
{
|
||||
// Probably a jump in the timeline. Warn and handle gracefully.
|
||||
mprint("\nFound large gap(%d) in PTS! Trying to recover ...\n", current_index);
|
||||
// Large PTS gap detected. This can happen with certain encoders
|
||||
// (like HDHomeRun) that produce streams where PTS jumps are common.
|
||||
// Instead of just resetting current_index to 0 (which causes captions
|
||||
// to pile up at the same buffer slot and become garbled), we need to:
|
||||
// 1. Flush any buffered captions
|
||||
// 2. Reset the reference PTS to the current PTS
|
||||
// 3. Set current_index to 0 for a fresh start
|
||||
// This ensures subsequent frames use the new reference point.
|
||||
dbg_print(CCX_DMT_VERBOSE, "\nLarge PTS gap(%d) detected, flushing buffer and resetting reference.\n", current_index);
|
||||
|
||||
// Flush any buffered captions before resetting
|
||||
if (dec_ctx->has_ccdata_buffered)
|
||||
{
|
||||
process_hdcc(enc_ctx, dec_ctx, sub);
|
||||
}
|
||||
|
||||
// Reset the reference point to current PTS
|
||||
dec_ctx->avc_ctx->currefpts = dec_ctx->timing->current_pts;
|
||||
|
||||
// Reset tracking variables for the new reference
|
||||
dec_ctx->avc_ctx->lastmaxidx = -1;
|
||||
dec_ctx->avc_ctx->maxidx = 0;
|
||||
dec_ctx->avc_ctx->lastminidx = 10000;
|
||||
dec_ctx->avc_ctx->minidx = 10000;
|
||||
|
||||
// Start with index 0 relative to the new reference
|
||||
current_index = 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
#ifndef AVC_FUNCTION_H
|
||||
#define AVC_FUNCTION_H
|
||||
|
||||
|
||||
struct avc_ctx
|
||||
{
|
||||
unsigned char cc_count;
|
||||
// buffer to hold cc data
|
||||
unsigned char *cc_data;
|
||||
long cc_databufsize;
|
||||
int64_t cc_databufsize;
|
||||
int cc_buffer_saved; // Was the CC buffer saved after it was last updated?
|
||||
|
||||
int is_hevc; // Flag to indicate HEVC (H.265) mode vs H.264
|
||||
int got_seq_para;
|
||||
unsigned nal_ref_idc;
|
||||
LLONG seq_parameter_set_id;
|
||||
@@ -19,11 +19,11 @@ struct avc_ctx
|
||||
int frame_mbs_only_flag;
|
||||
|
||||
// Use and throw stats for debug, remove this ugliness soon
|
||||
long num_nal_unit_type_7;
|
||||
long num_vcl_hrd;
|
||||
long num_nal_hrd;
|
||||
long num_jump_in_frames;
|
||||
long num_unexpected_sei_length;
|
||||
int64_t num_nal_unit_type_7;
|
||||
int64_t num_vcl_hrd;
|
||||
int64_t num_nal_hrd;
|
||||
int64_t num_jump_in_frames;
|
||||
int64_t num_unexpected_sei_length;
|
||||
|
||||
int ccblocks_in_avc_total;
|
||||
int ccblocks_in_avc_lost;
|
||||
@@ -50,6 +50,6 @@ struct avc_ctx
|
||||
|
||||
struct avc_ctx *init_avc(void);
|
||||
void dinit_avc(struct avc_ctx **ctx);
|
||||
void do_NAL (struct encoder_ctx *enc_ctx, struct lib_cc_decode *ctx, unsigned char *NAL_start, LLONG NAL_length, struct cc_subtitle *sub);
|
||||
void do_NAL(struct encoder_ctx *enc_ctx, struct lib_cc_decode *ctx, unsigned char *NAL_start, LLONG NAL_length, struct cc_subtitle *sub);
|
||||
size_t process_avc(struct encoder_ctx *enc_ctx, struct lib_cc_decode *ctx, unsigned char *avcbuf, size_t avcbuflen, struct cc_subtitle *sub);
|
||||
#endif
|
||||
|
||||
@@ -26,26 +26,25 @@ struct bitstream
|
||||
int _i_bpos;
|
||||
};
|
||||
|
||||
#define read_u8(bstream) (uint8_t)bitstream_get_num(bstream,1,1)
|
||||
#define read_u16(bstream) (uint16_t)bitstream_get_num(bstream,2,1)
|
||||
#define read_u32(bstream) (uint32_t)bitstream_get_num(bstream,4,1)
|
||||
#define read_u64(bstream) (uint64_t)bitstream_get_num(bstream,8,1)
|
||||
#define read_i8(bstream) (int8_t)bitstream_get_num(bstream,1,1)
|
||||
#define read_i16(bstream) (int16_t)bitstream_get_num(bstream,2,1)
|
||||
#define read_i32(bstream) (int32_t)bitstream_get_num(bstream,4,1)
|
||||
#define read_i64(bstream) (int64_t)bitstream_get_num(bstream,8,1)
|
||||
#define read_u8(bstream) (uint8_t)bitstream_get_num(bstream, 1, 1)
|
||||
#define read_u16(bstream) (uint16_t)bitstream_get_num(bstream, 2, 1)
|
||||
#define read_u32(bstream) (uint32_t)bitstream_get_num(bstream, 4, 1)
|
||||
#define read_u64(bstream) (uint64_t)bitstream_get_num(bstream, 8, 1)
|
||||
#define read_i8(bstream) (int8_t)bitstream_get_num(bstream, 1, 1)
|
||||
#define read_i16(bstream) (int16_t)bitstream_get_num(bstream, 2, 1)
|
||||
#define read_i32(bstream) (int32_t)bitstream_get_num(bstream, 4, 1)
|
||||
#define read_i64(bstream) (int64_t)bitstream_get_num(bstream, 8, 1)
|
||||
|
||||
#define skip_u32(bstream) (void)bitstream_get_num(bstream,4,1)
|
||||
|
||||
#define next_u8(bstream) (uint8_t)bitstream_get_num(bstream,1,0)
|
||||
#define next_u16(bstream) (uint16_t)bitstream_get_num(bstream,2,0)
|
||||
#define next_u32(bstream) (uint32_t)bitstream_get_num(bstream,4,0)
|
||||
#define next_u64(bstream) (uint64_t)bitstream_get_num(bstream,8,0)
|
||||
#define next_i8(bstream) (int8_t)bitstream_get_num(bstream,1,0)
|
||||
#define next_i16(bstream) (int16_t)bitstream_get_num(bstream,2,0)
|
||||
#define next_i32(bstream) (int32_t)bitstream_get_num(bstream,4,0)
|
||||
#define next_i64(bstream) (int64_t)bitstream_get_num(bstream,8,0)
|
||||
#define skip_u32(bstream) (void)bitstream_get_num(bstream, 4, 1)
|
||||
|
||||
#define next_u8(bstream) (uint8_t)bitstream_get_num(bstream, 1, 0)
|
||||
#define next_u16(bstream) (uint16_t)bitstream_get_num(bstream, 2, 0)
|
||||
#define next_u32(bstream) (uint32_t)bitstream_get_num(bstream, 4, 0)
|
||||
#define next_u64(bstream) (uint64_t)bitstream_get_num(bstream, 8, 0)
|
||||
#define next_i8(bstream) (int8_t)bitstream_get_num(bstream, 1, 0)
|
||||
#define next_i16(bstream) (int16_t)bitstream_get_num(bstream, 2, 0)
|
||||
#define next_i32(bstream) (int32_t)bitstream_get_num(bstream, 4, 0)
|
||||
#define next_i64(bstream) (int64_t)bitstream_get_num(bstream, 8, 0)
|
||||
|
||||
int init_bitstream(struct bitstream *bstr, unsigned char *start, unsigned char *end);
|
||||
uint64_t next_bits(struct bitstream *bstr, unsigned bnum);
|
||||
|
||||
@@ -10,47 +10,47 @@
|
||||
<100 means display whatever was output to stderr as a warning
|
||||
>=100 means display whatever was output to stdout as an error
|
||||
*/
|
||||
#define EXIT_OK 0
|
||||
#define EXIT_NO_INPUT_FILES 2
|
||||
#define EXIT_TOO_MANY_INPUT_FILES 3
|
||||
#define EXIT_INCOMPATIBLE_PARAMETERS 4
|
||||
#define EXIT_UNABLE_TO_DETERMINE_FILE_SIZE 6
|
||||
#define EXIT_MALFORMED_PARAMETER 7
|
||||
#define EXIT_READ_ERROR 8
|
||||
#define EXIT_NO_CAPTIONS 10
|
||||
#define EXIT_WITH_HELP 11
|
||||
#define EXIT_NOT_CLASSIFIED 300
|
||||
#define EXIT_ERROR_IN_CAPITALIZATION_FILE 501
|
||||
#define EXIT_BUFFER_FULL 502
|
||||
#define EXIT_MISSING_ASF_HEADER 1001
|
||||
#define EXIT_MISSING_RCWT_HEADER 1002
|
||||
#define EXIT_OK 0
|
||||
#define EXIT_NO_INPUT_FILES 2
|
||||
#define EXIT_TOO_MANY_INPUT_FILES 3
|
||||
#define EXIT_INCOMPATIBLE_PARAMETERS 4
|
||||
#define EXIT_UNABLE_TO_DETERMINE_FILE_SIZE 6
|
||||
#define EXIT_MALFORMED_PARAMETER 7
|
||||
#define EXIT_READ_ERROR 8
|
||||
#define EXIT_NO_CAPTIONS 10
|
||||
#define EXIT_WITH_HELP 11
|
||||
#define EXIT_NOT_CLASSIFIED 300
|
||||
#define EXIT_ERROR_IN_CAPITALIZATION_FILE 501
|
||||
#define EXIT_BUFFER_FULL 502
|
||||
#define EXIT_MISSING_ASF_HEADER 1001
|
||||
#define EXIT_MISSING_RCWT_HEADER 1002
|
||||
|
||||
#define CCX_COMMON_EXIT_FILE_CREATION_FAILED 5
|
||||
#define CCX_COMMON_EXIT_UNSUPPORTED 9
|
||||
#define EXIT_NOT_ENOUGH_MEMORY 500
|
||||
#define CCX_COMMON_EXIT_BUG_BUG 1000
|
||||
#define CCX_COMMON_EXIT_FILE_CREATION_FAILED 5
|
||||
#define CCX_COMMON_EXIT_UNSUPPORTED 9
|
||||
#define EXIT_NOT_ENOUGH_MEMORY 500
|
||||
#define CCX_COMMON_EXIT_BUG_BUG 1000
|
||||
|
||||
#define CCX_OK 0
|
||||
#define CCX_FALSE 0
|
||||
#define CCX_TRUE 1
|
||||
#define CCX_EAGAIN -100
|
||||
#define CCX_EOF -101
|
||||
#define CCX_EINVAL -102
|
||||
#define CCX_OK 0
|
||||
#define CCX_FALSE 0
|
||||
#define CCX_TRUE 1
|
||||
#define CCX_EAGAIN -100
|
||||
#define CCX_EOF -101
|
||||
#define CCX_EINVAL -102
|
||||
#define CCX_ENOSUPP -103
|
||||
#define CCX_ENOMEM -104
|
||||
#define CCX_ENOMEM -104
|
||||
|
||||
// Declarations
|
||||
int cc608_parity(unsigned int byte);
|
||||
int fdprintf(int fd, const char *fmt, ...);
|
||||
void millis_to_time(LLONG milli, unsigned *hours, unsigned *minutes,unsigned *seconds, unsigned *ms);
|
||||
void millis_to_time(LLONG milli, unsigned *hours, unsigned *minutes, unsigned *seconds, unsigned *ms);
|
||||
|
||||
extern void ccxr_millis_to_time(LLONG milli, unsigned *hours, unsigned *minutes,unsigned *seconds, unsigned *ms);
|
||||
extern void ccxr_millis_to_time(LLONG milli, unsigned *hours, unsigned *minutes, unsigned *seconds, unsigned *ms);
|
||||
|
||||
void freep(void *arg);
|
||||
void dbg_print(LLONG mask, const char *fmt, ...);
|
||||
unsigned char *debug_608_to_ASC(unsigned char *ccdata, int channel);
|
||||
int add_cc_sub_text(struct cc_subtitle *sub, char *str, LLONG start_time,
|
||||
LLONG end_time, char *info, char *mode, enum ccx_encoding_type);
|
||||
LLONG end_time, char *info, char *mode, enum ccx_encoding_type);
|
||||
|
||||
extern int cc608_parity_table[256]; // From myth
|
||||
#endif
|
||||
|
||||
@@ -17,8 +17,8 @@ const unsigned char UTF8_BOM[] = {0xef, 0xbb, 0xbf};
|
||||
const unsigned char DVD_HEADER[8] = {0x00, 0x00, 0x01, 0xb2, 0x43, 0x43, 0x01, 0xf8};
|
||||
const unsigned char lc1[1] = {0x8a};
|
||||
const unsigned char lc2[1] = {0x8f};
|
||||
const unsigned char lc3[2] = {0x16, 0xfe};
|
||||
const unsigned char lc4[2] = {0x1e, 0xfe};
|
||||
const unsigned char lc3[1] = {0x16}; // McPoodle uses single-byte loop markers
|
||||
const unsigned char lc4[1] = {0x1e};
|
||||
const unsigned char lc5[1] = {0xff};
|
||||
const unsigned char lc6[1] = {0xfe};
|
||||
|
||||
|
||||
@@ -22,42 +22,42 @@ extern const unsigned char UTF8_BOM[3];
|
||||
extern const unsigned char DVD_HEADER[8];
|
||||
extern const unsigned char lc1[1];
|
||||
extern const unsigned char lc2[1];
|
||||
extern const unsigned char lc3[2];
|
||||
extern const unsigned char lc4[2];
|
||||
extern const unsigned char lc3[1];
|
||||
extern const unsigned char lc4[1];
|
||||
extern const unsigned char lc5[1];
|
||||
extern const unsigned char lc6[1];
|
||||
|
||||
extern unsigned char rcwt_header[11];
|
||||
|
||||
#define ONEPASS 120 /* Bytes we can always look ahead without going out of limits */
|
||||
#define BUFSIZE (2048*1024+ONEPASS) /* 2 Mb plus the safety pass */
|
||||
#define ONEPASS 120 /* Bytes we can always look ahead without going out of limits */
|
||||
#define BUFSIZE (2048 * 1024 + ONEPASS) /* 2 Mb plus the safety pass */
|
||||
#define MAX_CLOSED_CAPTION_DATA_PER_PICTURE 32
|
||||
#define EIA_708_BUFFER_LENGTH 2048 // TODO: Find out what the real limit is
|
||||
#define TS_PACKET_PAYLOAD_LENGTH 184 // From specs
|
||||
#define SUBLINESIZE 2048 // Max. length of a .srt line - TODO: Get rid of this
|
||||
#define STARTBYTESLENGTH (1024*1024)
|
||||
#define EIA_708_BUFFER_LENGTH 2048 // TODO: Find out what the real limit is
|
||||
#define TS_PACKET_PAYLOAD_LENGTH 184 // From specs
|
||||
#define SUBLINESIZE 2048 // Max. length of a .srt line - TODO: Get rid of this
|
||||
#define STARTBYTESLENGTH (1024 * 1024)
|
||||
#define UTF8_MAX_BYTES 6
|
||||
#define XMLRPC_CHUNK_SIZE (64*1024) // 64 Kb per chunk, to avoid too many realloc()
|
||||
#define XMLRPC_CHUNK_SIZE (64 * 1024) // 64 Kb per chunk, to avoid too many realloc()
|
||||
|
||||
enum ccx_debug_message_types
|
||||
{
|
||||
/* Each debug message now belongs to one of these types. Use bitmaps in case
|
||||
we want one message to belong to more than one type. */
|
||||
CCX_DMT_PARSE = 1, // Show information related to parsing the container
|
||||
CCX_DMT_VIDES = 2, // Show video stream related information
|
||||
CCX_DMT_TIME = 4, // Show GOP and PTS timing information
|
||||
CCX_DMT_VERBOSE = 8, // Show lots of debugging output
|
||||
CCX_DMT_DECODER_608 = 0x10, // Show CC-608 decoder debug?
|
||||
CCX_DMT_708 = 0x20, // Show CC-708 decoder debug?
|
||||
CCX_DMT_DECODER_XDS = 0x40, // Show XDS decoder debug?
|
||||
CCX_DMT_CBRAW = 0x80, // Caption blocks with FTS timing
|
||||
CCX_DMT_PARSE = 1, // Show information related to parsing the container
|
||||
CCX_DMT_VIDES = 2, // Show video stream related information
|
||||
CCX_DMT_TIME = 4, // Show GOP and PTS timing information
|
||||
CCX_DMT_VERBOSE = 8, // Show lots of debugging output
|
||||
CCX_DMT_DECODER_608 = 0x10, // Show CC-608 decoder debug?
|
||||
CCX_DMT_708 = 0x20, // Show CC-708 decoder debug?
|
||||
CCX_DMT_DECODER_XDS = 0x40, // Show XDS decoder debug?
|
||||
CCX_DMT_CBRAW = 0x80, // Caption blocks with FTS timing
|
||||
CCX_DMT_GENERIC_NOTICES = 0x100, // Generic, always displayed even if no debug is selected
|
||||
CCX_DMT_TELETEXT = 0x200, // Show teletext debug?
|
||||
CCX_DMT_PAT = 0x400, // Program Allocation Table dump
|
||||
CCX_DMT_PMT = 0x800, // Program Map Table dump
|
||||
CCX_DMT_LEVENSHTEIN = 0x1000, // Levenshtein distance calculations
|
||||
CCX_DMT_DVB = 0x2000, // DVB
|
||||
CCX_DMT_DUMPDEF = 0x4000 // Dump defective TS packets
|
||||
CCX_DMT_TELETEXT = 0x200, // Show teletext debug?
|
||||
CCX_DMT_PAT = 0x400, // Program Allocation Table dump
|
||||
CCX_DMT_PMT = 0x800, // Program Map Table dump
|
||||
CCX_DMT_LEVENSHTEIN = 0x1000, // Levenshtein distance calculations
|
||||
CCX_DMT_DVB = 0x2000, // DVB
|
||||
CCX_DMT_DUMPDEF = 0x4000 // Dump defective TS packets
|
||||
};
|
||||
|
||||
// AVC NAL types
|
||||
@@ -101,95 +101,95 @@ enum ccx_avc_nal_types
|
||||
enum ccx_stream_type
|
||||
{
|
||||
CCX_STREAM_TYPE_UNKNOWNSTREAM = 0,
|
||||
|
||||
/*
|
||||
|
||||
/*
|
||||
The later constants are defined by MPEG-TS standard
|
||||
Explore at: https://exiftool.org/TagNames/M2TS.html
|
||||
Explore at: https://exiftool.org/TagNames/M2TS.html
|
||||
*/
|
||||
CCX_STREAM_TYPE_VIDEO_MPEG1 = 0x01,
|
||||
CCX_STREAM_TYPE_VIDEO_MPEG2 = 0x02,
|
||||
CCX_STREAM_TYPE_AUDIO_MPEG1 = 0x03,
|
||||
CCX_STREAM_TYPE_AUDIO_MPEG2 = 0x04,
|
||||
CCX_STREAM_TYPE_PRIVATE_TABLE_MPEG2 = 0x05,
|
||||
CCX_STREAM_TYPE_PRIVATE_MPEG2 = 0x06,
|
||||
CCX_STREAM_TYPE_MHEG_PACKETS = 0x07,
|
||||
CCX_STREAM_TYPE_MPEG2_ANNEX_A_DSM_CC = 0x08,
|
||||
CCX_STREAM_TYPE_ITU_T_H222_1 = 0x09,
|
||||
CCX_STREAM_TYPE_VIDEO_MPEG1 = 0x01,
|
||||
CCX_STREAM_TYPE_VIDEO_MPEG2 = 0x02,
|
||||
CCX_STREAM_TYPE_AUDIO_MPEG1 = 0x03,
|
||||
CCX_STREAM_TYPE_AUDIO_MPEG2 = 0x04,
|
||||
CCX_STREAM_TYPE_PRIVATE_TABLE_MPEG2 = 0x05,
|
||||
CCX_STREAM_TYPE_PRIVATE_MPEG2 = 0x06,
|
||||
CCX_STREAM_TYPE_MHEG_PACKETS = 0x07,
|
||||
CCX_STREAM_TYPE_MPEG2_ANNEX_A_DSM_CC = 0x08,
|
||||
CCX_STREAM_TYPE_ITU_T_H222_1 = 0x09,
|
||||
CCX_STREAM_TYPE_ISO_IEC_13818_6_TYPE_A = 0x0A,
|
||||
CCX_STREAM_TYPE_ISO_IEC_13818_6_TYPE_B = 0x0B,
|
||||
CCX_STREAM_TYPE_ISO_IEC_13818_6_TYPE_C = 0x0C,
|
||||
CCX_STREAM_TYPE_ISO_IEC_13818_6_TYPE_D = 0x0D,
|
||||
CCX_STREAM_TYPE_AUDIO_AAC = 0x0f,
|
||||
CCX_STREAM_TYPE_VIDEO_MPEG4 = 0x10,
|
||||
CCX_STREAM_TYPE_VIDEO_H264 = 0x1b,
|
||||
CCX_STREAM_TYPE_PRIVATE_USER_MPEG2 = 0x80,
|
||||
CCX_STREAM_TYPE_AUDIO_AC3 = 0x81,
|
||||
CCX_STREAM_TYPE_AUDIO_HDMV_DTS = 0x82,
|
||||
CCX_STREAM_TYPE_AUDIO_DTS = 0x8a
|
||||
CCX_STREAM_TYPE_AUDIO_AAC = 0x0f,
|
||||
CCX_STREAM_TYPE_VIDEO_MPEG4 = 0x10,
|
||||
CCX_STREAM_TYPE_VIDEO_H264 = 0x1b,
|
||||
CCX_STREAM_TYPE_VIDEO_HEVC = 0x24,
|
||||
CCX_STREAM_TYPE_PRIVATE_USER_MPEG2 = 0x80,
|
||||
CCX_STREAM_TYPE_AUDIO_AC3 = 0x81,
|
||||
CCX_STREAM_TYPE_AUDIO_HDMV_DTS = 0x82,
|
||||
CCX_STREAM_TYPE_AUDIO_DTS = 0x8a
|
||||
};
|
||||
|
||||
enum ccx_mpeg_descriptor
|
||||
{
|
||||
/*
|
||||
/*
|
||||
The later constants are defined by ETSI EN 300 468 standard
|
||||
Explore at: https://www.etsi.org/deliver/etsi_en/300400_300499/300468/01.11.01_60/en_300468v011101p.pdf
|
||||
Explore at: https://www.etsi.org/deliver/etsi_en/300400_300499/300468/01.11.01_60/en_300468v011101p.pdf
|
||||
*/
|
||||
CCX_MPEG_DSC_REGISTRATION = 0x05,
|
||||
CCX_MPEG_DSC_DATA_STREAM_ALIGNMENT = 0x06,
|
||||
CCX_MPEG_DSC_ISO639_LANGUAGE = 0x0A,
|
||||
CCX_MPEG_DSC_VBI_DATA_DESCRIPTOR = 0x45,
|
||||
CCX_MPEG_DSC_REGISTRATION = 0x05,
|
||||
CCX_MPEG_DSC_DATA_STREAM_ALIGNMENT = 0x06,
|
||||
CCX_MPEG_DSC_ISO639_LANGUAGE = 0x0A,
|
||||
CCX_MPEG_DSC_VBI_DATA_DESCRIPTOR = 0x45,
|
||||
CCX_MPEG_DSC_VBI_TELETEXT_DESCRIPTOR = 0x46,
|
||||
CCX_MPEG_DSC_TELETEXT_DESCRIPTOR = 0x56,
|
||||
CCX_MPEG_DSC_DVB_SUBTITLE = 0x59,
|
||||
CCX_MPEG_DSC_TELETEXT_DESCRIPTOR = 0x56,
|
||||
CCX_MPEG_DSC_DVB_SUBTITLE = 0x59,
|
||||
/* User defined */
|
||||
CCX_MPEG_DSC_CAPTION_SERVICE = 0x86,
|
||||
CCX_MPEG_DESC_DATA_COMP = 0xfd // Consider to change DESC to DSC
|
||||
CCX_MPEG_DSC_CAPTION_SERVICE = 0x86,
|
||||
CCX_MPEG_DESC_DATA_COMP = 0xfd // Consider to change DESC to DSC
|
||||
};
|
||||
|
||||
|
||||
enum
|
||||
{
|
||||
CCX_MESSAGES_QUIET = 0,
|
||||
CCX_MESSAGES_QUIET = 0,
|
||||
CCX_MESSAGES_STDOUT = 1,
|
||||
CCX_MESSAGES_STDERR = 2
|
||||
};
|
||||
|
||||
enum ccx_datasource
|
||||
{
|
||||
CCX_DS_FILE = 0,
|
||||
CCX_DS_STDIN = 1,
|
||||
CCX_DS_FILE = 0,
|
||||
CCX_DS_STDIN = 1,
|
||||
CCX_DS_NETWORK = 2,
|
||||
CCX_DS_TCP = 3
|
||||
CCX_DS_TCP = 3
|
||||
};
|
||||
|
||||
enum ccx_output_format
|
||||
{
|
||||
CCX_OF_RAW = 0,
|
||||
CCX_OF_SRT = 1,
|
||||
CCX_OF_SAMI = 2,
|
||||
CCX_OF_RAW = 0,
|
||||
CCX_OF_SRT = 1,
|
||||
CCX_OF_SAMI = 2,
|
||||
CCX_OF_TRANSCRIPT = 3,
|
||||
CCX_OF_RCWT = 4,
|
||||
CCX_OF_NULL = 5,
|
||||
CCX_OF_SMPTETT = 6,
|
||||
CCX_OF_SPUPNG = 7,
|
||||
CCX_OF_DVDRAW = 8, // See -d at http://www.theneitherworld.com/mcpoodle/SCC_TOOLS/DOCS/SCC_TOOLS.HTML#CCExtract
|
||||
CCX_OF_WEBVTT = 9,
|
||||
CCX_OF_RCWT = 4,
|
||||
CCX_OF_NULL = 5,
|
||||
CCX_OF_SMPTETT = 6,
|
||||
CCX_OF_SPUPNG = 7,
|
||||
CCX_OF_DVDRAW = 8, // See -d at http://www.theneitherworld.com/mcpoodle/SCC_TOOLS/DOCS/SCC_TOOLS.HTML#CCExtract
|
||||
CCX_OF_WEBVTT = 9,
|
||||
CCX_OF_SIMPLE_XML = 10,
|
||||
CCX_OF_G608 = 11,
|
||||
CCX_OF_CURL = 12,
|
||||
CCX_OF_SSA = 13,
|
||||
CCX_OF_MCC = 14,
|
||||
CCX_OF_SCC = 15,
|
||||
CCX_OF_CCD = 16,
|
||||
CCX_OF_G608 = 11,
|
||||
CCX_OF_CURL = 12,
|
||||
CCX_OF_SSA = 13,
|
||||
CCX_OF_MCC = 14,
|
||||
CCX_OF_SCC = 15,
|
||||
CCX_OF_CCD = 16,
|
||||
};
|
||||
|
||||
enum ccx_output_date_format
|
||||
{
|
||||
ODF_NONE = 0,
|
||||
ODF_HHMMSS = 1,
|
||||
ODF_SECONDS = 2,
|
||||
ODF_DATE = 3,
|
||||
ODF_HHMMSSMS = 4 // HH:MM:SS,MILIS (.srt style)
|
||||
ODF_NONE = 0,
|
||||
ODF_HHMMSS = 1,
|
||||
ODF_SECONDS = 2,
|
||||
ODF_DATE = 3,
|
||||
ODF_HHMMSSMS = 4 // HH:MM:SS,MILIS (.srt style)
|
||||
};
|
||||
|
||||
enum ccx_stream_mode_enum
|
||||
@@ -199,9 +199,9 @@ enum ccx_stream_mode_enum
|
||||
CCX_SM_PROGRAM = 2,
|
||||
CCX_SM_ASF = 3,
|
||||
CCX_SM_MCPOODLESRAW = 4,
|
||||
CCX_SM_RCWT = 5, // Raw Captions With Time, not used yet.
|
||||
CCX_SM_MYTH = 6, // Use the myth loop
|
||||
CCX_SM_MP4 = 7, // MP4, ISO-
|
||||
CCX_SM_RCWT = 5, // Raw Captions With Time, not used yet.
|
||||
CCX_SM_MYTH = 6, // Use the myth loop
|
||||
CCX_SM_MP4 = 7, // MP4, ISO-
|
||||
#ifdef WTV_DEBUG
|
||||
CCX_SM_HEX_DUMP = 8, // Hexadecimal dump generated by wtvccdump
|
||||
#endif
|
||||
@@ -212,6 +212,7 @@ enum ccx_stream_mode_enum
|
||||
CCX_SM_GXF = 11,
|
||||
CCX_SM_MKV = 12,
|
||||
CCX_SM_MXF = 13,
|
||||
CCX_SM_SCC = 14, // Scenarist Closed Caption input
|
||||
|
||||
CCX_SM_AUTODETECT = 16
|
||||
};
|
||||
@@ -220,8 +221,8 @@ enum ccx_encoding_type
|
||||
{
|
||||
CCX_ENC_UNICODE = 0,
|
||||
CCX_ENC_LATIN_1 = 1,
|
||||
CCX_ENC_UTF_8 = 2,
|
||||
CCX_ENC_ASCII = 3
|
||||
CCX_ENC_UTF_8 = 2,
|
||||
CCX_ENC_ASCII = 3
|
||||
};
|
||||
|
||||
enum ccx_bufferdata_type
|
||||
@@ -237,7 +238,8 @@ enum ccx_bufferdata_type
|
||||
CCX_ISDB_SUBTITLE = 8,
|
||||
/* BUffer where cc data contain 3 byte cc_valid ccdata 1 ccdata 2 */
|
||||
CCX_RAW_TYPE = 9,
|
||||
CCX_DVD_SUBTITLE = 10
|
||||
CCX_DVD_SUBTITLE = 10,
|
||||
CCX_HEVC = 11
|
||||
};
|
||||
|
||||
enum ccx_frame_type
|
||||
@@ -249,32 +251,33 @@ enum ccx_frame_type
|
||||
CCX_FRAME_TYPE_D_FRAME = 4
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
NO = 0,
|
||||
typedef enum
|
||||
{
|
||||
NO = 0,
|
||||
YES = 1,
|
||||
UNDEFINED = 0xff
|
||||
} bool_t;
|
||||
|
||||
enum ccx_code_type
|
||||
{
|
||||
CCX_CODEC_ANY = 0,
|
||||
CCX_CODEC_ANY = 0,
|
||||
CCX_CODEC_TELETEXT = 1,
|
||||
CCX_CODEC_DVB = 2,
|
||||
CCX_CODEC_ISDB_CC = 3,
|
||||
CCX_CODEC_ATSC_CC = 4,
|
||||
CCX_CODEC_NONE = 5
|
||||
CCX_CODEC_DVB = 2,
|
||||
CCX_CODEC_ISDB_CC = 3,
|
||||
CCX_CODEC_ATSC_CC = 4,
|
||||
CCX_CODEC_NONE = 5
|
||||
};
|
||||
|
||||
/* Caption Distribution Packet */
|
||||
enum cdp_section_type
|
||||
{
|
||||
/*
|
||||
/*
|
||||
The later constants are defined by SMPTE ST 334
|
||||
Purchase for 80$ at: https://ieeexplore.ieee.org/document/8255806
|
||||
*/
|
||||
CDP_SECTION_DATA = 0x72,
|
||||
CDP_SECTION_DATA = 0x72,
|
||||
CDP_SECTION_SVC_INFO = 0x73,
|
||||
CDP_SECTION_FOOTER = 0x74
|
||||
CDP_SECTION_FOOTER = 0x74
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -287,9 +290,9 @@ enum cdp_section_type
|
||||
*
|
||||
*/
|
||||
|
||||
#define IS_VALID_TELETEXT_DESC(desc) ( ((desc) == CCX_MPEG_DSC_VBI_DATA_DESCRIPTOR )|| \
|
||||
( (desc) == CCX_MPEG_DSC_VBI_TELETEXT_DESCRIPTOR ) || \
|
||||
( (desc) == CCX_MPEG_DSC_TELETEXT_DESCRIPTOR ) )
|
||||
#define IS_VALID_TELETEXT_DESC(desc) (((desc) == CCX_MPEG_DSC_VBI_DATA_DESCRIPTOR) || \
|
||||
((desc) == CCX_MPEG_DSC_VBI_TELETEXT_DESCRIPTOR) || \
|
||||
((desc) == CCX_MPEG_DSC_TELETEXT_DESCRIPTOR))
|
||||
|
||||
/*
|
||||
* This macro to be used when you want to find out whether you
|
||||
@@ -308,19 +311,19 @@ enum cdp_section_type
|
||||
* @param f_sel pass the codec name whom you are testing to be feasible
|
||||
* to parse.
|
||||
*/
|
||||
#define IS_FEASIBLE(u_sel,u_nsel,f_sel) ( ( (u_sel) == CCX_CODEC_ANY && (u_nsel) != (f_sel) ) || (u_sel) == (f_sel) )
|
||||
#define CCX_TXT_FORBIDDEN 0 // Ignore teletext packets
|
||||
#define CCX_TXT_AUTO_NOT_YET_FOUND 1
|
||||
#define CCX_TXT_IN_USE 2 // Positive auto-detected, or forced, etc
|
||||
#define IS_FEASIBLE(u_sel, u_nsel, f_sel) (((u_sel) == CCX_CODEC_ANY && (u_nsel) != (f_sel)) || (u_sel) == (f_sel))
|
||||
#define CCX_TXT_FORBIDDEN 0 // Ignore teletext packets
|
||||
#define CCX_TXT_AUTO_NOT_YET_FOUND 1
|
||||
#define CCX_TXT_IN_USE 2 // Positive auto-detected, or forced, etc
|
||||
|
||||
#define NB_LANGUAGE 100
|
||||
extern const char *language[NB_LANGUAGE];
|
||||
|
||||
#define DEF_VAL_STARTCREDITSNOTBEFORE "0"
|
||||
#define DEF_VAL_STARTCREDITSNOTBEFORE "0"
|
||||
// To catch the theme after the teaser in TV shows
|
||||
#define DEF_VAL_STARTCREDITSNOTAFTER "5:00"
|
||||
#define DEF_VAL_STARTCREDITSFORATLEAST "2"
|
||||
#define DEF_VAL_STARTCREDITSFORATMOST "5"
|
||||
#define DEF_VAL_ENDCREDITSFORATLEAST "2"
|
||||
#define DEF_VAL_ENDCREDITSFORATMOST "5"
|
||||
#define DEF_VAL_STARTCREDITSNOTAFTER "5:00"
|
||||
#define DEF_VAL_STARTCREDITSFORATLEAST "2"
|
||||
#define DEF_VAL_STARTCREDITSFORATMOST "5"
|
||||
#define DEF_VAL_ENDCREDITSFORATLEAST "2"
|
||||
#define DEF_VAL_ENDCREDITSFORATMOST "5"
|
||||
#endif
|
||||
|
||||
@@ -73,7 +73,9 @@ void init_options(struct ccx_s_options *options)
|
||||
options->ocrlang = NULL; // By default, autodetect .traineddata file
|
||||
options->ocr_oem = -1; // By default, OEM mode depends on the tesseract version
|
||||
options->psm = 3; // Default PSM mode (3 is the default tesseract as well)
|
||||
options->ocr_quantmode = 1; // CCExtractor's internal
|
||||
options->ocr_quantmode = 0; // No quantization (better OCR accuracy for DVB subtitles)
|
||||
options->ocr_line_split = 0; // By default, don't split images into lines (pending testing)
|
||||
options->ocr_blacklist = 1; // By default, use character blacklist to prevent common OCR errors (| vs I, etc.)
|
||||
options->mkvlang = NULL; // By default, all the languages are extracted
|
||||
options->ignore_pts_jumps = 1;
|
||||
options->analyze_video_stream = 0;
|
||||
@@ -139,7 +141,9 @@ void init_options(struct ccx_s_options *options)
|
||||
options->enc_cfg.services_charsets = NULL;
|
||||
options->enc_cfg.all_services_charset = NULL;
|
||||
options->enc_cfg.with_semaphore = 0;
|
||||
options->enc_cfg.force_dropframe = 0; // Assume No Drop Frame for MCC Encode.
|
||||
options->enc_cfg.force_dropframe = 0; // Assume No Drop Frame for MCC Encode.
|
||||
options->enc_cfg.scc_framerate = 0; // Default: 29.97fps for SCC output
|
||||
options->enc_cfg.scc_accurate_timing = 0; // Default: off for backwards compatibility (issue #1120)
|
||||
options->enc_cfg.extract_only_708 = 0;
|
||||
|
||||
options->settings_dtvcc.enabled = 0;
|
||||
@@ -147,10 +151,13 @@ void init_options(struct ccx_s_options *options)
|
||||
options->settings_dtvcc.print_file_reports = 1;
|
||||
options->settings_dtvcc.no_rollup = 0;
|
||||
options->settings_dtvcc.report = NULL;
|
||||
options->settings_dtvcc.timing = NULL;
|
||||
memset(
|
||||
options->settings_dtvcc.services_enabled, 0,
|
||||
CCX_DTVCC_MAX_SERVICES * sizeof(options->settings_dtvcc.services_enabled[0]));
|
||||
|
||||
options->scc_framerate = 0; // Default: 29.97fps
|
||||
|
||||
#ifdef WITH_LIBCURL
|
||||
options->curlposturl = NULL;
|
||||
#endif
|
||||
|
||||
@@ -15,38 +15,38 @@ struct demuxer_cfg
|
||||
enum ccx_code_type codec;
|
||||
enum ccx_code_type nocodec;
|
||||
|
||||
unsigned ts_autoprogram; // Try to find a stream with captions automatically (no -pn needed)
|
||||
unsigned ts_autoprogram; // Try to find a stream with captions automatically (no -pn needed)
|
||||
unsigned ts_allprogram;
|
||||
unsigned ts_cappids[128]; // PID for stream that holds caption information
|
||||
unsigned ts_cappids[128]; // PID for stream that holds caption information
|
||||
int nb_ts_cappid;
|
||||
unsigned ts_forced_cappid ; // If 1, never mess with the selected PID
|
||||
int ts_forced_program; // Specific program to process in TS files, if ts_forced_program_selected==1
|
||||
unsigned ts_forced_cappid; // If 1, never mess with the selected PID
|
||||
int ts_forced_program; // Specific program to process in TS files, if ts_forced_program_selected==1
|
||||
unsigned ts_forced_program_selected;
|
||||
int ts_datastreamtype ; // User WANTED stream type (i.e. use the stream that has this type)
|
||||
int ts_datastreamtype; // User WANTED stream type (i.e. use the stream that has this type)
|
||||
unsigned ts_forced_streamtype; // User selected (forced) stream type
|
||||
};
|
||||
|
||||
struct encoder_cfg
|
||||
{
|
||||
int extract; // Extract 1st, 2nd or both fields
|
||||
int dtvcc_extract; // 1 or 0
|
||||
int gui_mode_reports; // If 1, output in stderr progress updates so the GUI can grab them
|
||||
int extract; // Extract 1st, 2nd or both fields
|
||||
int dtvcc_extract; // 1 or 0
|
||||
int gui_mode_reports; // If 1, output in stderr progress updates so the GUI can grab them
|
||||
char *output_filename;
|
||||
enum ccx_output_format write_format; // 0=Raw, 1=srt, 2=SMI
|
||||
int keep_output_closed;
|
||||
int force_flush; // Force flush on content write
|
||||
int append_mode; // Append mode for output files
|
||||
int ucla; // 1 if -UCLA used, 0 if not
|
||||
int force_flush; // Force flush on content write
|
||||
int append_mode; // Append mode for output files
|
||||
int ucla; // 1 if -UCLA used, 0 if not
|
||||
|
||||
enum ccx_encoding_type encoding;
|
||||
enum ccx_output_date_format date_format;
|
||||
char millis_separator;
|
||||
int autodash; // Add dashes (-) before each speaker automatically?
|
||||
int trim_subs; // " Remove spaces at sides? "
|
||||
int autodash; // Add dashes (-) before each speaker automatically?
|
||||
int trim_subs; // " Remove spaces at sides? "
|
||||
int sentence_cap; // FIX CASE? = Fix case?
|
||||
int splitbysentence; // Split text into complete sentences and prorate time?
|
||||
#ifdef WITH_LIBCURL
|
||||
char *curlposturl; // If out=curl, where do we send the data to?
|
||||
char *curlposturl; // If out=curl, where do we send the data to?
|
||||
#endif
|
||||
int filter_profanity; // Censors profane words from subtitles
|
||||
|
||||
@@ -54,45 +54,49 @@ struct encoder_cfg
|
||||
/* Credit stuff */
|
||||
char *start_credits_text;
|
||||
char *end_credits_text;
|
||||
struct ccx_boundary_time startcreditsnotbefore, startcreditsnotafter; // Where to insert start credits, if possible
|
||||
struct ccx_boundary_time startcreditsnotbefore, startcreditsnotafter; // Where to insert start credits, if possible
|
||||
struct ccx_boundary_time startcreditsforatleast, startcreditsforatmost; // How long to display them?
|
||||
struct ccx_boundary_time endcreditsforatleast, endcreditsforatmost;
|
||||
|
||||
ccx_encoders_transcript_format transcript_settings; // Keeps the settings for generating transcript output files.
|
||||
unsigned int send_to_srv;
|
||||
int no_bom; // Set to 1 when no BOM (Byte Order Mark) should be used for files. Note, this might make files unreadable in windows!
|
||||
int no_bom; // Set to 1 when no BOM (Byte Order Mark) should be used for files. Note, this might make files unreadable in windows!
|
||||
char *first_input_file;
|
||||
int multiple_files;
|
||||
int no_font_color;
|
||||
int no_type_setting;
|
||||
int cc_to_stdout; // If this is set to 1, the stdout will be flushed when data was written to the screen during a process_608 call.
|
||||
int line_terminator_lf; // 0 = CRLF, 1=LF
|
||||
LLONG subs_delay; // ms to delay (or advance) subs
|
||||
int cc_to_stdout; // If this is set to 1, the stdout will be flushed when data was written to the screen during a process_608 call.
|
||||
int line_terminator_lf; // 0 = CRLF, 1=LF
|
||||
LLONG subs_delay; // ms to delay (or advance) subs
|
||||
int program_number;
|
||||
unsigned char in_format;
|
||||
int nospupngocr; // 1 if we don't want to OCR bitmaps to add the text as comments in the XML file in spupng
|
||||
int nospupngocr; // 1 if we don't want to OCR bitmaps to add the text as comments in the XML file in spupng
|
||||
|
||||
// MCC File
|
||||
int force_dropframe; // 1 if dropframe frame count should be used. defaults to no drop frame.
|
||||
|
||||
int force_dropframe; // 1 if dropframe frame count should be used. defaults to no drop frame.
|
||||
|
||||
// SCC output framerate
|
||||
int scc_framerate; // SCC output framerate: 0=29.97 (default), 1=24, 2=25, 3=30
|
||||
int scc_accurate_timing; // If 1, use bandwidth-aware timing for broadcast compliance (issue #1120)
|
||||
|
||||
// text -> png (text render)
|
||||
char *render_font; // The font used to render text if needed (e.g. teletext->spupng)
|
||||
char *render_font; // The font used to render text if needed (e.g. teletext->spupng)
|
||||
char *render_font_italics;
|
||||
|
||||
//CEA-708
|
||||
// CEA-708
|
||||
int services_enabled[CCX_DTVCC_MAX_SERVICES];
|
||||
char** services_charsets;
|
||||
char* all_services_charset;
|
||||
int extract_only_708; // 1 if only 708 subs extraction is enabled
|
||||
char **services_charsets;
|
||||
char *all_services_charset;
|
||||
int extract_only_708; // 1 if only 708 subs extraction is enabled
|
||||
};
|
||||
|
||||
struct ccx_s_options // Options from user parameters
|
||||
{
|
||||
int extract; // Extract 1st, 2nd or both fields
|
||||
int no_rollup; // Disable roll-up emulation (no duplicate output in generated file)
|
||||
int extract; // Extract 1st, 2nd or both fields
|
||||
int no_rollup; // Disable roll-up emulation (no duplicate output in generated file)
|
||||
int noscte20;
|
||||
int webvtt_create_css;
|
||||
int cc_channel; // Channel we want to dump in srt mode
|
||||
int cc_channel; // Channel we want to dump in srt mode
|
||||
int buffer_input;
|
||||
int nofontcolor;
|
||||
int nohtmlescape;
|
||||
@@ -100,57 +104,59 @@ struct ccx_s_options // Options from user parameters
|
||||
struct ccx_boundary_time extraction_start, extraction_end; // Segment we actually process
|
||||
int print_file_reports;
|
||||
|
||||
ccx_decoder_608_settings settings_608; // Contains the settings for the 608 decoder.
|
||||
ccx_decoder_dtvcc_settings settings_dtvcc; // Same for 708 decoder
|
||||
int is_608_enabled; // Is 608 enabled by explicitly using flags(-1,-2,-12)
|
||||
int is_708_enabled; // Is 708 enabled by explicitly using flags(-svc)
|
||||
ccx_decoder_608_settings settings_608; // Contains the settings for the 608 decoder.
|
||||
ccx_decoder_dtvcc_settings settings_dtvcc; // Same for 708 decoder
|
||||
int is_608_enabled; // Is 608 enabled by explicitly using flags(-1,-2,-12)
|
||||
int is_708_enabled; // Is 708 enabled by explicitly using flags(-svc)
|
||||
|
||||
char millis_separator;
|
||||
int binary_concat; // Disabled by -ve or --videoedited
|
||||
int use_gop_as_pts; // Use GOP instead of PTS timing (0=do as needed, 1=always, -1=never)
|
||||
int fix_padding; // Replace 0000 with 8080 in HDTV (needed for some cards)
|
||||
int gui_mode_reports; // If 1, output in stderr progress updates so the GUI can grab them
|
||||
int no_progress_bar; // If 1, suppress the output of the progress to stdout
|
||||
char *sentence_cap_file; // Extra capitalization word file
|
||||
int live_stream; /* -1 -> Not a complete file but a live stream, without timeout
|
||||
0 -> A regular file
|
||||
>0 -> Live stream with a timeout of this value in seconds */
|
||||
char *filter_profanity_file; // Extra profanity word file
|
||||
int messages_target; // 0 = nowhere (quiet), 1=stdout, 2=stderr
|
||||
int timestamp_map; // If 1, add WebVTT X-TIMESTAMP-MAP header
|
||||
int binary_concat; // Disabled by -ve or --videoedited
|
||||
int use_gop_as_pts; // Use GOP instead of PTS timing (0=do as needed, 1=always, -1=never)
|
||||
int fix_padding; // Replace 0000 with 8080 in HDTV (needed for some cards)
|
||||
int gui_mode_reports; // If 1, output in stderr progress updates so the GUI can grab them
|
||||
int no_progress_bar; // If 1, suppress the output of the progress to stdout
|
||||
char *sentence_cap_file; // Extra capitalization word file
|
||||
int live_stream; /* -1 -> Not a complete file but a live stream, without timeout
|
||||
0 -> A regular file
|
||||
>0 -> Live stream with a timeout of this value in seconds */
|
||||
char *filter_profanity_file; // Extra profanity word file
|
||||
int messages_target; // 0 = nowhere (quiet), 1=stdout, 2=stderr
|
||||
int timestamp_map; // If 1, add WebVTT X-TIMESTAMP-MAP header
|
||||
/* Levenshtein's parameters, for string comparison */
|
||||
int dolevdist; // 0 => don't attempt to correct typos with this algorithm
|
||||
int dolevdist; // 0 => don't attempt to correct typos with this algorithm
|
||||
int levdistmincnt, levdistmaxpct; // Means 2 fails or less is "the same", 10% or less is also "the same"
|
||||
int investigate_packets; // Look for captions in all packets when everything else fails
|
||||
int fullbin; // Disable pruning of padding cc blocks
|
||||
int nosync; // Disable syncing
|
||||
unsigned int hauppauge_mode; // If 1, use PID=1003, process specially and so on
|
||||
int wtvconvertfix; // Fix broken Windows 7 conversion
|
||||
int investigate_packets; // Look for captions in all packets when everything else fails
|
||||
int fullbin; // Disable pruning of padding cc blocks
|
||||
int nosync; // Disable syncing
|
||||
unsigned int hauppauge_mode; // If 1, use PID=1003, process specially and so on
|
||||
int wtvconvertfix; // Fix broken Windows 7 conversion
|
||||
int wtvmpeg2;
|
||||
int auto_myth; // Use myth-tv mpeg code? 0=no, 1=yes, 2=auto
|
||||
int auto_myth; // Use myth-tv mpeg code? 0=no, 1=yes, 2=auto
|
||||
/* MP4 related stuff */
|
||||
unsigned mp4vidtrack; // Process the video track even if a CC dedicated track exists.
|
||||
int extract_chapters; // If 1, extracts chapters (if present), from MP4 files.
|
||||
unsigned mp4vidtrack; // Process the video track even if a CC dedicated track exists.
|
||||
int extract_chapters; // If 1, extracts chapters (if present), from MP4 files.
|
||||
/* General settings */
|
||||
int usepicorder; // Force the use of pic_order_cnt_lsb in AVC/H.264 data streams
|
||||
int xmltv; // 1 = full output. 2 = live output. 3 = both
|
||||
int xmltvliveinterval; // interval in seconds between writing xmltv output files in live mode
|
||||
int xmltvoutputinterval; // interval in seconds between writing xmltv full file output
|
||||
int xmltvonlycurrent; // 0 off 1 on
|
||||
int usepicorder; // Force the use of pic_order_cnt_lsb in AVC/H.264 data streams
|
||||
int xmltv; // 1 = full output. 2 = live output. 3 = both
|
||||
int xmltvliveinterval; // interval in seconds between writing xmltv output files in live mode
|
||||
int xmltvoutputinterval; // interval in seconds between writing xmltv full file output
|
||||
int xmltvonlycurrent; // 0 off 1 on
|
||||
int keep_output_closed;
|
||||
int force_flush; // Force flush on content write
|
||||
int append_mode; // Append mode for output files
|
||||
int ucla; // 1 if UCLA used, 0 if not
|
||||
int tickertext; // 1 if ticker text style burned in subs, 0 if not
|
||||
int hardsubx; // 1 if burned-in subtitles to be extracted
|
||||
int hardsubx_and_common; // 1 if both burned-in and not burned in need to be extracted
|
||||
char *dvblang; // The name of the language stream for DVB
|
||||
const char *ocrlang; // The name of the .traineddata file to be loaded with tesseract
|
||||
int ocr_oem; // The Tesseract OEM mode, could be 0 (default), 1 or 2
|
||||
int psm; // The Tesseract PSM mode, could be between 0 and 13. 3 is tesseract default
|
||||
int ocr_quantmode; // How to quantize the bitmap before passing to to tesseract (0=no quantization at all, 1=CCExtractor's internal)
|
||||
char *mkvlang; // The name of the language stream for MKV
|
||||
int analyze_video_stream; // If 1, the video stream will be processed even if we're using a different one for subtitles.
|
||||
int force_flush; // Force flush on content write
|
||||
int append_mode; // Append mode for output files
|
||||
int ucla; // 1 if UCLA used, 0 if not
|
||||
int tickertext; // 1 if ticker text style burned in subs, 0 if not
|
||||
int hardsubx; // 1 if burned-in subtitles to be extracted
|
||||
int hardsubx_and_common; // 1 if both burned-in and not burned in need to be extracted
|
||||
char *dvblang; // The name of the language stream for DVB
|
||||
const char *ocrlang; // The name of the .traineddata file to be loaded with tesseract
|
||||
int ocr_oem; // The Tesseract OEM mode, could be 0 (default), 1 or 2
|
||||
int psm; // The Tesseract PSM mode, could be between 0 and 13. 3 is tesseract default
|
||||
int ocr_quantmode; // How to quantize the bitmap before passing to to tesseract (0=no quantization at all, 1=CCExtractor's internal)
|
||||
int ocr_line_split; // If 1, split images into lines before OCR (uses PSM 7 for better accuracy)
|
||||
int ocr_blacklist; // If 1, use character blacklist to prevent common OCR errors (default: enabled)
|
||||
char *mkvlang; // The name of the language stream for MKV
|
||||
int analyze_video_stream; // If 1, the video stream will be processed even if we're using a different one for subtitles.
|
||||
|
||||
/*HardsubX related stuff*/
|
||||
int hardsubx_ocr_mode;
|
||||
@@ -164,42 +170,43 @@ struct ccx_s_options // Options from user parameters
|
||||
ccx_encoders_transcript_format transcript_settings; // Keeps the settings for generating transcript output files.
|
||||
enum ccx_output_date_format date_format;
|
||||
unsigned send_to_srv;
|
||||
enum ccx_output_format write_format; // 0=Raw, 1=srt, 2=SMI
|
||||
enum ccx_output_format write_format; // 0=Raw, 1=srt, 2=SMI
|
||||
int write_format_rewritten;
|
||||
int use_ass_instead_of_ssa;
|
||||
int use_webvtt_styling;
|
||||
LLONG debug_mask; // dbg_print will use this mask to print or ignore different types
|
||||
LLONG debug_mask_on_debug; // If we're using temp_debug to enable/disable debug "live", this is the mask when temp_debug=1
|
||||
LLONG debug_mask; // dbg_print will use this mask to print or ignore different types
|
||||
LLONG debug_mask_on_debug; // If we're using temp_debug to enable/disable debug "live", this is the mask when temp_debug=1
|
||||
/* Networking */
|
||||
char *udpsrc;
|
||||
char *udpaddr;
|
||||
unsigned udpport; // Non-zero => Listen for UDP packets on this port, no files.
|
||||
unsigned udpport; // Non-zero => Listen for UDP packets on this port, no files.
|
||||
char *tcpport;
|
||||
char *tcp_password;
|
||||
char *tcp_desc;
|
||||
char *srv_addr;
|
||||
char *srv_port;
|
||||
int noautotimeref; // Do NOT set time automatically?
|
||||
enum ccx_datasource input_source; // Files, stdin or network
|
||||
int noautotimeref; // Do NOT set time automatically?
|
||||
enum ccx_datasource input_source; // Files, stdin or network
|
||||
|
||||
char *output_filename;
|
||||
|
||||
char **inputfile; // List of files to process
|
||||
int num_input_files; // How many?
|
||||
char **inputfile; // List of files to process
|
||||
int num_input_files; // How many?
|
||||
struct demuxer_cfg demux_cfg;
|
||||
struct encoder_cfg enc_cfg;
|
||||
LLONG subs_delay; // ms to delay (or advance) subs
|
||||
int cc_to_stdout; // If this is set to 1, the stdout will be flushed when data was written to the screen during a process_608 call.
|
||||
int pes_header_to_stdout; // If this is set to 1, the PES Header will be printed to console (debugging purposes)
|
||||
int ignore_pts_jumps; // If 1, the program will ignore PTS jumps. Sometimes this parameter is required for DVB subs with > 30s pause time
|
||||
LLONG subs_delay; // ms to delay (or advance) subs
|
||||
int cc_to_stdout; // If this is set to 1, the stdout will be flushed when data was written to the screen during a process_608 call.
|
||||
int pes_header_to_stdout; // If this is set to 1, the PES Header will be printed to console (debugging purposes)
|
||||
int ignore_pts_jumps; // If 1, the program will ignore PTS jumps. Sometimes this parameter is required for DVB subs with > 30s pause time
|
||||
int multiprogram;
|
||||
int out_interval;
|
||||
int segment_on_key_frames_only;
|
||||
int scc_framerate; // SCC input framerate: 0=29.97 (default), 1=24, 2=25, 3=30
|
||||
#ifdef WITH_LIBCURL
|
||||
char *curlposturl;
|
||||
#endif
|
||||
};
|
||||
|
||||
extern struct ccx_s_options ccx_options;
|
||||
void init_options (struct ccx_s_options *options);
|
||||
void init_options(struct ccx_s_options *options);
|
||||
#endif
|
||||
|
||||
@@ -1,123 +1,122 @@
|
||||
#ifndef CCX_PLATFORM_H
|
||||
#define CCX_PLATFORM_H
|
||||
#define CCX_PLATFORM_H
|
||||
|
||||
// Default includes (cross-platform)
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <time.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdarg.h>
|
||||
#include <errno.h>
|
||||
// Default includes (cross-platform)
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <time.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdarg.h>
|
||||
#include <errno.h>
|
||||
|
||||
#define __STDC_FORMAT_MACROS
|
||||
#define __STDC_FORMAT_MACROS
|
||||
|
||||
#ifdef _WIN32
|
||||
#define inline _inline
|
||||
#define typeof decltype
|
||||
#include <io.h>
|
||||
#include <ws2tcpip.h>
|
||||
#include <windows.h>
|
||||
#define STDIN_FILENO 0
|
||||
#define STDOUT_FILENO 1
|
||||
#define STDERR_FILENO 2
|
||||
#include "inttypes.h"
|
||||
#undef UINT64_MAX
|
||||
#define UINT64_MAX _UI64_MAX
|
||||
typedef int socklen_t;
|
||||
#if !defined(__MINGW64__) && !defined(__MINGW32__)
|
||||
typedef int ssize_t;
|
||||
#endif
|
||||
typedef uint32_t in_addr_t;
|
||||
#ifndef IN_CLASSD
|
||||
#define IN_CLASSD(i) (((INT32)(i) & 0xf0000000) == 0xe0000000)
|
||||
#define IN_MULTICAST(i) IN_CLASSD(i)
|
||||
#endif
|
||||
#include <direct.h>
|
||||
#define mkdir(path, mode) _mkdir(path)
|
||||
#ifndef snprintf
|
||||
// Added ifndef because VS2013 warns for macro redefinition.
|
||||
#define snprintf(buf, len, fmt, ...) _snprintf(buf, len, fmt, __VA_ARGS__)
|
||||
#endif
|
||||
#define sleep(sec) Sleep((sec) * 1000)
|
||||
#ifdef _WIN32
|
||||
#define inline _inline
|
||||
#define typeof decltype
|
||||
#include <io.h>
|
||||
#include <ws2tcpip.h>
|
||||
#include <windows.h>
|
||||
#define STDIN_FILENO 0
|
||||
#define STDOUT_FILENO 1
|
||||
#define STDERR_FILENO 2
|
||||
#include "inttypes.h"
|
||||
#undef UINT64_MAX
|
||||
#define UINT64_MAX _UI64_MAX
|
||||
typedef int socklen_t;
|
||||
#if !defined(__MINGW64__) && !defined(__MINGW32__)
|
||||
typedef int ssize_t;
|
||||
#endif
|
||||
typedef uint32_t in_addr_t;
|
||||
#ifndef IN_CLASSD
|
||||
#define IN_CLASSD(i) (((INT32)(i) & 0xf0000000) == 0xe0000000)
|
||||
#define IN_MULTICAST(i) IN_CLASSD(i)
|
||||
#endif
|
||||
#include <direct.h>
|
||||
#define mkdir(path, mode) _mkdir(path)
|
||||
#ifndef snprintf
|
||||
// Added ifndef because VS2013 warns for macro redefinition.
|
||||
#define snprintf(buf, len, fmt, ...) _snprintf(buf, len, fmt, __VA_ARGS__)
|
||||
#endif
|
||||
#define sleep(sec) Sleep((sec) * 1000)
|
||||
|
||||
#include <fcntl.h>
|
||||
#else // _WIN32
|
||||
#include <unistd.h>
|
||||
#define __STDC_LIMIT_MACROS
|
||||
#include <inttypes.h>
|
||||
#include <stdint.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <netdb.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#endif // _WIN32
|
||||
#include <fcntl.h>
|
||||
#else // _WIN32
|
||||
#include <unistd.h>
|
||||
#define __STDC_LIMIT_MACROS
|
||||
#include <inttypes.h>
|
||||
#include <stdint.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <netdb.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#endif // _WIN32
|
||||
|
||||
//#include "disable_warnings.h"
|
||||
// #include "disable_warnings.h"
|
||||
|
||||
#if defined(_MSC_VER) && !defined(__clang__)
|
||||
#include "stdintmsc.h"
|
||||
// Don't bug me with strcpy() deprecation warnings
|
||||
#pragma warning(disable : 4996)
|
||||
#else
|
||||
#include <stdint.h>
|
||||
#endif
|
||||
#if defined(_MSC_VER) && !defined(__clang__)
|
||||
#include "stdintmsc.h"
|
||||
// Don't bug me with strcpy() deprecation warnings
|
||||
#pragma warning(disable : 4996)
|
||||
#else
|
||||
#include <stdint.h>
|
||||
#endif
|
||||
|
||||
#ifdef __OpenBSD__
|
||||
#define FOPEN64 fopen
|
||||
#define OPEN open
|
||||
#define FSEEK fseek
|
||||
#define FTELL ftell
|
||||
#define LSEEK lseek
|
||||
#define FSTAT fstat
|
||||
#else
|
||||
#ifdef _WIN32
|
||||
#define OPEN _open
|
||||
// 64 bit file functions
|
||||
#if defined(_MSC_VER)
|
||||
#define FSEEK _fseeki64
|
||||
#define FTELL _ftelli64
|
||||
#else
|
||||
// For MinGW
|
||||
#define FSEEK fseeko64
|
||||
#define FTELL ftello64
|
||||
#endif
|
||||
#define TELL _telli64
|
||||
#define LSEEK _lseeki64
|
||||
typedef struct _stati64 FSTATSTRUCT;
|
||||
#else
|
||||
// Linux internally maps these functions to 64bit usage,
|
||||
// if _FILE_OFFSET_BITS macro is set to 64
|
||||
#define FOPEN64 fopen
|
||||
#define OPEN open
|
||||
#define LSEEK lseek
|
||||
#define FSEEK fseek
|
||||
#define FTELL ftell
|
||||
#define FSTAT fstat
|
||||
#define TELL tell
|
||||
#include <stdint.h>
|
||||
#endif
|
||||
#endif
|
||||
#ifdef __OpenBSD__
|
||||
#define FOPEN64 fopen
|
||||
#define OPEN open
|
||||
#define FSEEK fseek
|
||||
#define FTELL ftell
|
||||
#define LSEEK lseek
|
||||
#define FSTAT fstat
|
||||
#else
|
||||
#ifdef _WIN32
|
||||
#define OPEN _open
|
||||
// 64 bit file functions
|
||||
#if defined(_MSC_VER)
|
||||
#define FSEEK _fseeki64
|
||||
#define FTELL _ftelli64
|
||||
#else
|
||||
// For MinGW
|
||||
#define FSEEK fseeko64
|
||||
#define FTELL ftello64
|
||||
#endif
|
||||
#define TELL _telli64
|
||||
#define LSEEK _lseeki64
|
||||
typedef struct _stati64 FSTATSTRUCT;
|
||||
#else
|
||||
// Linux internally maps these functions to 64bit usage,
|
||||
// if _FILE_OFFSET_BITS macro is set to 64
|
||||
#define FOPEN64 fopen
|
||||
#define OPEN open
|
||||
#define LSEEK lseek
|
||||
#define FSEEK fseek
|
||||
#define FTELL ftell
|
||||
#define FSTAT fstat
|
||||
#define TELL tell
|
||||
#include <stdint.h>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef int64_t_C
|
||||
#define int64_t_C(c) (c ## LL)
|
||||
#define uint64_t_C(c) (c ## ULL)
|
||||
#endif
|
||||
#ifndef int64_t_C
|
||||
#define int64_t_C(c) (c##LL)
|
||||
#define uint64_t_C(c) (c##ULL)
|
||||
#endif
|
||||
|
||||
#ifndef O_BINARY
|
||||
#define O_BINARY 0 // Not present in Linux because it's always binary
|
||||
#endif
|
||||
#ifndef O_BINARY
|
||||
#define O_BINARY 0 // Not present in Linux because it's always binary
|
||||
#endif
|
||||
|
||||
#ifndef max
|
||||
#define max(a,b) (((a) > (b)) ? (a) : (b))
|
||||
#endif
|
||||
#ifndef max
|
||||
#define max(a, b) (((a) > (b)) ? (a) : (b))
|
||||
#endif
|
||||
|
||||
typedef int64_t LLONG;
|
||||
typedef uint64_t ULLONG;
|
||||
typedef uint8_t UBYTE;
|
||||
typedef int64_t LLONG;
|
||||
typedef uint64_t ULLONG;
|
||||
typedef uint8_t UBYTE;
|
||||
|
||||
#endif // CCX_PLATFORM_H
|
||||
|
||||
|
||||
@@ -3,26 +3,28 @@
|
||||
|
||||
#include "ccx_common_constants.h"
|
||||
|
||||
enum ccx_common_logging_gui {
|
||||
CCX_COMMON_LOGGING_GUI_XDS_PROGRAM_NAME, // Called with xds_program_name
|
||||
CCX_COMMON_LOGGING_GUI_XDS_PROGRAM_ID_NR, // Called with current_xds_min, current_xds_hour, current_xds_date, current_xds_month
|
||||
enum ccx_common_logging_gui
|
||||
{
|
||||
CCX_COMMON_LOGGING_GUI_XDS_PROGRAM_NAME, // Called with xds_program_name
|
||||
CCX_COMMON_LOGGING_GUI_XDS_PROGRAM_ID_NR, // Called with current_xds_min, current_xds_hour, current_xds_date, current_xds_month
|
||||
CCX_COMMON_LOGGING_GUI_XDS_PROGRAM_DESCRIPTION, // Called with line_num, xds_desc
|
||||
CCX_COMMON_LOGGING_GUI_XDS_CALL_LETTERS // Called with current_xds_call_letters
|
||||
CCX_COMMON_LOGGING_GUI_XDS_CALL_LETTERS // Called with current_xds_call_letters
|
||||
};
|
||||
|
||||
struct ccx_common_logging_t {
|
||||
LLONG debug_mask; // The debug mask that is used to determine if things should be printed or not.
|
||||
void(*fatal_ftn) (int exit_code, const char *fmt, ...); // Used when an unrecoverable error happens. This should log output/save the error and then exit the program.
|
||||
void(*debug_ftn) (LLONG mask, const char *fmt, ...); // Used to process debug output. Mask can be ignored (custom set by debug_mask).
|
||||
void(*log_ftn)(const char *fmt, ...); // Used to print things. Replacement of standard printf, to allow more control.
|
||||
void(*gui_ftn)(enum ccx_common_logging_gui message_type, ...); // Used to display things in a gui (if appropriate). Is called with the message_type and appropriate variables (described in enum)
|
||||
struct ccx_common_logging_t
|
||||
{
|
||||
LLONG debug_mask; // The debug mask that is used to determine if things should be printed or not.
|
||||
void (*fatal_ftn)(int exit_code, const char *fmt, ...); // Used when an unrecoverable error happens. This should log output/save the error and then exit the program.
|
||||
void (*debug_ftn)(LLONG mask, const char *fmt, ...); // Used to process debug output. Mask can be ignored (custom set by debug_mask).
|
||||
void (*log_ftn)(const char *fmt, ...); // Used to print things. Replacement of standard printf, to allow more control.
|
||||
void (*gui_ftn)(enum ccx_common_logging_gui message_type, ...); // Used to display things in a gui (if appropriate). Is called with the message_type and appropriate variables (described in enum)
|
||||
};
|
||||
extern struct ccx_common_logging_t ccx_common_logging;
|
||||
|
||||
enum subdatatype
|
||||
{
|
||||
CC_DATATYPE_GENERIC=0,
|
||||
CC_DATATYPE_DVB=1
|
||||
CC_DATATYPE_GENERIC = 0,
|
||||
CC_DATATYPE_DVB = 1
|
||||
};
|
||||
|
||||
enum subtype
|
||||
@@ -34,18 +36,18 @@ enum subtype
|
||||
};
|
||||
|
||||
/**
|
||||
* Raw Subtitle struct used as output of decoder (cc608)
|
||||
* and input for encoder (sami, srt, transcript or smptett etc)
|
||||
*
|
||||
* if subtype CC_BITMAP then data contain nb_data numbers of rectangle
|
||||
* which have to be displayed at same time.
|
||||
*/
|
||||
* Raw Subtitle struct used as output of decoder (cc608)
|
||||
* and input for encoder (sami, srt, transcript or smptett etc)
|
||||
*
|
||||
* if subtype CC_BITMAP then data contain nb_data numbers of rectangle
|
||||
* which have to be displayed at same time.
|
||||
*/
|
||||
struct cc_subtitle
|
||||
{
|
||||
/**
|
||||
* A generic data which contain data according to decoder
|
||||
* @warn decoder cant output multiple types of data
|
||||
*/
|
||||
* A generic data which contain data according to decoder
|
||||
* @warn decoder cant output multiple types of data
|
||||
*/
|
||||
void *data;
|
||||
enum subdatatype datatype;
|
||||
|
||||
@@ -56,11 +58,11 @@ struct cc_subtitle
|
||||
enum subtype type;
|
||||
|
||||
/** Encoding type of Text, must be ignored in case of subtype as bitmap or cc_screen*/
|
||||
enum ccx_encoding_type enc_type;
|
||||
enum ccx_encoding_type enc_type;
|
||||
|
||||
/* set only when all the data is to be displayed at same time
|
||||
* Unit is of time is ms
|
||||
*/
|
||||
* Unit is of time is ms
|
||||
*/
|
||||
LLONG start_time;
|
||||
LLONG end_time;
|
||||
|
||||
@@ -72,13 +74,19 @@ struct cc_subtitle
|
||||
|
||||
/** flag to tell that decoder has given output */
|
||||
int got_output;
|
||||
|
||||
|
||||
char mode[5];
|
||||
char info[4];
|
||||
|
||||
/** Used for DVB end time in ms */
|
||||
int time_out;
|
||||
|
||||
/** Raw PTS value when this subtitle started (for DVB timing) */
|
||||
LLONG start_pts;
|
||||
|
||||
/** Teletext page number (for multi-page extraction, issue #665) */
|
||||
uint16_t teletext_page;
|
||||
|
||||
struct cc_subtitle *next;
|
||||
struct cc_subtitle *prev;
|
||||
};
|
||||
|
||||
@@ -35,6 +35,8 @@ void ccxr_set_current_pts(struct ccx_common_timing_ctx *ctx, LLONG pts);
|
||||
int ccxr_set_fts(struct ccx_common_timing_ctx *ctx);
|
||||
LLONG ccxr_get_fts(struct ccx_common_timing_ctx *ctx, int current_field);
|
||||
LLONG ccxr_get_fts_max(struct ccx_common_timing_ctx *ctx);
|
||||
LLONG ccxr_get_visible_start(struct ccx_common_timing_ctx *ctx, int current_field);
|
||||
LLONG ccxr_get_visible_end(struct ccx_common_timing_ctx *ctx, int current_field);
|
||||
char *ccxr_print_mstime_static(LLONG mstime, char *buf);
|
||||
void ccxr_print_debug_timing(struct ccx_common_timing_ctx *ctx);
|
||||
void ccxr_calculate_ms_gop_time(struct gop_time_code *g);
|
||||
@@ -63,6 +65,9 @@ struct ccx_common_timing_ctx *init_timing_ctx(struct ccx_common_timing_settings_
|
||||
ctx->current_pts = 0;
|
||||
ctx->current_picture_coding_type = CCX_FRAME_TYPE_RESET_OR_UNKNOWN;
|
||||
ctx->min_pts_adjusted = 0;
|
||||
ctx->seen_known_frame_type = 0;
|
||||
ctx->pending_min_pts = 0x01FFFFFFFFLL;
|
||||
ctx->unknown_frame_count = 0;
|
||||
ctx->min_pts = 0x01FFFFFFFFLL; // 33 bit
|
||||
ctx->max_pts = 0;
|
||||
ctx->sync_pts = 0;
|
||||
@@ -108,15 +113,18 @@ LLONG get_fts_max(struct ccx_common_timing_ctx *ctx)
|
||||
|
||||
/**
|
||||
* SCC Time formatting
|
||||
* Note: buf must have at least 32 bytes available from the write position
|
||||
*/
|
||||
size_t print_scc_time(struct ccx_boundary_time time, char *buf)
|
||||
{
|
||||
char *fmt = "%02u:%02u:%02u;%02u";
|
||||
double frame;
|
||||
// Format produces "HH:MM:SS;FF" = 11 chars + null, use 32 for safety
|
||||
const size_t max_time_len = 32;
|
||||
|
||||
frame = ((double)(time.time_in_ms - 1000 * (time.ss + 60 * (time.mm + 60 * time.hh))) * 29.97 / 1000);
|
||||
|
||||
return (size_t)sprintf(buf + time.set, fmt, time.hh, time.mm, time.ss, (unsigned)frame);
|
||||
return (size_t)snprintf(buf + time.set, max_time_len, fmt, time.hh, time.mm, time.ss, (unsigned)frame);
|
||||
}
|
||||
|
||||
struct ccx_boundary_time get_time(LLONG time)
|
||||
@@ -137,11 +145,14 @@ struct ccx_boundary_time get_time(LLONG time)
|
||||
/**
|
||||
* Fill buffer with a time string using specified format
|
||||
* @param fmt has to contain 4 format specifiers for h, m, s and ms respectively
|
||||
* Note: buf must have at least 32 bytes available from the write position
|
||||
*/
|
||||
size_t print_mstime_buff(LLONG mstime, char *fmt, char *buf)
|
||||
{
|
||||
unsigned hh, mm, ss, ms;
|
||||
int signoffset = (mstime < 0 ? 1 : 0);
|
||||
// Typical format produces "HH:MM:SS:MSS" = 12 chars + null, use 32 for safety
|
||||
const size_t max_time_len = 32;
|
||||
|
||||
if (mstime < 0) // Avoid loss of data warning with abs()
|
||||
mstime = -mstime;
|
||||
@@ -153,7 +164,7 @@ size_t print_mstime_buff(LLONG mstime, char *fmt, char *buf)
|
||||
|
||||
buf[0] = '-';
|
||||
|
||||
return (size_t)sprintf(buf + signoffset, fmt, hh, mm, ss, ms);
|
||||
return (size_t)snprintf(buf + signoffset, max_time_len, fmt, hh, mm, ss, ms);
|
||||
}
|
||||
|
||||
/* Fill a static buffer with a time string (hh:mm:ss:ms) corresponding
|
||||
|
||||
@@ -17,40 +17,43 @@ struct gop_time_code
|
||||
|
||||
struct ccx_common_timing_settings_t
|
||||
{
|
||||
int disable_sync_check; // If 1, timeline jumps will be ignored. This is important in several input formats that are assumed to have correct timing, no matter what.
|
||||
int no_sync; // If 1, there will be no sync at all. Mostly useful for debugging.
|
||||
int disable_sync_check; // If 1, timeline jumps will be ignored. This is important in several input formats that are assumed to have correct timing, no matter what.
|
||||
int no_sync; // If 1, there will be no sync at all. Mostly useful for debugging.
|
||||
int is_elementary_stream; // Needs to be set, as it's used in set_fts.
|
||||
LLONG *file_position; // The position of the file
|
||||
LLONG *file_position; // The position of the file
|
||||
};
|
||||
extern struct ccx_common_timing_settings_t ccx_common_timing_settings;
|
||||
|
||||
struct ccx_boundary_time
|
||||
{
|
||||
int hh,mm,ss;
|
||||
int hh, mm, ss;
|
||||
LLONG time_in_ms;
|
||||
int set;
|
||||
};
|
||||
|
||||
struct ccx_common_timing_ctx
|
||||
{
|
||||
int pts_set; // 0 = No, 1 = received, 2 = min_pts set
|
||||
int min_pts_adjusted; // 0 = No, 1=Yes (don't adjust again)
|
||||
int pts_set; // 0 = No, 1 = received, 2 = min_pts set
|
||||
int min_pts_adjusted; // 0 = No, 1=Yes (don't adjust again)
|
||||
int seen_known_frame_type; // 0 = No, 1 = Yes. Tracks if we've seen a frame with known type
|
||||
LLONG pending_min_pts; // Minimum PTS seen while waiting for frame type determination
|
||||
unsigned int unknown_frame_count; // Count of set_fts calls with unknown frame type
|
||||
LLONG current_pts;
|
||||
enum ccx_frame_type current_picture_coding_type;
|
||||
int current_tref; // Store temporal reference of current frame
|
||||
int current_tref; // Store temporal reference of current frame
|
||||
LLONG min_pts;
|
||||
LLONG max_pts;
|
||||
LLONG sync_pts;
|
||||
LLONG minimum_fts; // No screen should start before this FTS
|
||||
LLONG fts_now; // Time stamp of current file (w/ fts_offset, w/o fts_global)
|
||||
LLONG fts_now; // Time stamp of current file (w/ fts_offset, w/o fts_global)
|
||||
LLONG fts_offset; // Time before first sync_pts
|
||||
LLONG fts_fc_offset; // Time before first GOP
|
||||
LLONG fts_max; // Remember the maximum fts that we saw in current file
|
||||
LLONG fts_max; // Remember the maximum fts that we saw in current file
|
||||
LLONG fts_global; // Duration of previous files (-ve mode)
|
||||
int sync_pts2fts_set; // 0 = No, 1 = Yes
|
||||
LLONG sync_pts2fts_fts;
|
||||
LLONG sync_pts2fts_pts;
|
||||
int pts_reset; // 0 = No, 1 = Yes. PTS resets when current_pts is lower than prev
|
||||
int pts_reset; // 0 = No, 1 = Yes. PTS resets when current_pts is lower than prev
|
||||
};
|
||||
// Count 608 (per field) and 708 blocks since last set_fts() call
|
||||
extern int cb_field1, cb_field2, cb_708;
|
||||
@@ -60,7 +63,6 @@ extern int MPEG_CLOCK_FREQ; // This is part of the standard
|
||||
extern int max_dif;
|
||||
extern unsigned pts_big_change;
|
||||
|
||||
|
||||
extern enum ccx_frame_type current_picture_coding_type;
|
||||
extern double current_fps;
|
||||
extern int frames_since_ref_time;
|
||||
@@ -85,7 +87,7 @@ LLONG get_fts_max(struct ccx_common_timing_ctx *ctx);
|
||||
char *print_mstime_static(LLONG mstime);
|
||||
size_t print_mstime_buff(LLONG mstime, char *fmt, char *buf);
|
||||
void print_debug_timing(struct ccx_common_timing_ctx *ctx);
|
||||
int gop_accepted(struct gop_time_code* g);
|
||||
int gop_accepted(struct gop_time_code *g);
|
||||
void calculate_ms_gop_time(struct gop_time_code *g);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -127,6 +127,8 @@ ccx_decoder_608_context *ccx_decoder_608_init_library(struct ccx_decoder_608_set
|
||||
ccx_decoder_608_context *data = NULL;
|
||||
|
||||
data = malloc(sizeof(ccx_decoder_608_context));
|
||||
if (!data)
|
||||
return NULL;
|
||||
|
||||
data->cursor_column = 0;
|
||||
data->cursor_row = 0;
|
||||
@@ -147,10 +149,11 @@ ccx_decoder_608_context *ccx_decoder_608_init_library(struct ccx_decoder_608_set
|
||||
data->my_field = field;
|
||||
data->my_channel = channel;
|
||||
data->have_cursor_position = 0;
|
||||
data->rollup_from_popon = 0;
|
||||
data->output_format = output_format;
|
||||
data->cc_to_stdout = cc_to_stdout;
|
||||
data->textprinted = 0;
|
||||
data->ts_start_of_current_line = 0;
|
||||
// Note: ts_start_of_current_line already set to -1 above
|
||||
|
||||
data->halt = halt;
|
||||
|
||||
@@ -198,6 +201,9 @@ void delete_to_end_of_row(ccx_decoder_608_context *context)
|
||||
{
|
||||
if (context->mode != MODE_TEXT)
|
||||
{
|
||||
if (context->cursor_row >= CCX_DECODER_608_SCREEN_ROWS)
|
||||
return;
|
||||
|
||||
struct eia608_screen *use_buffer = get_writing_buffer(context);
|
||||
for (int i = context->cursor_column; i <= CCX_DECODER_608_SCREEN_WIDTH - 1; i++)
|
||||
{
|
||||
@@ -218,6 +224,10 @@ void write_char(const unsigned char c, ccx_decoder_608_context *context)
|
||||
/* printf ("\rWriting char [%c] at %s:%d:%d\n",c,
|
||||
use_buffer == &wb->data608->buffer1?"B1":"B2",
|
||||
wb->data608->cursor_row,wb->data608->cursor_column); */
|
||||
|
||||
if (context->cursor_row >= CCX_DECODER_608_SCREEN_ROWS || context->cursor_column >= CCX_DECODER_608_SCREEN_WIDTH)
|
||||
return;
|
||||
|
||||
use_buffer->characters[context->cursor_row][context->cursor_column] = c;
|
||||
use_buffer->colors[context->cursor_row][context->cursor_column] = context->current_color;
|
||||
use_buffer->fonts[context->cursor_row][context->cursor_column] = context->font;
|
||||
@@ -225,7 +235,9 @@ void write_char(const unsigned char c, ccx_decoder_608_context *context)
|
||||
|
||||
if (use_buffer->empty)
|
||||
{
|
||||
if (MODE_POPON != context->mode)
|
||||
// Don't set start time if we're in a transition from pop-on to roll-up
|
||||
// In this case, start time will be set when CR causes scrolling
|
||||
if (MODE_POPON != context->mode && !context->rollup_from_popon)
|
||||
context->current_visible_start_ms = get_visible_start(context->timing, context->my_field);
|
||||
}
|
||||
use_buffer->empty = 0;
|
||||
@@ -311,12 +323,23 @@ int write_cc_buffer(ccx_decoder_608_context *context, struct cc_subtitle *sub)
|
||||
|
||||
if (!data->empty && context->output_format != CCX_OF_NULL)
|
||||
{
|
||||
sub->data = (struct eia608_screen *)realloc(sub->data, (sub->nb_data + 1) * sizeof(*data));
|
||||
if (!sub->data)
|
||||
size_t new_size;
|
||||
|
||||
if (sub->nb_data + 1 > SIZE_MAX / sizeof(struct eia608_screen))
|
||||
{
|
||||
ccx_common_logging.log_ftn("No Memory left");
|
||||
ccx_common_logging.log_ftn("Too many screens, cannot allocate more memory.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
new_size = (sub->nb_data + 1) * sizeof(struct eia608_screen);
|
||||
|
||||
struct eia608_screen *new_data = (struct eia608_screen *)realloc(sub->data, new_size);
|
||||
if (!new_data)
|
||||
{
|
||||
ccx_common_logging.log_ftn("Out of memory while reallocating screen buffer\n");
|
||||
return 0;
|
||||
}
|
||||
sub->data = new_data;
|
||||
sub->datatype = CC_DATATYPE_GENERIC;
|
||||
memcpy(((struct eia608_screen *)sub->data) + sub->nb_data, data, sizeof(*data));
|
||||
sub->nb_data++;
|
||||
@@ -380,12 +403,23 @@ int write_cc_line(ccx_decoder_608_context *context, struct cc_subtitle *sub)
|
||||
|
||||
if (!data->empty)
|
||||
{
|
||||
sub->data = (struct eia608_screen *)realloc(sub->data, (sub->nb_data + 1) * sizeof(*data));
|
||||
if (!sub->data)
|
||||
size_t new_size;
|
||||
|
||||
if (sub->nb_data + 1 > SIZE_MAX / sizeof(struct eia608_screen))
|
||||
{
|
||||
ccx_common_logging.log_ftn("No Memory left");
|
||||
ccx_common_logging.log_ftn("Too many screens, cannot allocate more memory.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
new_size = (sub->nb_data + 1) * sizeof(struct eia608_screen);
|
||||
|
||||
struct eia608_screen *new_data = (struct eia608_screen *)realloc(sub->data, new_size);
|
||||
if (!new_data)
|
||||
{
|
||||
ccx_common_logging.log_ftn("Out of memory while reallocating screen buffer\n");
|
||||
return 0;
|
||||
}
|
||||
sub->data = new_data;
|
||||
memcpy(((struct eia608_screen *)sub->data) + sub->nb_data, data, sizeof(*data));
|
||||
data = (struct eia608_screen *)sub->data + sub->nb_data;
|
||||
sub->datatype = CC_DATATYPE_GENERIC;
|
||||
@@ -720,6 +754,10 @@ void handle_command(unsigned char c1, const unsigned char c2, ccx_decoder_608_co
|
||||
if (write_cc_buffer(context, sub))
|
||||
context->screenfuls_counter++;
|
||||
erase_memory(context, true);
|
||||
// Track transition from pop-on/paint-on to roll-up for timing adjustment
|
||||
// Start time will be set when CR causes scrolling (matching FFmpeg behavior)
|
||||
context->rollup_from_popon = 1;
|
||||
context->ts_start_of_current_line = -1;
|
||||
}
|
||||
erase_memory(context, false);
|
||||
|
||||
@@ -772,6 +810,15 @@ void handle_command(unsigned char c1, const unsigned char c2, ccx_decoder_608_co
|
||||
changes = check_roll_up(context);
|
||||
if (changes)
|
||||
{
|
||||
// Handle pop-on to roll-up transition timing
|
||||
// Use ts_start_of_current_line (when current line started) as the start time
|
||||
// This matches FFmpeg's behavior of timestamping when the display changed
|
||||
if (context->rollup_from_popon && context->ts_start_of_current_line > 0)
|
||||
{
|
||||
context->current_visible_start_ms = context->ts_start_of_current_line;
|
||||
context->rollup_from_popon = 0;
|
||||
}
|
||||
|
||||
// Only if the roll up would actually cause a line to disappear we write the buffer
|
||||
if (context->output_format != CCX_OF_TRANSCRIPT)
|
||||
{
|
||||
@@ -781,8 +828,18 @@ void handle_command(unsigned char c1, const unsigned char c2, ccx_decoder_608_co
|
||||
erase_memory(context, true); // Make sure the lines we just wrote aren't written again
|
||||
}
|
||||
}
|
||||
roll_up(context); // The roll must be done anyway of course.
|
||||
context->ts_start_of_current_line = -1; // Unknown.
|
||||
roll_up(context); // The roll must be done anyway of course.
|
||||
// When in pop-on to roll-up transition with changes=0 (first CR, only 1 line),
|
||||
// preserve the CR time so the next caption uses the display state change time,
|
||||
// not the character typing time. This matches FFmpeg's timing behavior.
|
||||
if (context->rollup_from_popon && !changes)
|
||||
{
|
||||
context->ts_start_of_current_line = get_fts(context->timing, context->my_field);
|
||||
}
|
||||
else
|
||||
{
|
||||
context->ts_start_of_current_line = -1; // Unknown.
|
||||
}
|
||||
if (changes)
|
||||
context->current_visible_start_ms = get_visible_start(context->timing, context->my_field);
|
||||
context->cursor_column = 0;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#ifndef CCX_DECODER_608_H
|
||||
#define CCX_DECODER_608_H
|
||||
#define CCX_DECODER_608_H
|
||||
#include "ccx_common_platform.h"
|
||||
#include "ccx_common_structs.h"
|
||||
#include "ccx_decoders_structs.h"
|
||||
@@ -19,11 +19,11 @@ struct ccx_decoder_608_report
|
||||
|
||||
typedef struct ccx_decoder_608_settings
|
||||
{
|
||||
int direct_rollup; // Write roll-up captions directly instead of line by line?
|
||||
int force_rollup; // 0=Disabled, 1, 2 or 3=max lines in roll-up mode
|
||||
int no_rollup; // If 1, write one line at a time
|
||||
int direct_rollup; // Write roll-up captions directly instead of line by line?
|
||||
int force_rollup; // 0=Disabled, 1, 2 or 3=max lines in roll-up mode
|
||||
int no_rollup; // If 1, write one line at a time
|
||||
enum ccx_decoder_608_color_code default_color; // Default color to use.
|
||||
int screens_to_process; // How many screenfuls we want? Use -1 for unlimited
|
||||
int screens_to_process; // How many screenfuls we want? Use -1 for unlimited
|
||||
struct ccx_decoder_608_report *report;
|
||||
} ccx_decoder_608_settings;
|
||||
|
||||
@@ -34,33 +34,33 @@ typedef struct ccx_decoder_608_context
|
||||
struct eia608_screen buffer2;
|
||||
int cursor_row, cursor_column;
|
||||
int visible_buffer;
|
||||
int screenfuls_counter; // Number of meaningful screenfuls written
|
||||
int screenfuls_counter; // Number of meaningful screenfuls written
|
||||
LLONG current_visible_start_ms; // At what time did the current visible buffer became so?
|
||||
enum cc_modes mode;
|
||||
unsigned char last_c1, last_c2;
|
||||
int channel; // Currently selected channel
|
||||
enum ccx_decoder_608_color_code current_color; // Color we are currently using to write
|
||||
enum font_bits font; // Font we are currently using to write
|
||||
int channel; // Currently selected channel
|
||||
enum ccx_decoder_608_color_code current_color; // Color we are currently using to write
|
||||
enum font_bits font; // Font we are currently using to write
|
||||
int rollup_base_row;
|
||||
LLONG ts_start_of_current_line; /* Time at which the first character for current line was received, =-1 no character received yet */
|
||||
LLONG ts_last_char_received; /* Time at which the last written character was received, =-1 no character received yet */
|
||||
int new_channel; // The new channel after a channel change
|
||||
int my_field; // Used for sanity checks
|
||||
int my_channel; // Used for sanity checks
|
||||
long bytes_processed_608; // To be written ONLY by process_608
|
||||
LLONG ts_last_char_received; /* Time at which the last written character was received, =-1 no character received yet */
|
||||
int new_channel; // The new channel after a channel change
|
||||
int my_field; // Used for sanity checks
|
||||
int my_channel; // Used for sanity checks
|
||||
int rollup_from_popon; // Track transition from pop-on/paint-on to roll-up mode
|
||||
int64_t bytes_processed_608; // To be written ONLY by process_608
|
||||
int have_cursor_position;
|
||||
|
||||
int *halt; // Can be used to halt the feeding of caption data. Set to 1 if screens_to_progress != -1 && screenfuls_counter >= screens_to_process
|
||||
int cc_to_stdout; // If this is set to 1, the stdout will be flushed when data was written to the screen during a process_608 call.
|
||||
int *halt; // Can be used to halt the feeding of caption data. Set to 1 if screens_to_progress != -1 && screenfuls_counter >= screens_to_process
|
||||
int cc_to_stdout; // If this is set to 1, the stdout will be flushed when data was written to the screen during a process_608 call.
|
||||
struct ccx_decoder_608_report *report;
|
||||
LLONG subs_delay; // ms to delay (or advance) subs
|
||||
LLONG subs_delay; // ms to delay (or advance) subs
|
||||
enum ccx_output_format output_format; // What kind of output format should be used?
|
||||
int textprinted;
|
||||
struct ccx_common_timing_ctx *timing;
|
||||
|
||||
} ccx_decoder_608_context;
|
||||
|
||||
|
||||
#define MAX_COLOR 10
|
||||
extern const char *color_text[MAX_COLOR][2];
|
||||
|
||||
@@ -80,7 +80,7 @@ enum command_code
|
||||
COM_ERASENONDISPLAYEDMEMORY = 11,
|
||||
COM_BACKSPACE = 12,
|
||||
COM_RESUMETEXTDISPLAY = 13,
|
||||
COM_ALARMOFF =14,
|
||||
COM_ALARMOFF = 14,
|
||||
COM_ALARMON = 15,
|
||||
COM_DELETETOENDOFROW = 16,
|
||||
COM_RESUMEDIRECTCAPTIONING = 17,
|
||||
@@ -89,15 +89,14 @@ enum command_code
|
||||
COM_FAKE_RULLUP1 = 18
|
||||
};
|
||||
|
||||
|
||||
void ccx_decoder_608_dinit_library(void **ctx);
|
||||
/*
|
||||
*
|
||||
*/
|
||||
ccx_decoder_608_context* ccx_decoder_608_init_library(struct ccx_decoder_608_settings *settings, int channel,
|
||||
int field, int *halt,
|
||||
int cc_to_stdout,
|
||||
enum ccx_output_format output_format, struct ccx_common_timing_ctx *timing);
|
||||
ccx_decoder_608_context *ccx_decoder_608_init_library(struct ccx_decoder_608_settings *settings, int channel,
|
||||
int field, int *halt,
|
||||
int cc_to_stdout,
|
||||
enum ccx_output_format output_format, struct ccx_common_timing_ctx *timing);
|
||||
|
||||
/**
|
||||
* @param data raw cc608 data to be processed
|
||||
|
||||
@@ -998,6 +998,14 @@ void dtvcc_handle_DFx_DefineWindow(dtvcc_service_decoder *decoder, int window_id
|
||||
int row_count = (data[4] & 0xf) + 1; // according to CEA-708-D
|
||||
int anchor_point = data[4] >> 4;
|
||||
int col_count = (data[5] & 0x3f) + 1; // according to CEA-708-D
|
||||
|
||||
if (row_count > CCX_DTVCC_MAX_ROWS || col_count > CCX_DTVCC_MAX_COLUMNS)
|
||||
{
|
||||
ccx_common_logging.log_ftn("[CEA-708] Invalid window size %dx%d (max %dx%d), rejecting window definition\n",
|
||||
row_count, col_count, CCX_DTVCC_MAX_ROWS, CCX_DTVCC_MAX_COLUMNS);
|
||||
return;
|
||||
}
|
||||
|
||||
int pen_style = data[6] & 0x7;
|
||||
int win_style = (data[6] >> 3) & 0x7;
|
||||
|
||||
@@ -1025,6 +1033,18 @@ void dtvcc_handle_DFx_DefineWindow(dtvcc_service_decoder *decoder, int window_id
|
||||
if (anchor_horizontal > CCX_DTVCC_SCREENGRID_COLUMNS - col_count)
|
||||
anchor_horizontal = CCX_DTVCC_SCREENGRID_COLUMNS - col_count;
|
||||
|
||||
if (window->is_defined)
|
||||
{
|
||||
if (row_count < window->row_count)
|
||||
{
|
||||
// Remove the oldest row if the row count is reduced
|
||||
for (int i = row_count; i < window->row_count; i++)
|
||||
{
|
||||
dtvcc_window_rollup(decoder, window);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
window->priority = priority;
|
||||
window->col_lock = col_lock;
|
||||
window->row_lock = row_lock;
|
||||
@@ -1329,6 +1349,14 @@ void dtvcc_handle_SPL_SetPenLocation(dtvcc_service_decoder *decoder, unsigned ch
|
||||
}
|
||||
|
||||
dtvcc_window *window = &decoder->windows[decoder->current_window];
|
||||
if (row >= window->row_count || col >= window->col_count)
|
||||
{
|
||||
ccx_common_logging.log_ftn("[CEA-708] dtvcc_handle_SPL_SetPenLocation: "
|
||||
"Invalid pen location %d:%d for window size %dx%d, rejecting command\n",
|
||||
row, col, window->row_count, window->col_count);
|
||||
return;
|
||||
}
|
||||
|
||||
window->pen_row = row;
|
||||
window->pen_column = col;
|
||||
}
|
||||
@@ -1467,7 +1495,12 @@ int dtvcc_handle_C0(dtvcc_ctx *dtvcc,
|
||||
else if (c0 >= 0x18 && c0 <= 0x1F)
|
||||
{
|
||||
if (c0 == DTVCC_C0_P16) // PE16
|
||||
dtvcc_handle_C0_P16(decoder, data + 1);
|
||||
{
|
||||
if (data_length >= 3)
|
||||
dtvcc_handle_C0_P16(decoder, data + 1);
|
||||
else
|
||||
ccx_common_logging.debug_ftn(CCX_DMT_708, "[CEA-708] dtvcc_handle_C0: Not enough data for P16\n");
|
||||
}
|
||||
len = 3;
|
||||
}
|
||||
if (len == -1)
|
||||
@@ -1621,6 +1654,9 @@ int dtvcc_handle_extended_char(dtvcc_service_decoder *decoder, unsigned char *da
|
||||
ccx_common_logging.debug_ftn(CCX_DMT_708, "[CEA-708] In dtvcc_handle_extended_char, "
|
||||
"first data code: [%c], length: [%u]\n",
|
||||
data[0], data_length);
|
||||
if (data_length < 1)
|
||||
return 0;
|
||||
|
||||
unsigned char c = 0x20; // Default to space
|
||||
unsigned char code = data[0];
|
||||
if (/* data[i]>=0x00 && */ code <= 0x1F) // Comment to silence warning
|
||||
@@ -1689,8 +1725,17 @@ void dtvcc_process_service_block(dtvcc_ctx *dtvcc,
|
||||
}
|
||||
else // Use extended set
|
||||
{
|
||||
used = dtvcc_handle_extended_char(decoder, data + i + 1, data_length - 1);
|
||||
used++; // Since we had DTVCC_C0_EXT1
|
||||
if (i + 1 >= data_length)
|
||||
{
|
||||
used = 1; // skip EXT1
|
||||
}
|
||||
else
|
||||
{
|
||||
used = dtvcc_handle_extended_char(decoder,
|
||||
data + i + 1,
|
||||
data_length - i - 1) +
|
||||
1;
|
||||
}
|
||||
}
|
||||
i += used;
|
||||
}
|
||||
@@ -1742,6 +1787,12 @@ void dtvcc_process_current_packet(dtvcc_ctx *dtvcc, int len)
|
||||
|
||||
if (service_number == 7) // There is an extended header
|
||||
{
|
||||
if (pos + 1 >= dtvcc->current_packet + len)
|
||||
{
|
||||
ccx_common_logging.debug_ftn(CCX_DMT_708, "[CEA-708] dtvcc_process_current_packet: "
|
||||
"Truncated extended header, stopping.\n");
|
||||
break;
|
||||
}
|
||||
pos++;
|
||||
service_number = (pos[0] & 0x3F); // 6 more significant bits
|
||||
// printf ("Extended header: Service number: [%d]\n",service_number);
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
#include "ccx_common_constants.h"
|
||||
#include "ccx_common_structs.h"
|
||||
|
||||
#define CCX_DTVCC_MAX_PACKET_LENGTH 128 //According to EIA-708B, part 5
|
||||
#define CCX_DTVCC_MAX_PACKET_LENGTH 128 // According to EIA-708B, part 5
|
||||
#define CCX_DTVCC_MAX_SERVICES 63
|
||||
|
||||
#define CCX_DTVCC_MAX_ROWS 15
|
||||
@@ -14,7 +14,7 @@
|
||||
* This value should be 32, but there were 16-bit encoded samples (from Korea),
|
||||
* where RowCount calculated another way and equals 46 (23[8bit]*2)
|
||||
*/
|
||||
#define CCX_DTVCC_MAX_COLUMNS (32*2)
|
||||
#define CCX_DTVCC_MAX_COLUMNS (32 * 2)
|
||||
|
||||
#define CCX_DTVCC_SCREENGRID_ROWS 75
|
||||
#define CCX_DTVCC_SCREENGRID_COLUMNS 210
|
||||
@@ -30,14 +30,14 @@
|
||||
|
||||
enum DTVCC_COMMANDS_C0_CODES
|
||||
{
|
||||
DTVCC_C0_NUL = 0x00,
|
||||
DTVCC_C0_ETX = 0x03,
|
||||
DTVCC_C0_BS = 0x08,
|
||||
DTVCC_C0_FF = 0x0c,
|
||||
DTVCC_C0_CR = 0x0d,
|
||||
DTVCC_C0_HCR = 0x0e,
|
||||
DTVCC_C0_NUL = 0x00,
|
||||
DTVCC_C0_ETX = 0x03,
|
||||
DTVCC_C0_BS = 0x08,
|
||||
DTVCC_C0_FF = 0x0c,
|
||||
DTVCC_C0_CR = 0x0d,
|
||||
DTVCC_C0_HCR = 0x0e,
|
||||
DTVCC_C0_EXT1 = 0x10,
|
||||
DTVCC_C0_P16 = 0x18
|
||||
DTVCC_C0_P16 = 0x18
|
||||
};
|
||||
|
||||
enum DTVCC_COMMANDS_C1_CODES
|
||||
@@ -86,21 +86,21 @@ struct DTVCC_S_COMMANDS_C1
|
||||
|
||||
enum dtvcc_window_justify
|
||||
{
|
||||
DTVCC_WINDOW_JUSTIFY_LEFT = 0,
|
||||
DTVCC_WINDOW_JUSTIFY_RIGHT = 1,
|
||||
DTVCC_WINDOW_JUSTIFY_CENTER = 2,
|
||||
DTVCC_WINDOW_JUSTIFY_FULL = 3
|
||||
DTVCC_WINDOW_JUSTIFY_LEFT = 0,
|
||||
DTVCC_WINDOW_JUSTIFY_RIGHT = 1,
|
||||
DTVCC_WINDOW_JUSTIFY_CENTER = 2,
|
||||
DTVCC_WINDOW_JUSTIFY_FULL = 3
|
||||
};
|
||||
|
||||
enum dtvcc_window_pd //Print Direction
|
||||
enum dtvcc_window_pd // Print Direction
|
||||
{
|
||||
DTVCC_WINDOW_PD_LEFT_RIGHT = 0, //left -> right
|
||||
DTVCC_WINDOW_PD_LEFT_RIGHT = 0, // left -> right
|
||||
DTVCC_WINDOW_PD_RIGHT_LEFT = 1,
|
||||
DTVCC_WINDOW_PD_TOP_BOTTOM = 2,
|
||||
DTVCC_WINDOW_PD_BOTTOM_TOP = 3
|
||||
};
|
||||
|
||||
enum dtvcc_window_sd //Scroll Direction
|
||||
enum dtvcc_window_sd // Scroll Direction
|
||||
{
|
||||
DTVCC_WINDOW_SD_LEFT_RIGHT = 0,
|
||||
DTVCC_WINDOW_SD_RIGHT_LEFT = 1,
|
||||
@@ -108,14 +108,14 @@ enum dtvcc_window_sd //Scroll Direction
|
||||
DTVCC_WINDOW_SD_BOTTOM_TOP = 3
|
||||
};
|
||||
|
||||
enum dtvcc_window_sde //Scroll Display Effect
|
||||
enum dtvcc_window_sde // Scroll Display Effect
|
||||
{
|
||||
DTVCC_WINDOW_SDE_SNAP = 0,
|
||||
DTVCC_WINDOW_SDE_FADE = 1,
|
||||
DTVCC_WINDOW_SDE_WIPE = 2
|
||||
};
|
||||
|
||||
enum dtvcc_window_ed //Effect Direction
|
||||
enum dtvcc_window_ed // Effect Direction
|
||||
{
|
||||
DTVCC_WINDOW_ED_LEFT_RIGHT = 0,
|
||||
DTVCC_WINDOW_ED_RIGHT_LEFT = 1,
|
||||
@@ -123,91 +123,91 @@ enum dtvcc_window_ed //Effect Direction
|
||||
DTVCC_WINDOW_ED_BOTTOM_TOP = 3
|
||||
};
|
||||
|
||||
enum dtvcc_window_fo //Fill Opacity
|
||||
enum dtvcc_window_fo // Fill Opacity
|
||||
{
|
||||
DTVCC_WINDOW_FO_SOLID = 0,
|
||||
DTVCC_WINDOW_FO_FLASH = 1,
|
||||
DTVCC_WINDOW_FO_TRANSLUCENT = 2,
|
||||
DTVCC_WINDOW_FO_SOLID = 0,
|
||||
DTVCC_WINDOW_FO_FLASH = 1,
|
||||
DTVCC_WINDOW_FO_TRANSLUCENT = 2,
|
||||
DTVCC_WINDOW_FO_TRANSPARENT = 3
|
||||
};
|
||||
|
||||
enum dtvcc_window_border
|
||||
{
|
||||
DTVCC_WINDOW_BORDER_NONE = 0,
|
||||
DTVCC_WINDOW_BORDER_RAISED = 1,
|
||||
DTVCC_WINDOW_BORDER_DEPRESSED = 2,
|
||||
DTVCC_WINDOW_BORDER_UNIFORM = 3,
|
||||
DTVCC_WINDOW_BORDER_SHADOW_LEFT = 4,
|
||||
DTVCC_WINDOW_BORDER_SHADOW_RIGHT = 5
|
||||
DTVCC_WINDOW_BORDER_NONE = 0,
|
||||
DTVCC_WINDOW_BORDER_RAISED = 1,
|
||||
DTVCC_WINDOW_BORDER_DEPRESSED = 2,
|
||||
DTVCC_WINDOW_BORDER_UNIFORM = 3,
|
||||
DTVCC_WINDOW_BORDER_SHADOW_LEFT = 4,
|
||||
DTVCC_WINDOW_BORDER_SHADOW_RIGHT = 5
|
||||
};
|
||||
|
||||
enum dtvcc_pen_size
|
||||
{
|
||||
DTVCC_PEN_SIZE_SMALL = 0,
|
||||
DTVCC_PEN_SIZE_SMALL = 0,
|
||||
DTVCC_PEN_SIZE_STANDART = 1,
|
||||
DTVCC_PEN_SIZE_LARGE = 2
|
||||
DTVCC_PEN_SIZE_LARGE = 2
|
||||
};
|
||||
|
||||
enum dtvcc_pen_font_style
|
||||
{
|
||||
DTVCC_PEN_FONT_STYLE_DEFAULT_OR_UNDEFINED = 0,
|
||||
DTVCC_PEN_FONT_STYLE_MONOSPACED_WITH_SERIFS = 1,
|
||||
DTVCC_PEN_FONT_STYLE_PROPORTIONALLY_SPACED_WITH_SERIFS = 2,
|
||||
DTVCC_PEN_FONT_STYLE_MONOSPACED_WITHOUT_SERIFS = 3,
|
||||
DTVCC_PEN_FONT_STYLE_PROPORTIONALLY_SPACED_WITHOUT_SERIFS = 4,
|
||||
DTVCC_PEN_FONT_STYLE_CASUAL_FONT_TYPE = 5,
|
||||
DTVCC_PEN_FONT_STYLE_CURSIVE_FONT_TYPE = 6,
|
||||
DTVCC_PEN_FONT_STYLE_SMALL_CAPITALS = 7
|
||||
DTVCC_PEN_FONT_STYLE_DEFAULT_OR_UNDEFINED = 0,
|
||||
DTVCC_PEN_FONT_STYLE_MONOSPACED_WITH_SERIFS = 1,
|
||||
DTVCC_PEN_FONT_STYLE_PROPORTIONALLY_SPACED_WITH_SERIFS = 2,
|
||||
DTVCC_PEN_FONT_STYLE_MONOSPACED_WITHOUT_SERIFS = 3,
|
||||
DTVCC_PEN_FONT_STYLE_PROPORTIONALLY_SPACED_WITHOUT_SERIFS = 4,
|
||||
DTVCC_PEN_FONT_STYLE_CASUAL_FONT_TYPE = 5,
|
||||
DTVCC_PEN_FONT_STYLE_CURSIVE_FONT_TYPE = 6,
|
||||
DTVCC_PEN_FONT_STYLE_SMALL_CAPITALS = 7
|
||||
};
|
||||
|
||||
enum dtvcc_pen_text_tag
|
||||
{
|
||||
DTVCC_PEN_TEXT_TAG_DIALOG = 0,
|
||||
DTVCC_PEN_TEXT_TAG_SOURCE_OR_SPEAKER_ID = 1,
|
||||
DTVCC_PEN_TEXT_TAG_ELECTRONIC_VOICE = 2,
|
||||
DTVCC_PEN_TEXT_TAG_FOREIGN_LANGUAGE = 3,
|
||||
DTVCC_PEN_TEXT_TAG_VOICEOVER = 4,
|
||||
DTVCC_PEN_TEXT_TAG_AUDIBLE_TRANSLATION = 5,
|
||||
DTVCC_PEN_TEXT_TAG_SUBTITLE_TRANSLATION = 6,
|
||||
DTVCC_PEN_TEXT_TAG_VOICE_QUALITY_DESCRIPTION = 7,
|
||||
DTVCC_PEN_TEXT_TAG_SONG_LYRICS = 8,
|
||||
DTVCC_PEN_TEXT_TAG_SOUND_EFFECT_DESCRIPTION = 9,
|
||||
DTVCC_PEN_TEXT_TAG_MUSICAL_SCORE_DESCRIPTION = 10,
|
||||
DTVCC_PEN_TEXT_TAG_EXPLETIVE = 11,
|
||||
DTVCC_PEN_TEXT_TAG_UNDEFINED_12 = 12,
|
||||
DTVCC_PEN_TEXT_TAG_UNDEFINED_13 = 13,
|
||||
DTVCC_PEN_TEXT_TAG_UNDEFINED_14 = 14,
|
||||
DTVCC_PEN_TEXT_TAG_NOT_TO_BE_DISPLAYED = 15
|
||||
DTVCC_PEN_TEXT_TAG_DIALOG = 0,
|
||||
DTVCC_PEN_TEXT_TAG_SOURCE_OR_SPEAKER_ID = 1,
|
||||
DTVCC_PEN_TEXT_TAG_ELECTRONIC_VOICE = 2,
|
||||
DTVCC_PEN_TEXT_TAG_FOREIGN_LANGUAGE = 3,
|
||||
DTVCC_PEN_TEXT_TAG_VOICEOVER = 4,
|
||||
DTVCC_PEN_TEXT_TAG_AUDIBLE_TRANSLATION = 5,
|
||||
DTVCC_PEN_TEXT_TAG_SUBTITLE_TRANSLATION = 6,
|
||||
DTVCC_PEN_TEXT_TAG_VOICE_QUALITY_DESCRIPTION = 7,
|
||||
DTVCC_PEN_TEXT_TAG_SONG_LYRICS = 8,
|
||||
DTVCC_PEN_TEXT_TAG_SOUND_EFFECT_DESCRIPTION = 9,
|
||||
DTVCC_PEN_TEXT_TAG_MUSICAL_SCORE_DESCRIPTION = 10,
|
||||
DTVCC_PEN_TEXT_TAG_EXPLETIVE = 11,
|
||||
DTVCC_PEN_TEXT_TAG_UNDEFINED_12 = 12,
|
||||
DTVCC_PEN_TEXT_TAG_UNDEFINED_13 = 13,
|
||||
DTVCC_PEN_TEXT_TAG_UNDEFINED_14 = 14,
|
||||
DTVCC_PEN_TEXT_TAG_NOT_TO_BE_DISPLAYED = 15
|
||||
};
|
||||
|
||||
enum dtvcc_pen_offset
|
||||
{
|
||||
DTVCC_PEN_OFFSET_SUBSCRIPT = 0,
|
||||
DTVCC_PEN_OFFSET_NORMAL = 1,
|
||||
DTVCC_PEN_OFFSET_SUPERSCRIPT = 2
|
||||
DTVCC_PEN_OFFSET_SUBSCRIPT = 0,
|
||||
DTVCC_PEN_OFFSET_NORMAL = 1,
|
||||
DTVCC_PEN_OFFSET_SUPERSCRIPT = 2
|
||||
};
|
||||
|
||||
enum dtvcc_pen_edge
|
||||
{
|
||||
DTVCC_PEN_EDGE_NONE = 0,
|
||||
DTVCC_PEN_EDGE_RAISED = 1,
|
||||
DTVCC_PEN_EDGE_DEPRESSED = 2,
|
||||
DTVCC_PEN_EDGE_UNIFORM = 3,
|
||||
DTVCC_PEN_EDGE_LEFT_DROP_SHADOW = 4,
|
||||
DTVCC_PEN_EDGE_RIGHT_DROP_SHADOW = 5
|
||||
DTVCC_PEN_EDGE_NONE = 0,
|
||||
DTVCC_PEN_EDGE_RAISED = 1,
|
||||
DTVCC_PEN_EDGE_DEPRESSED = 2,
|
||||
DTVCC_PEN_EDGE_UNIFORM = 3,
|
||||
DTVCC_PEN_EDGE_LEFT_DROP_SHADOW = 4,
|
||||
DTVCC_PEN_EDGE_RIGHT_DROP_SHADOW = 5
|
||||
};
|
||||
|
||||
enum dtvcc_pen_anchor_point
|
||||
{
|
||||
DTVCC_ANCHOR_POINT_TOP_LEFT = 0,
|
||||
DTVCC_ANCHOR_POINT_TOP_CENTER = 1,
|
||||
DTVCC_ANCHOR_POINT_TOP_RIGHT = 2,
|
||||
DTVCC_ANCHOR_POINT_MIDDLE_LEFT = 3,
|
||||
DTVCC_ANCHOR_POINT_MIDDLE_CENTER = 4,
|
||||
DTVCC_ANCHOR_POINT_MIDDLE_RIGHT = 5,
|
||||
DTVCC_ANCHOR_POINT_BOTTOM_LEFT = 6,
|
||||
DTVCC_ANCHOR_POINT_BOTTOM_CENTER = 7,
|
||||
DTVCC_ANCHOR_POINT_BOTTOM_RIGHT = 8
|
||||
DTVCC_ANCHOR_POINT_TOP_LEFT = 0,
|
||||
DTVCC_ANCHOR_POINT_TOP_CENTER = 1,
|
||||
DTVCC_ANCHOR_POINT_TOP_RIGHT = 2,
|
||||
DTVCC_ANCHOR_POINT_MIDDLE_LEFT = 3,
|
||||
DTVCC_ANCHOR_POINT_MIDDLE_CENTER = 4,
|
||||
DTVCC_ANCHOR_POINT_MIDDLE_RIGHT = 5,
|
||||
DTVCC_ANCHOR_POINT_BOTTOM_LEFT = 6,
|
||||
DTVCC_ANCHOR_POINT_BOTTOM_CENTER = 7,
|
||||
DTVCC_ANCHOR_POINT_BOTTOM_RIGHT = 8
|
||||
};
|
||||
|
||||
typedef struct dtvcc_pen_color
|
||||
@@ -252,12 +252,20 @@ typedef struct dtvcc_window_attribs
|
||||
*/
|
||||
typedef struct dtvcc_symbol
|
||||
{
|
||||
unsigned short sym; //symbol itself, at least 16 bit
|
||||
unsigned char init; //initialized or not. could be 0 or 1
|
||||
unsigned short sym; // symbol itself, at least 16 bit
|
||||
unsigned char init; // initialized or not. could be 0 or 1
|
||||
} dtvcc_symbol;
|
||||
|
||||
#define CCX_DTVCC_SYM_SET(x, c) {x.init = 1; x.sym = c;}
|
||||
#define CCX_DTVCC_SYM_SET_16(x, c1, c2) {x.init = 1; x.sym = (c1 << 8) | c2;}
|
||||
#define CCX_DTVCC_SYM_SET(x, c) \
|
||||
{ \
|
||||
x.init = 1; \
|
||||
x.sym = c; \
|
||||
}
|
||||
#define CCX_DTVCC_SYM_SET_16(x, c1, c2) \
|
||||
{ \
|
||||
x.init = 1; \
|
||||
x.sym = (c1 << 8) | c2; \
|
||||
}
|
||||
#define CCX_DTVCC_SYM(x) ((unsigned char)(x.sym))
|
||||
#define CCX_DTVCC_SYM_IS_EMPTY(x) (x.init == 0)
|
||||
#define CCX_DTVCC_SYM_IS_SET(x) (x.init == 1)
|
||||
@@ -344,7 +352,7 @@ typedef struct dtvcc_ctx
|
||||
{
|
||||
int is_active;
|
||||
int active_services_count;
|
||||
int services_active[CCX_DTVCC_MAX_SERVICES]; //0 - inactive, 1 - active
|
||||
int services_active[CCX_DTVCC_MAX_SERVICES]; // 0 - inactive, 1 - active
|
||||
int report_enabled;
|
||||
|
||||
ccx_decoder_dtvcc_report *report;
|
||||
@@ -357,21 +365,20 @@ typedef struct dtvcc_ctx
|
||||
|
||||
int last_sequence;
|
||||
|
||||
void *encoder; //we can't include header, so keeping it this way
|
||||
void *encoder; // we can't include header, so keeping it this way
|
||||
int no_rollup;
|
||||
struct ccx_common_timing_ctx *timing;
|
||||
} dtvcc_ctx;
|
||||
|
||||
|
||||
void dtvcc_clear_packet(dtvcc_ctx *ctx);
|
||||
void dtvcc_windows_reset(dtvcc_service_decoder *decoder);
|
||||
void dtvcc_decoder_flush(dtvcc_ctx *dtvcc, dtvcc_service_decoder *decoder);
|
||||
|
||||
void dtvcc_process_current_packet(dtvcc_ctx *dtvcc, int len);
|
||||
void dtvcc_process_service_block(dtvcc_ctx *dtvcc,
|
||||
dtvcc_service_decoder *decoder,
|
||||
unsigned char *data,
|
||||
int data_length);
|
||||
dtvcc_service_decoder *decoder,
|
||||
unsigned char *data,
|
||||
int data_length);
|
||||
|
||||
void dtvcc_tv_clear(dtvcc_service_decoder *decoder);
|
||||
int dtvcc_decoder_has_visible_windows(dtvcc_service_decoder *decoder);
|
||||
@@ -381,9 +388,9 @@ void dtvcc_window_clear(dtvcc_service_decoder *decoder, int window_id);
|
||||
void dtvcc_window_apply_style(dtvcc_window *window, dtvcc_window_attribs *style);
|
||||
|
||||
#ifdef DTVCC_PRINT_DEBUG
|
||||
int dtvcc_is_win_row_empty(dtvcc_window *window, int row_index);
|
||||
void dtvcc_get_win_write_interval(dtvcc_window *window, int row_index, int *first, int *last);
|
||||
void dtvcc_window_dump(dtvcc_service_decoder *decoder, dtvcc_window *window);
|
||||
int dtvcc_is_win_row_empty(dtvcc_window *window, int row_index);
|
||||
void dtvcc_get_win_write_interval(dtvcc_window *window, int row_index, int *first, int *last);
|
||||
void dtvcc_window_dump(dtvcc_service_decoder *decoder, dtvcc_window *window);
|
||||
#endif
|
||||
|
||||
void dtvcc_decoders_reset(dtvcc_ctx *dtvcc);
|
||||
@@ -406,7 +413,7 @@ void dtvcc_process_character(dtvcc_service_decoder *decoder, dtvcc_symbol symbol
|
||||
void dtvcc_handle_CWx_SetCurrentWindow(dtvcc_service_decoder *decoder, int window_id);
|
||||
void dtvcc_handle_CLW_ClearWindows(dtvcc_ctx *dtvcc, dtvcc_service_decoder *decoder, int windows_bitmap);
|
||||
void dtvcc_handle_DSW_DisplayWindows(dtvcc_service_decoder *decoder, int windows_bitmap, struct ccx_common_timing_ctx *timing);
|
||||
void dtvcc_handle_HDW_HideWindows(dtvcc_ctx *dtvcc,dtvcc_service_decoder *decoder,
|
||||
void dtvcc_handle_HDW_HideWindows(dtvcc_ctx *dtvcc, dtvcc_service_decoder *decoder,
|
||||
int windows_bitmap);
|
||||
void dtvcc_handle_TGW_ToggleWindows(dtvcc_ctx *dtvcc,
|
||||
dtvcc_service_decoder *decoder,
|
||||
@@ -426,13 +433,13 @@ void dtvcc_handle_C0_P16(dtvcc_service_decoder *decoder, unsigned char *data);
|
||||
int dtvcc_handle_G0(dtvcc_service_decoder *decoder, unsigned char *data, int data_length);
|
||||
int dtvcc_handle_G1(dtvcc_service_decoder *decoder, unsigned char *data, int data_length);
|
||||
int dtvcc_handle_C0(dtvcc_ctx *dtvcc,
|
||||
dtvcc_service_decoder *decoder,
|
||||
unsigned char *data,
|
||||
int data_length);
|
||||
dtvcc_service_decoder *decoder,
|
||||
unsigned char *data,
|
||||
int data_length);
|
||||
int dtvcc_handle_C1(dtvcc_ctx *dtvcc,
|
||||
dtvcc_service_decoder *decoder,
|
||||
unsigned char *data,
|
||||
int data_length);
|
||||
dtvcc_service_decoder *decoder,
|
||||
unsigned char *data,
|
||||
int data_length);
|
||||
int dtvcc_handle_C2(dtvcc_service_decoder *decoder, unsigned char *data, int data_length);
|
||||
int dtvcc_handle_C3(dtvcc_service_decoder *decoder, unsigned char *data, int data_length);
|
||||
int dtvcc_handle_extended_char(dtvcc_service_decoder *decoder, unsigned char *data, int data_length);
|
||||
|
||||
@@ -57,6 +57,7 @@ void dtvcc_change_pen_colors(dtvcc_tv_screen *tv, dtvcc_pen_color pen_color, int
|
||||
return;
|
||||
|
||||
char *buf = (char *)encoder->buffer;
|
||||
size_t remaining = INITIAL_ENC_BUFFER_CAPACITY - *buf_len;
|
||||
|
||||
dtvcc_pen_color new_pen_color;
|
||||
if (column_index >= CCX_DTVCC_SCREENGRID_COLUMNS)
|
||||
@@ -66,7 +67,11 @@ void dtvcc_change_pen_colors(dtvcc_tv_screen *tv, dtvcc_pen_color pen_color, int
|
||||
if (pen_color.fg_color != new_pen_color.fg_color)
|
||||
{
|
||||
if (pen_color.fg_color != 0x3f && !open)
|
||||
(*buf_len) += sprintf(buf + (*buf_len), "</font>"); // should close older non-white color
|
||||
{
|
||||
int written = snprintf(buf + (*buf_len), remaining, "</font>"); // should close older non-white color
|
||||
if (written > 0 && (size_t)written < remaining)
|
||||
(*buf_len) += written;
|
||||
}
|
||||
|
||||
if (new_pen_color.fg_color != 0x3f && open)
|
||||
{
|
||||
@@ -75,7 +80,10 @@ void dtvcc_change_pen_colors(dtvcc_tv_screen *tv, dtvcc_pen_color pen_color, int
|
||||
red = (255 / 3) * red;
|
||||
green = (255 / 3) * green;
|
||||
blue = (255 / 3) * blue;
|
||||
(*buf_len) += sprintf(buf + (*buf_len), "<font color=\"#%02x%02x%02x\">", red, green, blue);
|
||||
remaining = INITIAL_ENC_BUFFER_CAPACITY - *buf_len;
|
||||
int written = snprintf(buf + (*buf_len), remaining, "<font color=\"#%02x%02x%02x\">", red, green, blue);
|
||||
if (written > 0 && (size_t)written < remaining)
|
||||
(*buf_len) += written;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -86,6 +94,8 @@ void dtvcc_change_pen_attribs(dtvcc_tv_screen *tv, dtvcc_pen_attribs pen_attribs
|
||||
return;
|
||||
|
||||
char *buf = (char *)encoder->buffer;
|
||||
size_t remaining;
|
||||
int written;
|
||||
|
||||
dtvcc_pen_attribs new_pen_attribs;
|
||||
if (column_index >= CCX_DTVCC_SCREENGRID_COLUMNS)
|
||||
@@ -94,33 +104,47 @@ void dtvcc_change_pen_attribs(dtvcc_tv_screen *tv, dtvcc_pen_attribs pen_attribs
|
||||
new_pen_attribs = tv->pen_attribs[row_index][column_index];
|
||||
if (pen_attribs.italic != new_pen_attribs.italic)
|
||||
{
|
||||
remaining = INITIAL_ENC_BUFFER_CAPACITY - *buf_len;
|
||||
if (pen_attribs.italic && !open)
|
||||
(*buf_len) += sprintf(buf + (*buf_len), "</i>");
|
||||
{
|
||||
written = snprintf(buf + (*buf_len), remaining, "</i>");
|
||||
if (written > 0 && (size_t)written < remaining)
|
||||
(*buf_len) += written;
|
||||
}
|
||||
if (!pen_attribs.italic && open)
|
||||
(*buf_len) += sprintf(buf + (*buf_len), "<i>");
|
||||
{
|
||||
written = snprintf(buf + (*buf_len), remaining, "<i>");
|
||||
if (written > 0 && (size_t)written < remaining)
|
||||
(*buf_len) += written;
|
||||
}
|
||||
}
|
||||
if (pen_attribs.underline != new_pen_attribs.underline)
|
||||
{
|
||||
remaining = INITIAL_ENC_BUFFER_CAPACITY - *buf_len;
|
||||
if (pen_attribs.underline && !open)
|
||||
(*buf_len) += sprintf(buf + (*buf_len), "</u>");
|
||||
{
|
||||
written = snprintf(buf + (*buf_len), remaining, "</u>");
|
||||
if (written > 0 && (size_t)written < remaining)
|
||||
(*buf_len) += written;
|
||||
}
|
||||
if (!pen_attribs.underline && open)
|
||||
(*buf_len) += sprintf(buf + (*buf_len), "<u>");
|
||||
{
|
||||
written = snprintf(buf + (*buf_len), remaining, "<u>");
|
||||
if (written > 0 && (size_t)written < remaining)
|
||||
(*buf_len) += written;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
size_t write_utf16_char(unsigned short utf16_char, char *out)
|
||||
{
|
||||
if ((utf16_char >> 8) != 0)
|
||||
{
|
||||
out[0] = (unsigned char)(utf16_char >> 8);
|
||||
out[1] = (unsigned char)(utf16_char & 0xff);
|
||||
return 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
out[0] = (unsigned char)(utf16_char);
|
||||
return 1;
|
||||
}
|
||||
// Always write 2 bytes for consistent UTF-16BE encoding.
|
||||
// Previously, this function wrote 1 byte for ASCII characters and 2 bytes
|
||||
// for non-ASCII, creating an invalid mix that iconv couldn't handle properly.
|
||||
// This caused garbled output with Japanese/Chinese characters (issue #1451).
|
||||
out[0] = (unsigned char)(utf16_char >> 8);
|
||||
out[1] = (unsigned char)(utf16_char & 0xff);
|
||||
return 2;
|
||||
}
|
||||
|
||||
void dtvcc_write_row(dtvcc_writer_ctx *writer, dtvcc_service_decoder *decoder, int row_index, struct encoder_ctx *encoder, int use_colors)
|
||||
@@ -207,16 +231,31 @@ void dtvcc_write_srt(dtvcc_writer_ctx *writer, dtvcc_service_decoder *decoder, s
|
||||
|
||||
char *buf = (char *)encoder->buffer;
|
||||
memset(buf, 0, INITIAL_ENC_BUFFER_CAPACITY);
|
||||
size_t buf_len = 0;
|
||||
size_t remaining = INITIAL_ENC_BUFFER_CAPACITY;
|
||||
int written;
|
||||
|
||||
sprintf(buf, "%u%s", encoder->cea_708_counter, encoder->encoded_crlf);
|
||||
written = snprintf(buf, remaining, "%u%s", encoder->cea_708_counter, encoder->encoded_crlf);
|
||||
if (written > 0 && (size_t)written < remaining)
|
||||
buf_len += written;
|
||||
remaining = INITIAL_ENC_BUFFER_CAPACITY - buf_len;
|
||||
print_mstime_buff(tv->time_ms_show + encoder->subs_delay,
|
||||
"%02u:%02u:%02u,%03u", buf + strlen(buf));
|
||||
sprintf(buf + strlen(buf), " --> ");
|
||||
"%02u:%02u:%02u,%03u", buf + buf_len);
|
||||
buf_len = strlen(buf);
|
||||
remaining = INITIAL_ENC_BUFFER_CAPACITY - buf_len;
|
||||
written = snprintf(buf + buf_len, remaining, " --> ");
|
||||
if (written > 0 && (size_t)written < remaining)
|
||||
buf_len += written;
|
||||
remaining = INITIAL_ENC_BUFFER_CAPACITY - buf_len;
|
||||
print_mstime_buff(tv->time_ms_hide + encoder->subs_delay,
|
||||
"%02u:%02u:%02u,%03u", buf + strlen(buf));
|
||||
sprintf(buf + strlen(buf), "%s", (char *)encoder->encoded_crlf);
|
||||
"%02u:%02u:%02u,%03u", buf + buf_len);
|
||||
buf_len = strlen(buf);
|
||||
remaining = INITIAL_ENC_BUFFER_CAPACITY - buf_len;
|
||||
written = snprintf(buf + buf_len, remaining, "%s", (char *)encoder->encoded_crlf);
|
||||
if (written > 0 && (size_t)written < remaining)
|
||||
buf_len += written;
|
||||
|
||||
write_wrapped(encoder->dtvcc_writers[tv->service_number - 1].fd, buf, strlen(buf));
|
||||
write_wrapped(encoder->dtvcc_writers[tv->service_number - 1].fd, buf, buf_len);
|
||||
|
||||
for (int i = 0; i < CCX_DTVCC_SCREENGRID_ROWS; i++)
|
||||
{
|
||||
@@ -263,28 +302,47 @@ void dtvcc_write_transcript(dtvcc_writer_ctx *writer, dtvcc_service_decoder *dec
|
||||
return;
|
||||
|
||||
char *buf = (char *)encoder->buffer;
|
||||
size_t buf_len;
|
||||
size_t remaining;
|
||||
int written;
|
||||
|
||||
for (int i = 0; i < CCX_DTVCC_SCREENGRID_ROWS; i++)
|
||||
{
|
||||
if (!dtvcc_is_row_empty(tv, i))
|
||||
{
|
||||
buf[0] = 0;
|
||||
buf_len = 0;
|
||||
|
||||
if (encoder->transcript_settings->showStartTime)
|
||||
{
|
||||
print_mstime_buff(tv->time_ms_show + encoder->subs_delay,
|
||||
"%02u:%02u:%02u,%03u|", buf + strlen(buf));
|
||||
"%02u:%02u:%02u,%03u|", buf + buf_len);
|
||||
buf_len = strlen(buf);
|
||||
}
|
||||
|
||||
if (encoder->transcript_settings->showEndTime)
|
||||
{
|
||||
print_mstime_buff(tv->time_ms_hide + encoder->subs_delay,
|
||||
"%02u:%02u:%02u,%03u|", buf + strlen(buf));
|
||||
"%02u:%02u:%02u,%03u|", buf + buf_len);
|
||||
buf_len = strlen(buf);
|
||||
}
|
||||
|
||||
if (encoder->transcript_settings->showCC)
|
||||
sprintf(buf + strlen(buf), "CC1|"); // always CC1 because CEA-708 is field-independent
|
||||
{
|
||||
remaining = INITIAL_ENC_BUFFER_CAPACITY - buf_len;
|
||||
written = snprintf(buf + buf_len, remaining, "CC1|"); // always CC1 because CEA-708 is field-independent
|
||||
if (written > 0 && (size_t)written < remaining)
|
||||
buf_len += written;
|
||||
}
|
||||
|
||||
if (encoder->transcript_settings->showMode)
|
||||
sprintf(buf + strlen(buf), "POP|"); // TODO caption mode(pop, rollup, etc.)
|
||||
{
|
||||
remaining = INITIAL_ENC_BUFFER_CAPACITY - buf_len;
|
||||
written = snprintf(buf + buf_len, remaining, "POP|"); // TODO caption mode(pop, rollup, etc.)
|
||||
if (written > 0 && (size_t)written < remaining)
|
||||
buf_len += written;
|
||||
}
|
||||
|
||||
const size_t buf_len = strlen(buf);
|
||||
if (buf_len != 0)
|
||||
write_wrapped(encoder->dtvcc_writers[tv->service_number - 1].fd, buf, buf_len);
|
||||
|
||||
@@ -300,22 +358,33 @@ void dtvcc_write_sami_header(dtvcc_tv_screen *tv, struct encoder_ctx *encoder)
|
||||
char *buf = (char *)encoder->buffer;
|
||||
memset(buf, 0, INITIAL_ENC_BUFFER_CAPACITY);
|
||||
size_t buf_len = 0;
|
||||
size_t remaining = INITIAL_ENC_BUFFER_CAPACITY;
|
||||
int written;
|
||||
|
||||
buf_len += sprintf(buf + buf_len, "<sami>%s", encoder->encoded_crlf);
|
||||
buf_len += sprintf(buf + buf_len, "<head>%s", encoder->encoded_crlf);
|
||||
buf_len += sprintf(buf + buf_len, "<style type=\"text/css\">%s", encoder->encoded_crlf);
|
||||
buf_len += sprintf(buf + buf_len, "<!--%s", encoder->encoded_crlf);
|
||||
buf_len += sprintf(buf + buf_len,
|
||||
"p {margin-left: 16pt; margin-right: 16pt; margin-bottom: 16pt; margin-top: 16pt;%s",
|
||||
encoder->encoded_crlf);
|
||||
buf_len += sprintf(buf + buf_len,
|
||||
"text-align: center; font-size: 18pt; font-family: arial; font-weight: bold; color: #f0f0f0;}%s",
|
||||
encoder->encoded_crlf);
|
||||
buf_len += sprintf(buf + buf_len, ".unknowncc {Name:Unknown; lang:en-US; SAMIType:CC;}%s", encoder->encoded_crlf);
|
||||
buf_len += sprintf(buf + buf_len, "-->%s", encoder->encoded_crlf);
|
||||
buf_len += sprintf(buf + buf_len, "</style>%s", encoder->encoded_crlf);
|
||||
buf_len += sprintf(buf + buf_len, "</head>%s%s", encoder->encoded_crlf, encoder->encoded_crlf);
|
||||
buf_len += sprintf(buf + buf_len, "<body>%s", encoder->encoded_crlf);
|
||||
#define SAMI_SNPRINTF(fmt, ...) \
|
||||
do \
|
||||
{ \
|
||||
remaining = INITIAL_ENC_BUFFER_CAPACITY - buf_len; \
|
||||
written = snprintf(buf + buf_len, remaining, fmt, ##__VA_ARGS__); \
|
||||
if (written > 0 && (size_t)written < remaining) \
|
||||
buf_len += written; \
|
||||
} while (0)
|
||||
|
||||
SAMI_SNPRINTF("<sami>%s", encoder->encoded_crlf);
|
||||
SAMI_SNPRINTF("<head>%s", encoder->encoded_crlf);
|
||||
SAMI_SNPRINTF("<style type=\"text/css\">%s", encoder->encoded_crlf);
|
||||
SAMI_SNPRINTF("<!--%s", encoder->encoded_crlf);
|
||||
SAMI_SNPRINTF("p {margin-left: 16pt; margin-right: 16pt; margin-bottom: 16pt; margin-top: 16pt;%s",
|
||||
encoder->encoded_crlf);
|
||||
SAMI_SNPRINTF("text-align: center; font-size: 18pt; font-family: arial; font-weight: bold; color: #f0f0f0;}%s",
|
||||
encoder->encoded_crlf);
|
||||
SAMI_SNPRINTF(".unknowncc {Name:Unknown; lang:en-US; SAMIType:CC;}%s", encoder->encoded_crlf);
|
||||
SAMI_SNPRINTF("-->%s", encoder->encoded_crlf);
|
||||
SAMI_SNPRINTF("</style>%s", encoder->encoded_crlf);
|
||||
SAMI_SNPRINTF("</head>%s%s", encoder->encoded_crlf, encoder->encoded_crlf);
|
||||
SAMI_SNPRINTF("<body>%s", encoder->encoded_crlf);
|
||||
|
||||
#undef SAMI_SNPRINTF
|
||||
|
||||
write_wrapped(encoder->dtvcc_writers[tv->service_number - 1].fd, buf, buf_len);
|
||||
}
|
||||
@@ -323,8 +392,9 @@ void dtvcc_write_sami_header(dtvcc_tv_screen *tv, struct encoder_ctx *encoder)
|
||||
void dtvcc_write_sami_footer(dtvcc_tv_screen *tv, struct encoder_ctx *encoder)
|
||||
{
|
||||
char *buf = (char *)encoder->buffer;
|
||||
sprintf(buf, "</body></sami>");
|
||||
write_wrapped(encoder->dtvcc_writers[tv->service_number - 1].fd, buf, strlen(buf));
|
||||
int written = snprintf(buf, INITIAL_ENC_BUFFER_CAPACITY, "</body></sami>");
|
||||
if (written > 0 && (size_t)written < INITIAL_ENC_BUFFER_CAPACITY)
|
||||
write_wrapped(encoder->dtvcc_writers[tv->service_number - 1].fd, buf, written);
|
||||
write_wrapped(encoder->dtvcc_writers[tv->service_number - 1].fd,
|
||||
encoder->encoded_crlf, encoder->encoded_crlf_length);
|
||||
}
|
||||
@@ -342,12 +412,14 @@ void dtvcc_write_sami(dtvcc_writer_ctx *writer, dtvcc_service_decoder *decoder,
|
||||
dtvcc_write_sami_header(tv, encoder);
|
||||
|
||||
char *buf = (char *)encoder->buffer;
|
||||
int written;
|
||||
|
||||
buf[0] = 0;
|
||||
sprintf(buf, "<sync start=%llu><p class=\"unknowncc\">%s",
|
||||
(unsigned long long)tv->time_ms_show + encoder->subs_delay,
|
||||
encoder->encoded_crlf);
|
||||
write_wrapped(encoder->dtvcc_writers[tv->service_number - 1].fd, buf, strlen(buf));
|
||||
written = snprintf(buf, INITIAL_ENC_BUFFER_CAPACITY, "<sync start=%llu><p class=\"unknowncc\">%s",
|
||||
(unsigned long long)tv->time_ms_show + encoder->subs_delay,
|
||||
encoder->encoded_crlf);
|
||||
if (written > 0 && (size_t)written < INITIAL_ENC_BUFFER_CAPACITY)
|
||||
write_wrapped(encoder->dtvcc_writers[tv->service_number - 1].fd, buf, written);
|
||||
|
||||
for (int i = 0; i < CCX_DTVCC_SCREENGRID_ROWS; i++)
|
||||
{
|
||||
@@ -361,10 +433,11 @@ void dtvcc_write_sami(dtvcc_writer_ctx *writer, dtvcc_service_decoder *decoder,
|
||||
}
|
||||
}
|
||||
|
||||
sprintf(buf, "<sync start=%llu><p class=\"unknowncc\"> </p></sync>%s%s",
|
||||
(unsigned long long)tv->time_ms_hide + encoder->subs_delay,
|
||||
encoder->encoded_crlf, encoder->encoded_crlf);
|
||||
write_wrapped(encoder->dtvcc_writers[tv->service_number - 1].fd, buf, strlen(buf));
|
||||
written = snprintf(buf, INITIAL_ENC_BUFFER_CAPACITY, "<sync start=%llu><p class=\"unknowncc\"> </p></sync>%s%s",
|
||||
(unsigned long long)tv->time_ms_hide + encoder->subs_delay,
|
||||
encoder->encoded_crlf, encoder->encoded_crlf);
|
||||
if (written > 0 && (size_t)written < INITIAL_ENC_BUFFER_CAPACITY)
|
||||
write_wrapped(encoder->dtvcc_writers[tv->service_number - 1].fd, buf, written);
|
||||
}
|
||||
|
||||
unsigned char adjust_odd_parity(const unsigned char value)
|
||||
@@ -388,11 +461,12 @@ unsigned char adjust_odd_parity(const unsigned char value)
|
||||
void dtvcc_write_scc_header(dtvcc_tv_screen *tv, struct encoder_ctx *encoder)
|
||||
{
|
||||
char *buf = (char *)encoder->buffer;
|
||||
// 18 characters long + 2 new lines
|
||||
memset(buf, 0, 20);
|
||||
sprintf(buf, "Scenarist_SCC V1.0\n\n");
|
||||
// 18 characters long + 2 new lines = 20 characters total
|
||||
memset(buf, 0, INITIAL_ENC_BUFFER_CAPACITY);
|
||||
int written = snprintf(buf, INITIAL_ENC_BUFFER_CAPACITY, "Scenarist_SCC V1.0\n\n");
|
||||
|
||||
write_wrapped(encoder->dtvcc_writers[tv->service_number - 1].fd, buf, strlen(buf));
|
||||
if (written > 0 && (size_t)written < INITIAL_ENC_BUFFER_CAPACITY)
|
||||
write_wrapped(encoder->dtvcc_writers[tv->service_number - 1].fd, buf, written);
|
||||
}
|
||||
|
||||
int count_captions_lines_scc(dtvcc_tv_screen *tv)
|
||||
@@ -415,22 +489,31 @@ int count_captions_lines_scc(dtvcc_tv_screen *tv)
|
||||
* 2 line length subtitles can be placed in 14th and 15th row
|
||||
* 3 line length subtitles can be placed in 13th, 14th and 15th row
|
||||
*/
|
||||
void add_needed_scc_labels(char *buf, int total_subtitle_count, int current_subtitle_count)
|
||||
void add_needed_scc_labels(char *buf, size_t buf_size, size_t *buf_len, int total_subtitle_count, int current_subtitle_count)
|
||||
{
|
||||
size_t remaining = buf_size - *buf_len;
|
||||
int written;
|
||||
const char *label;
|
||||
|
||||
switch (total_subtitle_count)
|
||||
{
|
||||
case 1:
|
||||
// row 15, column 00
|
||||
sprintf(buf + strlen(buf), " 94e0 94e0");
|
||||
label = " 94e0 94e0";
|
||||
break;
|
||||
case 2:
|
||||
// 9440: row 14, column 00 | 94e0: row 15, column 00
|
||||
sprintf(buf + strlen(buf), current_subtitle_count == 1 ? " 9440 9440" : " 94e0 94e0");
|
||||
label = (current_subtitle_count == 1) ? " 9440 9440" : " 94e0 94e0";
|
||||
break;
|
||||
default:
|
||||
// 13e0: row 13, column 04 | 9440: row 14, column 00 | 94e0: row 15, column 00
|
||||
sprintf(buf + strlen(buf), current_subtitle_count == 1 ? " 13e0 13e0" : (current_subtitle_count == 2 ? " 9440 9440" : " 94e0 94e0"));
|
||||
label = (current_subtitle_count == 1) ? " 13e0 13e0" : ((current_subtitle_count == 2) ? " 9440 9440" : " 94e0 94e0");
|
||||
break;
|
||||
}
|
||||
|
||||
written = snprintf(buf + *buf_len, remaining, "%s", label);
|
||||
if (written > 0 && (size_t)written < remaining)
|
||||
*buf_len += written;
|
||||
}
|
||||
|
||||
void dtvcc_write_scc(dtvcc_writer_ctx *writer, dtvcc_service_decoder *decoder, struct encoder_ctx *encoder)
|
||||
@@ -447,38 +530,55 @@ void dtvcc_write_scc(dtvcc_writer_ctx *writer, dtvcc_service_decoder *decoder, s
|
||||
dtvcc_write_scc_header(tv, encoder);
|
||||
|
||||
char *buf = (char *)encoder->buffer;
|
||||
size_t buf_len;
|
||||
size_t remaining;
|
||||
int written;
|
||||
|
||||
struct ccx_boundary_time time_show = get_time(tv->time_ms_show + encoder->subs_delay);
|
||||
// when hiding subtract a frame (1 frame = 34 ms)
|
||||
struct ccx_boundary_time time_end = get_time(tv->time_ms_hide + encoder->subs_delay - 34);
|
||||
|
||||
#define SCC_SNPRINTF(fmt, ...) \
|
||||
do \
|
||||
{ \
|
||||
remaining = INITIAL_ENC_BUFFER_CAPACITY - buf_len; \
|
||||
written = snprintf(buf + buf_len, remaining, fmt, ##__VA_ARGS__); \
|
||||
if (written > 0 && (size_t)written < remaining) \
|
||||
buf_len += written; \
|
||||
} while (0)
|
||||
|
||||
if (tv->old_cc_time_end > time_show.time_in_ms)
|
||||
{
|
||||
// Correct the frame delay
|
||||
time_show.time_in_ms -= 1000 / 29.97;
|
||||
print_scc_time(time_show, buf);
|
||||
sprintf(buf + strlen(buf), "\t942c 942c");
|
||||
buf_len = strlen(buf);
|
||||
SCC_SNPRINTF("\t942c 942c");
|
||||
time_show.time_in_ms += 1000 / 29.97;
|
||||
// Clear the buffer and start pop on caption
|
||||
sprintf(buf + strlen(buf), "94ae 94ae 9420 9420");
|
||||
SCC_SNPRINTF("94ae 94ae 9420 9420");
|
||||
}
|
||||
else if (tv->old_cc_time_end < time_show.time_in_ms)
|
||||
{
|
||||
// Clear the screen for new caption
|
||||
struct ccx_boundary_time time_to_display = get_time(tv->old_cc_time_end);
|
||||
print_scc_time(time_to_display, buf);
|
||||
sprintf(buf + strlen(buf), "\t942c 942c \n\n");
|
||||
buf_len = strlen(buf);
|
||||
SCC_SNPRINTF("\t942c 942c \n\n");
|
||||
// Correct the frame delay
|
||||
time_show.time_in_ms -= 1000 / 29.97;
|
||||
// Clear the buffer and start pop on caption in new time
|
||||
print_scc_time(time_show, buf);
|
||||
sprintf(buf + strlen(buf), "\t94ae 94ae 9420 9420");
|
||||
print_scc_time(time_show, buf + buf_len);
|
||||
buf_len = strlen(buf);
|
||||
SCC_SNPRINTF("\t94ae 94ae 9420 9420");
|
||||
time_show.time_in_ms += 1000 / 29.97;
|
||||
}
|
||||
else
|
||||
{
|
||||
time_show.time_in_ms -= 1000 / 29.97;
|
||||
print_scc_time(time_show, buf);
|
||||
sprintf(buf + strlen(buf), "\t942c 942c 94ae 94ae 9420 9420");
|
||||
buf_len = strlen(buf);
|
||||
SCC_SNPRINTF("\t942c 942c 94ae 94ae 9420 9420");
|
||||
time_show.time_in_ms += 1000 / 29.97;
|
||||
}
|
||||
|
||||
@@ -490,27 +590,29 @@ void dtvcc_write_scc(dtvcc_writer_ctx *writer, dtvcc_service_decoder *decoder, s
|
||||
if (!dtvcc_is_row_empty(tv, i))
|
||||
{
|
||||
current_subtitle_count++;
|
||||
add_needed_scc_labels(buf, total_subtitle_count, current_subtitle_count);
|
||||
add_needed_scc_labels(buf, INITIAL_ENC_BUFFER_CAPACITY, &buf_len, total_subtitle_count, current_subtitle_count);
|
||||
|
||||
int first, last, bytes_written = 0;
|
||||
dtvcc_get_write_interval(tv, i, &first, &last);
|
||||
for (int j = first; j <= last; j++)
|
||||
{
|
||||
if (bytes_written % 2 == 0)
|
||||
sprintf(buf + strlen(buf), " ");
|
||||
sprintf(buf + strlen(buf), "%x", adjust_odd_parity(tv->chars[i][j].sym));
|
||||
SCC_SNPRINTF(" ");
|
||||
SCC_SNPRINTF("%x", adjust_odd_parity(tv->chars[i][j].sym));
|
||||
bytes_written += 1;
|
||||
}
|
||||
// if byte pair are not even then make it even by adding 0x80 as padding
|
||||
if (bytes_written % 2 == 1)
|
||||
sprintf(buf + strlen(buf), "80 ");
|
||||
SCC_SNPRINTF("80 ");
|
||||
else
|
||||
sprintf(buf + strlen(buf), " ");
|
||||
SCC_SNPRINTF(" ");
|
||||
}
|
||||
}
|
||||
|
||||
// Display caption (942f 942f)
|
||||
sprintf(buf + strlen(buf), "942f 942f \n\n");
|
||||
SCC_SNPRINTF("942f 942f \n\n");
|
||||
|
||||
#undef SCC_SNPRINTF
|
||||
write_wrapped(encoder->dtvcc_writers[tv->service_number - 1].fd, buf, strlen(buf));
|
||||
|
||||
tv->old_cc_time_end = time_end.time_in_ms;
|
||||
@@ -579,7 +681,7 @@ void dtvcc_writer_init(dtvcc_writer_ctx *writer,
|
||||
|
||||
const char *ext = get_file_extension(write_format);
|
||||
char suffix[32];
|
||||
sprintf(suffix, CCX_DTVCC_FILENAME_TEMPLATE, program_number, service_number);
|
||||
snprintf(suffix, sizeof(suffix), CCX_DTVCC_FILENAME_TEMPLATE, program_number, service_number);
|
||||
|
||||
writer->filename = create_outfilename(base_filename, suffix, ext);
|
||||
if (!writer->filename)
|
||||
|
||||
@@ -31,7 +31,7 @@ void dtvcc_write_sami_header(dtvcc_tv_screen *tv, struct encoder_ctx *encoder);
|
||||
void dtvcc_write_sami_footer(dtvcc_tv_screen *tv, struct encoder_ctx *encoder);
|
||||
void dtvcc_write_sami(dtvcc_writer_ctx *writer, dtvcc_service_decoder *decoder, struct encoder_ctx *encoder);
|
||||
void dtvcc_write_scc_header(dtvcc_tv_screen *tv, struct encoder_ctx *encoder);
|
||||
void add_needed_scc_labels(char *buf, int total_subtitle_count, int current_subtitle_count);
|
||||
void add_needed_scc_labels(char *buf, size_t buf_size, size_t *buf_len, int total_subtitle_count, int current_subtitle_count);
|
||||
void dtvcc_write_scc(dtvcc_writer_ctx *writer, dtvcc_service_decoder *decoder, struct encoder_ctx *encoder);
|
||||
void dtvcc_write(dtvcc_writer_ctx *writer, dtvcc_service_decoder *decoder, struct encoder_ctx *encoder);
|
||||
|
||||
|
||||
@@ -21,23 +21,22 @@ extern void ccxr_flush_decoder(struct dtvcc_ctx *dtvcc, struct dtvcc_service_dec
|
||||
uint64_t utc_refvalue = UINT64_MAX; /* _UI64_MAX/UINT64_MAX means don't use UNIX, 0 = use current system time as reference, +1 use a specific reference */
|
||||
extern int in_xds_mode;
|
||||
|
||||
LLONG ccxr_get_visible_start(struct ccx_common_timing_ctx *ctx, int current_field);
|
||||
LLONG ccxr_get_visible_end(struct ccx_common_timing_ctx *ctx, int current_field);
|
||||
|
||||
/* This function returns a FTS that is guaranteed to be at least 1 ms later than the end of the previous screen. It shouldn't be needed
|
||||
obviously but it guarantees there's no timing overlap */
|
||||
LLONG get_visible_start(struct ccx_common_timing_ctx *ctx, int current_field)
|
||||
{
|
||||
LLONG fts = get_fts(ctx, current_field);
|
||||
if (fts <= ctx->minimum_fts)
|
||||
fts = ctx->minimum_fts + 1;
|
||||
LLONG fts = ccxr_get_visible_start(ctx, current_field);
|
||||
ccx_common_logging.debug_ftn(CCX_DMT_DECODER_608, "Visible Start time=%s\n", print_mstime_static(fts));
|
||||
return fts;
|
||||
}
|
||||
|
||||
/* This function returns the current FTS and saves it so it can be used by ctxget_visible_start */
|
||||
/* This function returns the current FTS and saves it so it can be used by get_visible_start */
|
||||
LLONG get_visible_end(struct ccx_common_timing_ctx *ctx, int current_field)
|
||||
{
|
||||
LLONG fts = get_fts(ctx, current_field);
|
||||
if (fts > ctx->minimum_fts)
|
||||
ctx->minimum_fts = fts;
|
||||
LLONG fts = ccxr_get_visible_end(ctx, current_field);
|
||||
ccx_common_logging.debug_ftn(CCX_DMT_DECODER_608, "Visible End time=%s\n", print_mstime_static(fts));
|
||||
return fts;
|
||||
}
|
||||
@@ -148,7 +147,11 @@ int do_cb(struct lib_cc_decode *ctx, unsigned char *cc_block, struct cc_subtitle
|
||||
else
|
||||
writercwtdata(ctx, cc_block, sub);
|
||||
}
|
||||
cb_field1++;
|
||||
// For container formats (H.264, MPEG-2 PES), don't increment cb_field
|
||||
// because the frame PTS already represents the correct timestamp.
|
||||
// The cb_field offset is only meaningful for raw/elementary streams.
|
||||
if (ctx->in_bufferdatatype != CCX_H264 && ctx->in_bufferdatatype != CCX_PES)
|
||||
cb_field1++;
|
||||
break;
|
||||
case 1:
|
||||
dbg_print(CCX_DMT_CBRAW, " .. %s ..\n", debug_608_to_ASC(cc_block, 1));
|
||||
@@ -172,7 +175,9 @@ int do_cb(struct lib_cc_decode *ctx, unsigned char *cc_block, struct cc_subtitle
|
||||
else
|
||||
writercwtdata(ctx, cc_block, sub);
|
||||
}
|
||||
cb_field2++;
|
||||
// For container formats, don't increment cb_field (see comment above)
|
||||
if (ctx->in_bufferdatatype != CCX_H264 && ctx->in_bufferdatatype != CCX_PES)
|
||||
cb_field2++;
|
||||
break;
|
||||
case 2: // EIA-708
|
||||
// DTVCC packet data
|
||||
@@ -197,7 +202,9 @@ int do_cb(struct lib_cc_decode *ctx, unsigned char *cc_block, struct cc_subtitle
|
||||
if (ctx->write_format == CCX_OF_RCWT)
|
||||
writercwtdata(ctx, cc_block, sub);
|
||||
}
|
||||
cb_708++;
|
||||
// For container formats, don't increment cb_708 (see comment above)
|
||||
if (ctx->in_bufferdatatype != CCX_H264 && ctx->in_bufferdatatype != CCX_PES)
|
||||
cb_708++;
|
||||
// Check for bytes read
|
||||
// printf ("Warning: Losing EIA-708 data!\n");
|
||||
break;
|
||||
@@ -217,13 +224,43 @@ int do_cb(struct lib_cc_decode *ctx, unsigned char *cc_block, struct cc_subtitle
|
||||
void dinit_cc_decode(struct lib_cc_decode **ctx)
|
||||
{
|
||||
struct lib_cc_decode *lctx = *ctx;
|
||||
#ifndef DISABLE_RUST
|
||||
ccxr_dtvcc_free(lctx->dtvcc_rust);
|
||||
lctx->dtvcc_rust = NULL;
|
||||
#else
|
||||
dtvcc_free(&lctx->dtvcc);
|
||||
#endif
|
||||
dinit_avc(&lctx->avc_ctx);
|
||||
ccx_decoder_608_dinit_library(&lctx->context_cc608_field_1);
|
||||
ccx_decoder_608_dinit_library(&lctx->context_cc608_field_2);
|
||||
dinit_timing_ctx(&lctx->timing);
|
||||
free_decoder_context(lctx->prev);
|
||||
free_subtitle(lctx->dec_sub.prev);
|
||||
/* Free the embedded dec_sub's data field (allocated by write_cc_buffer) */
|
||||
if (lctx->dec_sub.datatype == CC_DATATYPE_DVB)
|
||||
{
|
||||
struct cc_bitmap *bitmap = (struct cc_bitmap *)lctx->dec_sub.data;
|
||||
if (bitmap)
|
||||
{
|
||||
freep(&bitmap->data0);
|
||||
freep(&bitmap->data1);
|
||||
}
|
||||
}
|
||||
/* Free any leftover XDS strings that weren't processed by the encoder */
|
||||
if (lctx->dec_sub.type == CC_608 && lctx->dec_sub.data)
|
||||
{
|
||||
struct eia608_screen *data = (struct eia608_screen *)lctx->dec_sub.data;
|
||||
for (int i = 0; i < lctx->dec_sub.nb_data; i++, data++)
|
||||
{
|
||||
if (data->format == SFORMAT_XDS && data->xds_str)
|
||||
{
|
||||
freep(&data->xds_str);
|
||||
}
|
||||
}
|
||||
}
|
||||
freep(&lctx->dec_sub.data);
|
||||
/* Note: xds_ctx is freed in general_loop.c, mp4.c etc. during normal processing.
|
||||
Don't free it here as it may cause double-free if already freed elsewhere. */
|
||||
freep(ctx);
|
||||
}
|
||||
|
||||
@@ -233,11 +270,27 @@ struct lib_cc_decode *init_cc_decode(struct ccx_decoders_common_settings_t *sett
|
||||
|
||||
ctx = malloc(sizeof(struct lib_cc_decode));
|
||||
if (!ctx)
|
||||
return NULL;
|
||||
fatal(EXIT_NOT_ENOUGH_MEMORY, "In init_cc_decode: Out of memory allocating ctx.");
|
||||
|
||||
// Initialize all pointers to NULL
|
||||
ctx->avc_ctx = NULL;
|
||||
ctx->timing = NULL;
|
||||
ctx->dtvcc = NULL;
|
||||
ctx->context_cc608_field_1 = NULL;
|
||||
ctx->context_cc608_field_2 = NULL;
|
||||
ctx->xds_ctx = NULL;
|
||||
ctx->vbi_decoder = NULL;
|
||||
ctx->prev = NULL;
|
||||
memset(&ctx->dec_sub, 0, sizeof(ctx->dec_sub));
|
||||
|
||||
ctx->avc_ctx = init_avc();
|
||||
if (!ctx->avc_ctx)
|
||||
fatal(EXIT_NOT_ENOUGH_MEMORY, "In init_cc_decode: Out of memory initializing avc_ctx.");
|
||||
|
||||
ctx->codec = setting->codec;
|
||||
ctx->timing = init_timing_ctx(&ccx_common_timing_settings);
|
||||
if (!ctx->timing)
|
||||
fatal(EXIT_NOT_ENOUGH_MEMORY, "In init_cc_decode: Out of memory initializing timing.");
|
||||
|
||||
setting->settings_dtvcc->timing = ctx->timing;
|
||||
|
||||
@@ -246,8 +299,16 @@ struct lib_cc_decode *init_cc_decode(struct ccx_decoders_common_settings_t *sett
|
||||
ctx->no_rollup = setting->no_rollup;
|
||||
ctx->noscte20 = setting->noscte20;
|
||||
|
||||
#ifndef DISABLE_RUST
|
||||
ctx->dtvcc_rust = ccxr_dtvcc_init(setting->settings_dtvcc);
|
||||
ctx->dtvcc = NULL; // Not used when Rust is enabled
|
||||
#else
|
||||
ctx->dtvcc = dtvcc_init(setting->settings_dtvcc);
|
||||
if (!ctx->dtvcc)
|
||||
fatal(EXIT_NOT_ENOUGH_MEMORY, "In init_cc_decode: Out of memory initializing dtvcc.");
|
||||
ctx->dtvcc->is_active = setting->settings_dtvcc->enabled;
|
||||
ctx->dtvcc_rust = NULL;
|
||||
#endif
|
||||
|
||||
if (setting->codec == CCX_CODEC_ATSC_CC)
|
||||
{
|
||||
@@ -260,6 +321,8 @@ struct lib_cc_decode *init_cc_decode(struct ccx_decoders_common_settings_t *sett
|
||||
setting->cc_to_stdout,
|
||||
setting->output_format,
|
||||
ctx->timing);
|
||||
if (!ctx->context_cc608_field_1)
|
||||
fatal(EXIT_NOT_ENOUGH_MEMORY, "In init_cc_decode: Out of memory initializing context_cc608_field_1.");
|
||||
ctx->context_cc608_field_2 = ccx_decoder_608_init_library(
|
||||
setting->settings_608,
|
||||
setting->cc_channel,
|
||||
@@ -268,11 +331,8 @@ struct lib_cc_decode *init_cc_decode(struct ccx_decoders_common_settings_t *sett
|
||||
setting->cc_to_stdout,
|
||||
setting->output_format,
|
||||
ctx->timing);
|
||||
}
|
||||
else
|
||||
{
|
||||
ctx->context_cc608_field_1 = NULL;
|
||||
ctx->context_cc608_field_2 = NULL;
|
||||
if (!ctx->context_cc608_field_2)
|
||||
fatal(EXIT_NOT_ENOUGH_MEMORY, "In init_cc_decode: Out of memory initializing context_cc608_field_2.");
|
||||
}
|
||||
ctx->current_field = 1;
|
||||
ctx->private_data = setting->private_data;
|
||||
@@ -377,6 +437,8 @@ struct lib_cc_decode *init_cc_decode(struct ccx_decoders_common_settings_t *sett
|
||||
setting->xds_write_to_file = 0;
|
||||
}
|
||||
ctx->xds_ctx = ccx_decoders_xds_init_library(ctx->timing, setting->xds_write_to_file);
|
||||
if (!ctx->xds_ctx)
|
||||
fatal(EXIT_NOT_ENOUGH_MEMORY, "In init_cc_decode: Out of memory initializing xds_ctx.");
|
||||
|
||||
ctx->vbi_decoder = NULL;
|
||||
ctx->ocr_quantmode = setting->ocr_quantmode;
|
||||
@@ -426,6 +488,13 @@ void flush_cc_decode(struct lib_cc_decode *ctx, struct cc_subtitle *sub)
|
||||
}
|
||||
}
|
||||
}
|
||||
#ifndef DISABLE_RUST
|
||||
if (ccxr_dtvcc_is_active(ctx->dtvcc_rust))
|
||||
{
|
||||
ctx->current_field = 3;
|
||||
ccxr_flush_active_decoders(ctx->dtvcc_rust);
|
||||
}
|
||||
#else
|
||||
if (ctx->dtvcc->is_active)
|
||||
{
|
||||
for (int i = 0; i < CCX_DTVCC_MAX_SERVICES; i++)
|
||||
@@ -440,52 +509,86 @@ void flush_cc_decode(struct lib_cc_decode *ctx, struct cc_subtitle *sub)
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
struct encoder_ctx *copy_encoder_context(struct encoder_ctx *ctx)
|
||||
{
|
||||
struct encoder_ctx *ctx_copy = NULL;
|
||||
ctx_copy = malloc(sizeof(struct encoder_ctx));
|
||||
if (!ctx_copy)
|
||||
fatal(EXIT_NOT_ENOUGH_MEMORY, "In copy_encoder_context: Out of memory allocating ctx_copy.");
|
||||
memcpy(ctx_copy, ctx, sizeof(struct encoder_ctx));
|
||||
|
||||
// Initialize copied pointers to NULL before re-allocating
|
||||
ctx_copy->buffer = NULL;
|
||||
ctx_copy->first_input_file = NULL;
|
||||
ctx_copy->out = NULL;
|
||||
ctx_copy->timing = NULL;
|
||||
ctx_copy->transcript_settings = NULL;
|
||||
ctx_copy->subline = NULL;
|
||||
ctx_copy->start_credits_text = NULL;
|
||||
ctx_copy->end_credits_text = NULL;
|
||||
ctx_copy->prev = NULL;
|
||||
ctx_copy->last_string = NULL;
|
||||
|
||||
if (ctx->buffer)
|
||||
{
|
||||
ctx_copy->buffer = malloc(ctx->capacity * sizeof(unsigned char));
|
||||
if (!ctx_copy->buffer)
|
||||
fatal(EXIT_NOT_ENOUGH_MEMORY, "In copy_encoder_context: Out of memory allocating buffer.");
|
||||
memcpy(ctx_copy->buffer, ctx->buffer, ctx->capacity * sizeof(unsigned char));
|
||||
}
|
||||
if (ctx->first_input_file)
|
||||
{
|
||||
ctx_copy->first_input_file = malloc(strlen(ctx->first_input_file) * sizeof(char));
|
||||
memcpy(ctx_copy->first_input_file, ctx->first_input_file, strlen(ctx->first_input_file) * sizeof(char));
|
||||
size_t len = strlen(ctx->first_input_file) + 1;
|
||||
ctx_copy->first_input_file = malloc(len);
|
||||
if (!ctx_copy->first_input_file)
|
||||
fatal(EXIT_NOT_ENOUGH_MEMORY, "In copy_encoder_context: Out of memory allocating first_input_file.");
|
||||
memcpy(ctx_copy->first_input_file, ctx->first_input_file, len);
|
||||
}
|
||||
if (ctx->out)
|
||||
{
|
||||
ctx_copy->out = malloc(sizeof(struct ccx_s_write));
|
||||
if (!ctx_copy->out)
|
||||
fatal(EXIT_NOT_ENOUGH_MEMORY, "In copy_encoder_context: Out of memory allocating out.");
|
||||
memcpy(ctx_copy->out, ctx->out, sizeof(struct ccx_s_write));
|
||||
}
|
||||
if (ctx->timing)
|
||||
{
|
||||
ctx_copy->timing = malloc(sizeof(struct ccx_common_timing_ctx));
|
||||
if (!ctx_copy->timing)
|
||||
fatal(EXIT_NOT_ENOUGH_MEMORY, "In copy_encoder_context: Out of memory allocating timing.");
|
||||
memcpy(ctx_copy->timing, ctx->timing, sizeof(struct ccx_common_timing_ctx));
|
||||
}
|
||||
if (ctx->transcript_settings)
|
||||
{
|
||||
ctx_copy->transcript_settings = malloc(sizeof(struct ccx_encoders_transcript_format));
|
||||
if (!ctx_copy->transcript_settings)
|
||||
fatal(EXIT_NOT_ENOUGH_MEMORY, "In copy_encoder_context: Out of memory allocating transcript_settings.");
|
||||
memcpy(ctx_copy->transcript_settings, ctx->transcript_settings, sizeof(struct ccx_encoders_transcript_format));
|
||||
}
|
||||
if (ctx->subline)
|
||||
{
|
||||
ctx_copy->subline = malloc(SUBLINESIZE);
|
||||
if (!ctx_copy->subline)
|
||||
fatal(EXIT_NOT_ENOUGH_MEMORY, "In copy_encoder_context: Out of memory allocating subline.");
|
||||
memcpy(ctx_copy->subline, ctx->subline, SUBLINESIZE);
|
||||
}
|
||||
if (ctx->start_credits_text)
|
||||
{
|
||||
ctx_copy->start_credits_text = malloc(strlen(ctx->start_credits_text) * sizeof(char));
|
||||
memcpy(ctx_copy->start_credits_text, ctx->start_credits_text, (strlen(ctx->start_credits_text) + 1) * sizeof(char));
|
||||
size_t len = strlen(ctx->start_credits_text) + 1;
|
||||
ctx_copy->start_credits_text = malloc(len);
|
||||
if (!ctx_copy->start_credits_text)
|
||||
fatal(EXIT_NOT_ENOUGH_MEMORY, "In copy_encoder_context: Out of memory allocating start_credits_text.");
|
||||
memcpy(ctx_copy->start_credits_text, ctx->start_credits_text, len);
|
||||
}
|
||||
if (ctx->end_credits_text)
|
||||
{
|
||||
ctx_copy->end_credits_text = malloc(strlen(ctx->end_credits_text) * sizeof(char));
|
||||
memcpy(ctx_copy->end_credits_text, ctx->end_credits_text, (strlen(ctx->end_credits_text) + 1) * sizeof(char));
|
||||
size_t len = strlen(ctx->end_credits_text) + 1;
|
||||
ctx_copy->end_credits_text = malloc(len);
|
||||
if (!ctx_copy->end_credits_text)
|
||||
fatal(EXIT_NOT_ENOUGH_MEMORY, "In copy_encoder_context: Out of memory allocating end_credits_text.");
|
||||
memcpy(ctx_copy->end_credits_text, ctx->end_credits_text, len);
|
||||
}
|
||||
return ctx_copy;
|
||||
}
|
||||
@@ -493,42 +596,67 @@ struct lib_cc_decode *copy_decoder_context(struct lib_cc_decode *ctx)
|
||||
{
|
||||
struct lib_cc_decode *ctx_copy = NULL;
|
||||
ctx_copy = malloc(sizeof(struct lib_cc_decode));
|
||||
if (!ctx_copy)
|
||||
fatal(EXIT_NOT_ENOUGH_MEMORY, "In copy_decoder_context: Out of memory allocating ctx_copy.");
|
||||
memcpy(ctx_copy, ctx, sizeof(struct lib_cc_decode));
|
||||
|
||||
// Initialize copied pointers to NULL before re-allocating
|
||||
ctx_copy->context_cc608_field_1 = NULL;
|
||||
ctx_copy->context_cc608_field_2 = NULL;
|
||||
ctx_copy->timing = NULL;
|
||||
ctx_copy->avc_ctx = NULL;
|
||||
ctx_copy->private_data = NULL;
|
||||
ctx_copy->dtvcc = NULL;
|
||||
ctx_copy->xds_ctx = NULL;
|
||||
ctx_copy->vbi_decoder = NULL;
|
||||
|
||||
if (ctx->context_cc608_field_1)
|
||||
{
|
||||
ctx_copy->context_cc608_field_1 = malloc(sizeof(struct ccx_decoder_608_context));
|
||||
if (!ctx_copy->context_cc608_field_1)
|
||||
fatal(EXIT_NOT_ENOUGH_MEMORY, "In copy_decoder_context: Out of memory allocating context_cc608_field_1.");
|
||||
memcpy(ctx_copy->context_cc608_field_1, ctx->context_cc608_field_1, sizeof(struct ccx_decoder_608_context));
|
||||
}
|
||||
if (ctx->context_cc608_field_2)
|
||||
{
|
||||
ctx_copy->context_cc608_field_2 = malloc(sizeof(struct ccx_decoder_608_context));
|
||||
if (!ctx_copy->context_cc608_field_2)
|
||||
fatal(EXIT_NOT_ENOUGH_MEMORY, "In copy_decoder_context: Out of memory allocating context_cc608_field_2.");
|
||||
memcpy(ctx_copy->context_cc608_field_2, ctx->context_cc608_field_2, sizeof(struct ccx_decoder_608_context));
|
||||
}
|
||||
if (ctx->timing)
|
||||
{
|
||||
ctx_copy->timing = malloc(sizeof(struct ccx_common_timing_ctx));
|
||||
if (!ctx_copy->timing)
|
||||
fatal(EXIT_NOT_ENOUGH_MEMORY, "In copy_decoder_context: Out of memory allocating timing.");
|
||||
memcpy(ctx_copy->timing, ctx->timing, sizeof(struct ccx_common_timing_ctx));
|
||||
}
|
||||
if (ctx->avc_ctx)
|
||||
{
|
||||
ctx_copy->avc_ctx = malloc(sizeof(struct avc_ctx));
|
||||
if (!ctx_copy->avc_ctx)
|
||||
fatal(EXIT_NOT_ENOUGH_MEMORY, "In copy_decoder_context: Out of memory allocating avc_ctx.");
|
||||
memcpy(ctx_copy->avc_ctx, ctx->avc_ctx, sizeof(struct avc_ctx));
|
||||
}
|
||||
ctx_copy->private_data = NULL;
|
||||
if (ctx->dtvcc)
|
||||
{
|
||||
ctx_copy->dtvcc = malloc(sizeof(struct dtvcc_ctx));
|
||||
if (!ctx_copy->dtvcc)
|
||||
fatal(EXIT_NOT_ENOUGH_MEMORY, "In copy_decoder_context: Out of memory allocating dtvcc.");
|
||||
memcpy(ctx_copy->dtvcc, ctx->dtvcc, sizeof(struct dtvcc_ctx));
|
||||
}
|
||||
if (ctx->xds_ctx)
|
||||
{
|
||||
ctx_copy->xds_ctx = malloc(sizeof(struct ccx_decoders_xds_context));
|
||||
if (!ctx_copy->xds_ctx)
|
||||
fatal(EXIT_NOT_ENOUGH_MEMORY, "In copy_decoder_context: Out of memory allocating xds_ctx.");
|
||||
memcpy(ctx_copy->xds_ctx, ctx->xds_ctx, sizeof(struct ccx_decoders_xds_context));
|
||||
}
|
||||
if (ctx->vbi_decoder)
|
||||
{
|
||||
ctx_copy->vbi_decoder = malloc(sizeof(struct ccx_decoder_vbi_ctx));
|
||||
if (!ctx_copy->vbi_decoder)
|
||||
fatal(EXIT_NOT_ENOUGH_MEMORY, "In copy_decoder_context: Out of memory allocating vbi_decoder.");
|
||||
memcpy(ctx_copy->vbi_decoder, ctx->vbi_decoder, sizeof(struct ccx_decoder_vbi_ctx));
|
||||
}
|
||||
return ctx_copy;
|
||||
@@ -537,12 +665,17 @@ struct cc_subtitle *copy_subtitle(struct cc_subtitle *sub)
|
||||
{
|
||||
struct cc_subtitle *sub_copy = NULL;
|
||||
sub_copy = malloc(sizeof(struct cc_subtitle));
|
||||
if (!sub_copy)
|
||||
fatal(EXIT_NOT_ENOUGH_MEMORY, "In copy_subtitle: Out of memory allocating sub_copy.");
|
||||
memcpy(sub_copy, sub, sizeof(struct cc_subtitle));
|
||||
sub_copy->datatype = sub->datatype;
|
||||
sub_copy->data = NULL;
|
||||
|
||||
if (sub->data)
|
||||
{
|
||||
sub_copy->data = malloc(sub->nb_data * sizeof(struct eia608_screen));
|
||||
if (!sub_copy->data)
|
||||
fatal(EXIT_NOT_ENOUGH_MEMORY, "In copy_subtitle: Out of memory allocating data.");
|
||||
memcpy(sub_copy->data, sub->data, sub->nb_data * sizeof(struct eia608_screen));
|
||||
}
|
||||
return sub_copy;
|
||||
|
||||
@@ -11,25 +11,31 @@
|
||||
extern uint64_t utc_refvalue; // UTC referential value
|
||||
|
||||
// Declarations
|
||||
LLONG get_visible_start (struct ccx_common_timing_ctx *ctx, int current_field);
|
||||
LLONG get_visible_end (struct ccx_common_timing_ctx *ctx, int current_field);
|
||||
LLONG get_visible_start(struct ccx_common_timing_ctx *ctx, int current_field);
|
||||
LLONG get_visible_end(struct ccx_common_timing_ctx *ctx, int current_field);
|
||||
|
||||
unsigned int get_decoder_str_basic(unsigned char *buffer, unsigned char *line, int trim_subs, enum ccx_encoding_type encoding);
|
||||
|
||||
void ccx_decoders_common_settings_init(LLONG subs_delay, enum ccx_output_format output_format);
|
||||
|
||||
int validate_cc_data_pair (unsigned char *cc_data_pair);
|
||||
int process_cc_data (struct encoder_ctx *enc_ctx, struct lib_cc_decode *ctx, unsigned char *cc_data, int cc_count, struct cc_subtitle *sub);
|
||||
int do_cb (struct lib_cc_decode *ctx, unsigned char *cc_block, struct cc_subtitle *sub);
|
||||
void printdata (struct lib_cc_decode *ctx, const unsigned char *data1, int length1,
|
||||
const unsigned char *data2, int length2, struct cc_subtitle *sub);
|
||||
struct lib_cc_decode* init_cc_decode (struct ccx_decoders_common_settings_t *setting);
|
||||
int validate_cc_data_pair(unsigned char *cc_data_pair);
|
||||
int process_cc_data(struct encoder_ctx *enc_ctx, struct lib_cc_decode *ctx, unsigned char *cc_data, int cc_count, struct cc_subtitle *sub);
|
||||
int do_cb(struct lib_cc_decode *ctx, unsigned char *cc_block, struct cc_subtitle *sub);
|
||||
void printdata(struct lib_cc_decode *ctx, const unsigned char *data1, int length1,
|
||||
const unsigned char *data2, int length2, struct cc_subtitle *sub);
|
||||
struct lib_cc_decode *init_cc_decode(struct ccx_decoders_common_settings_t *setting);
|
||||
void dinit_cc_decode(struct lib_cc_decode **ctx);
|
||||
void flush_cc_decode(struct lib_cc_decode *ctx, struct cc_subtitle *sub);
|
||||
struct encoder_ctx* copy_encoder_context(struct encoder_ctx *ctx);
|
||||
struct lib_cc_decode* copy_decoder_context(struct lib_cc_decode *ctx);
|
||||
struct cc_subtitle* copy_subtitle(struct cc_subtitle *sub);
|
||||
struct encoder_ctx *copy_encoder_context(struct encoder_ctx *ctx);
|
||||
struct lib_cc_decode *copy_decoder_context(struct lib_cc_decode *ctx);
|
||||
struct cc_subtitle *copy_subtitle(struct cc_subtitle *sub);
|
||||
void free_encoder_context(struct encoder_ctx *ctx);
|
||||
void free_decoder_context(struct lib_cc_decode *ctx);
|
||||
void free_subtitle(struct cc_subtitle* sub);
|
||||
void free_subtitle(struct cc_subtitle *sub);
|
||||
|
||||
#ifndef DISABLE_RUST
|
||||
// Rust FFI function to flush active CEA-708 service decoders
|
||||
extern void ccxr_flush_active_decoders(void *dtvcc_rust);
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
@@ -345,6 +345,11 @@ static struct ISDBText *allocate_text_node(ISDBSubLayout *ls)
|
||||
|
||||
text->used = 0;
|
||||
text->buf = malloc(128);
|
||||
if (!text->buf)
|
||||
{
|
||||
free(text);
|
||||
return NULL;
|
||||
}
|
||||
text->len = 128;
|
||||
*text->buf = 0;
|
||||
return text;
|
||||
@@ -719,16 +724,17 @@ static int parse_csi(ISDBSubContext *ctx, const uint8_t *buf, int len)
|
||||
// Copy buf in arg
|
||||
for (i = 0; *buf != 0x20; i++)
|
||||
{
|
||||
if (i >= (sizeof(arg)) + 1)
|
||||
if (i >= sizeof(arg) - 1)
|
||||
{
|
||||
isdb_log("UnExpected CSI %d >= %d", sizeof(arg) + 1, i);
|
||||
isdb_log("UnExpected CSI: too long");
|
||||
break;
|
||||
}
|
||||
arg[i] = *buf;
|
||||
buf++;
|
||||
}
|
||||
/* ignore terminating 0x20 character */
|
||||
arg[i] = *buf++;
|
||||
if (i < sizeof(arg))
|
||||
arg[i] = *buf++;
|
||||
|
||||
switch (*buf)
|
||||
{
|
||||
|
||||
@@ -9,5 +9,4 @@ int isdbsub_decode(struct lib_cc_decode *dec_ctx, const uint8_t *buf, size_t buf
|
||||
void delete_isdb_decoder(void **isdb_ctx);
|
||||
void *init_isdb_decoder(void);
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
@@ -11,11 +11,10 @@
|
||||
#define CCX_DECODER_608_SCREEN_ROWS 15
|
||||
#define CCX_DECODER_608_SCREEN_WIDTH 32
|
||||
#define MAXBFRAMES 50
|
||||
#define SORTBUF (2*MAXBFRAMES+1)
|
||||
|
||||
#define SORTBUF (2 * MAXBFRAMES + 1)
|
||||
|
||||
/* flag raised when end of display marker arrives in Dvb Subtitle */
|
||||
#define SUB_EOD_MARKER (1 << 0 )
|
||||
#define SUB_EOD_MARKER (1 << 0)
|
||||
struct cc_bitmap
|
||||
{
|
||||
int x;
|
||||
@@ -77,13 +76,13 @@ enum ccx_decoder_608_color_code
|
||||
};
|
||||
|
||||
/**
|
||||
* This structure have fields which need to be ignored according to format,
|
||||
* for example if format is SFORMAT_XDS then all fields other then
|
||||
* xds related (xds_str, xds_len and cur_xds_packet_class) should be
|
||||
* ignored and not to be dereferenced.
|
||||
*
|
||||
* TODO use union inside struct for each kind of fields
|
||||
*/
|
||||
* This structure have fields which need to be ignored according to format,
|
||||
* for example if format is SFORMAT_XDS then all fields other then
|
||||
* xds related (xds_str, xds_len and cur_xds_packet_class) should be
|
||||
* ignored and not to be dereferenced.
|
||||
*
|
||||
* TODO use union inside struct for each kind of fields
|
||||
*/
|
||||
struct eia608_screen // A CC buffer
|
||||
{
|
||||
/** format of data inside this structure */
|
||||
@@ -91,8 +90,8 @@ struct eia608_screen // A CC buffer
|
||||
unsigned char characters[CCX_DECODER_608_SCREEN_ROWS][CCX_DECODER_608_SCREEN_WIDTH + 1];
|
||||
enum ccx_decoder_608_color_code colors[CCX_DECODER_608_SCREEN_ROWS][CCX_DECODER_608_SCREEN_WIDTH + 1];
|
||||
enum font_bits fonts[CCX_DECODER_608_SCREEN_ROWS][CCX_DECODER_608_SCREEN_WIDTH + 1]; // Extra char at the end for a 0
|
||||
int row_used[CCX_DECODER_608_SCREEN_ROWS]; // Any data in row?
|
||||
int empty; // Buffer completely empty?
|
||||
int row_used[CCX_DECODER_608_SCREEN_ROWS]; // Any data in row?
|
||||
int empty; // Buffer completely empty?
|
||||
/** start time of this CC buffer */
|
||||
LLONG start_time;
|
||||
/** end time of this CC buffer */
|
||||
@@ -110,20 +109,20 @@ struct eia608_screen // A CC buffer
|
||||
|
||||
struct ccx_decoders_common_settings_t
|
||||
{
|
||||
LLONG subs_delay; // ms to delay (or advance) subs
|
||||
enum ccx_output_format output_format; // What kind of output format should be used?
|
||||
int fix_padding; // Replace 0000 with 8080 in HDTV (needed for some cards)
|
||||
LLONG subs_delay; // ms to delay (or advance) subs
|
||||
enum ccx_output_format output_format; // What kind of output format should be used?
|
||||
int fix_padding; // Replace 0000 with 8080 in HDTV (needed for some cards)
|
||||
struct ccx_boundary_time extraction_start, extraction_end; // Segment we actually process
|
||||
int cc_to_stdout;
|
||||
int extract; // Extract 1st, 2nd or both fields
|
||||
int fullbin; // Disable pruning of padding cc blocks
|
||||
int extract; // Extract 1st, 2nd or both fields
|
||||
int fullbin; // Disable pruning of padding cc blocks
|
||||
int no_rollup;
|
||||
int noscte20;
|
||||
struct ccx_decoder_608_settings *settings_608; // Contains the settings for the 608 decoder.
|
||||
ccx_decoder_dtvcc_settings *settings_dtvcc; // Same for cea 708 captions decoder (dtvcc)
|
||||
int cc_channel; // Channel we want to dump in srt mode
|
||||
struct ccx_decoder_608_settings *settings_608; // Contains the settings for the 608 decoder.
|
||||
ccx_decoder_dtvcc_settings *settings_dtvcc; // Same for cea 708 captions decoder (dtvcc)
|
||||
int cc_channel; // Channel we want to dump in srt mode
|
||||
unsigned send_to_srv;
|
||||
unsigned int hauppauge_mode; // If 1, use PID=1003, process specially and so on
|
||||
unsigned int hauppauge_mode; // If 1, use PID=1003, process specially and so on
|
||||
int program_number;
|
||||
enum ccx_code_type codec;
|
||||
int xds_write_to_file;
|
||||
@@ -142,17 +141,17 @@ struct lib_cc_decode
|
||||
void *context_cc608_field_1;
|
||||
void *context_cc608_field_2;
|
||||
|
||||
int no_rollup; // If 1, write one line at a time
|
||||
int no_rollup; // If 1, write one line at a time
|
||||
int noscte20;
|
||||
int fix_padding; // Replace 0000 with 8080 in HDTV (needed for some cards)
|
||||
enum ccx_output_format write_format; // 0 = Raw, 1 = srt, 2 = SMI
|
||||
int fix_padding; // Replace 0000 with 8080 in HDTV (needed for some cards)
|
||||
enum ccx_output_format write_format; // 0 = Raw, 1 = srt, 2 = SMI
|
||||
struct ccx_boundary_time extraction_start, extraction_end; // Segment we actually process
|
||||
LLONG subs_delay; // ms to delay (or advance) subs
|
||||
int extract; // Extract 1st, 2nd or both fields
|
||||
int fullbin; // Disable pruning of padding cc blocks
|
||||
LLONG subs_delay; // ms to delay (or advance) subs
|
||||
int extract; // Extract 1st, 2nd or both fields
|
||||
int fullbin; // Disable pruning of padding cc blocks
|
||||
struct cc_subtitle dec_sub;
|
||||
enum ccx_bufferdata_type in_bufferdatatype;
|
||||
unsigned int hauppauge_mode; // If 1, use PID=1003, process specially and so on
|
||||
unsigned int hauppauge_mode; // If 1, use PID=1003, process specially and so on
|
||||
|
||||
int frames_since_last_gop;
|
||||
/* GOP-based timing */
|
||||
@@ -185,7 +184,7 @@ struct lib_cc_decode
|
||||
int in_pic_data;
|
||||
|
||||
unsigned int current_progressive_sequence;
|
||||
unsigned int current_pulldownfields ;
|
||||
unsigned int current_pulldownfields;
|
||||
|
||||
int temporal_reference;
|
||||
enum ccx_frame_type picture_coding_type;
|
||||
@@ -197,18 +196,19 @@ struct lib_cc_decode
|
||||
/* Required in es_function.c and es_userdata.c */
|
||||
unsigned top_field_first; // Needs to be global
|
||||
|
||||
/* Stats. Modified in es_userdata.c*/
|
||||
int stat_numuserheaders;
|
||||
int stat_dvdccheaders;
|
||||
int stat_scte20ccheaders;
|
||||
int stat_replay5000headers;
|
||||
int stat_replay4000headers;
|
||||
int stat_dishheaders;
|
||||
int stat_hdtv;
|
||||
int stat_divicom;
|
||||
int false_pict_header;
|
||||
/* Stats. Modified in es_userdata.c*/
|
||||
int stat_numuserheaders;
|
||||
int stat_dvdccheaders;
|
||||
int stat_scte20ccheaders;
|
||||
int stat_replay5000headers;
|
||||
int stat_replay4000headers;
|
||||
int stat_dishheaders;
|
||||
int stat_hdtv;
|
||||
int stat_divicom;
|
||||
int false_pict_header;
|
||||
|
||||
dtvcc_ctx *dtvcc;
|
||||
void *dtvcc_rust; // Persistent Rust CEA-708 decoder context
|
||||
int current_field;
|
||||
// Analyse/use the picture information
|
||||
int maxtref; // Use to remember the temporal reference number
|
||||
@@ -217,7 +217,7 @@ struct lib_cc_decode
|
||||
// Store fts;
|
||||
LLONG cc_fts[SORTBUF];
|
||||
// Store HD CC packets
|
||||
unsigned char cc_data_pkts[SORTBUF][10*31*3+1]; // *10, because MP4 seems to have different limits
|
||||
unsigned char cc_data_pkts[SORTBUF][10 * 31 * 3 + 1]; // *10, because MP4 seems to have different limits
|
||||
|
||||
// The sequence number of the current anchor frame. All currently read
|
||||
// B-Frames belong to this I- or P-frame.
|
||||
@@ -227,7 +227,7 @@ struct lib_cc_decode
|
||||
|
||||
int (*writedata)(const unsigned char *data, int length, void *private_data, struct cc_subtitle *sub);
|
||||
|
||||
//dvb subtitle related
|
||||
// dvb subtitle related
|
||||
int ocr_quantmode;
|
||||
struct lib_cc_decode *prev;
|
||||
};
|
||||
|
||||
@@ -18,11 +18,10 @@ struct ccx_decoder_vbi_ctx
|
||||
{
|
||||
int vbi_decoder_inited;
|
||||
vbi_raw_decoder zvbi_decoder;
|
||||
//vbi3_raw_decoder zvbi_decoder;
|
||||
// vbi3_raw_decoder zvbi_decoder;
|
||||
#ifdef VBI_DEBUG
|
||||
FILE *vbi_debug_dump;
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
@@ -175,6 +175,8 @@ void xdsprint(struct cc_subtitle *sub, struct ccx_decoders_xds_context *ctx, con
|
||||
if (n > -1 && n < size)
|
||||
{
|
||||
write_xds_string(sub, ctx, p, n);
|
||||
/* Note: Don't free(p) here - the pointer is stored in data->xds_str
|
||||
and will be freed by the encoder or decoder cleanup code */
|
||||
return;
|
||||
}
|
||||
/* Else try again with more space. */
|
||||
@@ -347,9 +349,9 @@ void xds_do_copy_generation_management_system(struct cc_subtitle *sub, struct cc
|
||||
const char *copytext[4] = {"Copy permitted (no restrictions)", "No more copies (one generation copy has been made)",
|
||||
"One generation of copies can be made", "No copying is permitted"};
|
||||
const char *apstext[4] = {"No APS", "PSP On; Split Burst Off", "PSP On; 2 line Split Burst On", "PSP On; 4 line Split Burst On"};
|
||||
sprintf(copy_permited, "CGMS: %s", copytext[cgms_a_b4 * 2 + cgms_a_b3]);
|
||||
sprintf(aps, "APS: %s", apstext[aps_b2 * 2 + aps_b1]);
|
||||
sprintf(rcd, "Redistribution Control Descriptor: %d", rcd0);
|
||||
snprintf(copy_permited, sizeof(copy_permited), "CGMS: %s", copytext[cgms_a_b4 * 2 + cgms_a_b3]);
|
||||
snprintf(aps, sizeof(aps), "APS: %s", apstext[aps_b2 * 2 + aps_b1]);
|
||||
snprintf(rcd, sizeof(rcd), "Redistribution Control Descriptor: %d", rcd0);
|
||||
}
|
||||
|
||||
xdsprint(sub, ctx, copy_permited);
|
||||
@@ -407,30 +409,45 @@ void xds_do_content_advisory(struct cc_subtitle *sub, struct ccx_decoders_xds_co
|
||||
const char *agetext[8] = {"None", "TV-Y (All Children)", "TV-Y7 (Older Children)",
|
||||
"TV-G (General Audience)", "TV-PG (Parental Guidance Suggested)",
|
||||
"TV-14 (Parents Strongly Cautioned)", "TV-MA (Mature Audience Only)", "None"};
|
||||
sprintf(age, "ContentAdvisory: US TV Parental Guidelines. Age Rating: %s", agetext[g2 * 4 + g1 * 2 + g0]);
|
||||
snprintf(age, sizeof(age), "ContentAdvisory: US TV Parental Guidelines. Age Rating: %s", agetext[g2 * 4 + g1 * 2 + g0]);
|
||||
content[0] = 0;
|
||||
size_t content_len = 0;
|
||||
if (!g2 && g1 && !g0) // For TV-Y7 (Older children), the Violence bit is "fantasy violence"
|
||||
{
|
||||
if (FV)
|
||||
strcpy(content, "[Fantasy Violence] ");
|
||||
{
|
||||
snprintf(content, sizeof(content), "[Fantasy Violence] ");
|
||||
content_len = strlen(content);
|
||||
}
|
||||
}
|
||||
else // For all others, is real
|
||||
{
|
||||
if (FV)
|
||||
strcpy(content, "[Violence] ");
|
||||
{
|
||||
snprintf(content, sizeof(content), "[Violence] ");
|
||||
content_len = strlen(content);
|
||||
}
|
||||
}
|
||||
if (S)
|
||||
strcat(content, "[Sexual Situations] ");
|
||||
{
|
||||
snprintf(content + content_len, sizeof(content) - content_len, "[Sexual Situations] ");
|
||||
content_len = strlen(content);
|
||||
}
|
||||
if (La3)
|
||||
strcat(content, "[Adult Language] ");
|
||||
{
|
||||
snprintf(content + content_len, sizeof(content) - content_len, "[Adult Language] ");
|
||||
content_len = strlen(content);
|
||||
}
|
||||
if (Da2)
|
||||
strcat(content, "[Sexually Suggestive Dialog] ");
|
||||
{
|
||||
snprintf(content + content_len, sizeof(content) - content_len, "[Sexually Suggestive Dialog] ");
|
||||
}
|
||||
supported = 1;
|
||||
}
|
||||
if (!a0) // MPA
|
||||
{
|
||||
const char *ratingtext[8] = {"N/A", "G", "PG", "PG-13", "R", "NC-17", "X", "Not Rated"};
|
||||
sprintf(rating, "ContentAdvisory: MPA Rating: %s", ratingtext[r2 * 4 + r1 * 2 + r0]);
|
||||
snprintf(rating, sizeof(rating), "ContentAdvisory: MPA Rating: %s", ratingtext[r2 * 4 + r1 * 2 + r0]);
|
||||
supported = 1;
|
||||
}
|
||||
if (a0 && a1 && !Da2 && !La3) // Canadian English Language Rating
|
||||
@@ -438,7 +455,7 @@ void xds_do_content_advisory(struct cc_subtitle *sub, struct ccx_decoders_xds_co
|
||||
const char *ratingtext[8] = {"Exempt", "Children", "Children eight years and older",
|
||||
"General programming suitable for all audiences", "Parental Guidance",
|
||||
"Viewers 14 years and older", "Adult Programming", "[undefined]"};
|
||||
sprintf(rating, "ContentAdvisory: Canadian English Rating: %s", ratingtext[g2 * 4 + g1 * 2 + g0]);
|
||||
snprintf(rating, sizeof(rating), "ContentAdvisory: Canadian English Rating: %s", ratingtext[g2 * 4 + g1 * 2 + g0]);
|
||||
supported = 1;
|
||||
}
|
||||
if (a0 && a1 && Da2 && !La3) // Canadian French Language Rating
|
||||
@@ -447,7 +464,7 @@ void xds_do_content_advisory(struct cc_subtitle *sub, struct ccx_decoders_xds_co
|
||||
"Cette ?mission peut ne pas convenir aux enfants de moins de 13 ans",
|
||||
"Cette ?mission ne convient pas aux moins de 16 ans",
|
||||
"Cette ?mission est r?serv?e aux adultes", "[invalid]", "[invalid]"};
|
||||
sprintf(rating, "ContentAdvisory: Canadian French Rating: %s", ratingtext[g2 * 4 + g1 * 2 + g0]);
|
||||
snprintf(rating, sizeof(rating), "ContentAdvisory: Canadian French Rating: %s", ratingtext[g2 * 4 + g1 * 2 + g0]);
|
||||
supported = 1;
|
||||
}
|
||||
}
|
||||
@@ -455,14 +472,17 @@ void xds_do_content_advisory(struct cc_subtitle *sub, struct ccx_decoders_xds_co
|
||||
if (!a1 && a0) // US TV parental guidelines
|
||||
{
|
||||
xdsprint(sub, ctx, age);
|
||||
xdsprint(sub, ctx, content);
|
||||
if (content[0]) // Only output content if not empty
|
||||
xdsprint(sub, ctx, content);
|
||||
if (changed)
|
||||
{
|
||||
ccx_common_logging.log_ftn("\rXDS: %s\n ", age);
|
||||
ccx_common_logging.log_ftn("\rXDS: %s\n ", content);
|
||||
if (content[0])
|
||||
ccx_common_logging.log_ftn("\rXDS: %s\n ", content);
|
||||
}
|
||||
ccx_common_logging.debug_ftn(CCX_DMT_DECODER_XDS, "\rXDS: %s\n", age);
|
||||
ccx_common_logging.debug_ftn(CCX_DMT_DECODER_XDS, "\rXDS: %s\n", content);
|
||||
if (content[0])
|
||||
ccx_common_logging.debug_ftn(CCX_DMT_DECODER_XDS, "\rXDS: %s\n", content);
|
||||
}
|
||||
if (!a0 || // MPA
|
||||
(a0 && a1 && !Da2 && !La3) || // Canadian English Language Rating
|
||||
@@ -484,6 +504,10 @@ int xds_do_current_and_future(struct cc_subtitle *sub, struct ccx_decoders_xds_c
|
||||
int was_proc = 0;
|
||||
|
||||
char *str = malloc(1024);
|
||||
if (!str)
|
||||
{
|
||||
return CCX_ENOMEM;
|
||||
}
|
||||
char *tstr = NULL;
|
||||
int str_len = 1024;
|
||||
|
||||
@@ -712,7 +736,8 @@ int xds_do_current_and_future(struct cc_subtitle *sub, struct ccx_decoders_xds_c
|
||||
if (changed)
|
||||
{
|
||||
ccx_common_logging.log_ftn("\rXDS description line %d: %s\n", line_num, xds_desc);
|
||||
strcpy(ctx->xds_program_description[line_num], xds_desc);
|
||||
strncpy(ctx->xds_program_description[line_num], xds_desc, 32);
|
||||
ctx->xds_program_description[line_num][32] = '\0';
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -749,7 +774,8 @@ int xds_do_channel(struct cc_subtitle *sub, struct ccx_decoders_xds_context *ctx
|
||||
if (strcmp(xds_network_name, ctx->current_xds_network_name)) // Change of station
|
||||
{
|
||||
ccx_common_logging.log_ftn("XDS Notice: Network is now %s\n", xds_network_name);
|
||||
strcpy(ctx->current_xds_network_name, xds_network_name);
|
||||
strncpy(ctx->current_xds_network_name, xds_network_name, 32);
|
||||
ctx->current_xds_network_name[32] = '\0';
|
||||
}
|
||||
break;
|
||||
case XDS_TYPE_CALL_LETTERS_AND_CHANNEL:
|
||||
@@ -800,12 +826,19 @@ int xds_do_private_data(struct cc_subtitle *sub, struct ccx_decoders_xds_context
|
||||
if (!ctx)
|
||||
return CCX_EINVAL;
|
||||
|
||||
str = malloc((ctx->cur_xds_payload_length * 3) + 1);
|
||||
size_t str_size = (ctx->cur_xds_payload_length * 3) + 1;
|
||||
str = malloc(str_size);
|
||||
if (str == NULL) // Only thing we can do with private data is dump it.
|
||||
return 1;
|
||||
|
||||
str[0] = '\0';
|
||||
size_t offset = 0;
|
||||
for (i = 2; i < ctx->cur_xds_payload_length - 1; i++)
|
||||
sprintf(str, "%02X ", ctx->cur_xds_payload[i]);
|
||||
{
|
||||
int written = snprintf(str + offset, str_size - offset, "%02X ", ctx->cur_xds_payload[i]);
|
||||
if (written > 0)
|
||||
offset += written;
|
||||
}
|
||||
|
||||
xdsprint(sub, ctx, str);
|
||||
free(str);
|
||||
|
||||
@@ -4,11 +4,11 @@
|
||||
#include "ccx_decoders_common.h"
|
||||
|
||||
#define NUM_BYTES_PER_PACKET 35 // Class + type (repeated for convenience) + data + zero
|
||||
#define NUM_XDS_BUFFERS 9 // CEA recommends no more than one level of interleaving. Play it safe
|
||||
#define NUM_XDS_BUFFERS 9 // CEA recommends no more than one level of interleaving. Play it safe
|
||||
|
||||
struct ccx_decoders_xds_context;
|
||||
void process_xds_bytes (struct ccx_decoders_xds_context *ctx, const unsigned char hi, int lo);
|
||||
void do_end_of_xds (struct cc_subtitle *sub, struct ccx_decoders_xds_context *ctx, unsigned char expected_checksum);
|
||||
void process_xds_bytes(struct ccx_decoders_xds_context *ctx, const unsigned char hi, int lo);
|
||||
void do_end_of_xds(struct cc_subtitle *sub, struct ccx_decoders_xds_context *ctx, unsigned char expected_checksum);
|
||||
|
||||
struct ccx_decoders_xds_context *ccx_decoders_xds_init_library(struct ccx_common_timing_ctx *timing, int xds_write_to_file);
|
||||
|
||||
|
||||
@@ -3,9 +3,20 @@
|
||||
#include "lib_ccx.h"
|
||||
#include "utility.h"
|
||||
#include "ffmpeg_intgr.h"
|
||||
#ifndef DISABLE_RUST
|
||||
void ccxr_demuxer_reset(struct ccx_demuxer *ctx);
|
||||
void ccxr_demuxer_close(struct ccx_demuxer *ctx);
|
||||
int ccxr_demuxer_isopen(const struct ccx_demuxer *ctx);
|
||||
int ccxr_demuxer_open(struct ccx_demuxer *ctx, const char *file);
|
||||
LLONG ccxr_demuxer_get_file_size(struct ccx_demuxer *ctx);
|
||||
void ccxr_demuxer_print_cfg(const struct ccx_demuxer *ctx);
|
||||
#endif
|
||||
|
||||
static void ccx_demuxer_reset(struct ccx_demuxer *ctx)
|
||||
{
|
||||
#ifndef DISABLE_RUST
|
||||
ccxr_demuxer_reset(ctx);
|
||||
#else
|
||||
ctx->startbytes_pos = 0;
|
||||
ctx->startbytes_avail = 0;
|
||||
ctx->num_of_PIDs = 0;
|
||||
@@ -17,10 +28,14 @@ static void ccx_demuxer_reset(struct ccx_demuxer *ctx)
|
||||
}
|
||||
memset(ctx->stream_id_of_each_pid, 0, (MAX_PSI_PID + 1) * sizeof(uint8_t));
|
||||
memset(ctx->PIDs_programs, 0, 65536 * sizeof(struct PMT_entry *));
|
||||
#endif
|
||||
}
|
||||
|
||||
static void ccx_demuxer_close(struct ccx_demuxer *ctx)
|
||||
{
|
||||
#ifndef DISABLE_RUST
|
||||
ccxr_demuxer_close(ctx);
|
||||
#else
|
||||
ctx->past = 0;
|
||||
if (ctx->infd != -1 && ccx_options.input_source == CCX_DS_FILE)
|
||||
{
|
||||
@@ -28,14 +43,23 @@ static void ccx_demuxer_close(struct ccx_demuxer *ctx)
|
||||
ctx->infd = -1;
|
||||
activity_input_file_closed();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static int ccx_demuxer_isopen(struct ccx_demuxer *ctx)
|
||||
{
|
||||
#ifndef DISABLE_RUST
|
||||
return ccxr_demuxer_isopen(ctx);
|
||||
#else
|
||||
return ctx->infd != -1;
|
||||
#endif
|
||||
}
|
||||
|
||||
static int ccx_demuxer_open(struct ccx_demuxer *ctx, const char *file)
|
||||
{
|
||||
#ifndef DISABLE_RUST
|
||||
return ccxr_demuxer_open(ctx, file);
|
||||
#else
|
||||
ctx->past = 0;
|
||||
ctx->min_global_timestamp = 0;
|
||||
ctx->global_timestamp_inited = 0;
|
||||
@@ -193,9 +217,14 @@ static int ccx_demuxer_open(struct ccx_demuxer *ctx, const char *file)
|
||||
}
|
||||
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
LLONG ccx_demuxer_get_file_size(struct ccx_demuxer *ctx)
|
||||
{
|
||||
#ifndef DISABLE_RUST
|
||||
return ccxr_demuxer_get_file_size(ctx);
|
||||
#else
|
||||
LLONG ret = 0;
|
||||
int in = ctx->infd;
|
||||
LLONG current = LSEEK(in, 0, SEEK_CUR);
|
||||
@@ -208,6 +237,7 @@ LLONG ccx_demuxer_get_file_size(struct ccx_demuxer *ctx)
|
||||
return -1;
|
||||
|
||||
return length;
|
||||
#endif
|
||||
}
|
||||
|
||||
static int ccx_demuxer_get_stream_mode(struct ccx_demuxer *ctx)
|
||||
@@ -217,6 +247,9 @@ static int ccx_demuxer_get_stream_mode(struct ccx_demuxer *ctx)
|
||||
|
||||
static void ccx_demuxer_print_cfg(struct ccx_demuxer *ctx)
|
||||
{
|
||||
#ifndef DISABLE_RUST
|
||||
ccxr_demuxer_print_cfg(ctx);
|
||||
#else
|
||||
switch (ctx->auto_stream)
|
||||
{
|
||||
case CCX_SM_ELEMENTARY_OR_NOT_FOUND:
|
||||
@@ -252,6 +285,9 @@ static void ccx_demuxer_print_cfg(struct ccx_demuxer *ctx)
|
||||
case CCX_SM_MXF:
|
||||
mprint("MXF");
|
||||
break;
|
||||
case CCX_SM_SCC:
|
||||
mprint("SCC");
|
||||
break;
|
||||
#ifdef WTV_DEBUG
|
||||
case CCX_SM_HEX_DUMP:
|
||||
mprint("Hex");
|
||||
@@ -261,6 +297,7 @@ static void ccx_demuxer_print_cfg(struct ccx_demuxer *ctx)
|
||||
fatal(CCX_COMMON_EXIT_BUG_BUG, "BUG: Unknown stream mode. Please file a bug report on Github.\n");
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void ccx_demuxer_delete(struct ccx_demuxer **ctx)
|
||||
@@ -292,7 +329,7 @@ void ccx_demuxer_delete(struct ccx_demuxer **ctx)
|
||||
struct ccx_demuxer *init_demuxer(void *parent, struct demuxer_cfg *cfg)
|
||||
{
|
||||
int i;
|
||||
struct ccx_demuxer *ctx = malloc(sizeof(struct ccx_demuxer));
|
||||
struct ccx_demuxer *ctx = calloc(1, sizeof(struct ccx_demuxer));
|
||||
if (!ctx)
|
||||
return NULL;
|
||||
|
||||
@@ -314,7 +351,6 @@ struct ccx_demuxer *init_demuxer(void *parent, struct demuxer_cfg *cfg)
|
||||
{
|
||||
ctx->pinfo[i].got_important_streams_min_pts[j] = UINT64_MAX;
|
||||
}
|
||||
ctx->pinfo[i].initialized_ocr = 0;
|
||||
ctx->pinfo[i].version = 0xFF; // Not real in a real stream since it's 5 bits. FF => Not initialized
|
||||
}
|
||||
|
||||
@@ -407,4 +443,4 @@ struct demuxer_data *alloc_demuxer_data(void)
|
||||
data->next_stream = 0;
|
||||
data->next_program = 0;
|
||||
return data;
|
||||
}
|
||||
}
|
||||
@@ -25,22 +25,21 @@ enum STREAM_TYPE
|
||||
};
|
||||
struct ccx_demux_report
|
||||
{
|
||||
unsigned program_cnt;
|
||||
unsigned dvb_sub_pid[SUB_STREAMS_CNT];
|
||||
unsigned tlt_sub_pid[SUB_STREAMS_CNT];
|
||||
unsigned mp4_cc_track_cnt;
|
||||
unsigned program_cnt;
|
||||
unsigned dvb_sub_pid[SUB_STREAMS_CNT];
|
||||
unsigned tlt_sub_pid[SUB_STREAMS_CNT];
|
||||
unsigned mp4_cc_track_cnt;
|
||||
};
|
||||
|
||||
struct program_info
|
||||
{
|
||||
int pid;
|
||||
int program_number;
|
||||
int initialized_ocr; // Avoid initializing the OCR more than once
|
||||
uint8_t analysed_PMT_once:1;
|
||||
uint8_t analysed_PMT_once : 1;
|
||||
uint8_t version;
|
||||
uint8_t saved_section[1021];
|
||||
int32_t crc;
|
||||
uint8_t valid_crc:1;
|
||||
uint8_t valid_crc : 1;
|
||||
char name[MAX_PROGRAM_NAME_LEN];
|
||||
/**
|
||||
* -1 pid represent that pcr_pid is not available
|
||||
@@ -48,6 +47,7 @@ struct program_info
|
||||
int16_t pcr_pid;
|
||||
uint64_t got_important_streams_min_pts[COUNT];
|
||||
int has_all_min_pts;
|
||||
char virtual_channel[16]; // Stores ATSC virtual channel like "2.1"
|
||||
};
|
||||
|
||||
struct cap_info
|
||||
@@ -56,9 +56,9 @@ struct cap_info
|
||||
int program_number;
|
||||
enum ccx_stream_type stream;
|
||||
enum ccx_code_type codec;
|
||||
long capbufsize;
|
||||
int64_t capbufsize;
|
||||
unsigned char *capbuf;
|
||||
long capbuflen; // Bytes read in capbuf
|
||||
int64_t capbuflen; // Bytes read in capbuf
|
||||
int saw_pesstart;
|
||||
int prev_counter;
|
||||
void *codec_private_data;
|
||||
@@ -77,7 +77,6 @@ struct cap_info
|
||||
List joining all sibling Stream in Program
|
||||
*/
|
||||
struct list_head pg_stream;
|
||||
|
||||
};
|
||||
struct ccx_demuxer
|
||||
{
|
||||
@@ -97,7 +96,6 @@ struct ccx_demuxer
|
||||
int flag_ts_forced_cappid;
|
||||
int ts_datastreamtype;
|
||||
|
||||
|
||||
struct program_info pinfo[MAX_PROGRAM];
|
||||
int nb_program;
|
||||
/* subtitle codec type */
|
||||
@@ -119,10 +117,10 @@ struct ccx_demuxer
|
||||
struct PSI_buffer *PID_buffers[MAX_PSI_PID];
|
||||
int PIDs_seen[MAX_PID];
|
||||
|
||||
/*51 possible stream ids in total, 0xbd is private stream, 0xbe is padding stream,
|
||||
0xbf private stream 2, 0xc0 - 0xdf audio, 0xe0 - 0xef video
|
||||
/*51 possible stream ids in total, 0xbd is private stream, 0xbe is padding stream,
|
||||
0xbf private stream 2, 0xc0 - 0xdf audio, 0xe0 - 0xef video
|
||||
(stream ids range from 0xbd to 0xef so 0xef - 0xbd + 1 = 51)*/
|
||||
//uint8_t found_stream_ids[MAX_NUM_OF_STREAMIDS];
|
||||
// uint8_t found_stream_ids[MAX_NUM_OF_STREAMIDS];
|
||||
|
||||
uint8_t stream_id_of_each_pid[MAX_PSI_PID + 1];
|
||||
uint64_t min_pts[MAX_PSI_PID + 1];
|
||||
@@ -141,7 +139,7 @@ struct ccx_demuxer
|
||||
unsigned last_pat_length;
|
||||
|
||||
unsigned char *filebuffer;
|
||||
LLONG filebuffer_start; // Position of buffer start relative to file
|
||||
LLONG filebuffer_start; // Position of buffer start relative to file
|
||||
unsigned int filebuffer_pos; // Position of pointer relative to buffer start
|
||||
unsigned int bytesinbuffer; // Number of bytes we actually have on buffer
|
||||
|
||||
@@ -156,7 +154,7 @@ struct ccx_demuxer
|
||||
|
||||
void *parent;
|
||||
|
||||
//Will contain actual Demuxer Context
|
||||
// Will contain actual Demuxer Context
|
||||
void *private_data;
|
||||
void (*print_cfg)(struct ccx_demuxer *ctx);
|
||||
void (*reset)(struct ccx_demuxer *ctx);
|
||||
@@ -164,7 +162,7 @@ struct ccx_demuxer
|
||||
int (*open)(struct ccx_demuxer *ctx, const char *file_name);
|
||||
int (*is_open)(struct ccx_demuxer *ctx);
|
||||
int (*get_stream_mode)(struct ccx_demuxer *ctx);
|
||||
LLONG (*get_filesize) (struct ccx_demuxer *ctx);
|
||||
LLONG (*get_filesize)(struct ccx_demuxer *ctx);
|
||||
};
|
||||
|
||||
struct demuxer_data
|
||||
@@ -182,21 +180,21 @@ struct demuxer_data
|
||||
struct demuxer_data *next_program;
|
||||
};
|
||||
|
||||
struct cap_info *get_sib_stream_by_type(struct cap_info* program, enum ccx_code_type type);
|
||||
struct cap_info *get_sib_stream_by_type(struct cap_info *program, enum ccx_code_type type);
|
||||
struct ccx_demuxer *init_demuxer(void *parent, struct demuxer_cfg *cfg);
|
||||
void ccx_demuxer_delete(struct ccx_demuxer **ctx);
|
||||
struct demuxer_data* alloc_demuxer_data(void);
|
||||
struct demuxer_data *alloc_demuxer_data(void);
|
||||
void delete_demuxer_data(struct demuxer_data *data);
|
||||
int update_capinfo(struct ccx_demuxer *ctx, int pid, enum ccx_stream_type stream, enum ccx_code_type codec, int pn, void *private_data);
|
||||
struct cap_info * get_cinfo(struct ccx_demuxer *ctx, int pid);
|
||||
struct cap_info *get_cinfo(struct ccx_demuxer *ctx, int pid);
|
||||
int need_cap_info(struct ccx_demuxer *ctx, int program_number);
|
||||
int need_cap_info_for_pid(struct ccx_demuxer *ctx, int pid);
|
||||
struct demuxer_data *get_best_data(struct demuxer_data *data);
|
||||
struct demuxer_data *get_data_stream(struct demuxer_data *data, int pid);
|
||||
int get_best_stream(struct ccx_demuxer *ctx);
|
||||
void ignore_other_stream(struct ccx_demuxer *ctx, int pid);
|
||||
void dinit_cap (struct ccx_demuxer *ctx);
|
||||
void dinit_cap(struct ccx_demuxer *ctx);
|
||||
int get_programme_number(struct ccx_demuxer *ctx, int pid);
|
||||
struct cap_info* get_best_sib_stream(struct cap_info* program);
|
||||
void ignore_other_sib_stream(struct cap_info* head, int pid);
|
||||
struct cap_info *get_best_sib_stream(struct cap_info *program);
|
||||
void ignore_other_sib_stream(struct cap_info *head, int pid);
|
||||
#endif
|
||||
|
||||
@@ -14,13 +14,6 @@
|
||||
#define IS_KLV_KEY(x, y) (!memcmp(x, y, sizeof(y)))
|
||||
#define IS_KLV_KEY_ANY_VERSION(x, y) (!memcmp(x, y, 7) && !memcmp(x + 8, y + 8, sizeof(y) - 8))
|
||||
|
||||
enum MXFCaptionType
|
||||
{
|
||||
MXF_CT_VBI,
|
||||
MXF_CT_ANC,
|
||||
};
|
||||
|
||||
typedef uint8_t UID[16];
|
||||
typedef struct KLVPacket
|
||||
{
|
||||
UID key;
|
||||
@@ -35,29 +28,12 @@ typedef struct MXFCodecUL
|
||||
|
||||
typedef int ReadFunc(struct ccx_demuxer *ctx, uint64_t size);
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int track_id;
|
||||
uint8_t track_number[4];
|
||||
} MXFTrack;
|
||||
|
||||
typedef struct MXFReadTableEntry
|
||||
{
|
||||
const UID key;
|
||||
ReadFunc *read;
|
||||
} MXFReadTableEntry;
|
||||
|
||||
typedef struct MXFContext
|
||||
{
|
||||
enum MXFCaptionType type;
|
||||
int cap_track_id;
|
||||
UID cap_essence_key;
|
||||
MXFTrack tracks[32];
|
||||
int nb_tracks;
|
||||
int cap_count;
|
||||
struct ccx_rational edit_rate;
|
||||
} MXFContext;
|
||||
|
||||
typedef struct MXFLocalTAGS
|
||||
{
|
||||
uint16_t tag;
|
||||
@@ -99,12 +75,15 @@ enum MXFLocalTag
|
||||
void update_tid_lut(struct MXFContext *ctx, uint32_t track_id, uint8_t *track_number, struct ccx_rational edit_rate)
|
||||
{
|
||||
int i;
|
||||
debug("update_tid_lut: track_id=%u (0x%x), track_number=%02X%02X%02X%02X, cap_track_id=%u\n",
|
||||
track_id, track_id, track_number[0], track_number[1], track_number[2], track_number[3], ctx->cap_track_id);
|
||||
// Update essence element key if we have track Id of caption
|
||||
if (ctx->cap_track_id == track_id)
|
||||
{
|
||||
memcpy(ctx->cap_essence_key, mxf_essence_element_key, 12);
|
||||
memcpy(ctx->cap_essence_key + 12, track_number, 4);
|
||||
ctx->edit_rate = edit_rate;
|
||||
debug("MXF: Found caption track, track_id=%u\n", track_id);
|
||||
}
|
||||
|
||||
for (i = 0; i < ctx->nb_tracks; i++)
|
||||
@@ -272,6 +251,7 @@ static int mxf_read_vanc_vbi_desc(struct ccx_demuxer *demux, uint64_t size)
|
||||
{
|
||||
case MXF_TAG_LTRACK_ID:
|
||||
ctx->cap_track_id = buffered_get_be32(demux);
|
||||
debug("MXF: VANC/VBI descriptor found, Linked Track ID = %u\n", ctx->cap_track_id);
|
||||
update_cap_essence_key(ctx, ctx->cap_track_id);
|
||||
break;
|
||||
default:
|
||||
@@ -328,6 +308,17 @@ static int mxf_read_cdp_data(struct ccx_demuxer *demux, int size, struct demuxer
|
||||
log("Incomplete CDP packet\n");
|
||||
|
||||
ret = buffered_read(demux, data->buffer + data->len, cc_count * 3);
|
||||
// Log first few bytes of cc_data for debugging
|
||||
if (cc_count > 0)
|
||||
{
|
||||
unsigned char *cc_ptr = data->buffer + data->len;
|
||||
debug("cc_data (first 6 triplets): ");
|
||||
for (int j = 0; j < (cc_count < 6 ? cc_count : 6); j++)
|
||||
{
|
||||
debug("%02X%02X%02X ", cc_ptr[j * 3], cc_ptr[j * 3 + 1], cc_ptr[j * 3 + 2]);
|
||||
}
|
||||
debug("\n");
|
||||
}
|
||||
data->len += cc_count * 3;
|
||||
demux->past += cc_count * 3;
|
||||
len += ret;
|
||||
@@ -385,7 +376,10 @@ static int mxf_read_vanc_data(struct ccx_demuxer *demux, uint64_t size, struct d
|
||||
// uint8_t count; /* Currently unused */
|
||||
|
||||
if (size < 19)
|
||||
{
|
||||
debug("VANC data too small: %" PRIu64 " < 19\n", size);
|
||||
goto error;
|
||||
}
|
||||
|
||||
ret = buffered_read(demux, vanc_header, 16);
|
||||
|
||||
@@ -394,31 +388,39 @@ static int mxf_read_vanc_data(struct ccx_demuxer *demux, uint64_t size, struct d
|
||||
return CCX_EOF;
|
||||
len += ret;
|
||||
|
||||
debug("VANC header: num_packets=%d, line=0x%02x%02x, wrap_type=0x%02x, sample_config=0x%02x\n",
|
||||
vanc_header[1], vanc_header[2], vanc_header[3], vanc_header[4], vanc_header[5]);
|
||||
|
||||
for (int i = 0; i < vanc_header[1]; i++)
|
||||
{
|
||||
DID = buffered_get_byte(demux);
|
||||
len++;
|
||||
debug("VANC packet %d: DID=0x%02x\n", i, DID);
|
||||
if (!(DID == 0x61 || DID == 0x80))
|
||||
{
|
||||
debug("DID 0x%02x not recognized as caption DID\n", DID);
|
||||
goto error;
|
||||
}
|
||||
|
||||
SDID = buffered_get_byte(demux);
|
||||
len++;
|
||||
debug("VANC packet %d: SDID=0x%02x\n", i, SDID);
|
||||
if (SDID == 0x01)
|
||||
debug("Caption Type 708\n");
|
||||
else if (SDID == 0x02)
|
||||
debug("Caption Type 608\n");
|
||||
|
||||
cdp_size = buffered_get_byte(demux);
|
||||
debug("VANC packet %d: cdp_size=%d\n", i, cdp_size);
|
||||
if (cdp_size + 19 > size)
|
||||
{
|
||||
debug("Incomplete cdp(%d) in anc data(%d)\n", cdp_size, size);
|
||||
log("Incomplete cdp(%d) in anc data(%" PRIu64 ")\n", cdp_size, size);
|
||||
goto error;
|
||||
}
|
||||
len++;
|
||||
|
||||
ret = mxf_read_cdp_data(demux, cdp_size, data);
|
||||
debug("mxf_read_cdp_data returned %d, data->len=%d\n", ret, data->len);
|
||||
len += ret;
|
||||
// len += (3 + count + 4);
|
||||
}
|
||||
@@ -435,15 +437,33 @@ static int mxf_read_essence_element(struct ccx_demuxer *demux, uint64_t size, st
|
||||
int ret;
|
||||
struct MXFContext *ctx = demux->private_data;
|
||||
|
||||
debug("mxf_read_essence_element: ctx->type=%d (ANC=%d, VBI=%d), size=%" PRIu64 "\n",
|
||||
ctx->type, MXF_CT_ANC, MXF_CT_VBI, size);
|
||||
|
||||
if (ctx->type == MXF_CT_ANC)
|
||||
{
|
||||
data->bufferdatatype = CCX_RAW_TYPE;
|
||||
ret = mxf_read_vanc_data(demux, size, data);
|
||||
data->pts = ctx->cap_count;
|
||||
debug("mxf_read_vanc_data returned %d, data->len=%d\n", ret, data->len);
|
||||
// Calculate PTS in 90kHz units from frame count and edit rate
|
||||
// edit_rate is frames per second (e.g., 25/1 for 25fps)
|
||||
// PTS = frame_count * 90000 / fps = frame_count * 90000 * edit_rate.den / edit_rate.num
|
||||
if (ctx->edit_rate.num > 0 && ctx->edit_rate.den > 0)
|
||||
{
|
||||
data->pts = (int64_t)ctx->cap_count * 90000 * ctx->edit_rate.den / ctx->edit_rate.num;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Fallback to 25fps if edit_rate not set
|
||||
data->pts = (int64_t)ctx->cap_count * 90000 / 25;
|
||||
}
|
||||
debug("Frame %d, PTS=%" PRId64 " (edit_rate=%d/%d)\n",
|
||||
ctx->cap_count, data->pts, ctx->edit_rate.num, ctx->edit_rate.den);
|
||||
ctx->cap_count++;
|
||||
}
|
||||
else
|
||||
{
|
||||
debug("Skipping essence element (not ANC type)\n");
|
||||
ret = buffered_skip(demux, size);
|
||||
demux->past += ret;
|
||||
}
|
||||
@@ -538,6 +558,7 @@ static int read_packet(struct ccx_demuxer *demux, struct demuxer_data *data)
|
||||
KLVPacket klv;
|
||||
const MXFReadTableEntry *reader;
|
||||
struct MXFContext *ctx = demux->private_data;
|
||||
static int first_essence_logged = 0;
|
||||
while ((ret = klv_read_packet(&klv, demux)) == 0)
|
||||
{
|
||||
debug("Key %02X%02X%02X%02X%02X%02X%02X%02X.%02X%02X%02X%02X%02X%02X%02X%02X size %" PRIu64 "\n",
|
||||
@@ -547,8 +568,25 @@ static int read_packet(struct ccx_demuxer *demux, struct demuxer_data *data)
|
||||
klv.key[12], klv.key[13], klv.key[14], klv.key[15],
|
||||
klv.length);
|
||||
|
||||
// Check if this is an essence element key (first 12 bytes match)
|
||||
if (IS_KLV_KEY(klv.key, mxf_essence_element_key) && !first_essence_logged)
|
||||
{
|
||||
debug("MXF: First essence element key: %02X%02X%02X%02X%02X%02X%02X%02X.%02X%02X%02X%02X%02X%02X%02X%02X\n",
|
||||
klv.key[0], klv.key[1], klv.key[2], klv.key[3],
|
||||
klv.key[4], klv.key[5], klv.key[6], klv.key[7],
|
||||
klv.key[8], klv.key[9], klv.key[10], klv.key[11],
|
||||
klv.key[12], klv.key[13], klv.key[14], klv.key[15]);
|
||||
debug("MXF: cap_essence_key: %02X%02X%02X%02X%02X%02X%02X%02X.%02X%02X%02X%02X%02X%02X%02X%02X\n",
|
||||
ctx->cap_essence_key[0], ctx->cap_essence_key[1], ctx->cap_essence_key[2], ctx->cap_essence_key[3],
|
||||
ctx->cap_essence_key[4], ctx->cap_essence_key[5], ctx->cap_essence_key[6], ctx->cap_essence_key[7],
|
||||
ctx->cap_essence_key[8], ctx->cap_essence_key[9], ctx->cap_essence_key[10], ctx->cap_essence_key[11],
|
||||
ctx->cap_essence_key[12], ctx->cap_essence_key[13], ctx->cap_essence_key[14], ctx->cap_essence_key[15]);
|
||||
first_essence_logged = 1;
|
||||
}
|
||||
|
||||
if (IS_KLV_KEY(klv.key, ctx->cap_essence_key))
|
||||
{
|
||||
debug("MXF: Found ANC essence element, size=%" PRIu64 "\n", klv.length);
|
||||
mxf_read_essence_element(demux, klv.length, data);
|
||||
if (data->len > 0)
|
||||
break;
|
||||
@@ -590,8 +628,15 @@ int ccx_mxf_getmoredata(struct lib_ccx_ctx *ctx, struct demuxer_data **ppdata)
|
||||
data->program_number = 1;
|
||||
data->stream_pid = 1;
|
||||
data->codec = CCX_CODEC_ATSC_CC;
|
||||
data->tb.num = 1001;
|
||||
data->tb.den = 30000;
|
||||
// PTS is already calculated in 90kHz units by mxf_read_essence_element
|
||||
data->tb.num = 1;
|
||||
data->tb.den = 90000;
|
||||
|
||||
// Enable CEA-708 (DTVCC) decoder for MXF files with VANC captions
|
||||
if (ctx->dec_global_setting && ctx->dec_global_setting->settings_dtvcc)
|
||||
{
|
||||
ctx->dec_global_setting->settings_dtvcc->enabled = 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -600,6 +645,11 @@ int ccx_mxf_getmoredata(struct lib_ccx_ctx *ctx, struct demuxer_data **ppdata)
|
||||
|
||||
ret = read_packet(ctx->demux_ctx, data);
|
||||
|
||||
// Ensure timebase is 90kHz since PTS is calculated in 90kHz units
|
||||
// CDP parsing may have set a frame-based timebase which would cause incorrect conversion
|
||||
data->tb.num = 1;
|
||||
data->tb.den = 90000;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,31 @@
|
||||
|
||||
#include "ccx_demuxer.h"
|
||||
|
||||
typedef uint8_t UID[16];
|
||||
|
||||
enum MXFCaptionType
|
||||
{
|
||||
MXF_CT_VBI,
|
||||
MXF_CT_ANC,
|
||||
};
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int track_id;
|
||||
uint8_t track_number[4];
|
||||
} MXFTrack;
|
||||
|
||||
typedef struct MXFContext
|
||||
{
|
||||
enum MXFCaptionType type;
|
||||
int cap_track_id;
|
||||
UID cap_essence_key;
|
||||
MXFTrack tracks[32];
|
||||
int nb_tracks;
|
||||
int cap_count;
|
||||
struct ccx_rational edit_rate;
|
||||
} MXFContext;
|
||||
|
||||
int ccx_probe_mxf(struct ccx_demuxer *ctx);
|
||||
struct MXFContext *ccx_mxf_init(struct ccx_demuxer *demux);
|
||||
#endif
|
||||
|
||||
@@ -25,7 +25,7 @@ void dtvcc_process_data(struct dtvcc_ctx *dtvcc,
|
||||
ccx_common_logging.debug_ftn(CCX_DMT_708, "[CEA-708] dtvcc_process_data: DTVCC Channel Packet Data\n");
|
||||
if (cc_valid && dtvcc->is_current_packet_header_parsed)
|
||||
{
|
||||
if (dtvcc->current_packet_length > 253)
|
||||
if (dtvcc->current_packet_length + 2 > CCX_DTVCC_MAX_PACKET_LENGTH)
|
||||
{
|
||||
ccx_common_logging.debug_ftn(CCX_DMT_708, "[CEA-708] dtvcc_process_data: "
|
||||
"Warning: Legal packet size exceeded (1), data not added.\n");
|
||||
@@ -51,7 +51,7 @@ void dtvcc_process_data(struct dtvcc_ctx *dtvcc,
|
||||
ccx_common_logging.debug_ftn(CCX_DMT_708, "[CEA-708] dtvcc_process_data: DTVCC Channel Packet Start\n");
|
||||
if (cc_valid)
|
||||
{
|
||||
if (dtvcc->current_packet_length > CCX_DTVCC_MAX_PACKET_LENGTH - 1)
|
||||
if (dtvcc->current_packet_length + 2 > CCX_DTVCC_MAX_PACKET_LENGTH)
|
||||
{
|
||||
ccx_common_logging.debug_ftn(CCX_DMT_708, "[CEA-708] dtvcc_process_data: "
|
||||
"Warning: Legal packet size exceeded (2), data not added.\n");
|
||||
@@ -115,10 +115,10 @@ dtvcc_ctx *dtvcc_init(struct ccx_decoder_dtvcc_settings *opts)
|
||||
dtvcc_service_decoder *decoder = &ctx->decoders[i];
|
||||
decoder->cc_count = 0;
|
||||
decoder->tv = (dtvcc_tv_screen *)malloc(sizeof(dtvcc_tv_screen));
|
||||
decoder->tv->service_number = i + 1;
|
||||
decoder->tv->cc_count = 0;
|
||||
if (!decoder->tv)
|
||||
ccx_common_logging.fatal_ftn(EXIT_NOT_ENOUGH_MEMORY, "dtvcc_init");
|
||||
decoder->tv->service_number = i + 1;
|
||||
decoder->tv->cc_count = 0;
|
||||
|
||||
for (int j = 0; j < CCX_DTVCC_MAX_WINDOWS; j++)
|
||||
decoder->windows[j].memory_reserved = 0;
|
||||
|
||||
@@ -10,4 +10,14 @@ void dtvcc_process_data(struct dtvcc_ctx *dtvcc,
|
||||
dtvcc_ctx *dtvcc_init(ccx_decoder_dtvcc_settings *opts);
|
||||
void dtvcc_free(dtvcc_ctx **);
|
||||
|
||||
#endif //CCEXTRACTOR_CCX_DTVCC_H
|
||||
#ifndef DISABLE_RUST
|
||||
// Rust FFI functions for persistent CEA-708 decoder
|
||||
extern void *ccxr_dtvcc_init(struct ccx_decoder_dtvcc_settings *settings_dtvcc);
|
||||
extern void ccxr_dtvcc_free(void *dtvcc_rust);
|
||||
extern void ccxr_dtvcc_process_data(void *dtvcc_rust, const unsigned char cc_valid,
|
||||
const unsigned char cc_type, const unsigned char data1, const unsigned char data2);
|
||||
extern int ccxr_dtvcc_is_active(void *dtvcc_rust);
|
||||
extern void ccxr_dtvcc_set_active(void *dtvcc_rust, int active);
|
||||
#endif
|
||||
|
||||
#endif // CCEXTRACTOR_CCX_DTVCC_H
|
||||
|
||||
@@ -30,7 +30,7 @@ ccx_encoders_transcript_format ccx_encoders_default_transcript_settings =
|
||||
.useColors = 1,
|
||||
.isFinal = 0};
|
||||
|
||||
// TODO sami header doesn't carry about CRLF/LF option
|
||||
// TODO sami header doesn't care about CRLF/LF option
|
||||
static const char *sami_header = // TODO: Revise the <!-- comments
|
||||
"<SAMI>\n\
|
||||
<HEAD>\n\
|
||||
@@ -131,7 +131,7 @@ int write_subtitle_file_footer(struct encoder_ctx *ctx, struct ccx_s_write *out)
|
||||
switch (ctx->write_format)
|
||||
{
|
||||
case CCX_OF_SAMI:
|
||||
sprintf((char *)str, "</BODY></SAMI>\n");
|
||||
snprintf((char *)str, sizeof(str), "</BODY></SAMI>\n");
|
||||
if (ctx->encoding != CCX_ENC_UNICODE)
|
||||
{
|
||||
dbg_print(CCX_DMT_DECODER_608, "\r%s\n", str);
|
||||
@@ -144,7 +144,7 @@ int write_subtitle_file_footer(struct encoder_ctx *ctx, struct ccx_s_write *out)
|
||||
}
|
||||
break;
|
||||
case CCX_OF_SMPTETT:
|
||||
sprintf((char *)str, " </div>\n </body>\n</tt>\n");
|
||||
snprintf((char *)str, sizeof(str), " </div>\n </body>\n</tt>\n");
|
||||
if (ctx->encoding != CCX_ENC_UNICODE)
|
||||
{
|
||||
dbg_print(CCX_DMT_DECODER_608, "\r%s\n", str);
|
||||
@@ -160,7 +160,7 @@ int write_subtitle_file_footer(struct encoder_ctx *ctx, struct ccx_s_write *out)
|
||||
write_spumux_footer(out);
|
||||
break;
|
||||
case CCX_OF_SIMPLE_XML:
|
||||
sprintf((char *)str, "</captions>\n");
|
||||
snprintf((char *)str, sizeof(str), "</captions>\n");
|
||||
if (ctx->encoding != CCX_ENC_UNICODE)
|
||||
{
|
||||
dbg_print(CCX_DMT_DECODER_608, "\r%s\n", str);
|
||||
@@ -176,6 +176,14 @@ int write_subtitle_file_footer(struct encoder_ctx *ctx, struct ccx_s_write *out)
|
||||
case CCX_OF_CCD:
|
||||
ret = write(out->fh, ctx->encoded_crlf, ctx->encoded_crlf_length);
|
||||
break;
|
||||
case CCX_OF_WEBVTT:
|
||||
// Ensure WebVTT header is written even if no subtitles were found (issue #1743)
|
||||
// This is required for HLS compatibility
|
||||
if (!ctx->wrote_webvtt_header)
|
||||
{
|
||||
write_webvtt_header(ctx);
|
||||
}
|
||||
break;
|
||||
default: // Nothing to do, no footer on this format
|
||||
break;
|
||||
}
|
||||
@@ -193,7 +201,7 @@ static int write_bom(struct encoder_ctx *ctx, struct ccx_s_write *out)
|
||||
ret = write(out->fh, UTF8_BOM, sizeof(UTF8_BOM));
|
||||
if (ret < sizeof(UTF8_BOM))
|
||||
{
|
||||
mprint("WARNING: Unable tp write UTF BOM\n");
|
||||
mprint("WARNING: Unable to write UTF BOM\n");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
@@ -667,8 +675,13 @@ static int init_output_ctx(struct encoder_ctx *ctx, struct encoder_cfg *cfg)
|
||||
|
||||
if (cfg->cc_to_stdout)
|
||||
{
|
||||
#ifdef WIN32
|
||||
ctx->dtvcc_writers[i].fd = -1;
|
||||
ctx->dtvcc_writers[i].fhandle = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
#else
|
||||
ctx->dtvcc_writers[i].fd = STDOUT_FILENO;
|
||||
ctx->dtvcc_writers[i].fhandle = NULL;
|
||||
#endif
|
||||
ctx->dtvcc_writers[i].charset = NULL;
|
||||
ctx->dtvcc_writers[i].filename = NULL;
|
||||
ctx->dtvcc_writers[i].cd = (iconv_t)-1;
|
||||
@@ -714,6 +727,9 @@ void dinit_encoder(struct encoder_ctx **arg, LLONG current_fts)
|
||||
write_subtitle_file_footer(ctx, ctx->out + i);
|
||||
}
|
||||
|
||||
// Clean up teletext multi-page output files (issue #665)
|
||||
dinit_teletext_outputs(ctx);
|
||||
|
||||
free_encoder_context(ctx->prev);
|
||||
dinit_output_ctx(ctx);
|
||||
freep(&ctx->subline);
|
||||
@@ -767,6 +783,7 @@ struct encoder_ctx *init_encoder(struct encoder_cfg *opt)
|
||||
return NULL;
|
||||
}
|
||||
ctx->in_fileformat = opt->in_format;
|
||||
ctx->is_pal = (opt->in_format == 2);
|
||||
|
||||
/** used in case of SUB_EOD_MARKER */
|
||||
ctx->prev_start = -1;
|
||||
@@ -832,6 +849,19 @@ struct encoder_ctx *init_encoder(struct encoder_cfg *opt)
|
||||
ctx->segment_pending = 0;
|
||||
ctx->segment_last_key_frame = 0;
|
||||
ctx->nospupngocr = opt->nospupngocr;
|
||||
ctx->scc_framerate = opt->scc_framerate;
|
||||
ctx->scc_accurate_timing = opt->scc_accurate_timing;
|
||||
ctx->scc_last_transmission_end = 0;
|
||||
ctx->scc_last_display_end = 0;
|
||||
|
||||
// Initialize teletext multi-page output arrays (issue #665)
|
||||
ctx->tlt_out_count = 0;
|
||||
for (int i = 0; i < MAX_TLT_PAGES_EXTRACT; i++)
|
||||
{
|
||||
ctx->tlt_out[i] = NULL;
|
||||
ctx->tlt_out_pages[i] = 0;
|
||||
ctx->tlt_srt_counter[i] = 0;
|
||||
}
|
||||
|
||||
ctx->prev = NULL;
|
||||
return ctx;
|
||||
@@ -867,8 +897,30 @@ int encode_sub(struct encoder_ctx *context, struct cc_subtitle *sub)
|
||||
int wrote_something = 0;
|
||||
int ret = 0;
|
||||
|
||||
/* If there is no encoder context (e.g. -out=report), we must still free
|
||||
any allocated subtitle data to avoid memory leaks. */
|
||||
if (!context)
|
||||
{
|
||||
if (sub)
|
||||
{
|
||||
/* DVB subtitles store bitmap planes inside cc_bitmap */
|
||||
if (sub->datatype == CC_DATATYPE_DVB)
|
||||
{
|
||||
struct cc_bitmap *bitmap = (struct cc_bitmap *)sub->data;
|
||||
if (bitmap)
|
||||
{
|
||||
freep(&bitmap->data0);
|
||||
freep(&bitmap->data1);
|
||||
}
|
||||
}
|
||||
|
||||
/* Free generic subtitle payload buffer */
|
||||
freep(&sub->data);
|
||||
sub->nb_data = 0;
|
||||
}
|
||||
|
||||
return CCX_OK;
|
||||
}
|
||||
|
||||
context = change_filename(context);
|
||||
|
||||
@@ -902,6 +954,11 @@ int encode_sub(struct encoder_ctx *context, struct cc_subtitle *sub)
|
||||
// After adding delay, if start/end time is lower than 0, then continue with the next subtitle
|
||||
if (data->start_time < 0 || data->end_time <= 0)
|
||||
{
|
||||
// Free XDS string if skipping to avoid memory leak
|
||||
if (data->format == SFORMAT_XDS && data->xds_str)
|
||||
{
|
||||
freep(&data->xds_str);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -1001,6 +1058,28 @@ int encode_sub(struct encoder_ctx *context, struct cc_subtitle *sub)
|
||||
freep(&sub->data);
|
||||
break;
|
||||
case CC_BITMAP:;
|
||||
// Apply subs_delay to bitmap subtitles (DVB, DVD, etc.)
|
||||
// This is the same as what's done for CC_608 above
|
||||
sub->start_time += context->subs_delay;
|
||||
sub->end_time += context->subs_delay;
|
||||
|
||||
// After adding delay, if start/end time is lower than 0, skip this subtitle
|
||||
if (sub->start_time < 0 || sub->end_time <= 0)
|
||||
{
|
||||
// Free bitmap data to avoid memory leak
|
||||
if (sub->datatype == CC_DATATYPE_DVB)
|
||||
{
|
||||
struct cc_bitmap *bitmap_tmp = (struct cc_bitmap *)sub->data;
|
||||
if (bitmap_tmp)
|
||||
{
|
||||
freep(&bitmap_tmp->data0);
|
||||
freep(&bitmap_tmp->data1);
|
||||
}
|
||||
}
|
||||
freep(&sub->data);
|
||||
sub->nb_data = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
#ifdef ENABLE_OCR
|
||||
struct cc_bitmap *rect;
|
||||
@@ -1181,7 +1260,7 @@ unsigned int get_line_encoded(struct encoder_ctx *ctx, unsigned char *buffer, in
|
||||
{
|
||||
unsigned char *orig = buffer; // Keep for debugging
|
||||
unsigned char *line = data->characters[line_num];
|
||||
for (int i = 0; i < 33; i++)
|
||||
for (int i = 0; i < 32; i++)
|
||||
{
|
||||
int bytes = 0;
|
||||
switch (ctx->encoding)
|
||||
@@ -1215,7 +1294,6 @@ unsigned int get_color_encoded(struct encoder_ctx *ctx, unsigned char *buffer, i
|
||||
else
|
||||
*buffer++ = 'E';
|
||||
}
|
||||
*buffer = 0;
|
||||
return (unsigned)(buffer - orig); // Return length
|
||||
}
|
||||
unsigned int get_font_encoded(struct encoder_ctx *ctx, unsigned char *buffer, int line_num, struct eia608_screen *data)
|
||||
@@ -1252,7 +1330,7 @@ void switch_output_file(struct lib_ccx_ctx *ctx, struct encoder_ctx *enc_ctx, in
|
||||
}
|
||||
const char *ext = get_file_extension(ctx->write_format);
|
||||
char suffix[32];
|
||||
sprintf(suffix, "_%d", track_id);
|
||||
snprintf(suffix, sizeof(suffix), "_%d", track_id);
|
||||
char *basename = get_basename(enc_ctx->out->original_filename);
|
||||
if (basename != NULL)
|
||||
{
|
||||
@@ -1267,3 +1345,168 @@ void switch_output_file(struct lib_ccx_ctx *ctx, struct encoder_ctx *enc_ctx, in
|
||||
enc_ctx->cea_708_counter = 0;
|
||||
enc_ctx->srt_counter = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get or create the output file for a specific teletext page (issue #665)
|
||||
* Creates output files on-demand with suffix _pNNN (e.g., output_p891.srt)
|
||||
* Returns NULL if we're in stdout mode or if too many pages are being extracted
|
||||
*/
|
||||
struct ccx_s_write *get_teletext_output(struct encoder_ctx *ctx, uint16_t teletext_page)
|
||||
{
|
||||
// If teletext_page is 0, use the default output
|
||||
if (teletext_page == 0 || ctx->out == NULL)
|
||||
return ctx->out;
|
||||
|
||||
// Check if we're sending to stdout - can't do multi-page in that case
|
||||
if (ctx->out[0].fh == STDOUT_FILENO)
|
||||
return ctx->out;
|
||||
|
||||
// Check if we already have an output file for this page
|
||||
for (int i = 0; i < ctx->tlt_out_count; i++)
|
||||
{
|
||||
if (ctx->tlt_out_pages[i] == teletext_page)
|
||||
return ctx->tlt_out[i];
|
||||
}
|
||||
|
||||
// If we only have one teletext page requested, use the default output
|
||||
// (no suffix needed for backward compatibility)
|
||||
extern struct ccx_s_teletext_config tlt_config;
|
||||
if (tlt_config.num_user_pages <= 1 && !tlt_config.extract_all_pages)
|
||||
return ctx->out;
|
||||
|
||||
// Need to create a new output file for this page
|
||||
if (ctx->tlt_out_count >= MAX_TLT_PAGES_EXTRACT)
|
||||
{
|
||||
mprint("Warning: Too many teletext pages to extract (max %d), using default output for page %03d\n",
|
||||
MAX_TLT_PAGES_EXTRACT, teletext_page);
|
||||
return ctx->out;
|
||||
}
|
||||
|
||||
// Allocate the new write structure
|
||||
struct ccx_s_write *new_out = (struct ccx_s_write *)malloc(sizeof(struct ccx_s_write));
|
||||
if (!new_out)
|
||||
{
|
||||
mprint("Error: Memory allocation failed for teletext output\n");
|
||||
return ctx->out;
|
||||
}
|
||||
memset(new_out, 0, sizeof(struct ccx_s_write));
|
||||
|
||||
// Create the filename with page suffix
|
||||
const char *ext = get_file_extension(ctx->write_format);
|
||||
char suffix[16];
|
||||
snprintf(suffix, sizeof(suffix), "_p%03d", teletext_page);
|
||||
|
||||
char *basefilename = NULL;
|
||||
if (ctx->out[0].filename != NULL)
|
||||
{
|
||||
basefilename = get_basename(ctx->out[0].filename);
|
||||
}
|
||||
else if (ctx->first_input_file != NULL)
|
||||
{
|
||||
basefilename = get_basename(ctx->first_input_file);
|
||||
}
|
||||
else
|
||||
{
|
||||
basefilename = strdup("untitled");
|
||||
}
|
||||
|
||||
if (basefilename == NULL)
|
||||
{
|
||||
free(new_out);
|
||||
return ctx->out;
|
||||
}
|
||||
|
||||
char *filename = create_outfilename(basefilename, suffix, ext);
|
||||
free(basefilename);
|
||||
|
||||
if (filename == NULL)
|
||||
{
|
||||
free(new_out);
|
||||
return ctx->out;
|
||||
}
|
||||
|
||||
// Open the file
|
||||
new_out->filename = filename;
|
||||
new_out->fh = open(filename, O_RDWR | O_CREAT | O_TRUNC | O_BINARY, S_IREAD | S_IWRITE);
|
||||
if (new_out->fh == -1)
|
||||
{
|
||||
mprint("Error: Failed to open output file %s: %s\n", filename, strerror(errno));
|
||||
free(filename);
|
||||
free(new_out);
|
||||
return ctx->out;
|
||||
}
|
||||
|
||||
mprint("Creating teletext output file: %s\n", filename);
|
||||
|
||||
// Store in our array
|
||||
int idx = ctx->tlt_out_count;
|
||||
ctx->tlt_out[idx] = new_out;
|
||||
ctx->tlt_out_pages[idx] = teletext_page;
|
||||
ctx->tlt_srt_counter[idx] = 0;
|
||||
ctx->tlt_out_count++;
|
||||
|
||||
// Write the subtitle file header
|
||||
write_subtitle_file_header(ctx, new_out);
|
||||
|
||||
return new_out;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the SRT counter for a specific teletext page (issue #665)
|
||||
* Returns pointer to the counter, or NULL if page not found
|
||||
*/
|
||||
unsigned int *get_teletext_srt_counter(struct encoder_ctx *ctx, uint16_t teletext_page)
|
||||
{
|
||||
// If teletext_page is 0, use the default counter
|
||||
if (teletext_page == 0)
|
||||
return &ctx->srt_counter;
|
||||
|
||||
// Check if we're using multi-page mode
|
||||
extern struct ccx_s_teletext_config tlt_config;
|
||||
if (tlt_config.num_user_pages <= 1 && !tlt_config.extract_all_pages)
|
||||
return &ctx->srt_counter;
|
||||
|
||||
// Find the counter for this page
|
||||
for (int i = 0; i < ctx->tlt_out_count; i++)
|
||||
{
|
||||
if (ctx->tlt_out_pages[i] == teletext_page)
|
||||
return &ctx->tlt_srt_counter[i];
|
||||
}
|
||||
|
||||
// Not found, use default counter
|
||||
return &ctx->srt_counter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clean up all teletext output files (issue #665)
|
||||
*/
|
||||
void dinit_teletext_outputs(struct encoder_ctx *ctx)
|
||||
{
|
||||
if (!ctx)
|
||||
return;
|
||||
|
||||
for (int i = 0; i < ctx->tlt_out_count; i++)
|
||||
{
|
||||
if (ctx->tlt_out[i] != NULL)
|
||||
{
|
||||
// Write footer
|
||||
write_subtitle_file_footer(ctx, ctx->tlt_out[i]);
|
||||
|
||||
// Close file
|
||||
if (ctx->tlt_out[i]->fh != -1)
|
||||
{
|
||||
close(ctx->tlt_out[i]->fh);
|
||||
}
|
||||
|
||||
// Free filename
|
||||
if (ctx->tlt_out[i]->filename != NULL)
|
||||
{
|
||||
free(ctx->tlt_out[i]->filename);
|
||||
}
|
||||
|
||||
free(ctx->tlt_out[i]);
|
||||
ctx->tlt_out[i] = NULL;
|
||||
}
|
||||
}
|
||||
ctx->tlt_out_count = 0;
|
||||
}
|
||||
|
||||
@@ -2,13 +2,13 @@
|
||||
#define _CC_ENCODER_COMMON_H
|
||||
|
||||
#ifdef WIN32
|
||||
#if defined(__MINGW64__) || defined(__MINGW32__)
|
||||
#include <iconv.h>
|
||||
#else
|
||||
#include "..\\thirdparty\\win_iconv\\iconv.h"
|
||||
#endif
|
||||
#if defined(__MINGW64__) || defined(__MINGW32__)
|
||||
#include <iconv.h>
|
||||
#else
|
||||
#include "iconv.h"
|
||||
#include "..\\thirdparty\\win_iconv\\iconv.h"
|
||||
#endif
|
||||
#else
|
||||
#include "iconv.h"
|
||||
#endif
|
||||
|
||||
#include "ccx_common_structs.h"
|
||||
@@ -16,14 +16,25 @@
|
||||
#include "ccx_encoders_structs.h"
|
||||
#include "ccx_common_option.h"
|
||||
|
||||
#define REQUEST_BUFFER_CAPACITY(ctx,length) if (length>ctx->capacity) \
|
||||
{ctx->capacity = length * 2; ctx->buffer = (unsigned char*)realloc(ctx->buffer, ctx->capacity); \
|
||||
if (ctx->buffer == NULL) { fatal(EXIT_NOT_ENOUGH_MEMORY, "Not enough memory for reallocating buffer, bailing out\n"); } \
|
||||
}
|
||||
// Maximum number of teletext pages to extract simultaneously (issue #665)
|
||||
#ifndef MAX_TLT_PAGES_EXTRACT
|
||||
#define MAX_TLT_PAGES_EXTRACT 8
|
||||
#endif
|
||||
|
||||
#define REQUEST_BUFFER_CAPACITY(ctx, length) \
|
||||
if (length > ctx->capacity) \
|
||||
{ \
|
||||
ctx->capacity = length * 2; \
|
||||
ctx->buffer = (unsigned char *)realloc(ctx->buffer, ctx->capacity); \
|
||||
if (ctx->buffer == NULL) \
|
||||
{ \
|
||||
fatal(EXIT_NOT_ENOUGH_MEMORY, "Not enough memory for reallocating buffer, bailing out\n"); \
|
||||
} \
|
||||
}
|
||||
|
||||
// CC page dimensions
|
||||
#define ROWS 15
|
||||
#define COLUMNS 32
|
||||
#define ROWS 15
|
||||
#define COLUMNS 32
|
||||
|
||||
typedef struct dtvcc_writer_ctx
|
||||
{
|
||||
@@ -46,11 +57,11 @@ typedef struct ccx_sbs_utf8_character
|
||||
|
||||
struct ccx_mcc_caption_time
|
||||
{
|
||||
unsigned int hour;
|
||||
unsigned int minute;
|
||||
unsigned int second;
|
||||
unsigned int millisecond;
|
||||
unsigned int frame;
|
||||
unsigned int hour;
|
||||
unsigned int minute;
|
||||
unsigned int second;
|
||||
unsigned int millisecond;
|
||||
unsigned int frame;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -84,7 +95,7 @@ struct encoder_ctx
|
||||
/* number of member in array of write out array */
|
||||
int nb_out;
|
||||
/* Input file format used in Teletext for exceptional output */
|
||||
unsigned int in_fileformat; //1 = Normal, 2 = Teletext
|
||||
unsigned int in_fileformat; // 1 = Normal, 2 = Teletext
|
||||
/* Keep output file closed when not actually writing to it and start over each time (add headers, etc) */
|
||||
unsigned int keep_output_closed;
|
||||
/* Force a flush on the file buffer whenever content is written */
|
||||
@@ -96,22 +107,22 @@ struct encoder_ctx
|
||||
|
||||
/* Flag saying BOM to be written in each output file */
|
||||
enum ccx_encoding_type encoding;
|
||||
enum ccx_output_format write_format; // 0=Raw, 1=srt, 2=SMI
|
||||
enum ccx_output_format write_format; // 0=Raw, 1=srt, 2=SMI
|
||||
int generates_file;
|
||||
struct ccx_encoders_transcript_format *transcript_settings; // Keeps the settings for generating transcript output files.
|
||||
int no_bom;
|
||||
int sentence_cap; // FIX CASE? = Fix case?
|
||||
int sentence_cap; // FIX CASE? = Fix case?
|
||||
int filter_profanity;
|
||||
|
||||
int trim_subs; // " Remove spaces at sides? "
|
||||
int autodash; // Add dashes (-) before each speaker automatically?
|
||||
int trim_subs; // " Remove spaces at sides? "
|
||||
int autodash; // Add dashes (-) before each speaker automatically?
|
||||
int no_font_color;
|
||||
int no_type_setting;
|
||||
int gui_mode_reports; // If 1, output in stderr progress updates so the GUI can grab them
|
||||
unsigned char *subline; // Temp storage for storing each line
|
||||
int gui_mode_reports; // If 1, output in stderr progress updates so the GUI can grab them
|
||||
unsigned char *subline; // Temp storage for storing each line
|
||||
int extract;
|
||||
|
||||
int dtvcc_extract; // 1 or 0 depending if we have to handle dtvcc
|
||||
int dtvcc_extract; // 1 or 0 depending if we have to handle dtvcc
|
||||
dtvcc_writer_ctx dtvcc_writers[CCX_DTVCC_MAX_SERVICES];
|
||||
|
||||
/* Timing related variables*/
|
||||
@@ -126,7 +137,7 @@ struct encoder_ctx
|
||||
int startcredits_displayed;
|
||||
char *start_credits_text;
|
||||
char *end_credits_text;
|
||||
struct ccx_boundary_time startcreditsnotbefore, startcreditsnotafter; // Where to insert start credits, if possible
|
||||
struct ccx_boundary_time startcreditsnotbefore, startcreditsnotafter; // Where to insert start credits, if possible
|
||||
struct ccx_boundary_time startcreditsforatleast, startcreditsforatmost; // How long to display them?
|
||||
struct ccx_boundary_time endcreditsforatleast, endcreditsforatmost;
|
||||
|
||||
@@ -138,9 +149,17 @@ struct encoder_ctx
|
||||
|
||||
// MCC File
|
||||
int header_printed_flag;
|
||||
struct ccx_mcc_caption_time next_caption_time;
|
||||
unsigned int cdp_hdr_seq;
|
||||
int force_dropframe;
|
||||
struct ccx_mcc_caption_time next_caption_time;
|
||||
unsigned int cdp_hdr_seq;
|
||||
int force_dropframe;
|
||||
|
||||
// SCC output framerate
|
||||
int scc_framerate; // SCC output framerate: 0=29.97 (default), 1=24, 2=25, 3=30
|
||||
|
||||
// SCC accurate timing (issue #1120)
|
||||
int scc_accurate_timing; // If 1, use bandwidth-aware timing for broadcast compliance
|
||||
LLONG scc_last_transmission_end; // When last caption transmission ends (ms)
|
||||
LLONG scc_last_display_end; // When last caption display ends (ms)
|
||||
|
||||
int new_sentence; // Capitalize next letter?
|
||||
|
||||
@@ -150,12 +169,12 @@ struct encoder_ctx
|
||||
/* split-by-sentence stuff */
|
||||
int sbs_enabled;
|
||||
|
||||
//for dvb subs
|
||||
struct encoder_ctx* prev;
|
||||
// for dvb subs
|
||||
struct encoder_ctx *prev;
|
||||
int write_previous;
|
||||
//for dvb in .mkv
|
||||
int is_mkv; //are we working with .mkv file
|
||||
char* last_string; //last recognized DVB sub
|
||||
// for dvb in .mkv
|
||||
int is_mkv; // are we working with .mkv file
|
||||
char *last_string; // last recognized DVB sub
|
||||
|
||||
// Segmenting
|
||||
int segment_pending;
|
||||
@@ -163,9 +182,15 @@ struct encoder_ctx
|
||||
|
||||
// OCR in SPUPNG
|
||||
int nospupngocr;
|
||||
int is_pal;
|
||||
|
||||
struct ccx_s_write *tlt_out[MAX_TLT_PAGES_EXTRACT]; // Output files per teletext page
|
||||
uint16_t tlt_out_pages[MAX_TLT_PAGES_EXTRACT]; // Page numbers for each output slot
|
||||
unsigned int tlt_srt_counter[MAX_TLT_PAGES_EXTRACT]; // SRT counter per page
|
||||
int tlt_out_count; // Number of teletext output files
|
||||
};
|
||||
|
||||
#define INITIAL_ENC_BUFFER_CAPACITY 2048
|
||||
#define INITIAL_ENC_BUFFER_CAPACITY 2048
|
||||
/**
|
||||
* Inialize encoder context with output context
|
||||
* allocate initial memory to buffer of context
|
||||
@@ -186,7 +211,7 @@ struct encoder_ctx *init_encoder(struct encoder_cfg *opt);
|
||||
* after deallocating user need to allocate encoder ctx again
|
||||
*
|
||||
* @oaram arg pointer to initialized encoder ctx using init_encoder
|
||||
*
|
||||
*
|
||||
* @param current_fts to calculate window for end credits
|
||||
*/
|
||||
void dinit_encoder(struct encoder_ctx **arg, LLONG current_fts);
|
||||
@@ -195,52 +220,53 @@ void dinit_encoder(struct encoder_ctx **arg, LLONG current_fts);
|
||||
* @param ctx encoder context
|
||||
* @param sub subtitle context returned by decoder
|
||||
*/
|
||||
int encode_sub(struct encoder_ctx *ctx,struct cc_subtitle *sub);
|
||||
int encode_sub(struct encoder_ctx *ctx, struct cc_subtitle *sub);
|
||||
|
||||
int write_cc_buffer_as_ccd (const struct eia608_screen *data, struct encoder_ctx *context);
|
||||
int write_cc_buffer_as_scc (const struct eia608_screen *data, struct encoder_ctx *context);
|
||||
int write_cc_buffer_as_srt (struct eia608_screen *data, struct encoder_ctx *context);
|
||||
int write_cc_buffer_as_ssa (struct eia608_screen *data, struct encoder_ctx *context);
|
||||
int write_cc_buffer_as_webvtt (struct eia608_screen *data, struct encoder_ctx *context);
|
||||
int write_cc_buffer_as_sami (struct eia608_screen *data, struct encoder_ctx *context);
|
||||
int write_cc_buffer_as_smptett (struct eia608_screen *data, struct encoder_ctx *context);
|
||||
int write_cc_buffer_as_spupng (struct eia608_screen *data, struct encoder_ctx *context);
|
||||
void write_cc_buffer_to_gui (struct eia608_screen *data, struct encoder_ctx *context);
|
||||
int write_cc_buffer_as_ccd(const struct eia608_screen *data, struct encoder_ctx *context);
|
||||
int write_cc_buffer_as_scc(const struct eia608_screen *data, struct encoder_ctx *context);
|
||||
int write_cc_buffer_as_srt(struct eia608_screen *data, struct encoder_ctx *context);
|
||||
int write_cc_buffer_as_ssa(struct eia608_screen *data, struct encoder_ctx *context);
|
||||
int write_cc_buffer_as_webvtt(struct eia608_screen *data, struct encoder_ctx *context);
|
||||
int write_cc_buffer_as_sami(struct eia608_screen *data, struct encoder_ctx *context);
|
||||
int write_cc_buffer_as_smptett(struct eia608_screen *data, struct encoder_ctx *context);
|
||||
int write_cc_buffer_as_spupng(struct eia608_screen *data, struct encoder_ctx *context);
|
||||
void write_cc_buffer_to_gui(struct eia608_screen *data, struct encoder_ctx *context);
|
||||
|
||||
int write_cc_buffer_as_g608 (struct eia608_screen *data, struct encoder_ctx *context);
|
||||
int write_cc_buffer_as_transcript2 (struct eia608_screen *data, struct encoder_ctx *context);
|
||||
int write_cc_buffer_as_g608(struct eia608_screen *data, struct encoder_ctx *context);
|
||||
int write_cc_buffer_as_transcript2(struct eia608_screen *data, struct encoder_ctx *context);
|
||||
|
||||
void write_cc_line_as_transcript2 (struct eia608_screen *data, struct encoder_ctx *context, int line_number);
|
||||
void write_cc_line_as_transcript2(struct eia608_screen *data, struct encoder_ctx *context, int line_number);
|
||||
|
||||
int write_cc_subtitle_as_srt (struct cc_subtitle *sub, struct encoder_ctx *context);
|
||||
int write_cc_subtitle_as_ssa (struct cc_subtitle *sub, struct encoder_ctx *context);
|
||||
int write_cc_subtitle_as_webvtt (struct cc_subtitle *sub, struct encoder_ctx *context);
|
||||
int write_cc_subtitle_as_sami (struct cc_subtitle *sub, struct encoder_ctx *context);
|
||||
int write_cc_subtitle_as_smptett (struct cc_subtitle *sub, struct encoder_ctx *context);
|
||||
int write_cc_subtitle_as_spupng (struct cc_subtitle *sub, struct encoder_ctx *context);
|
||||
int write_cc_subtitle_as_transcript (struct cc_subtitle *sub, struct encoder_ctx *context);
|
||||
int write_cc_subtitle_as_srt(struct cc_subtitle *sub, struct encoder_ctx *context);
|
||||
int write_cc_subtitle_as_ssa(struct cc_subtitle *sub, struct encoder_ctx *context);
|
||||
int write_cc_subtitle_as_webvtt(struct cc_subtitle *sub, struct encoder_ctx *context);
|
||||
int write_cc_subtitle_as_sami(struct cc_subtitle *sub, struct encoder_ctx *context);
|
||||
int write_cc_subtitle_as_smptett(struct cc_subtitle *sub, struct encoder_ctx *context);
|
||||
int write_cc_subtitle_as_spupng(struct cc_subtitle *sub, struct encoder_ctx *context);
|
||||
int write_cc_subtitle_as_transcript(struct cc_subtitle *sub, struct encoder_ctx *context);
|
||||
|
||||
int write_stringz_as_srt(char *string, struct encoder_ctx *context, LLONG ms_start, LLONG ms_end);
|
||||
int write_stringz_as_ssa(char *string, struct encoder_ctx *context, LLONG ms_start, LLONG ms_end);
|
||||
int write_stringz_as_webvtt(char *string, struct encoder_ctx *context, LLONG ms_start, LLONG ms_end);
|
||||
int write_stringz_as_sami(char *string, struct encoder_ctx *context, LLONG ms_start, LLONG ms_end);
|
||||
void write_stringz_as_smptett(char *string, struct encoder_ctx *context, LLONG ms_start, LLONG ms_end);
|
||||
|
||||
int write_stringz_as_srt (char *string, struct encoder_ctx *context, LLONG ms_start, LLONG ms_end);
|
||||
int write_stringz_as_ssa (char *string, struct encoder_ctx *context, LLONG ms_start, LLONG ms_end);
|
||||
int write_stringz_as_webvtt (char *string, struct encoder_ctx *context, LLONG ms_start, LLONG ms_end);
|
||||
int write_stringz_as_sami (char *string, struct encoder_ctx *context, LLONG ms_start, LLONG ms_end);
|
||||
void write_stringz_as_smptett (char *string, struct encoder_ctx *context, LLONG ms_start, LLONG ms_end);
|
||||
|
||||
|
||||
int write_cc_bitmap_as_srt (struct cc_subtitle *sub, struct encoder_ctx *context);
|
||||
int write_cc_bitmap_as_ssa (struct cc_subtitle *sub, struct encoder_ctx *context);
|
||||
int write_cc_bitmap_as_webvtt (struct cc_subtitle *sub, struct encoder_ctx *context);
|
||||
int write_cc_bitmap_as_sami (struct cc_subtitle *sub, struct encoder_ctx *context);
|
||||
int write_cc_bitmap_as_smptett (struct cc_subtitle *sub, struct encoder_ctx *context);
|
||||
int write_cc_bitmap_as_spupng (struct cc_subtitle *sub, struct encoder_ctx *context);
|
||||
int write_cc_bitmap_as_transcript (struct cc_subtitle *sub, struct encoder_ctx *context);
|
||||
int write_cc_bitmap_as_libcurl (struct cc_subtitle *sub, struct encoder_ctx *context);
|
||||
int write_cc_bitmap_as_srt(struct cc_subtitle *sub, struct encoder_ctx *context);
|
||||
int write_cc_bitmap_as_ssa(struct cc_subtitle *sub, struct encoder_ctx *context);
|
||||
int write_cc_bitmap_as_webvtt(struct cc_subtitle *sub, struct encoder_ctx *context);
|
||||
int write_cc_bitmap_as_sami(struct cc_subtitle *sub, struct encoder_ctx *context);
|
||||
int write_cc_bitmap_as_smptett(struct cc_subtitle *sub, struct encoder_ctx *context);
|
||||
int write_cc_bitmap_as_spupng(struct cc_subtitle *sub, struct encoder_ctx *context);
|
||||
int write_cc_bitmap_as_transcript(struct cc_subtitle *sub, struct encoder_ctx *context);
|
||||
int write_cc_bitmap_as_libcurl(struct cc_subtitle *sub, struct encoder_ctx *context);
|
||||
|
||||
void write_spumux_header(struct encoder_ctx *ctx, struct ccx_s_write *out);
|
||||
void write_spumux_footer(struct ccx_s_write *out);
|
||||
|
||||
struct cc_subtitle * reformat_cc_bitmap_through_sentence_buffer (struct cc_subtitle *sub, struct encoder_ctx *context);
|
||||
// WebVTT header writer (issue #1743 - ensures header is written even for empty files)
|
||||
void write_webvtt_header(struct encoder_ctx *context);
|
||||
|
||||
struct cc_subtitle *reformat_cc_bitmap_through_sentence_buffer(struct cc_subtitle *sub, struct encoder_ctx *context);
|
||||
|
||||
void set_encoder_last_displayed_subs_ms(struct encoder_ctx *ctx, LLONG last_displayed_subs_ms);
|
||||
void set_encoder_subs_delay(struct encoder_ctx *ctx, LLONG subs_delay);
|
||||
@@ -251,8 +277,7 @@ int reset_output_ctx(struct encoder_ctx *ctx, struct encoder_cfg *cfg);
|
||||
|
||||
void find_limit_characters(const unsigned char *line, int *first_non_blank, int *last_non_blank, int max_len);
|
||||
int get_str_basic(unsigned char *out_buffer, unsigned char *in_buffer, int trim_subs,
|
||||
enum ccx_encoding_type in_enc, enum ccx_encoding_type out_enc, int max_len);
|
||||
|
||||
enum ccx_encoding_type in_enc, enum ccx_encoding_type out_enc, int max_len);
|
||||
|
||||
unsigned int get_line_encoded(struct encoder_ctx *ctx, unsigned char *buffer, int line_num, struct eia608_screen *data);
|
||||
unsigned int get_color_encoded(struct encoder_ctx *ctx, unsigned char *buffer, int line_num, struct eia608_screen *data);
|
||||
@@ -260,4 +285,9 @@ unsigned int get_font_encoded(struct encoder_ctx *ctx, unsigned char *buffer, in
|
||||
|
||||
struct lib_ccx_ctx;
|
||||
void switch_output_file(struct lib_ccx_ctx *ctx, struct encoder_ctx *enc_ctx, int track_id);
|
||||
|
||||
// Teletext multi-page output (issue #665)
|
||||
struct ccx_s_write *get_teletext_output(struct encoder_ctx *ctx, uint16_t teletext_page);
|
||||
unsigned int *get_teletext_srt_counter(struct encoder_ctx *ctx, uint16_t teletext_page);
|
||||
void dinit_teletext_outputs(struct encoder_ctx *ctx);
|
||||
#endif
|
||||
|
||||
@@ -56,7 +56,7 @@ int write_cc_bitmap_as_libcurl(struct cc_subtitle *sub, struct encoder_ctx *cont
|
||||
millis_to_time(ms_start, &h1, &m1, &s1, &ms1);
|
||||
millis_to_time(ms_end - 1, &h2, &m2, &s2, &ms2); // -1 To prevent overlapping with next line.
|
||||
context->srt_counter++;
|
||||
sprintf(timeline, "group_id=ccextractordev&start_time=%" PRIu64 "&end_time=%" PRIu64 "&lang=en", ms_start, ms_end);
|
||||
snprintf(timeline, sizeof(timeline), "group_id=ccextractordev&start_time=%" PRIu64 "&end_time=%" PRIu64 "&lang=en", ms_start, ms_end);
|
||||
char *curlline = NULL;
|
||||
curlline = str_reallocncat(curlline, timeline);
|
||||
curlline = str_reallocncat(curlline, "&payload=");
|
||||
@@ -65,9 +65,13 @@ int write_cc_bitmap_as_libcurl(struct cc_subtitle *sub, struct encoder_ctx *cont
|
||||
curl_free(urlencoded);
|
||||
mprint("%s", curlline);
|
||||
|
||||
char *result = malloc(strlen(ccx_options.curlposturl) + strlen("/frame/") + 1);
|
||||
strcpy(result, ccx_options.curlposturl);
|
||||
strcat(result, "/frame/");
|
||||
size_t result_size = strlen(ccx_options.curlposturl) + strlen("/frame/") + 1;
|
||||
char *result = malloc(result_size);
|
||||
if (!result)
|
||||
{
|
||||
fatal(EXIT_NOT_ENOUGH_MEMORY, "In write_cc_bitmap_as_curl: Out of memory allocating result.");
|
||||
}
|
||||
snprintf(result, result_size, "%s/frame/", ccx_options.curlposturl);
|
||||
curl_easy_setopt(curl, CURLOPT_URL, result);
|
||||
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, curlline);
|
||||
free(result);
|
||||
|
||||
@@ -14,11 +14,11 @@ int write_cc_buffer_as_g608(struct eia608_screen *data, struct encoder_ctx *cont
|
||||
millis_to_time(data->end_time - 1, &h2, &m2, &s2, &ms2); // -1 To prevent overlapping with next line.
|
||||
char timeline[128];
|
||||
context->srt_counter++;
|
||||
sprintf(timeline, "%u%s", context->srt_counter, context->encoded_crlf);
|
||||
snprintf(timeline, sizeof(timeline), "%u%s", context->srt_counter, context->encoded_crlf);
|
||||
used = encode_line(context, context->buffer, (unsigned char *)timeline);
|
||||
write_wrapped(context->out->fh, context->buffer, used);
|
||||
sprintf(timeline, "%02u:%02u:%02u,%03u --> %02u:%02u:%02u,%03u%s",
|
||||
h1, m1, s1, ms1, h2, m2, s2, ms2, context->encoded_crlf);
|
||||
snprintf(timeline, sizeof(timeline), "%02u:%02u:%02u,%03u --> %02u:%02u:%02u,%03u%s",
|
||||
h1, m1, s1, ms1, h2, m2, s2, ms2, context->encoded_crlf);
|
||||
used = encode_line(context, context->buffer, (unsigned char *)timeline);
|
||||
|
||||
write_wrapped(context->out->fh, context->buffer, used);
|
||||
|
||||
@@ -316,7 +316,7 @@ unsigned get_decoder_line_encoded(struct encoder_ctx *ctx, unsigned char *buffer
|
||||
}
|
||||
if (color_text[its_color][1][0]) // That means a <font> was added to the buffer
|
||||
{
|
||||
strcat(tagstack, "F");
|
||||
strncat(tagstack, "F", sizeof(tagstack) - strlen(tagstack) - 1);
|
||||
changed_font++;
|
||||
}
|
||||
color = its_color;
|
||||
@@ -326,7 +326,7 @@ unsigned get_decoder_line_encoded(struct encoder_ctx *ctx, unsigned char *buffer
|
||||
if (is_underlined && underlined == 0 && !ctx->no_type_setting) // Open underline
|
||||
{
|
||||
buffer += encode_line(ctx, buffer, (unsigned char *)"<u>");
|
||||
strcat(tagstack, "U");
|
||||
strncat(tagstack, "U", sizeof(tagstack) - strlen(tagstack) - 1);
|
||||
underlined++;
|
||||
}
|
||||
if (is_underlined == 0 && underlined && !ctx->no_type_setting) // Close underline
|
||||
@@ -338,7 +338,7 @@ unsigned get_decoder_line_encoded(struct encoder_ctx *ctx, unsigned char *buffer
|
||||
if (has_ita && italics == 0 && !ctx->no_type_setting) // Open italics
|
||||
{
|
||||
buffer += encode_line(ctx, buffer, (unsigned char *)"<i>");
|
||||
strcat(tagstack, "I");
|
||||
strncat(tagstack, "I", sizeof(tagstack) - strlen(tagstack) - 1);
|
||||
italics++;
|
||||
}
|
||||
if (has_ita == 0 && italics && !ctx->no_type_setting) // Close italics
|
||||
@@ -410,10 +410,13 @@ int add_word(struct word_list *list, const char *word)
|
||||
if (list->len == list->capacity)
|
||||
{
|
||||
list->capacity += 50;
|
||||
if ((list->words = realloc(list->words, list->capacity * sizeof(char *))) == NULL)
|
||||
char **tmp = realloc(list->words, list->capacity * sizeof(char *));
|
||||
if (!tmp)
|
||||
{
|
||||
list->capacity -= 50; // Restore original capacity
|
||||
return -1;
|
||||
}
|
||||
list->words = tmp;
|
||||
}
|
||||
|
||||
size_t word_len = strlen(word);
|
||||
@@ -422,7 +425,7 @@ int add_word(struct word_list *list, const char *word)
|
||||
return -1;
|
||||
}
|
||||
|
||||
strcpy(list->words[list->len++], word);
|
||||
memcpy(list->words[list->len++], word, word_len + 1);
|
||||
return word_len;
|
||||
}
|
||||
|
||||
@@ -466,6 +469,11 @@ void shell_sort(void *base, int nb, size_t size, int (*compar)(const void *p1, c
|
||||
{
|
||||
unsigned char *lbase = (unsigned char *)base;
|
||||
unsigned char *tmp = (unsigned char *)malloc(size);
|
||||
if (!tmp)
|
||||
{
|
||||
// Cannot sort without temporary buffer, return silently
|
||||
return;
|
||||
}
|
||||
for (int gap = nb / 2; gap > 0; gap = gap / 2)
|
||||
{
|
||||
int p, j;
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include <stdarg.h>
|
||||
|
||||
#include "ccx_encoders_mcc.h"
|
||||
#include "lib_ccx.h"
|
||||
#include "utility.h"
|
||||
|
||||
#define MORE_DEBUG CCX_FALSE
|
||||
@@ -142,10 +143,16 @@ boolean mcc_encode_cc_data(struct encoder_ctx *enc_ctx, struct lib_cc_decode *de
|
||||
caption_time.second, caption_time.frame, num_chars_needed);
|
||||
#endif
|
||||
|
||||
char *compressed_data_buffer = malloc(num_chars_needed + 13);
|
||||
size_t compressed_data_size = num_chars_needed + 13;
|
||||
char *compressed_data_buffer = malloc(compressed_data_size);
|
||||
if (!compressed_data_buffer)
|
||||
{
|
||||
free(w_boilerplate_buffer);
|
||||
fatal(EXIT_NOT_ENOUGH_MEMORY, "In mcc_encode_cc_data: Out of memory allocating compressed_data_buffer.");
|
||||
}
|
||||
|
||||
sprintf(compressed_data_buffer, "%02d:%02d:%02d:%02d\t", caption_time.hour, caption_time.minute,
|
||||
caption_time.second, caption_time.frame);
|
||||
snprintf(compressed_data_buffer, compressed_data_size, "%02d:%02d:%02d:%02d\t", caption_time.hour, caption_time.minute,
|
||||
caption_time.second, caption_time.frame);
|
||||
|
||||
compress_data(w_boilerplate_buffer, w_boilerplate_buff_size, (uint8 *)&compressed_data_buffer[12]);
|
||||
free(w_boilerplate_buffer);
|
||||
@@ -155,7 +162,12 @@ boolean mcc_encode_cc_data(struct encoder_ctx *enc_ctx, struct lib_cc_decode *de
|
||||
caption_time.hour, caption_time.minute, caption_time.second, caption_time.frame);
|
||||
#endif
|
||||
|
||||
strcat(compressed_data_buffer, "\n");
|
||||
size_t current_len = strlen(compressed_data_buffer);
|
||||
if (current_len + 1 < compressed_data_size)
|
||||
{
|
||||
compressed_data_buffer[current_len] = '\n';
|
||||
compressed_data_buffer[current_len + 1] = '\0';
|
||||
}
|
||||
|
||||
write_wrapped(enc_ctx->out->fh, compressed_data_buffer, strlen(compressed_data_buffer));
|
||||
|
||||
@@ -168,58 +180,59 @@ boolean mcc_encode_cc_data(struct encoder_ctx *enc_ctx, struct lib_cc_decode *de
|
||||
static void generate_mcc_header(int fh, int fr_code, int dropframe_flag)
|
||||
{
|
||||
char uuid_str[50];
|
||||
char date_str[50];
|
||||
char time_str[30];
|
||||
char tcr_str[25];
|
||||
char date_str[64];
|
||||
char time_str[32];
|
||||
char tcr_str[32];
|
||||
time_t t = time(NULL);
|
||||
struct tm tm = *localtime(&t);
|
||||
|
||||
sprintf(uuid_str, "UUID=");
|
||||
snprintf(uuid_str, sizeof(uuid_str), "UUID=");
|
||||
uuid4(&uuid_str[5]);
|
||||
uuid_str[41] = '\n';
|
||||
uuid_str[42] = '\0';
|
||||
|
||||
ASSERT(tm.tm_wday < 7);
|
||||
ASSERT(tm.tm_mon < 12);
|
||||
sprintf(date_str, "Creation Date=%s, %s %d, %d\n", DayOfWeekStr[tm.tm_wday], MonthStr[tm.tm_mon], tm.tm_mday, tm.tm_year + 1900);
|
||||
sprintf(time_str, "Creation Time=%d:%02d:%02d\n", tm.tm_hour, tm.tm_min, tm.tm_sec);
|
||||
snprintf(date_str, sizeof(date_str), "Creation Date=%s, %s %d, %d\n", DayOfWeekStr[tm.tm_wday], MonthStr[tm.tm_mon], tm.tm_mday, tm.tm_year + 1900);
|
||||
snprintf(time_str, sizeof(time_str), "Creation Time=%d:%02d:%02d\n", tm.tm_hour, tm.tm_min, tm.tm_sec);
|
||||
|
||||
switch (fr_code)
|
||||
{
|
||||
case 1:
|
||||
case 2:
|
||||
sprintf(tcr_str, "Time Code Rate=24\n\n");
|
||||
snprintf(tcr_str, sizeof(tcr_str), "Time Code Rate=24\n\n");
|
||||
break;
|
||||
case 3:
|
||||
sprintf(tcr_str, "Time Code Rate=25\n\n");
|
||||
snprintf(tcr_str, sizeof(tcr_str), "Time Code Rate=25\n\n");
|
||||
break;
|
||||
case 4:
|
||||
case 5:
|
||||
if (dropframe_flag == CCX_TRUE)
|
||||
{
|
||||
sprintf(tcr_str, "Time Code Rate=30DF\n\n");
|
||||
snprintf(tcr_str, sizeof(tcr_str), "Time Code Rate=30DF\n\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
sprintf(tcr_str, "Time Code Rate=30\n\n");
|
||||
snprintf(tcr_str, sizeof(tcr_str), "Time Code Rate=30\n\n");
|
||||
}
|
||||
break;
|
||||
case 6:
|
||||
sprintf(tcr_str, "Time Code Rate=50\n\n");
|
||||
snprintf(tcr_str, sizeof(tcr_str), "Time Code Rate=50\n\n");
|
||||
break;
|
||||
case 7:
|
||||
case 8:
|
||||
if (dropframe_flag == CCX_TRUE)
|
||||
{
|
||||
sprintf(tcr_str, "Time Code Rate=60DF\n\n");
|
||||
snprintf(tcr_str, sizeof(tcr_str), "Time Code Rate=60DF\n\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
sprintf(tcr_str, "Time Code Rate=60\n\n");
|
||||
snprintf(tcr_str, sizeof(tcr_str), "Time Code Rate=60\n\n");
|
||||
}
|
||||
break;
|
||||
default:
|
||||
LOG("ERROR: Invalid Framerate Code: %d", fr_code);
|
||||
tcr_str[0] = '\0';
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -271,6 +284,10 @@ static uint8 *add_boilerplate(struct encoder_ctx *ctx, unsigned char *cc_data, i
|
||||
|
||||
uint8 data_size = cc_count * 3;
|
||||
uint8 *buff_ptr = malloc(data_size + 16);
|
||||
if (!buff_ptr)
|
||||
{
|
||||
fatal(EXIT_NOT_ENOUGH_MEMORY, "In add_boilerplate: Out of memory allocating buff_ptr.");
|
||||
}
|
||||
uint8 cdp_frame_rate = CDP_FRAME_RATE_FORBIDDEN;
|
||||
|
||||
switch (fr_code)
|
||||
@@ -323,15 +340,15 @@ static uint8 *add_boilerplate(struct encoder_ctx *ctx, unsigned char *cc_data, i
|
||||
buff_ptr[5] = data_size + 12;
|
||||
buff_ptr[6] = ((cdp_frame_rate << 4) | 0x0F);
|
||||
buff_ptr[7] = 0x43; // Timecode not Present; Service Info not Present; Captions Present
|
||||
buff_ptr[8] = (uint8)((ctx->cdp_hdr_seq & 0xF0) >> 8);
|
||||
buff_ptr[9] = (uint8)(ctx->cdp_hdr_seq & 0x0F);
|
||||
buff_ptr[8] = (uint8)((ctx->cdp_hdr_seq >> 8) & 0xFF);
|
||||
buff_ptr[9] = (uint8)(ctx->cdp_hdr_seq & 0xFF);
|
||||
buff_ptr[10] = CC_DATA_ID;
|
||||
buff_ptr[11] = cc_count | 0xE0;
|
||||
memcpy(&buff_ptr[12], cc_data, data_size);
|
||||
uint8 *data_ptr = &buff_ptr[data_size + 12];
|
||||
data_ptr[0] = CDP_FOOTER_ID;
|
||||
data_ptr[1] = (uint8)((ctx->cdp_hdr_seq & 0xF0) >> 8);
|
||||
data_ptr[2] = (uint8)(ctx->cdp_hdr_seq & 0x0F);
|
||||
data_ptr[1] = (uint8)((ctx->cdp_hdr_seq >> 8) & 0xFF);
|
||||
data_ptr[2] = (uint8)(ctx->cdp_hdr_seq & 0xFF);
|
||||
data_ptr[3] = 0;
|
||||
|
||||
for (int loop = 0; loop < (data_size + 15); loop++)
|
||||
@@ -639,9 +656,10 @@ static void compress_data(uint8 *data_ptr, uint16 num_elements, uint8 *out_data_
|
||||
|
||||
static void random_chars(char buffer[], int len)
|
||||
{
|
||||
static const char hex_chars[] = "0123456789ABCDEF";
|
||||
for (int i = 0; i < len; i++)
|
||||
{
|
||||
sprintf(buffer + i, "%X", rand() % 16);
|
||||
buffer[i] = hex_chars[rand() % 16];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -674,14 +692,15 @@ static void debug_log(char *file, int line, ...)
|
||||
|
||||
va_start(args, line);
|
||||
char *fmt = va_arg(args, char *);
|
||||
vsprintf(message, fmt, args);
|
||||
vsnprintf(message, sizeof(message), fmt, args);
|
||||
va_end(args);
|
||||
|
||||
char *basename = strrchr(file, '/');
|
||||
basename = basename ? basename + 1 : file;
|
||||
|
||||
if (message[(strlen(message) - 1)] == '\n')
|
||||
message[(strlen(message) - 1)] = '\0';
|
||||
size_t msg_len = strlen(message);
|
||||
if (msg_len > 0 && message[msg_len - 1] == '\n')
|
||||
message[msg_len - 1] = '\0';
|
||||
|
||||
dbg_print(CCX_DMT_VERBOSE, "[%s:%d] - %s\n", basename, line, message);
|
||||
} // debug_log()
|
||||
|
||||
@@ -9,22 +9,22 @@
|
||||
/*-- Constants --*/
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
#define ANC_DID_CLOSED_CAPTIONING 0x61
|
||||
#define ANC_SDID_CEA_708 0x01
|
||||
#define CDP_IDENTIFIER_VALUE_HIGH 0x96
|
||||
#define CDP_IDENTIFIER_VALUE_LOW 0x69
|
||||
#define CC_DATA_ID 0x72
|
||||
#define CDP_FOOTER_ID 0x74
|
||||
#define ANC_DID_CLOSED_CAPTIONING 0x61
|
||||
#define ANC_SDID_CEA_708 0x01
|
||||
#define CDP_IDENTIFIER_VALUE_HIGH 0x96
|
||||
#define CDP_IDENTIFIER_VALUE_LOW 0x69
|
||||
#define CC_DATA_ID 0x72
|
||||
#define CDP_FOOTER_ID 0x74
|
||||
|
||||
#define CDP_FRAME_RATE_FORBIDDEN 0x00
|
||||
#define CDP_FRAME_RATE_23_976 0x01
|
||||
#define CDP_FRAME_RATE_24 0x02
|
||||
#define CDP_FRAME_RATE_25 0x03
|
||||
#define CDP_FRAME_RATE_29_97 0x04
|
||||
#define CDP_FRAME_RATE_30 0x05
|
||||
#define CDP_FRAME_RATE_50 0x06
|
||||
#define CDP_FRAME_RATE_59_94 0x07
|
||||
#define CDP_FRAME_RATE_60 0x08
|
||||
#define CDP_FRAME_RATE_FORBIDDEN 0x00
|
||||
#define CDP_FRAME_RATE_23_976 0x01
|
||||
#define CDP_FRAME_RATE_24 0x02
|
||||
#define CDP_FRAME_RATE_25 0x03
|
||||
#define CDP_FRAME_RATE_29_97 0x04
|
||||
#define CDP_FRAME_RATE_30 0x05
|
||||
#define CDP_FRAME_RATE_50 0x06
|
||||
#define CDP_FRAME_RATE_59_94 0x07
|
||||
#define CDP_FRAME_RATE_60 0x08
|
||||
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/*-- Types --*/
|
||||
@@ -46,7 +46,9 @@ typedef long long int64;
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
#define LOG(...) debug_log(__FILE__, __LINE__, __VA_ARGS__)
|
||||
#define ASSERT(x) if(!(x)) debug_log(__FILE__, __LINE__, "ASSERT FAILED!")
|
||||
#define ASSERT(x) \
|
||||
if (!(x)) \
|
||||
debug_log(__FILE__, __LINE__, "ASSERT FAILED!")
|
||||
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/*-- Exposed Variables --*/
|
||||
|
||||
@@ -15,7 +15,7 @@ int write_stringz_as_sami(char *string, struct encoder_ctx *context, LLONG ms_st
|
||||
unsigned char *el = NULL;
|
||||
char str[1024];
|
||||
|
||||
sprintf(str, "<SYNC start=%llu><P class=\"UNKNOWNCC\">\r\n", (unsigned long long)ms_start);
|
||||
snprintf(str, sizeof(str), "<SYNC start=%llu><P class=\"UNKNOWNCC\">\r\n", (unsigned long long)ms_start);
|
||||
if (context->encoding != CCX_ENC_UNICODE)
|
||||
{
|
||||
dbg_print(CCX_DMT_DECODER_608, "\r%s\n", str);
|
||||
@@ -85,7 +85,7 @@ int write_stringz_as_sami(char *string, struct encoder_ctx *context, LLONG ms_st
|
||||
begin += strlen((const char *)begin) + 1;
|
||||
}
|
||||
|
||||
sprintf((char *)str, "</P></SYNC>\r\n");
|
||||
snprintf(str, sizeof(str), "</P></SYNC>\r\n");
|
||||
if (context->encoding != CCX_ENC_UNICODE)
|
||||
{
|
||||
dbg_print(CCX_DMT_DECODER_608, "\r%s\n", str);
|
||||
@@ -94,9 +94,9 @@ int write_stringz_as_sami(char *string, struct encoder_ctx *context, LLONG ms_st
|
||||
ret = write(context->out->fh, context->buffer, used);
|
||||
if (ret != used)
|
||||
goto end;
|
||||
sprintf((char *)str,
|
||||
"<SYNC start=%llu><P class=\"UNKNOWNCC\"> </P></SYNC>\r\n\r\n",
|
||||
(unsigned long long)ms_end);
|
||||
snprintf(str, sizeof(str),
|
||||
"<SYNC start=%llu><P class=\"UNKNOWNCC\"> </P></SYNC>\r\n\r\n",
|
||||
(unsigned long long)ms_end);
|
||||
if (context->encoding != CCX_ENC_UNICODE)
|
||||
{
|
||||
dbg_print(CCX_DMT_DECODER_608, "\r%s\n", str);
|
||||
@@ -127,8 +127,8 @@ int write_cc_bitmap_as_sami(struct cc_subtitle *sub, struct encoder_ctx *context
|
||||
|
||||
if (sub->data != NULL) // then we should write the sub
|
||||
{
|
||||
sprintf(buf,
|
||||
"<SYNC start=%llu><P class=\"UNKNOWNCC\">\r\n", (unsigned long long)sub->start_time);
|
||||
snprintf(buf, context->capacity,
|
||||
"<SYNC start=%llu><P class=\"UNKNOWNCC\">\r\n", (unsigned long long)sub->start_time);
|
||||
write_wrapped(context->out->fh, buf, strlen(buf));
|
||||
for (int i = sub->nb_data - 1; i >= 0; i--)
|
||||
{
|
||||
@@ -137,7 +137,7 @@ int write_cc_bitmap_as_sami(struct cc_subtitle *sub, struct encoder_ctx *context
|
||||
if (context->prev_start != -1 || !(sub->flags & SUB_EOD_MARKER))
|
||||
{
|
||||
token = strtok(rect[i].ocr_text, "\r\n");
|
||||
sprintf(buf, "%s", token);
|
||||
snprintf(buf, context->capacity, "%s", token);
|
||||
token = strtok(NULL, "\r\n");
|
||||
write_wrapped(context->out->fh, buf, strlen(buf));
|
||||
if (i != 0)
|
||||
@@ -146,13 +146,13 @@ int write_cc_bitmap_as_sami(struct cc_subtitle *sub, struct encoder_ctx *context
|
||||
}
|
||||
}
|
||||
}
|
||||
sprintf(buf, "</P></SYNC>\r\n");
|
||||
snprintf(buf, context->capacity, "</P></SYNC>\r\n");
|
||||
write_wrapped(context->out->fh, buf, strlen(buf));
|
||||
}
|
||||
else // we write an empty subtitle to clear the old one
|
||||
{
|
||||
sprintf(buf,
|
||||
"<SYNC start=%llu><P class=\"UNKNOWNCC\"> </P></SYNC>\r\n\r\n", (unsigned long long)sub->start_time);
|
||||
snprintf(buf, context->capacity,
|
||||
"<SYNC start=%llu><P class=\"UNKNOWNCC\"> </P></SYNC>\r\n\r\n", (unsigned long long)sub->start_time);
|
||||
write_wrapped(context->out->fh, buf, strlen(buf));
|
||||
}
|
||||
#endif
|
||||
@@ -194,8 +194,8 @@ int write_cc_buffer_as_sami(struct eia608_screen *data, struct encoder_ctx *cont
|
||||
int wrote_something = 0;
|
||||
char str[1024];
|
||||
|
||||
sprintf(str, "<SYNC start=%llu><P class=\"UNKNOWNCC\">\r\n",
|
||||
(unsigned long long)data->start_time);
|
||||
snprintf(str, sizeof(str), "<SYNC start=%llu><P class=\"UNKNOWNCC\">\r\n",
|
||||
(unsigned long long)data->start_time);
|
||||
if (context->encoding != CCX_ENC_UNICODE)
|
||||
{
|
||||
dbg_print(CCX_DMT_DECODER_608, "\r%s\n", str);
|
||||
@@ -219,16 +219,16 @@ int write_cc_buffer_as_sami(struct eia608_screen *data, struct encoder_ctx *cont
|
||||
write_wrapped(context->out->fh, context->encoded_crlf, context->encoded_crlf_length);
|
||||
}
|
||||
}
|
||||
sprintf((char *)str, "</P></SYNC>\r\n");
|
||||
snprintf(str, sizeof(str), "</P></SYNC>\r\n");
|
||||
if (context->encoding != CCX_ENC_UNICODE)
|
||||
{
|
||||
dbg_print(CCX_DMT_DECODER_608, "\r%s\n", str);
|
||||
}
|
||||
used = encode_line(context, context->buffer, (unsigned char *)str);
|
||||
write_wrapped(context->out->fh, context->buffer, used);
|
||||
sprintf((char *)str,
|
||||
"<SYNC start=%llu><P class=\"UNKNOWNCC\"> </P></SYNC>\r\n\r\n",
|
||||
(unsigned long long)data->end_time - 1); // - 1 to prevent overlap
|
||||
snprintf(str, sizeof(str),
|
||||
"<SYNC start=%llu><P class=\"UNKNOWNCC\"> </P></SYNC>\r\n\r\n",
|
||||
(unsigned long long)data->end_time - 1); // - 1 to prevent overlap
|
||||
if (context->encoding != CCX_ENC_UNICODE)
|
||||
{
|
||||
dbg_print(CCX_DMT_DECODER_608, "\r%s\n", str);
|
||||
|
||||
@@ -10,6 +10,171 @@ unsigned char odd_parity(const unsigned char byte)
|
||||
return byte | !(cc608_parity(byte) % 2) << 7;
|
||||
}
|
||||
|
||||
/**
|
||||
* SCC Accurate Timing Implementation (Issue #1120)
|
||||
*
|
||||
* EIA-608 bandwidth constraints:
|
||||
* - 2 bytes per frame at 29.97 FPS (or configured frame rate)
|
||||
* - Captions must be pre-loaded before display time
|
||||
* - Each control code takes 2 bytes (sent twice for reliability = 4 bytes total)
|
||||
* - Text characters take 1 byte each
|
||||
*/
|
||||
|
||||
// Get frame rate value from scc_framerate setting
|
||||
// 0=29.97 (default), 1=24, 2=25, 3=30
|
||||
static float get_scc_fps_internal(int scc_framerate)
|
||||
{
|
||||
switch (scc_framerate)
|
||||
{
|
||||
case 1:
|
||||
return 24.0f;
|
||||
case 2:
|
||||
return 25.0f;
|
||||
case 3:
|
||||
return 30.0f;
|
||||
default:
|
||||
return 29.97f;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate total bytes needed to transmit a caption
|
||||
*
|
||||
* Byte costs:
|
||||
* - Control code (RCL, EOC, ENM, EDM): 2 bytes x 2 (sent twice) = 4 bytes
|
||||
* - Preamble code: 2 bytes x 2 = 4 bytes
|
||||
* - Tab offset: 2 bytes x 2 = 4 bytes
|
||||
* - Mid-row code (color/style): 2 bytes x 2 = 4 bytes
|
||||
* - Text character: 1 byte each
|
||||
* - Padding: 1 byte if odd number of text bytes
|
||||
*/
|
||||
static unsigned int calculate_caption_bytes(const struct eia608_screen *data)
|
||||
{
|
||||
unsigned int total_bytes = 0;
|
||||
|
||||
// RCL (Resume Caption Loading): 4 bytes
|
||||
total_bytes += 4;
|
||||
|
||||
for (unsigned char row = 0; row < 15; ++row)
|
||||
{
|
||||
if (!data->row_used[row])
|
||||
continue;
|
||||
|
||||
int first, last;
|
||||
find_limit_characters(data->characters[row], &first, &last, CCX_DECODER_608_SCREEN_WIDTH);
|
||||
|
||||
if (first > last)
|
||||
continue;
|
||||
|
||||
// Assume we need at least one preamble per row: 4 bytes
|
||||
total_bytes += 4;
|
||||
|
||||
// Count characters on this row
|
||||
unsigned int char_count = 0;
|
||||
enum font_bits prev_font = FONT_REGULAR;
|
||||
enum ccx_decoder_608_color_code prev_color = COL_WHITE;
|
||||
int prev_col = -1;
|
||||
|
||||
for (int col = first; col <= last; ++col)
|
||||
{
|
||||
// Check if we need position codes
|
||||
if (prev_col != col - 1 && prev_col != -1)
|
||||
{
|
||||
// Need preamble + possible tab offset: 4-8 bytes
|
||||
total_bytes += 4;
|
||||
if (col % 4 != 0)
|
||||
total_bytes += 4; // Tab offset
|
||||
}
|
||||
|
||||
// Check if we need mid-row style codes
|
||||
if (data->fonts[row][col] != prev_font || data->colors[row][col] != prev_color)
|
||||
{
|
||||
total_bytes += 4; // Mid-row code
|
||||
prev_font = data->fonts[row][col];
|
||||
prev_color = data->colors[row][col];
|
||||
}
|
||||
|
||||
// Text character
|
||||
char_count++;
|
||||
prev_col = col;
|
||||
}
|
||||
|
||||
// Add text bytes (1 per character, rounded up to even)
|
||||
total_bytes += char_count;
|
||||
if (char_count % 2 == 1)
|
||||
total_bytes++; // Padding
|
||||
}
|
||||
|
||||
// EOC (End of Caption): 4 bytes
|
||||
total_bytes += 4;
|
||||
|
||||
// ENM (Erase Non-displayed Memory): 4 bytes
|
||||
total_bytes += 4;
|
||||
|
||||
return total_bytes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the pre-roll start time for a caption
|
||||
*
|
||||
* @param display_time When the caption should appear on screen (ms)
|
||||
* @param total_bytes Total bytes to transmit
|
||||
* @param fps Frame rate
|
||||
* @return Time to begin loading the caption (ms)
|
||||
*/
|
||||
static LLONG calculate_preroll_time(LLONG display_time, unsigned int total_bytes, float fps)
|
||||
{
|
||||
// Calculate transmission time in milliseconds
|
||||
// 2 bytes per frame, so frames_needed = (total_bytes + 1) / 2
|
||||
float ms_per_frame = 1000.0f / fps;
|
||||
unsigned int frames_needed = (total_bytes + 1) / 2;
|
||||
LLONG transmission_time_ms = (LLONG)(frames_needed * ms_per_frame);
|
||||
|
||||
// Add 1 frame for EOC to be sent before display
|
||||
LLONG one_frame_ms = (LLONG)ms_per_frame;
|
||||
|
||||
LLONG preroll_start = display_time - transmission_time_ms - one_frame_ms;
|
||||
|
||||
// Don't go negative
|
||||
if (preroll_start < 0)
|
||||
preroll_start = 0;
|
||||
|
||||
return preroll_start;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check for collision with previous caption transmission and resolve it
|
||||
*
|
||||
* @param context Encoder context with timing state
|
||||
* @param preroll_start Proposed pre-roll start time (will be modified if collision)
|
||||
* @param display_time Caption display time (may be adjusted)
|
||||
* @param fps Frame rate
|
||||
* @return true if timing was adjusted due to collision
|
||||
*/
|
||||
static bool resolve_collision(struct encoder_ctx *context, LLONG *preroll_start,
|
||||
LLONG *display_time, float fps)
|
||||
{
|
||||
// Check if our preroll would start before previous caption finishes transmitting
|
||||
// This prevents bandwidth collision but allows visual overlap (like scc_tools)
|
||||
// Visual overlap is fine - the EOC command swaps buffers atomically
|
||||
if (context->scc_last_transmission_end > 0 &&
|
||||
*preroll_start < context->scc_last_transmission_end)
|
||||
{
|
||||
// Bandwidth collision detected - shift our caption forward
|
||||
// Add 1 frame buffer to ensure no overlap
|
||||
LLONG one_frame_ms = (LLONG)(1000.0f / fps);
|
||||
LLONG new_preroll = context->scc_last_transmission_end + one_frame_ms;
|
||||
LLONG shift = new_preroll - *preroll_start;
|
||||
|
||||
*preroll_start = new_preroll;
|
||||
*display_time += shift;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
struct control_code_info
|
||||
{
|
||||
unsigned int byte1_odd;
|
||||
@@ -484,14 +649,156 @@ void write_control_code(const int fd, const unsigned char channel, const enum co
|
||||
* @param row 0-14 (inclusive)
|
||||
* @param column 0-31 (inclusive)
|
||||
*
|
||||
* //TODO: Preamble code need to take into account font as well
|
||||
*
|
||||
* Returns an indent-based preamble code (positions cursor at column with white color)
|
||||
*/
|
||||
enum control_code get_preamble_code(const unsigned char row, const unsigned char column)
|
||||
{
|
||||
return PREAMBLE_CC_START + 1 + (row * 8) + (column / 4);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get byte2 value for a styled PAC (color/font at column 0)
|
||||
* Returns 0x40-0x4F or 0x60-0x6F depending on the style
|
||||
*
|
||||
* @param color The color to use
|
||||
* @param font The font style to use
|
||||
* @param use_high_range If true, use 0x60-0x6F range instead of 0x40-0x4F
|
||||
*
|
||||
* PAC style encoding (byte2):
|
||||
* 0x40/0x60: white, regular 0x41/0x61: white, underline
|
||||
* 0x42/0x62: green, regular 0x43/0x63: green, underline
|
||||
* 0x44/0x64: blue, regular 0x45/0x65: blue, underline
|
||||
* 0x46/0x66: cyan, regular 0x47/0x67: cyan, underline
|
||||
* 0x48/0x68: red, regular 0x49/0x69: red, underline
|
||||
* 0x4a/0x6a: yellow, regular 0x4b/0x6b: yellow, underline
|
||||
* 0x4c/0x6c: magenta, regular 0x4d/0x6d: magenta, underline
|
||||
* 0x4e/0x6e: white, italics 0x4f/0x6f: white, italic underline
|
||||
*/
|
||||
static unsigned char get_styled_pac_byte2(enum ccx_decoder_608_color_code color, enum font_bits font, bool use_high_range)
|
||||
{
|
||||
unsigned char base = use_high_range ? 0x60 : 0x40;
|
||||
unsigned char style_offset;
|
||||
|
||||
// Handle italics specially - they're always white
|
||||
if (font == FONT_ITALICS)
|
||||
return base + 0x0e;
|
||||
if (font == FONT_UNDERLINED_ITALICS)
|
||||
return base + 0x0f;
|
||||
|
||||
// Map color to base offset (0, 2, 4, 6, 8, 10, 12)
|
||||
switch (color)
|
||||
{
|
||||
case COL_WHITE:
|
||||
style_offset = 0x00;
|
||||
break;
|
||||
case COL_GREEN:
|
||||
style_offset = 0x02;
|
||||
break;
|
||||
case COL_BLUE:
|
||||
style_offset = 0x04;
|
||||
break;
|
||||
case COL_CYAN:
|
||||
style_offset = 0x06;
|
||||
break;
|
||||
case COL_RED:
|
||||
style_offset = 0x08;
|
||||
break;
|
||||
case COL_YELLOW:
|
||||
style_offset = 0x0a;
|
||||
break;
|
||||
case COL_MAGENTA:
|
||||
style_offset = 0x0c;
|
||||
break;
|
||||
default:
|
||||
// For unsupported colors (black, transparent, userdefined), fall back to white
|
||||
style_offset = 0x00;
|
||||
break;
|
||||
}
|
||||
|
||||
// Add 1 for underlined
|
||||
if (font == FONT_UNDERLINED)
|
||||
style_offset += 1;
|
||||
|
||||
return base + style_offset;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the row uses high range (0x60-0x6F) or low range (0x40-0x4F) for styled PACs
|
||||
* Rows that have byte2 in 0x70-0x7F range for indents use 0x60-0x6F for styles
|
||||
*/
|
||||
static bool row_uses_high_range(unsigned char row)
|
||||
{
|
||||
// Based on the preamble code table:
|
||||
// Rows 2, 4, 6, 8, 10, 13, 15 use the "high" range (byte2 0x70-0x7F for indents)
|
||||
// which corresponds to 0x60-0x6F for styled PACs
|
||||
return (row == 1 || row == 3 || row == 5 || row == 7 || row == 9 || row == 12 || row == 14);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a styled PAC code (color/font at column 0) directly
|
||||
* This is more efficient than using indent PAC + mid-row code when at column 0
|
||||
*
|
||||
* @param fd File descriptor
|
||||
* @param channel Caption channel (1-4)
|
||||
* @param row Row number (0-14)
|
||||
* @param color Color to set
|
||||
* @param font Font style to set
|
||||
* @param disassemble If true, output assembly format
|
||||
* @param bytes_written Pointer to byte counter
|
||||
*/
|
||||
static void write_styled_preamble(const int fd, const unsigned char channel, const unsigned char row,
|
||||
enum ccx_decoder_608_color_code color, enum font_bits font,
|
||||
const bool disassemble, unsigned int *bytes_written)
|
||||
{
|
||||
// Get the preamble code for column 0 to obtain byte1
|
||||
enum control_code base_preamble = get_preamble_code(row, 0);
|
||||
unsigned char byte1 = odd_parity(get_first_byte(channel, base_preamble));
|
||||
|
||||
// Get styled byte2
|
||||
bool use_high_range = row_uses_high_range(row);
|
||||
unsigned char byte2 = odd_parity(get_styled_pac_byte2(color, font, use_high_range));
|
||||
|
||||
check_padding(fd, disassemble, bytes_written);
|
||||
|
||||
if (disassemble)
|
||||
{
|
||||
// Output assembly format like {0100Gr} for row 1, green
|
||||
const char *color_names[] = {"Wh", "Gr", "Bl", "Cy", "R", "Y", "Ma", "Wh", "Bk", "Wh"};
|
||||
const char *font_suffix = "";
|
||||
if (font == FONT_UNDERLINED)
|
||||
font_suffix = "U";
|
||||
else if (font == FONT_ITALICS)
|
||||
font_suffix = "I";
|
||||
else if (font == FONT_UNDERLINED_ITALICS)
|
||||
font_suffix = "IU";
|
||||
|
||||
fdprintf(fd, "{%02d00%s%s}", row + 1, color_names[color], font_suffix);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (*bytes_written % 2 == 0)
|
||||
write_wrapped(fd, " ", 1);
|
||||
fdprintf(fd, "%02x%02x", byte1, byte2);
|
||||
}
|
||||
*bytes_written += 2;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a styled PAC can be used (when color/font differs from white/regular and column is 0)
|
||||
*/
|
||||
static bool can_use_styled_pac(enum ccx_decoder_608_color_code color, enum font_bits font, unsigned char column)
|
||||
{
|
||||
// Styled PACs can only be used at column 0
|
||||
if (column != 0)
|
||||
return false;
|
||||
|
||||
// If style is already white/regular, no need for styled PAC
|
||||
if (color == COL_WHITE && font == FONT_REGULAR)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
enum control_code get_tab_offset_code(const unsigned char column)
|
||||
{
|
||||
int offset = column % 4;
|
||||
@@ -519,6 +826,23 @@ enum control_code get_font_code(enum font_bits font, enum ccx_decoder_608_color_
|
||||
}
|
||||
}
|
||||
|
||||
// Get frame rate value from scc_framerate setting
|
||||
// 0=29.97 (default), 1=24, 2=25, 3=30
|
||||
static float get_scc_fps(int scc_framerate)
|
||||
{
|
||||
switch (scc_framerate)
|
||||
{
|
||||
case 1:
|
||||
return 24.0f;
|
||||
case 2:
|
||||
return 25.0f;
|
||||
case 3:
|
||||
return 30.0f;
|
||||
default:
|
||||
return 29.97f;
|
||||
}
|
||||
}
|
||||
|
||||
void add_timestamp(const struct encoder_ctx *context, LLONG time, const bool disassemble)
|
||||
{
|
||||
write_wrapped(context->out->fh, context->encoded_crlf, context->encoded_crlf_length);
|
||||
@@ -528,9 +852,15 @@ void add_timestamp(const struct encoder_ctx *context, LLONG time, const bool dis
|
||||
unsigned hour, minute, second, milli;
|
||||
millis_to_time(time, &hour, &minute, &second, &milli);
|
||||
|
||||
// SMPTE format
|
||||
float frame = milli * 29.97 / 1000;
|
||||
fdprintf(context->out->fh, "%02u:%02u:%02u:%02.f\t", hour, minute, second, frame);
|
||||
// SMPTE format - use configurable frame rate (issue #1191)
|
||||
float fps = get_scc_fps(context->scc_framerate);
|
||||
// Calculate frame number from milliseconds, ensuring it stays in valid range 0 to fps-1
|
||||
// Use floor to avoid rounding up to fps (e.g., 29.97 -> 30 is invalid)
|
||||
int max_frames = (int)fps;
|
||||
int frame = (int)(milli * fps / 1000.0f);
|
||||
if (frame >= max_frames)
|
||||
frame = max_frames - 1; // Cap at max valid frame (e.g., 29 for 29.97fps)
|
||||
fdprintf(context->out->fh, "%02u:%02u:%02u:%02d\t", hour, minute, second, frame);
|
||||
}
|
||||
|
||||
void clear_screen(const struct encoder_ctx *context, LLONG end_time, const unsigned char channel, const bool disassemble)
|
||||
@@ -545,11 +875,56 @@ int write_cc_buffer_as_scenarist(const struct eia608_screen *data, struct encode
|
||||
unsigned int bytes_written = 0;
|
||||
enum font_bits current_font = FONT_REGULAR;
|
||||
enum ccx_decoder_608_color_code current_color = COL_WHITE;
|
||||
unsigned char current_row = 14;
|
||||
unsigned char current_column = 0;
|
||||
// Initialize to impossible values to ensure the first character always
|
||||
// triggers a position code write (fixes issue #1776)
|
||||
unsigned char current_row = UINT8_MAX;
|
||||
unsigned char current_column = UINT8_MAX;
|
||||
|
||||
// Timing variables for accurate timing mode (issue #1120)
|
||||
LLONG actual_start_time = data->start_time; // When caption should display
|
||||
LLONG actual_end_time = data->end_time; // When caption should clear
|
||||
LLONG preroll_start = data->start_time; // When to start loading (default: same as display)
|
||||
float fps = get_scc_fps_internal(context->scc_framerate);
|
||||
bool use_separate_display_time = false; // Whether to write EOC at separate timestamp
|
||||
|
||||
// If accurate timing is enabled, calculate pre-roll and handle collisions
|
||||
if (context->scc_accurate_timing)
|
||||
{
|
||||
// Calculate total bytes needed for this caption
|
||||
unsigned int total_bytes = calculate_caption_bytes(data);
|
||||
|
||||
// Calculate when we need to start loading
|
||||
preroll_start = calculate_preroll_time(actual_start_time, total_bytes, fps);
|
||||
|
||||
// Check for collisions with previous caption and resolve
|
||||
if (resolve_collision(context, &preroll_start, &actual_start_time, fps))
|
||||
{
|
||||
// Timing was adjusted due to collision
|
||||
// Also adjust end time by the same amount
|
||||
LLONG shift = actual_start_time - data->start_time;
|
||||
actual_end_time = data->end_time + shift;
|
||||
}
|
||||
|
||||
// Update timing state for next caption
|
||||
float ms_per_frame = 1000.0f / fps;
|
||||
unsigned int frames_needed = (total_bytes + 1) / 2;
|
||||
LLONG transmission_time_ms = (LLONG)(frames_needed * ms_per_frame);
|
||||
context->scc_last_transmission_end = preroll_start + transmission_time_ms;
|
||||
context->scc_last_display_end = actual_end_time;
|
||||
|
||||
// Enable separate display timing (like scc_tools)
|
||||
use_separate_display_time = true;
|
||||
|
||||
// 1. Load the caption at pre-roll time
|
||||
add_timestamp(context, preroll_start, disassemble);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Legacy mode: use original timing
|
||||
// 1. Load the caption
|
||||
add_timestamp(context, data->start_time, disassemble);
|
||||
}
|
||||
|
||||
// 1. Load the caption
|
||||
add_timestamp(context, data->start_time, disassemble);
|
||||
write_control_code(context->out->fh, data->channel, RCL, disassemble, &bytes_written);
|
||||
for (uint8_t row = 0; row < 15; ++row)
|
||||
{
|
||||
@@ -576,6 +951,23 @@ int write_cc_buffer_as_scenarist(const struct eia608_screen *data, struct encode
|
||||
{
|
||||
if (switch_font || switch_color)
|
||||
{
|
||||
// Optimization (issue #1191): Use styled PAC when at column 0 with non-default style
|
||||
// This avoids needing a separate mid-row code
|
||||
if (column == 0 && can_use_styled_pac(data->colors[row][column], data->fonts[row][column], 0))
|
||||
{
|
||||
write_styled_preamble(context->out->fh, data->channel, row,
|
||||
data->colors[row][column], data->fonts[row][column],
|
||||
disassemble, &bytes_written);
|
||||
current_row = row;
|
||||
current_column = 0;
|
||||
current_font = data->fonts[row][column];
|
||||
current_color = data->colors[row][column];
|
||||
// Write the character and continue
|
||||
write_character(context->out->fh, data->characters[row][column], disassemble, &bytes_written);
|
||||
++current_column;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (data->characters[row][column] == ' ')
|
||||
{
|
||||
// The MID-ROW code is going to move the cursor to the
|
||||
@@ -615,12 +1007,26 @@ int write_cc_buffer_as_scenarist(const struct eia608_screen *data, struct encode
|
||||
check_padding(context->out->fh, disassemble, &bytes_written);
|
||||
}
|
||||
|
||||
// 2. Show the caption
|
||||
// 2. Show the caption (EOC = End of Caption, makes it visible)
|
||||
if (use_separate_display_time)
|
||||
{
|
||||
// For accurate timing: write display command at actual display time
|
||||
// This matches scc_tools behavior where load and display are separate
|
||||
add_timestamp(context, actual_start_time, disassemble);
|
||||
}
|
||||
write_control_code(context->out->fh, data->channel, EOC, disassemble, &bytes_written);
|
||||
write_control_code(context->out->fh, data->channel, ENM, disassemble, &bytes_written);
|
||||
|
||||
// 3. Clear the caption
|
||||
clear_screen(context, data->end_time, data->channel, disassemble);
|
||||
// 3. Clear the caption at the end time
|
||||
// In accurate timing mode, skip clear - the next caption's EOC will handle the transition
|
||||
// This matches scc_tools behavior which doesn't write EDM between consecutive captions
|
||||
if (!use_separate_display_time)
|
||||
{
|
||||
// Legacy mode: always write clear
|
||||
clear_screen(context, actual_end_time, data->channel, disassemble);
|
||||
}
|
||||
// In accurate timing mode, scc_last_display_end is still tracked for reference
|
||||
// but we don't write the clear command to avoid out-of-order timestamps
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -36,18 +36,22 @@ void write_stringz_as_smptett(char *string, struct encoder_ctx *context, LLONG m
|
||||
unsigned h2, m2, s2, ms2;
|
||||
int len = strlen(string);
|
||||
unsigned char *unescaped = (unsigned char *)malloc(len + 1);
|
||||
if (!unescaped)
|
||||
fatal(EXIT_NOT_ENOUGH_MEMORY, "In write_stringz_as_smptett() - not enough memory for unescaped buffer.\n");
|
||||
unsigned char *el = (unsigned char *)malloc(len * 3 + 1); // Be generous
|
||||
if (!el)
|
||||
{
|
||||
free(unescaped);
|
||||
fatal(EXIT_NOT_ENOUGH_MEMORY, "In write_stringz_as_smptett() - not enough memory for el buffer.\n");
|
||||
}
|
||||
int pos_r = 0;
|
||||
int pos_w = 0;
|
||||
char str[1024];
|
||||
|
||||
if (el == NULL || unescaped == NULL)
|
||||
fatal(EXIT_NOT_ENOUGH_MEMORY, "In write_stringz_as_smptett() - not enough memory.\n");
|
||||
|
||||
millis_to_time(ms_start, &h1, &m1, &s1, &ms1);
|
||||
millis_to_time(ms_end - 1, &h2, &m2, &s2, &ms2);
|
||||
|
||||
sprintf((char *)str, "<p begin=\"%02u:%02u:%02u.%03u\" end=\"%02u:%02u:%02u.%03u\">\r\n", h1, m1, s1, ms1, h2, m2, s2, ms2);
|
||||
snprintf(str, sizeof(str), "<p begin=\"%02u:%02u:%02u.%03u\" end=\"%02u:%02u:%02u.%03u\">\r\n", h1, m1, s1, ms1, h2, m2, s2, ms2);
|
||||
if (context->encoding != CCX_ENC_UNICODE)
|
||||
{
|
||||
dbg_print(CCX_DMT_DECODER_608, "\r%s\n", str);
|
||||
@@ -87,7 +91,7 @@ void write_stringz_as_smptett(char *string, struct encoder_ctx *context, LLONG m
|
||||
begin += strlen((const char *)begin) + 1;
|
||||
}
|
||||
|
||||
sprintf((char *)str, "</p>\n");
|
||||
snprintf(str, sizeof(str), "</p>\n");
|
||||
if (context->encoding != CCX_ENC_UNICODE)
|
||||
{
|
||||
dbg_print(CCX_DMT_DECODER_608, "\r%s\n", str);
|
||||
@@ -126,13 +130,15 @@ int write_cc_bitmap_as_smptett(struct cc_subtitle *sub, struct encoder_ctx *cont
|
||||
unsigned h2, m2, s2, ms2;
|
||||
millis_to_time(sub->start_time, &h1, &m1, &s1, &ms1);
|
||||
millis_to_time(sub->end_time - 1, &h2, &m2, &s2, &ms2); // -1 To prevent overlapping with next line.
|
||||
sprintf((char *)context->buffer, "<p begin=\"%02u:%02u:%02u.%03u\" end=\"%02u:%02u:%02u.%03u\">\n", h1, m1, s1, ms1, h2, m2, s2, ms2);
|
||||
write_wrapped(context->out->fh, buf, strlen(buf));
|
||||
int written = snprintf(buf, INITIAL_ENC_BUFFER_CAPACITY, "<p begin=\"%02u:%02u:%02u.%03u\" end=\"%02u:%02u:%02u.%03u\">\n", h1, m1, s1, ms1, h2, m2, s2, ms2);
|
||||
if (written > 0 && (size_t)written < INITIAL_ENC_BUFFER_CAPACITY)
|
||||
write_wrapped(context->out->fh, buf, written);
|
||||
len = strlen(rect[i].ocr_text);
|
||||
write_wrapped(context->out->fh, rect[i].ocr_text, len);
|
||||
write_wrapped(context->out->fh, context->encoded_crlf, context->encoded_crlf_length);
|
||||
sprintf(buf, "</p>\n");
|
||||
write_wrapped(context->out->fh, buf, strlen(buf));
|
||||
written = snprintf(buf, INITIAL_ENC_BUFFER_CAPACITY, "</p>\n");
|
||||
if (written > 0 && (size_t)written < INITIAL_ENC_BUFFER_CAPACITY)
|
||||
write_wrapped(context->out->fh, buf, written);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -218,7 +224,7 @@ int write_cc_buffer_as_smptett(struct eia608_screen *data, struct encoder_ctx *c
|
||||
{
|
||||
wrote_something = 1;
|
||||
|
||||
sprintf(str, " <p begin=\"%02u:%02u:%02u.%03u\" end=\"%02u:%02u:%02u.%03u\" tts:origin=\"%1.3f%% %1.3f%%\">\n <span>", h1, m1, s1, ms1, h2, m2, s2, ms2, col1, row1);
|
||||
snprintf(str, sizeof(str), " <p begin=\"%02u:%02u:%02u.%03u\" end=\"%02u:%02u:%02u.%03u\" tts:origin=\"%1.3f%% %1.3f%%\">\n <span>", h1, m1, s1, ms1, h2, m2, s2, ms2, col1, row1);
|
||||
if (context->encoding != CCX_ENC_UNICODE)
|
||||
{
|
||||
dbg_print(CCX_DMT_DECODER_608, "\r%s\n", str);
|
||||
@@ -236,8 +242,16 @@ int write_cc_buffer_as_smptett(struct eia608_screen *data, struct encoder_ctx *c
|
||||
|
||||
get_decoder_line_encoded(context, context->subline, row, data);
|
||||
|
||||
char *final = malloc(strlen((const char *)(context->subline)) + 1000); // Being overly generous? :P
|
||||
char *temp = malloc(strlen((const char *)(context->subline)) + 1000);
|
||||
size_t subline_len = strlen((const char *)(context->subline));
|
||||
size_t buf_size = subline_len + 1000; // Being overly generous? :P
|
||||
char *final = malloc(buf_size);
|
||||
char *temp = malloc(buf_size);
|
||||
if (!final || !temp)
|
||||
{
|
||||
freep(&final);
|
||||
freep(&temp);
|
||||
fatal(EXIT_NOT_ENOUGH_MEMORY, "In write_cc_buffer_as_smptett() - not enough memory.\n");
|
||||
}
|
||||
*final = 0;
|
||||
*temp = 0;
|
||||
/*
|
||||
@@ -297,37 +311,56 @@ int write_cc_buffer_as_smptett(struct eia608_screen *data, struct encoder_ctx *c
|
||||
if (end == NULL)
|
||||
{
|
||||
// Incorrect styling, writing as it is
|
||||
strcpy(final, (const char *)(context->subline));
|
||||
snprintf(final, buf_size, "%s", (const char *)(context->subline));
|
||||
}
|
||||
else
|
||||
{
|
||||
size_t final_len = 0;
|
||||
int start_index = start - (char *)(context->subline);
|
||||
int end_index = end - (char *)(context->subline);
|
||||
|
||||
strncat(final, (const char *)(context->subline), start_index); // copying content before opening tag e.g. <i>
|
||||
// copying content before opening tag e.g. <i>
|
||||
if (start_index > 0 && (size_t)start_index < buf_size - 1)
|
||||
{
|
||||
memcpy(final, (const char *)(context->subline), start_index);
|
||||
final[start_index] = '\0';
|
||||
final_len = start_index;
|
||||
}
|
||||
|
||||
strcat(final, "<span>"); // adding <span> : replacement of <i>
|
||||
// adding <span> : replacement of <i>
|
||||
size_t remaining = buf_size - final_len;
|
||||
int written = snprintf(final + final_len, remaining, "<span>");
|
||||
if (written > 0 && (size_t)written < remaining)
|
||||
final_len += written;
|
||||
|
||||
// The content in italics is between <i> and </i>, i.e. between (start_index + 3) and end_index.
|
||||
int content_len = end_index - start_index - 3;
|
||||
if (content_len > 0)
|
||||
{
|
||||
remaining = buf_size - final_len;
|
||||
if ((size_t)content_len < remaining - 1)
|
||||
{
|
||||
memcpy(final + final_len, (const char *)(context->subline) + start_index + 3, content_len);
|
||||
final_len += content_len;
|
||||
final[final_len] = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
strncat(temp, (const char *)(context->subline) + start_index + 3, end_index - start_index - 3); // the content in italics
|
||||
|
||||
strcat(final, temp); // attaching to final sentence.
|
||||
|
||||
// adding appropriate style tag
|
||||
remaining = buf_size - final_len;
|
||||
if (style == 1)
|
||||
strcpy(temp, "<style tts:backgroundColor=\"#000000FF\" tts:fontSize=\"18px\" tts:fontStyle=\"italic\"/> </span>");
|
||||
|
||||
written = snprintf(final + final_len, remaining, "<style tts:backgroundColor=\"#000000FF\" tts:fontSize=\"18px\" tts:fontStyle=\"italic\"/> </span>");
|
||||
else if (style == 2)
|
||||
strcpy(temp, "<style tts:backgroundColor=\"#000000FF\" tts:fontSize=\"18px\" tts:fontWeight=\"bold\"/> </span>");
|
||||
|
||||
written = snprintf(final + final_len, remaining, "<style tts:backgroundColor=\"#000000FF\" tts:fontSize=\"18px\" tts:fontWeight=\"bold\"/> </span>");
|
||||
else
|
||||
strcpy(temp, "<style tts:backgroundColor=\"#000000FF\" tts:fontSize=\"18px\" tts:textDecoration=\"underline\"/> </span>");
|
||||
written = snprintf(final + final_len, remaining, "<style tts:backgroundColor=\"#000000FF\" tts:fontSize=\"18px\" tts:textDecoration=\"underline\"/> </span>");
|
||||
|
||||
strcat(final, temp); // adding appropriate style tag.
|
||||
if (written > 0 && (size_t)written < remaining)
|
||||
final_len += written;
|
||||
|
||||
sprintf(temp, "%s", (const char *)(context->subline) + end_index + 4); // finding remaining sentence.
|
||||
|
||||
strcat(final, temp); // adding remaining sentence.
|
||||
// finding remaining sentence and adding it
|
||||
remaining = buf_size - final_len;
|
||||
snprintf(final + final_len, remaining, "%s", (const char *)(context->subline) + end_index + 4);
|
||||
}
|
||||
}
|
||||
else // No style or Font Color
|
||||
@@ -340,44 +373,75 @@ int write_cc_buffer_as_smptett(struct eia608_screen *data, struct encoder_ctx *c
|
||||
if (end == NULL)
|
||||
{
|
||||
// Incorrect styling, writing as it is
|
||||
strcpy(final, (const char *)(context->subline));
|
||||
snprintf(final, buf_size, "%s", (const char *)(context->subline));
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
size_t final_len = 0;
|
||||
int start_index = start - (char *)(context->subline);
|
||||
int end_index = end - (char *)(context->subline);
|
||||
|
||||
strncat(final, (const char *)(context->subline), start_index); // copying content before opening tag e.g. <font ..>
|
||||
// copying content before opening tag e.g. <font ..>
|
||||
if (start_index > 0 && (size_t)start_index < buf_size - 1)
|
||||
{
|
||||
memcpy(final, (const char *)(context->subline), start_index);
|
||||
final[start_index] = '\0';
|
||||
final_len = start_index;
|
||||
}
|
||||
|
||||
strcat(final, "<span>"); // adding <span> : replacement of <font ..>
|
||||
// adding <span> : replacement of <font ..>
|
||||
size_t remaining = buf_size - final_len;
|
||||
int written = snprintf(final + final_len, remaining, "<span>");
|
||||
if (written > 0 && (size_t)written < remaining)
|
||||
final_len += written;
|
||||
|
||||
char *temp_pointer = strchr((const char *)(context->subline), '#'); // locating color code
|
||||
|
||||
char color_code[7];
|
||||
strncpy(color_code, temp_pointer + 1, 6); // obtained color code
|
||||
color_code[6] = '\0';
|
||||
char color_code[8];
|
||||
if (temp_pointer)
|
||||
{
|
||||
snprintf(color_code, sizeof(color_code), "%.6s", temp_pointer + 1); // obtained color code
|
||||
}
|
||||
else
|
||||
{
|
||||
color_code[0] = '\0';
|
||||
}
|
||||
|
||||
temp_pointer = strchr((const char *)(context->subline), '>'); // The content is in between <font ..> and </font>
|
||||
|
||||
strncat(temp, temp_pointer + 1, end_index - (temp_pointer - (char *)(context->subline) + 1));
|
||||
if (temp_pointer)
|
||||
{
|
||||
// Copy the content between <font ..> and </font>
|
||||
int content_len = end_index - (temp_pointer - (char *)(context->subline) + 1);
|
||||
if (content_len > 0)
|
||||
{
|
||||
remaining = buf_size - final_len;
|
||||
if ((size_t)content_len < remaining - 1)
|
||||
{
|
||||
memcpy(final + final_len, temp_pointer + 1, content_len);
|
||||
final_len += content_len;
|
||||
final[final_len] = '\0';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
strcat(final, temp); // attaching to final sentence.
|
||||
// adding font color tag
|
||||
remaining = buf_size - final_len;
|
||||
written = snprintf(final + final_len, remaining, "<style tts:backgroundColor=\"#FFFF00FF\" tts:color=\"%s\" tts:fontSize=\"18px\"/></span>", color_code);
|
||||
if (written > 0 && (size_t)written < remaining)
|
||||
final_len += written;
|
||||
|
||||
sprintf(temp, "<style tts:backgroundColor=\"#FFFF00FF\" tts:color=\"%s\" tts:fontSize=\"18px\"/></span>", color_code);
|
||||
|
||||
strcat(final, temp); // adding font color tag
|
||||
|
||||
sprintf(temp, "%s", (const char *)(context->subline) + end_index + 7); // finding remaining sentence.
|
||||
|
||||
strcat(final, temp); // adding remaining sentence
|
||||
// finding remaining sentence and adding it
|
||||
remaining = buf_size - final_len;
|
||||
snprintf(final + final_len, remaining, "%s", (const char *)(context->subline) + end_index + 7);
|
||||
}
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
// NO styling, writing as it is
|
||||
strcpy(final, (const char *)(context->subline));
|
||||
snprintf(final, buf_size, "%s", (const char *)(context->subline));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -386,7 +450,7 @@ int write_cc_buffer_as_smptett(struct eia608_screen *data, struct encoder_ctx *c
|
||||
write_wrapped(context->out->fh, context->encoded_crlf, context->encoded_crlf_length);
|
||||
context->trim_subs = old_trim_subs;
|
||||
|
||||
sprintf(str, " <style tts:backgroundColor=\"#000000FF\" tts:fontSize=\"18px\"/></span>\n </p>\n");
|
||||
snprintf(str, sizeof(str), " <style tts:backgroundColor=\"#000000FF\" tts:fontSize=\"18px\"/></span>\n </p>\n");
|
||||
if (context->encoding != CCX_ENC_UNICODE)
|
||||
{
|
||||
dbg_print(CCX_DMT_DECODER_608, "\r%s\n", str);
|
||||
|
||||
@@ -80,10 +80,20 @@ ____sbs_context: [%p]\n\
|
||||
LOG_DEBUG("SBS: init_sbs_context: INIT\n");
|
||||
|
||||
____sbs_context = malloc(sizeof(sbs_context_t));
|
||||
if (!____sbs_context)
|
||||
{
|
||||
fatal(EXIT_NOT_ENOUGH_MEMORY, "In init_sbs_context: Out of memory allocating sbs_context_t.");
|
||||
}
|
||||
____sbs_context->time_from = -1;
|
||||
____sbs_context->time_trim = -1;
|
||||
____sbs_context->capacity = 16;
|
||||
____sbs_context->buffer = malloc(____sbs_context->capacity * sizeof(unsigned char));
|
||||
if (!____sbs_context->buffer)
|
||||
{
|
||||
free(____sbs_context);
|
||||
____sbs_context = NULL;
|
||||
fatal(EXIT_NOT_ENOUGH_MEMORY, "In init_sbs_context: Out of memory allocating buffer.");
|
||||
}
|
||||
____sbs_context->buffer[0] = 0;
|
||||
____sbs_context->handled_len = 0;
|
||||
}
|
||||
@@ -222,7 +232,7 @@ char *sbs_find_insert_point_partial(char *old_tail, const char *new_start, size_
|
||||
{
|
||||
/*
|
||||
#ifdef DEBUG_SBS
|
||||
sprintf(fmtbuf, "SBS: sbs_find_insert_point_partial: compare\n\
|
||||
snprintf(fmtbuf, sizeof(fmtbuf), "SBS: sbs_find_insert_point_partial: compare\n\
|
||||
\tnot EQ: [TRUE]\n\
|
||||
\tmaxerr: [%%d]\n\
|
||||
\tL buffer: [%%.%zus]\n\
|
||||
@@ -291,7 +301,7 @@ char *sbs_find_insert_point_partial(char *old_tail, const char *new_start, size_
|
||||
|
||||
/*
|
||||
#ifdef DEBUG_SBS
|
||||
sprintf(fmtbuf, "SBS: sbs_find_insert_point_partial: REPLACE ENTIRE TAIL !!\n\
|
||||
snprintf(fmtbuf, sizeof(fmtbuf), "SBS: sbs_find_insert_point_partial: REPLACE ENTIRE TAIL !!\n\
|
||||
\tmaxerr: [%%d]\n\
|
||||
\tL buffer: [%%.%zus]\n\
|
||||
\tL string: [%%.%zus]\n\
|
||||
@@ -438,7 +448,9 @@ void sbs_strcpy_without_dup(const unsigned char *str, sbs_context_t *context)
|
||||
skip_ws++;
|
||||
}
|
||||
|
||||
strcpy(context->buffer, context->buffer + skip_ws);
|
||||
// Use memmove for overlapping memory regions (strcpy is undefined for overlapping buffers)
|
||||
size_t remaining_len = strlen((char *)(context->buffer + skip_ws)) + 1;
|
||||
memmove(context->buffer, context->buffer + skip_ws, remaining_len);
|
||||
context->handled_len = 0;
|
||||
}
|
||||
|
||||
@@ -451,10 +463,12 @@ void sbs_strcpy_without_dup(const unsigned char *str, sbs_context_t *context)
|
||||
&& !isspace(context->buffer[sbs_len - 1]) // not a space char at the end of existing buf
|
||||
)
|
||||
{
|
||||
strcat(context->buffer, " ");
|
||||
// Capacity is guaranteed by sbs_append_string before calling this function
|
||||
strncat((char *)context->buffer, " ", context->capacity - sbs_len - 1);
|
||||
sbs_len++;
|
||||
}
|
||||
|
||||
strcat(context->buffer, str);
|
||||
strncat((char *)context->buffer, (char *)str, context->capacity - sbs_len - 1);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -539,13 +553,16 @@ struct cc_subtitle *sbs_append_string(unsigned char *str, const LLONG time_from,
|
||||
: new_capacity;
|
||||
}
|
||||
|
||||
context->buffer = (unsigned char *)realloc(
|
||||
unsigned char *tmp = (unsigned char *)realloc(
|
||||
context->buffer,
|
||||
new_capacity * sizeof(/*unsigned char*/ context->buffer[0]));
|
||||
|
||||
if (!context->buffer)
|
||||
if (!tmp)
|
||||
{
|
||||
free(context->buffer);
|
||||
fatal(EXIT_NOT_ENOUGH_MEMORY, "In sbs_append_string: Not enough memory to append buffer");
|
||||
|
||||
}
|
||||
context->buffer = tmp;
|
||||
context->capacity = new_capacity;
|
||||
|
||||
LOG_DEBUG("SBS: sbs_append_string: REALLOC BUF DONE:\n\
|
||||
@@ -599,6 +616,10 @@ struct cc_subtitle *sbs_append_string(unsigned char *str, const LLONG time_from,
|
||||
{
|
||||
// it is new sentence!
|
||||
tmpsub = malloc(sizeof(struct cc_subtitle));
|
||||
if (!tmpsub)
|
||||
{
|
||||
fatal(EXIT_NOT_ENOUGH_MEMORY, "In sbs_append_string: Out of memory allocating cc_subtitle.");
|
||||
}
|
||||
|
||||
tmpsub->type = CC_TEXT;
|
||||
// length of new string:
|
||||
|
||||
@@ -67,17 +67,21 @@ struct spupng_t *spunpg_init(struct ccx_s_write *out)
|
||||
ccx_common_logging.fatal_ftn(CCX_COMMON_EXIT_FILE_CREATION_FAILED, "Cannot open %s: %s\n",
|
||||
out->filename, strerror(errno));
|
||||
}
|
||||
size_t filename_len = strlen(out->filename);
|
||||
sp->dirname = (char *)malloc(
|
||||
sizeof(char) * (strlen(out->filename) + 3));
|
||||
sizeof(char) * (filename_len + 3));
|
||||
if (NULL == sp->dirname)
|
||||
ccx_common_logging.fatal_ftn(EXIT_NOT_ENOUGH_MEMORY, "spunpg_init: Memory allocation failed (sp->dirname)");
|
||||
|
||||
strcpy(sp->dirname, out->filename);
|
||||
memcpy(sp->dirname, out->filename, filename_len + 1);
|
||||
char *p = strrchr(sp->dirname, '.');
|
||||
if (NULL == p)
|
||||
p = sp->dirname + strlen(sp->dirname);
|
||||
*p = '\0';
|
||||
strcat(sp->dirname, ".d");
|
||||
// Buffer size is filename_len + 3, current length is at most filename_len, appending ".d" (2 chars)
|
||||
size_t current_len = strlen(sp->dirname);
|
||||
size_t remaining = filename_len + 3 - current_len;
|
||||
strncat(sp->dirname, ".d", remaining - 1);
|
||||
if (0 != mkdir(sp->dirname, 0777))
|
||||
{
|
||||
if (errno != EEXIST)
|
||||
@@ -90,22 +94,23 @@ struct spupng_t *spunpg_init(struct ccx_s_write *out)
|
||||
}
|
||||
|
||||
// enough to append /subNNNN.png
|
||||
sp->pngfile = (char *)malloc(sizeof(char) * (strlen(sp->dirname) + 13));
|
||||
sp->relative_path_png = (char *)malloc(sizeof(char) * (strlen(sp->dirname) + 13));
|
||||
size_t pngfile_size = strlen(sp->dirname) + 13;
|
||||
sp->pngfile = (char *)malloc(sizeof(char) * pngfile_size);
|
||||
sp->relative_path_png = (char *)malloc(sizeof(char) * pngfile_size);
|
||||
|
||||
if (NULL == sp->pngfile || NULL == sp->relative_path_png)
|
||||
ccx_common_logging.fatal_ftn(EXIT_NOT_ENOUGH_MEMORY, "spunpg_init: Memory allocation failed (sp->pngfile)");
|
||||
sp->fileIndex = 0;
|
||||
sprintf(sp->pngfile, "%s/sub%04d.png", sp->dirname, sp->fileIndex);
|
||||
snprintf(sp->pngfile, pngfile_size, "%s/sub%04d.png", sp->dirname, sp->fileIndex);
|
||||
|
||||
// Make relative path
|
||||
char *last_slash = strrchr(sp->dirname, '/');
|
||||
if (last_slash == NULL)
|
||||
last_slash = strrchr(sp->dirname, '\\');
|
||||
if (last_slash != NULL)
|
||||
sprintf(sp->relative_path_png, "%s/sub%04d.png", last_slash + 1, sp->fileIndex);
|
||||
snprintf(sp->relative_path_png, pngfile_size, "%s/sub%04d.png", last_slash + 1, sp->fileIndex);
|
||||
else // do NOT do sp->relative_path_png = sp->pngfile (to avoid double free).
|
||||
strcpy(sp->relative_path_png, sp->pngfile);
|
||||
memcpy(sp->relative_path_png, sp->pngfile, strlen(sp->pngfile) + 1);
|
||||
|
||||
// For NTSC closed captions and 720x480 DVD subtitle resolution:
|
||||
// Each character is 16x26.
|
||||
@@ -186,7 +191,38 @@ void write_sputag_close(struct spupng_t *sp)
|
||||
}
|
||||
void write_spucomment(struct spupng_t *sp, const char *str)
|
||||
{
|
||||
fprintf(sp->fpxml, "<!--\n%s\n-->\n", str);
|
||||
fprintf(sp->fpxml, "<!--\n");
|
||||
|
||||
const char *p = str;
|
||||
const char *last_safe_pos = str; // Track the last safe position to flush
|
||||
|
||||
while (*p)
|
||||
{
|
||||
|
||||
if (*p == '-' && *(p + 1) == '-')
|
||||
{
|
||||
|
||||
if (p > last_safe_pos)
|
||||
{
|
||||
fwrite(last_safe_pos, 1, p - last_safe_pos, sp->fpxml);
|
||||
}
|
||||
|
||||
fputc('-', sp->fpxml);
|
||||
p += 2;
|
||||
last_safe_pos = p;
|
||||
}
|
||||
else
|
||||
{
|
||||
p++;
|
||||
}
|
||||
}
|
||||
|
||||
if (p > last_safe_pos)
|
||||
{
|
||||
fwrite(last_safe_pos, 1, p - last_safe_pos, sp->fpxml);
|
||||
}
|
||||
|
||||
fprintf(sp->fpxml, "\n-->\n");
|
||||
}
|
||||
|
||||
char *get_spupng_filename(void *ctx)
|
||||
@@ -197,16 +233,17 @@ char *get_spupng_filename(void *ctx)
|
||||
void inc_spupng_fileindex(struct spupng_t *sp)
|
||||
{
|
||||
sp->fileIndex++;
|
||||
sprintf(sp->pngfile, "%s/sub%04d.png", sp->dirname, sp->fileIndex);
|
||||
size_t pngfile_size = strlen(sp->dirname) + 13;
|
||||
snprintf(sp->pngfile, pngfile_size, "%s/sub%04d.png", sp->dirname, sp->fileIndex);
|
||||
|
||||
// Make relative path
|
||||
char *last_slash = strrchr(sp->dirname, '/');
|
||||
if (last_slash == NULL)
|
||||
last_slash = strrchr(sp->dirname, '\\');
|
||||
if (last_slash != NULL)
|
||||
sprintf(sp->relative_path_png, "%s/sub%04d.png", last_slash + 1, sp->fileIndex);
|
||||
snprintf(sp->relative_path_png, pngfile_size, "%s/sub%04d.png", last_slash + 1, sp->fileIndex);
|
||||
else // do NOT do sp->relative_path_png = sp->pngfile (to avoid double free).
|
||||
strcpy(sp->relative_path_png, sp->pngfile);
|
||||
memcpy(sp->relative_path_png, sp->pngfile, strlen(sp->pngfile) + 1);
|
||||
}
|
||||
void set_spupng_offset(void *ctx, int x, int y)
|
||||
{
|
||||
@@ -214,6 +251,9 @@ void set_spupng_offset(void *ctx, int x, int y)
|
||||
sp->xOffset = x;
|
||||
sp->yOffset = y;
|
||||
}
|
||||
|
||||
// Forward declaration for calculate_spupng_offsets
|
||||
static void calculate_spupng_offsets(struct spupng_t *sp, struct encoder_ctx *ctx);
|
||||
int save_spupng(const char *filename, uint8_t *bitmap, int w, int h,
|
||||
png_color *palette, png_byte *alpha, int nb_color)
|
||||
{
|
||||
@@ -347,7 +387,7 @@ int write_cc_bitmap_as_spupng(struct cc_subtitle *sub, struct encoder_ctx *conte
|
||||
struct cc_bitmap *rect;
|
||||
png_color *palette = NULL;
|
||||
png_byte *alpha = NULL;
|
||||
int wrote_opentag = 1;
|
||||
int wrote_opentag = 0; // Track if we actually wrote the tag
|
||||
|
||||
x_pos = -1;
|
||||
y_pos = -1;
|
||||
@@ -358,13 +398,11 @@ int write_cc_bitmap_as_spupng(struct cc_subtitle *sub, struct encoder_ctx *conte
|
||||
return 0;
|
||||
|
||||
inc_spupng_fileindex(sp);
|
||||
write_sputag_open(sp, sub->start_time, sub->end_time - 1);
|
||||
|
||||
if (sub->nb_data == 0 && (sub->flags & SUB_EOD_MARKER))
|
||||
{
|
||||
context->prev_start = -1;
|
||||
if (wrote_opentag)
|
||||
write_sputag_close(sp);
|
||||
// No subtitle data, skip writing
|
||||
return 0;
|
||||
}
|
||||
rect = sub->data;
|
||||
@@ -403,10 +441,20 @@ int write_cc_bitmap_as_spupng(struct cc_subtitle *sub, struct encoder_ctx *conte
|
||||
}
|
||||
}
|
||||
filename = get_spupng_filename(sp);
|
||||
set_spupng_offset(sp, x_pos, y_pos);
|
||||
|
||||
// Set image dimensions for offset calculation
|
||||
sp->img_w = width;
|
||||
sp->img_h = height;
|
||||
|
||||
// Calculate centered offsets based on screen size (PAL/NTSC)
|
||||
calculate_spupng_offsets(sp, context);
|
||||
if (sub->flags & SUB_EOD_MARKER)
|
||||
context->prev_start = sub->start_time;
|
||||
pbuf = (uint8_t *)malloc(width * height);
|
||||
if (!pbuf)
|
||||
{
|
||||
fatal(EXIT_NOT_ENOUGH_MEMORY, "In write_cc_bitmap_as_spupng: Out of memory allocating pbuf.");
|
||||
}
|
||||
memset(pbuf, 0x0, width * height);
|
||||
|
||||
for (i = 0; i < sub->nb_data; i++)
|
||||
@@ -434,6 +482,15 @@ int write_cc_bitmap_as_spupng(struct cc_subtitle *sub, struct encoder_ctx *conte
|
||||
|
||||
/* TODO do rectangle wise, one color table should not be used for all rectangles */
|
||||
mapclut_paletee(palette, alpha, (uint32_t *)rect[0].data1, rect[0].nb_colors);
|
||||
|
||||
// Save PNG file first
|
||||
save_spupng(filename, pbuf, width, height, palette, alpha, rect[0].nb_colors);
|
||||
freep(&pbuf);
|
||||
|
||||
// Write XML tag with calculated centered offsets
|
||||
write_sputag_open(sp, sub->start_time, sub->end_time - 1);
|
||||
wrote_opentag = 1; // Mark that we wrote the tag
|
||||
|
||||
#ifdef ENABLE_OCR
|
||||
if (!context->nospupngocr)
|
||||
{
|
||||
@@ -446,8 +503,6 @@ int write_cc_bitmap_as_spupng(struct cc_subtitle *sub, struct encoder_ctx *conte
|
||||
}
|
||||
}
|
||||
#endif
|
||||
save_spupng(filename, pbuf, width, height, palette, alpha, rect[0].nb_colors);
|
||||
freep(&pbuf);
|
||||
|
||||
end:
|
||||
if (wrote_opentag)
|
||||
@@ -504,6 +559,12 @@ int write_image(struct pixel_t *buffer, FILE *fp, int width, int height)
|
||||
|
||||
// Allocate memory for one row (4 bytes per pixel - RGBA)
|
||||
row = (png_bytep)malloc(4 * width * sizeof(png_byte));
|
||||
if (!row)
|
||||
{
|
||||
mprint("\nFailed to allocate memory for row in write_image.\n");
|
||||
ret_code = 0;
|
||||
goto finalise;
|
||||
}
|
||||
|
||||
// Write image data
|
||||
int x, y;
|
||||
@@ -523,10 +584,8 @@ int write_image(struct pixel_t *buffer, FILE *fp, int width, int height)
|
||||
png_write_end(png_ptr, NULL);
|
||||
|
||||
finalise:
|
||||
if (info_ptr != NULL)
|
||||
png_free_data(png_ptr, info_ptr, PNG_FREE_ALL, -1);
|
||||
if (png_ptr != NULL)
|
||||
png_destroy_write_struct(&png_ptr, (png_infopp)NULL);
|
||||
png_destroy_write_struct(&png_ptr, &info_ptr);
|
||||
if (row != NULL)
|
||||
free(row);
|
||||
|
||||
@@ -574,6 +633,10 @@ void center_justify(struct pixel_t *target, int target_w,
|
||||
// Note that the copy is smaller than the line:
|
||||
// its width is set to just enough to contain the valid area
|
||||
struct pixel_t *temp_buffer = malloc(valid_w * valid_h * sizeof(struct pixel_t));
|
||||
if (!temp_buffer)
|
||||
{
|
||||
fatal(EXIT_NOT_ENOUGH_MEMORY, "In center_justify: Out of memory allocating temp_buffer.");
|
||||
}
|
||||
|
||||
// x,y here is input-based (i.e. `buffer`)
|
||||
for (int x = 0; x < valid_w; ++x)
|
||||
@@ -677,6 +740,10 @@ uint32_t *utf8_to_utf32(char *src)
|
||||
len_dst = (len_src + 2) * 4; // one for FEFF and one for \0
|
||||
|
||||
uint32_t *string_utf32 = (uint32_t *)calloc(len_dst, 1);
|
||||
if (!string_utf32)
|
||||
{
|
||||
fatal(EXIT_NOT_ENOUGH_MEMORY, "In utf8_to_utf32: Out of memory allocating string_utf32.");
|
||||
}
|
||||
size_t inbufbytesleft = len_src;
|
||||
size_t outbufbytesleft = len_dst;
|
||||
char *inbuf = src;
|
||||
@@ -731,8 +798,8 @@ int spupng_export_string2png(struct spupng_t *sp, char *str, FILE *output)
|
||||
struct pixel_t *buffer = malloc(canvas_width * canvas_height * sizeof(struct pixel_t));
|
||||
if (buffer == NULL)
|
||||
{
|
||||
mprint("\nFailed to alloc memory for buffer. Need %d bytes.\n",
|
||||
canvas_width * canvas_height * sizeof(struct pixel_t));
|
||||
fatal(EXIT_NOT_ENOUGH_MEMORY, "In spupng_export_string2png: Out of memory allocating buffer. Need %lu bytes.",
|
||||
(unsigned long)(canvas_width * canvas_height * sizeof(struct pixel_t)));
|
||||
}
|
||||
memset(buffer, 0, canvas_width * canvas_height * sizeof(struct pixel_t));
|
||||
|
||||
@@ -740,7 +807,10 @@ int spupng_export_string2png(struct spupng_t *sp, char *str, FILE *output)
|
||||
char *tmp = strdup(str);
|
||||
|
||||
if (!tmp)
|
||||
{
|
||||
free(buffer);
|
||||
return -1;
|
||||
}
|
||||
|
||||
char *token = strtok(tmp, "<>");
|
||||
|
||||
@@ -849,9 +919,11 @@ int spupng_export_string2png(struct spupng_t *sp, char *str, FILE *output)
|
||||
struct pixel_t *new_buffer = realloc(buffer, canvas_width * canvas_height * sizeof(struct pixel_t));
|
||||
if (new_buffer == NULL)
|
||||
{
|
||||
mprint("\nFailed to alloc memory for buffer. Need %d bytes.\n",
|
||||
canvas_width * canvas_height * sizeof(struct pixel_t));
|
||||
return 0;
|
||||
free(buffer);
|
||||
free(tmp);
|
||||
free(string_utf32);
|
||||
fatal(EXIT_NOT_ENOUGH_MEMORY, "In spupng_export_string2png: Out of memory expanding buffer. Need %lu bytes.",
|
||||
(unsigned long)(canvas_width * canvas_height * sizeof(struct pixel_t)));
|
||||
}
|
||||
memset(new_buffer + old_height * canvas_width, 0, (canvas_height - old_height) * canvas_width * sizeof(struct pixel_t));
|
||||
buffer = new_buffer;
|
||||
@@ -933,6 +1005,8 @@ int spupng_export_string2png(struct spupng_t *sp, char *str, FILE *output)
|
||||
*/
|
||||
|
||||
// Save image
|
||||
sp->img_w = canvas_width;
|
||||
sp->img_h = canvas_height;
|
||||
write_image(buffer, output, canvas_width, canvas_height);
|
||||
free(tmp);
|
||||
free(buffer);
|
||||
@@ -944,11 +1018,13 @@ int spupng_export_string2png(struct spupng_t *sp, char *str, FILE *output)
|
||||
// Convert EIA608 Data(buffer) to string
|
||||
// out must have at least 256 characters' space
|
||||
// Return value is the length of the output string
|
||||
#define EIA608_OUT_SIZE 256
|
||||
int eia608_to_str(struct encoder_ctx *context, struct eia608_screen *data, char *out)
|
||||
{
|
||||
|
||||
int str_len = 0;
|
||||
int first = 1;
|
||||
out[0] = '\0'; // Initialize output buffer
|
||||
for (int row = 0; row < ROWS; row++)
|
||||
{
|
||||
if (data->row_used[row])
|
||||
@@ -997,11 +1073,22 @@ int eia608_to_str(struct encoder_ctx *context, struct eia608_screen *data, char
|
||||
|
||||
if (!first)
|
||||
{ // Add '\n' if it is not the first line.
|
||||
strcat(out, "\n");
|
||||
str_len += 2;
|
||||
if (str_len + 1 < EIA608_OUT_SIZE)
|
||||
{
|
||||
out[str_len++] = '\n';
|
||||
out[str_len] = '\0';
|
||||
}
|
||||
str_len++; // Count the newline even if truncated (for return value)
|
||||
}
|
||||
first = 0;
|
||||
strncat(out, start, len);
|
||||
// Copy at most what fits in remaining buffer
|
||||
size_t remaining = (str_len < EIA608_OUT_SIZE - 1) ? (EIA608_OUT_SIZE - 1 - str_len) : 0;
|
||||
size_t to_copy = (len < remaining) ? len : remaining;
|
||||
if (to_copy > 0)
|
||||
{
|
||||
memcpy(out + str_len, start, to_copy);
|
||||
out[str_len + to_copy] = '\0';
|
||||
}
|
||||
str_len += len;
|
||||
}
|
||||
}
|
||||
@@ -1010,6 +1097,28 @@ int eia608_to_str(struct encoder_ctx *context, struct eia608_screen *data, char
|
||||
|
||||
// string needs to be in UTF-8 encoding.
|
||||
// This function will take care of encoding.
|
||||
static void calculate_spupng_offsets(struct spupng_t *sp, struct encoder_ctx *ctx)
|
||||
{
|
||||
int screen_w = 720;
|
||||
int screen_h;
|
||||
|
||||
/* Teletext is always PAL */
|
||||
if (ctx->in_fileformat == 2 || ctx->is_pal)
|
||||
{
|
||||
screen_h = 576;
|
||||
}
|
||||
else
|
||||
{
|
||||
screen_h = 480;
|
||||
}
|
||||
|
||||
sp->xOffset = (screen_w - sp->img_w) / 2;
|
||||
sp->yOffset = (screen_h - sp->img_h) / 2;
|
||||
|
||||
// SPU / DVD requires even yOffset (interlacing)
|
||||
if (sp->yOffset & 1)
|
||||
sp->yOffset++;
|
||||
}
|
||||
int spupng_write_string(struct spupng_t *sp, char *string, LLONG start_time, LLONG end_time,
|
||||
struct encoder_ctx *context)
|
||||
{
|
||||
@@ -1028,6 +1137,7 @@ int spupng_write_string(struct spupng_t *sp, char *string, LLONG start_time, LLO
|
||||
}
|
||||
// free(string_utf32);
|
||||
fclose(sp->fppng);
|
||||
calculate_spupng_offsets(sp, context);
|
||||
write_sputag_open(sp, start_time, end_time);
|
||||
write_spucomment(sp, string);
|
||||
write_sputag_close(sp);
|
||||
|
||||
@@ -6,9 +6,10 @@
|
||||
#include "ocr.h"
|
||||
#include "ccextractor.h"
|
||||
|
||||
/* The timing here is not PTS based, but output based, i.e. user delay must be accounted for
|
||||
if there is any */
|
||||
int write_stringz_as_srt(char *string, struct encoder_ctx *context, LLONG ms_start, LLONG ms_end)
|
||||
/* Helper function to write SRT to a specific output file (issue #665 - teletext multi-page)
|
||||
Takes output file descriptor and counter pointer as parameters */
|
||||
static int write_stringz_as_srt_to_output(char *string, struct encoder_ctx *context, LLONG ms_start, LLONG ms_end,
|
||||
int out_fh, unsigned int *srt_counter)
|
||||
{
|
||||
int used;
|
||||
unsigned h1, m1, s1, ms1;
|
||||
@@ -20,22 +21,27 @@ int write_stringz_as_srt(char *string, struct encoder_ctx *context, LLONG ms_sta
|
||||
|
||||
millis_to_time(ms_start, &h1, &m1, &s1, &ms1);
|
||||
millis_to_time(ms_end - 1, &h2, &m2, &s2, &ms2); // -1 To prevent overlapping with next line.
|
||||
context->srt_counter++;
|
||||
sprintf(timeline, "%u%s", context->srt_counter, context->encoded_crlf);
|
||||
(*srt_counter)++;
|
||||
snprintf(timeline, sizeof(timeline), "%u%s", *srt_counter, context->encoded_crlf);
|
||||
used = encode_line(context, context->buffer, (unsigned char *)timeline);
|
||||
write_wrapped(context->out->fh, context->buffer, used);
|
||||
sprintf(timeline, "%02u:%02u:%02u,%03u --> %02u:%02u:%02u,%03u%s",
|
||||
h1, m1, s1, ms1, h2, m2, s2, ms2, context->encoded_crlf);
|
||||
write_wrapped(out_fh, context->buffer, used);
|
||||
snprintf(timeline, sizeof(timeline), "%02u:%02u:%02u,%03u --> %02u:%02u:%02u,%03u%s",
|
||||
h1, m1, s1, ms1, h2, m2, s2, ms2, context->encoded_crlf);
|
||||
used = encode_line(context, context->buffer, (unsigned char *)timeline);
|
||||
dbg_print(CCX_DMT_DECODER_608, "\n- - - SRT caption - - -\n");
|
||||
dbg_print(CCX_DMT_DECODER_608, "%s", timeline);
|
||||
|
||||
write_wrapped(context->out->fh, context->buffer, used);
|
||||
write_wrapped(out_fh, context->buffer, used);
|
||||
int len = strlen(string);
|
||||
unsigned char *unescaped = (unsigned char *)malloc(len + 1);
|
||||
if (!unescaped)
|
||||
fatal(EXIT_NOT_ENOUGH_MEMORY, "In write_stringz_as_srt() - not enough memory for unescaped buffer.\n");
|
||||
unsigned char *el = (unsigned char *)malloc(len * 3 + 1); // Be generous
|
||||
if (el == NULL || unescaped == NULL)
|
||||
fatal(EXIT_NOT_ENOUGH_MEMORY, "In write_stringz_as_srt() - not enough memory.\n");
|
||||
if (!el)
|
||||
{
|
||||
free(unescaped);
|
||||
fatal(EXIT_NOT_ENOUGH_MEMORY, "In write_stringz_as_srt() - not enough memory for el buffer.\n");
|
||||
}
|
||||
int pos_r = 0;
|
||||
int pos_w = 0;
|
||||
// Scan for \n in the string and replace it with a 0
|
||||
@@ -64,20 +70,28 @@ int write_stringz_as_srt(char *string, struct encoder_ctx *context, LLONG ms_sta
|
||||
dbg_print(CCX_DMT_DECODER_608, "\r");
|
||||
dbg_print(CCX_DMT_DECODER_608, "%s\n", context->subline);
|
||||
}
|
||||
write_wrapped(context->out->fh, el, u);
|
||||
write_wrapped(context->out->fh, context->encoded_crlf, context->encoded_crlf_length);
|
||||
write_wrapped(out_fh, el, u);
|
||||
write_wrapped(out_fh, context->encoded_crlf, context->encoded_crlf_length);
|
||||
begin += strlen((const char *)begin) + 1;
|
||||
}
|
||||
|
||||
dbg_print(CCX_DMT_DECODER_608, "- - - - - - - - - - - -\r\n");
|
||||
|
||||
write_wrapped(context->out->fh, context->encoded_crlf, context->encoded_crlf_length);
|
||||
write_wrapped(out_fh, context->encoded_crlf, context->encoded_crlf_length);
|
||||
free(el);
|
||||
free(unescaped);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* The timing here is not PTS based, but output based, i.e. user delay must be accounted for
|
||||
if there is any */
|
||||
int write_stringz_as_srt(char *string, struct encoder_ctx *context, LLONG ms_start, LLONG ms_end)
|
||||
{
|
||||
return write_stringz_as_srt_to_output(string, context, ms_start, ms_end,
|
||||
context->out->fh, &context->srt_counter);
|
||||
}
|
||||
|
||||
int write_cc_bitmap_as_srt(struct cc_subtitle *sub, struct encoder_ctx *context)
|
||||
{
|
||||
int ret = 0;
|
||||
@@ -112,11 +126,11 @@ int write_cc_bitmap_as_srt(struct cc_subtitle *sub, struct encoder_ctx *context)
|
||||
millis_to_time(sub->start_time, &h1, &m1, &s1, &ms1);
|
||||
millis_to_time(sub->end_time - 1, &h2, &m2, &s2, &ms2); // -1 To prevent overlapping with next line.
|
||||
context->srt_counter++;
|
||||
sprintf(timeline, "%u%s", context->srt_counter, context->encoded_crlf);
|
||||
snprintf(timeline, sizeof(timeline), "%u%s", context->srt_counter, context->encoded_crlf);
|
||||
used = encode_line(context, context->buffer, (unsigned char *)timeline);
|
||||
write_wrapped(context->out->fh, context->buffer, used);
|
||||
sprintf(timeline, "%02u:%02u:%02u,%03u --> %02u:%02u:%02u,%03u%s",
|
||||
h1, m1, s1, ms1, h2, m2, s2, ms2, context->encoded_crlf);
|
||||
snprintf(timeline, sizeof(timeline), "%02u:%02u:%02u,%03u --> %02u:%02u:%02u,%03u%s",
|
||||
h1, m1, s1, ms1, h2, m2, s2, ms2, context->encoded_crlf);
|
||||
used = encode_line(context, context->buffer, (unsigned char *)timeline);
|
||||
write_wrapped(context->out->fh, context->buffer, used);
|
||||
len = strlen(str);
|
||||
@@ -150,7 +164,18 @@ int write_cc_subtitle_as_srt(struct cc_subtitle *sub, struct encoder_ctx *contex
|
||||
{
|
||||
if (sub->type == CC_TEXT)
|
||||
{
|
||||
ret = write_stringz_as_srt(sub->data, context, sub->start_time, sub->end_time);
|
||||
// For teletext multi-page extraction (issue #665), use page-specific output
|
||||
struct ccx_s_write *out = get_teletext_output(context, sub->teletext_page);
|
||||
unsigned int *counter = get_teletext_srt_counter(context, sub->teletext_page);
|
||||
if (out && counter)
|
||||
{
|
||||
ret = write_stringz_as_srt_to_output(sub->data, context, sub->start_time, sub->end_time,
|
||||
out->fh, counter);
|
||||
}
|
||||
else
|
||||
{
|
||||
ret = write_stringz_as_srt(sub->data, context, sub->start_time, sub->end_time);
|
||||
}
|
||||
freep(&sub->data);
|
||||
sub->nb_data = 0;
|
||||
ret = 1;
|
||||
@@ -196,12 +221,12 @@ int write_cc_buffer_as_srt(struct eia608_screen *data, struct encoder_ctx *conte
|
||||
char timeline[128];
|
||||
|
||||
++context->srt_counter;
|
||||
sprintf(timeline, "%u%s", context->srt_counter, context->encoded_crlf);
|
||||
snprintf(timeline, sizeof(timeline), "%u%s", context->srt_counter, context->encoded_crlf);
|
||||
used = encode_line(context, context->buffer, (unsigned char *)timeline);
|
||||
write_wrapped(context->out->fh, context->buffer, used);
|
||||
|
||||
sprintf(timeline, "%02u:%02u:%02u,%03u --> %02u:%02u:%02u,%03u%s",
|
||||
h1, m1, s1, ms1, h2, m2, s2, ms2, context->encoded_crlf);
|
||||
snprintf(timeline, sizeof(timeline), "%02u:%02u:%02u,%03u --> %02u:%02u:%02u,%03u%s",
|
||||
h1, m1, s1, ms1, h2, m2, s2, ms2, context->encoded_crlf);
|
||||
used = encode_line(context, context->buffer, (unsigned char *)timeline);
|
||||
write_wrapped(context->out->fh, context->buffer, used);
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user