mirror of
https://github.com/CCExtractor/ccextractor.git
synced 2026-02-09 21:24:13 +00:00
Compare commits
493 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9768555e7c | ||
|
|
72e422d76e | ||
|
|
8860be13e4 | ||
|
|
3b7ad97f0a | ||
|
|
5e305f2769 | ||
|
|
0a798606ee | ||
|
|
3775daf36c | ||
|
|
8c5de01034 | ||
|
|
1c352e67e4 | ||
|
|
79838077f2 | ||
|
|
058bb83a25 | ||
|
|
d2f840bb36 | ||
|
|
a576c90f4e | ||
|
|
a1bd01de5f | ||
|
|
9a5e817f36 | ||
|
|
bc20bc3f45 | ||
|
|
783a6e2aa0 | ||
|
|
992c56b262 | ||
|
|
c674cef79e | ||
|
|
509ed756fd | ||
|
|
152b69ddba | ||
|
|
c2eedab33d | ||
|
|
677fee4145 | ||
|
|
72dad743dc | ||
|
|
e262b0699a | ||
|
|
6942089a32 | ||
|
|
993adc3dec | ||
|
|
fe7a39c0cb | ||
|
|
3bd704e495 | ||
|
|
6992e54f8e | ||
|
|
4d5ab77fed | ||
|
|
1b2328d728 | ||
|
|
2944e12541 | ||
|
|
763972ca4b | ||
|
|
01fe0c1fc9 | ||
|
|
8d160a70ff | ||
|
|
fc90b9c9f5 | ||
|
|
0a803df23f | ||
|
|
d3a1ed4b06 | ||
|
|
ad89e0074b | ||
|
|
169a39eb3e | ||
|
|
a6145af7f3 | ||
|
|
03b60ef140 | ||
|
|
1b7ef1e198 | ||
|
|
72de68a575 | ||
|
|
24e7b94599 | ||
|
|
c5dc4531ae | ||
|
|
a3e07b4a63 | ||
|
|
d48555b849 | ||
|
|
a1f05caddf | ||
|
|
669eb36603 | ||
|
|
ce379bcfda | ||
|
|
00657ffdcd | ||
|
|
d9e0ba027f | ||
|
|
a7279e3d8a | ||
|
|
61ae7e9f10 | ||
|
|
548d323ca1 | ||
|
|
d2d7a17f3b | ||
|
|
65b0ed0038 | ||
|
|
2b0523d34c | ||
|
|
5bef6bd5a4 | ||
|
|
5ca3965c7b | ||
|
|
e855665c87 | ||
|
|
e9bf8dad9f | ||
|
|
54c2b21a4c | ||
|
|
afe5cba480 | ||
|
|
8c34f4cbb2 | ||
|
|
e422e7075a | ||
|
|
cc0ee507dd | ||
|
|
540850f0b9 | ||
|
|
a973c0a9c3 | ||
|
|
ae27b3411b | ||
|
|
89c4ac091a | ||
|
|
843ca9b60a | ||
|
|
d135362d40 | ||
|
|
2f1b9df6e9 | ||
|
|
5ef2519bf5 | ||
|
|
f258d317cc | ||
|
|
4d3fcd6779 | ||
|
|
f1b0aff789 | ||
|
|
6e9a30b354 | ||
|
|
37091708b7 | ||
|
|
0885aae79c | ||
|
|
5e5d30d154 | ||
|
|
a614db1e20 | ||
|
|
f7f29e558b | ||
|
|
ddc1bfe74f | ||
|
|
18b95d9564 | ||
|
|
b012f87d87 | ||
|
|
73f277fe95 | ||
|
|
bd3df850a5 | ||
|
|
73b52462c0 | ||
|
|
267d7b4a2e | ||
|
|
35cdeda56c | ||
|
|
0f90eebdf6 | ||
|
|
962357ed0f | ||
|
|
69d077df9e | ||
|
|
e2b0534374 | ||
|
|
4204b7878d | ||
|
|
1e2237f7ec | ||
|
|
6bfe3b3f86 | ||
|
|
940bee33a4 | ||
|
|
7c787157d8 | ||
|
|
7514886199 | ||
|
|
ad5b917f3b | ||
|
|
66408fc950 | ||
|
|
94ec02eea9 | ||
|
|
5e33e2e2ac | ||
|
|
34613037fc | ||
|
|
f2b24f13af | ||
|
|
c718d21d0f | ||
|
|
a2828f0060 | ||
|
|
4b3b846a2c | ||
|
|
b232945c71 | ||
|
|
f91d1cb9b7 | ||
|
|
fadd8aad8b | ||
|
|
b0479b247a | ||
|
|
a659544bd2 | ||
|
|
9ce4c99180 | ||
|
|
52f51147b8 | ||
|
|
34836b50a1 | ||
|
|
7179796365 | ||
|
|
966c90eece | ||
|
|
3281bf929a | ||
|
|
906a0704bc | ||
|
|
20785c095c | ||
|
|
dee2280d06 | ||
|
|
f6b69b64c3 | ||
|
|
2f09b37f7c | ||
|
|
20cf0f5151 | ||
|
|
860e926b5a | ||
|
|
9f81a4b5c1 | ||
|
|
5d85220121 | ||
|
|
6423efa2d7 | ||
|
|
932cb77265 | ||
|
|
691244a00e | ||
|
|
440c59a5fd | ||
|
|
8a3e2b5efc | ||
|
|
119a4361df | ||
|
|
6c93b6dcf4 | ||
|
|
4d76cd30c0 | ||
|
|
96fd051e01 | ||
|
|
2fe0da30be | ||
|
|
890e045a88 | ||
|
|
b333df4317 | ||
|
|
33d91a979c | ||
|
|
b3614f592f | ||
|
|
3bdb5f3863 | ||
|
|
ad6346f802 | ||
|
|
66f41438be | ||
|
|
647a91eafa | ||
|
|
ecf200e290 | ||
|
|
da576bff2b | ||
|
|
fcbf113526 | ||
|
|
b90d144ff6 | ||
|
|
2ac0ed2032 | ||
|
|
0afefc0392 | ||
|
|
9efe2b4b22 | ||
|
|
d54d881390 | ||
|
|
58b496a434 | ||
|
|
41a8803860 | ||
|
|
4146423878 | ||
|
|
1c35dcae03 | ||
|
|
3bcdbce709 | ||
|
|
250ee1f29b | ||
|
|
fd1a86b2f5 | ||
|
|
3433ed2e5c | ||
|
|
c7606beae1 | ||
|
|
d639323e4c | ||
|
|
72e780f175 | ||
|
|
651dc67a5d | ||
|
|
6cfd7710a7 | ||
|
|
adc1d4662d | ||
|
|
5c51b582bf | ||
|
|
16e794163c | ||
|
|
4e25d80b48 | ||
|
|
2949918ed1 | ||
|
|
46ea522ecb | ||
|
|
f571a04ffc | ||
|
|
610b811d53 | ||
|
|
e5c4053c05 | ||
|
|
0b9bf16850 | ||
|
|
8489edf966 | ||
|
|
22127cccb0 | ||
|
|
57eb42c7bb | ||
|
|
0a6630f7ab | ||
|
|
b89dd8d401 | ||
|
|
778e66f951 | ||
|
|
7a06d99443 | ||
|
|
3fff94a555 | ||
|
|
b65cc8ad8e | ||
|
|
28ddfef2b1 | ||
|
|
f23cc1f41e | ||
|
|
1ca63ba125 | ||
|
|
af66ace345 | ||
|
|
c0079aee6f | ||
|
|
21a88f9cc1 | ||
|
|
9923ef98ee | ||
|
|
abce1dd873 | ||
|
|
308d500308 | ||
|
|
ae6cf97bce | ||
|
|
53bedd30c1 | ||
|
|
764d251986 | ||
|
|
1bfb96151f | ||
|
|
741e87e00e | ||
|
|
098bff1230 | ||
|
|
0374c5ec78 | ||
|
|
e7bec67c93 | ||
|
|
b9e0f67dec | ||
|
|
acd96d44d2 | ||
|
|
4e4da44c39 | ||
|
|
bb0f836e84 | ||
|
|
5f6a8c7f54 | ||
|
|
0c6bf5d8b1 | ||
|
|
f8210f94f2 | ||
|
|
243674ed96 | ||
|
|
322d352ca0 | ||
|
|
c8c71085e1 | ||
|
|
4969e6a8fd | ||
|
|
ece4a5fa8a | ||
|
|
7d034f44e4 | ||
|
|
c12d3856f2 | ||
|
|
24f9106cde | ||
|
|
58503141c3 | ||
|
|
3b214f491b | ||
|
|
f83ca18c7d | ||
|
|
1261cee8dc | ||
|
|
92ecaa9434 | ||
|
|
fd66228b26 | ||
|
|
bfd7556b50 | ||
|
|
3174e3dc9e | ||
|
|
ec26080bd2 | ||
|
|
5ea83ccf9d | ||
|
|
5af65d941a | ||
|
|
0d79ad3cb6 | ||
|
|
8499a6b426 | ||
|
|
577a79de47 | ||
|
|
6133b9c26d | ||
|
|
c4e91f8ccc | ||
|
|
589fd91d8e | ||
|
|
03eba6114b | ||
|
|
56f0786cc3 | ||
|
|
aff591fd9b | ||
|
|
bdaa3a1352 | ||
|
|
f3b104584d | ||
|
|
e47315e423 | ||
|
|
43335e30a9 | ||
|
|
79c4a48d60 | ||
|
|
a5538902ec | ||
|
|
1b9d905b28 | ||
|
|
89c8b018c8 | ||
|
|
cde69fdd88 | ||
|
|
a798fdf57e | ||
|
|
8b89ad0c5a | ||
|
|
309f4130e0 | ||
|
|
0f2ccdbe4f | ||
|
|
eeb33de590 | ||
|
|
91a8997826 | ||
|
|
5fb7bc434c | ||
|
|
77540355ed | ||
|
|
c5362b682e | ||
|
|
e0f3751fe8 | ||
|
|
54583f95a3 | ||
|
|
990e653e91 | ||
|
|
9d2fb48d3d | ||
|
|
e5e51c4389 | ||
|
|
74dbd4d7e5 | ||
|
|
784a46d165 | ||
|
|
83e1959db8 | ||
|
|
e0bf79f28b | ||
|
|
5c1f4772c7 | ||
|
|
deca14c2c2 | ||
|
|
8023ebe8e2 | ||
|
|
0e98604267 | ||
|
|
7e68c8e823 | ||
|
|
601dbfd062 | ||
|
|
35983534f1 | ||
|
|
f88330c7c9 | ||
|
|
a0bf7dadf8 | ||
|
|
f2added8d1 | ||
|
|
9f78a843ee | ||
|
|
3e78fdd675 | ||
|
|
c5dfd52eb9 | ||
|
|
08df39c3d4 | ||
|
|
9fad88fee3 | ||
|
|
6ae94fdbab | ||
|
|
8d5f0e5505 | ||
|
|
11b614cedf | ||
|
|
fc79649c9c | ||
|
|
b85e93a2cb | ||
|
|
dd8923b2e4 | ||
|
|
1437aea328 | ||
|
|
1fdad2314f | ||
|
|
6aac9dad43 | ||
|
|
fc248dd41f | ||
|
|
a2394df838 | ||
|
|
306d60ccf9 | ||
|
|
d78a722260 | ||
|
|
4a34799e09 | ||
|
|
489622fb0b | ||
|
|
9689dd8c19 | ||
|
|
d29f6817c6 | ||
|
|
f9ea7009fe | ||
|
|
ddd8381440 | ||
|
|
964b8c1165 | ||
|
|
0541a2fb62 | ||
|
|
f3654174fc | ||
|
|
0fc1055da6 | ||
|
|
a654b133e5 | ||
|
|
3a886c6a5b | ||
|
|
4a8d51aed1 | ||
|
|
70f5723c89 | ||
|
|
b85702a706 | ||
|
|
0bf1e83fa1 | ||
|
|
67d62631aa | ||
|
|
43f276abca | ||
|
|
7cea808f46 | ||
|
|
adbe8ed934 | ||
|
|
a3eba7cf45 | ||
|
|
3f26290614 | ||
|
|
d63660468a | ||
|
|
3daecf66a3 | ||
|
|
16db90a630 | ||
|
|
c1d7f82819 | ||
|
|
f8ee9504a8 | ||
|
|
c75d056b76 | ||
|
|
6c38f1b73b | ||
|
|
00b61a291d | ||
|
|
fcdc5f852b | ||
|
|
0424b0f119 | ||
|
|
82559fd572 | ||
|
|
3837ffae59 | ||
|
|
2e4cda0383 | ||
|
|
dc6ef18c21 | ||
|
|
bf3790183a | ||
|
|
bfa8b593ab | ||
|
|
401d679f80 | ||
|
|
157faaf0b5 | ||
|
|
cb49812d17 | ||
|
|
d88fa2a3ce | ||
|
|
5716ab7cfc | ||
|
|
04b9e40cc5 | ||
|
|
2f92036557 | ||
|
|
6aaa4fe2d4 | ||
|
|
ab0f54bb57 | ||
|
|
6642084132 | ||
|
|
c5ca7c7e1a | ||
|
|
5b24334b43 | ||
|
|
df93801021 | ||
|
|
64285879db | ||
|
|
0523f0bcd5 | ||
|
|
c6721ebc3e | ||
|
|
9d22f9a466 | ||
|
|
d58fb00970 | ||
|
|
62e189a9cb | ||
|
|
dc17946a55 | ||
|
|
78d8d858d2 | ||
|
|
30e7f95f38 | ||
|
|
8026d2e671 | ||
|
|
c98e00201a | ||
|
|
9b0ba130f1 | ||
|
|
83aa9709f4 | ||
|
|
340549e916 | ||
|
|
6d41964bba | ||
|
|
71f030f4ee | ||
|
|
67653d06d3 | ||
|
|
ab9f3a4b4a | ||
|
|
d9e9305e2c | ||
|
|
2bcc0c5561 | ||
|
|
cd03d8a658 | ||
|
|
589074886b | ||
|
|
c6ca493752 | ||
|
|
aef649d23a | ||
|
|
610b7323f4 | ||
|
|
eb6f6a9000 | ||
|
|
f3fe829d48 | ||
|
|
cb39d12615 | ||
|
|
5d52026736 | ||
|
|
48dce9c7ce | ||
|
|
2032a754e6 | ||
|
|
aa353140ef | ||
|
|
ee47e8458f | ||
|
|
081f127c85 | ||
|
|
7169c71360 | ||
|
|
5c3a757e5b | ||
|
|
673cadc6bf | ||
|
|
2318e714db | ||
|
|
a6d27a2de5 | ||
|
|
dc842b0312 | ||
|
|
3879f5c7ef | ||
|
|
e01d93afa8 | ||
|
|
6ec8086b0a | ||
|
|
b80572c3b6 | ||
|
|
3f6fd51f45 | ||
|
|
87af82f022 | ||
|
|
020ca9a1cf | ||
|
|
beccc3d5b7 | ||
|
|
1d03bd7d13 | ||
|
|
13290294a6 | ||
|
|
1eef500c73 | ||
|
|
ca5135c8aa | ||
|
|
740a1d798e | ||
|
|
b68a086698 | ||
|
|
0e60ceb4d0 | ||
|
|
2030c16b22 | ||
|
|
79b1bca8f7 | ||
|
|
76b3d3e4d1 | ||
|
|
90d8731a03 | ||
|
|
85270c7047 | ||
|
|
c9e596e60a | ||
|
|
24d83e130a | ||
|
|
4fc19d4289 | ||
|
|
74ad11b44f | ||
|
|
051a6f1f67 | ||
|
|
3f7eb981e6 | ||
|
|
8ca152262e | ||
|
|
f17501f0e5 | ||
|
|
6cc8d7de05 | ||
|
|
d4f6db8479 | ||
|
|
7b22a8d966 | ||
|
|
c5cebeaa4f | ||
|
|
f1cf6c7be8 | ||
|
|
80303ddde5 | ||
|
|
89ee62ea14 | ||
|
|
a1959d20f3 | ||
|
|
810cb73203 | ||
|
|
38b9ed7fcd | ||
|
|
35b8f2375f | ||
|
|
aa619b5f93 | ||
|
|
c03d3032aa | ||
|
|
191cdd2bdc | ||
|
|
9d5c8759e5 | ||
|
|
a4b5c6e028 | ||
|
|
113b606091 | ||
|
|
a69e031835 | ||
|
|
ba38055bed | ||
|
|
3c086e8ff0 | ||
|
|
f9c26d8684 | ||
|
|
2ed9789f9c | ||
|
|
282108942b | ||
|
|
6c107a0d4e | ||
|
|
6e4ab6faae | ||
|
|
e8016edfc1 | ||
|
|
30839f7c2c | ||
|
|
89459a6c2e | ||
|
|
c49db8c083 | ||
|
|
fa6588fa80 | ||
|
|
7d74256664 | ||
|
|
2210660ae4 | ||
|
|
f2fc93b7f8 | ||
|
|
d54a2d9486 | ||
|
|
88015d6d4b | ||
|
|
f1ff75b846 | ||
|
|
1123755dc7 | ||
|
|
6a4f379ab7 | ||
|
|
9369cde9a3 | ||
|
|
a1d985b4ca | ||
|
|
b26854d3d7 | ||
|
|
cbb4b4c7bb | ||
|
|
1a1aa746d5 | ||
|
|
b4f5b5b98e | ||
|
|
fa3b651ee1 | ||
|
|
91d079be1e | ||
|
|
d16ea0a7ea | ||
|
|
6133bf6297 | ||
|
|
14e1420b7e | ||
|
|
10568a0270 | ||
|
|
53da058fe1 | ||
|
|
4d199fb03b | ||
|
|
f04cbbd99c | ||
|
|
e572ba8b6e | ||
|
|
45974e88be | ||
|
|
8140bf3e52 | ||
|
|
4570965af2 | ||
|
|
fcd250a557 | ||
|
|
b92e42e685 | ||
|
|
5daccf4268 | ||
|
|
e0903a0789 | ||
|
|
bcf1546fc2 | ||
|
|
92f53be666 | ||
|
|
8ae0bd7f16 | ||
|
|
754159e2bc | ||
|
|
3f817d655c | ||
|
|
572837d7ba | ||
|
|
fb8dcf4025 | ||
|
|
c7b77b5555 | ||
|
|
fe1ac8564a | ||
|
|
9230390937 | ||
|
|
14f926124d | ||
|
|
76e8489304 | ||
|
|
0e0b1973f0 | ||
|
|
a6666a4fbd | ||
|
|
3a66db200d |
5
.gitignore
vendored
5
.gitignore
vendored
@@ -9,10 +9,13 @@ CVS
|
||||
*.o
|
||||
linux/ccextractor
|
||||
linux/depend
|
||||
windows/debug/*
|
||||
windows/debug/**
|
||||
windows/release/**
|
||||
|
||||
####
|
||||
# Visual Studio project Ignored files
|
||||
|
||||
*.suo
|
||||
*.sdf
|
||||
*.opensdf
|
||||
*.user
|
||||
|
||||
@@ -4,7 +4,7 @@ MAINTAINER = Marc Espie <espie@openbsd.org>
|
||||
CATEGORIES = multimedia
|
||||
COMMENT = closed caption subtitles extractor
|
||||
HOMEPAGE = http://ccextractor.sourceforge.net/
|
||||
V = 0.75
|
||||
V = 0.77
|
||||
DISTFILES = ccextractor.${V:S/.//}-src.zip
|
||||
MASTER_SITES = ${MASTER_SITE_SOURCEFORGE:=ccextractor/}
|
||||
DISTNAME = ccextractor-$V
|
||||
|
||||
@@ -28,4 +28,17 @@ To do:
|
||||
CCExtractor fully support Asian languages. I do need samples
|
||||
though. No samples, no support.
|
||||
- A few commands are not yet supported, specifically those related
|
||||
to delay.
|
||||
to delay.
|
||||
- Detect and extract captions from MP4 (MOV) files, handled by gpacmp4
|
||||
|
||||
Done (18.08.2015):
|
||||
|
||||
- Major refactoring
|
||||
- Librarized as much as possible (global vars moved to dtvcc context)
|
||||
- Added some control commands support
|
||||
- 16 bit charset support added (not "moved everything to 16-bit", but they could be successfully exported)
|
||||
- SAMI output added
|
||||
- Transcript output added
|
||||
- Timed transcript output added
|
||||
- Added colour support (only one colour/style can be applied for the whole screen at the moment)
|
||||
- Roll up captions handling
|
||||
|
||||
@@ -1,3 +1,36 @@
|
||||
0.79 (2016-01-09)
|
||||
-----------------
|
||||
- Support for Grid Format (g608)
|
||||
- Show Correct number of teletext packet processed
|
||||
- Removed Segfault on incorrect mp4 detection
|
||||
- Remove xml header from transcript format
|
||||
- Help message updated for Teletext
|
||||
- Added --help and -h for help message
|
||||
- Added --nohtmlescape option
|
||||
- Added --noscte20 option
|
||||
|
||||
0.78 (2015-12-12)
|
||||
-----------------
|
||||
- Support to extract Closed Caption from MultiProgram at once.
|
||||
- CEA-708: exporting to SAMI (.smi), Transcript (.txt), Timed Transcript (ttxt) and SubRip (.srt).
|
||||
- CEA-708: 16 bit charset support (tested on Korean).
|
||||
- CEA-708: Roll Up captions handling.
|
||||
- Changed TCP connection protocol (BIN data is now wrapped in packets, added EPG support and keep-alive packets).
|
||||
- TCP connection password prompt is removed. To set connection password use -tcppassword argument instead.
|
||||
- Support ISDB Closed Caption.
|
||||
- Added a new output format, simplexml (used internally by a CCExtractor user, may or may not be useful for
|
||||
anyone else).
|
||||
|
||||
0.77 (2015-06-20)
|
||||
-----------------
|
||||
- Fixed bug in capitalization code ('I' was not being capitalized).
|
||||
- GUI should now run in Windows 8 (using the include .Net runtime, since
|
||||
3.5 cannot be installed in Windows 8 apparently).
|
||||
- Fixed Mac build script, binary is now compiled with support for
|
||||
files over 2 GB.
|
||||
- Fixed bug in PMT code, damaged PMT sections could make CCExtractor
|
||||
crash.
|
||||
|
||||
0.76 (2015-03-28)
|
||||
-----------------
|
||||
- Added basic M2TS support
|
||||
|
||||
58
docs/G608.TXT
Normal file
58
docs/G608.TXT
Normal file
@@ -0,0 +1,58 @@
|
||||
G608
|
||||
====
|
||||
G608 (for grid 608) is generated by CCExtractor by using -out=g608.
|
||||
|
||||
This is a verbose format that exports the contents of the 608 grid verbatim
|
||||
so there's no loss of positioning or colors due the limitations or complexy
|
||||
or other output formats.
|
||||
|
||||
G608 is a text file with a structure based on .srt and looks like this:
|
||||
|
||||
1
|
||||
00:00:02,019 --> 00:00:03,585
|
||||
99999999999999999999999999999999RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR
|
||||
99999999999999999999999999999999RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR
|
||||
99999999999999999999999999999999RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR
|
||||
99999999999999999999999999999999RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR
|
||||
99999999999999999999999999999999RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR
|
||||
99999999999999999999999999999999RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR
|
||||
99999999999999999999999999999999RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR
|
||||
99999999999999999999999999999999RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR
|
||||
99999999999999999999999999999999RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR
|
||||
99999999999999999999999999999999RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR
|
||||
99999999999999999999999999999999RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR
|
||||
99999999999999999999999999999999RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR
|
||||
99999999999999999999999999999999RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR
|
||||
- Previously on The Tudors... 90000000000000000000000000000009RIIIIIIIIIIIIIIIIRRRRRRRRRRRRRRR
|
||||
- Your Holy Father offs you 99000000000000000000000000000999RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR
|
||||
|
||||
For each subtitle frame there's exactly 15 rows (which represent the 15 rows of the 608 screen)
|
||||
of 96 characters each.
|
||||
|
||||
Each row is divided in 3 blocks: 32 characters for the text, 32 characters for the color, and
|
||||
32 characters for the font.
|
||||
|
||||
The possible color values are:
|
||||
COL_WHITE = 0,
|
||||
COL_GREEN = 1,
|
||||
COL_BLUE = 2,
|
||||
COL_CYAN = 3,
|
||||
COL_RED = 4,
|
||||
COL_YELLOW = 5,
|
||||
COL_MAGENTA = 6,
|
||||
COL_USERDEFINED = 7,
|
||||
COL_BLACK = 8,
|
||||
COL_TRANSPARENT = 9
|
||||
|
||||
And the possible font values are:
|
||||
R => Regular
|
||||
I => Italic
|
||||
U => Underlined
|
||||
B => Underlined + italic
|
||||
|
||||
If a 'E' is found in ether color or font that means a bug in CCExtractor. Should you ever get
|
||||
an E please send us a .bin file that causes it.
|
||||
|
||||
This format is intended for post processing tools that need to represent the output of a 608
|
||||
decoder accurately but that don't want to deal with the madness of other more generic subtitle
|
||||
formats.
|
||||
18
docs/OCR.txt
18
docs/OCR.txt
@@ -43,9 +43,10 @@ sudo make install
|
||||
sudo ldconfig
|
||||
|
||||
Note:
|
||||
1) CCExtractor is tested with Tesseract 3.02.02 version.
|
||||
1) CCExtractor is tested with Tesseract 3.04 version but it works with older versions.
|
||||
|
||||
you can download tesseract from https://drive.google.com/folderview?id=0B7l10Bj_LprhQnpSRkpGMGV2eE0&usp=sharing
|
||||
you can download tesseract from https://github.com/tesseract-ocr/tesseract/archive/3.04.00.tar.gz
|
||||
you can download tesseract training data from https://github.com/tesseract-ocr/tessdata/archive/3.04.00.tar.gz
|
||||
|
||||
|
||||
|
||||
@@ -57,11 +58,8 @@ make ENABLE_OCR=yes
|
||||
How to compile ccextractor on Windows with OCR
|
||||
===============================================
|
||||
|
||||
Download prebuild library of leptonica from following link
|
||||
http://www.leptonica.com/source/leptonica-1.68-win32-lib-include-dirs.zip
|
||||
|
||||
Download prebuild library of tesseract from following tesseract official link
|
||||
https://code.google.com/p/tesseract-ocr/downloads/detail?name=tesseract-3.02.02-win32-lib-include-dirs.zip
|
||||
Download prebuild library of leptonica and tesseract from following link
|
||||
https://drive.google.com/file/d/0B2ou7ZfB-2nZOTRtc3hJMHBtUFk/view?usp=sharing
|
||||
|
||||
put the path of libs/include of leptonica and tesseract in library paths.
|
||||
step 1) In visual studio 2013 right click <Project> and select property.
|
||||
@@ -85,12 +83,12 @@ Step 2)Select Configuration properties
|
||||
Step 3)Select Linker in left panel(column)
|
||||
Step 4)Select Input
|
||||
Step 5)Select Additional dependencies in right panel
|
||||
Step 6)Add libtesseract302.lib in new line
|
||||
Step 7)Add liblept168.lib in new line
|
||||
Step 6)Add libtesseract304d.lib in new line
|
||||
Step 7)Add liblept172.lib in new line
|
||||
|
||||
Download language data from following link
|
||||
https://code.google.com/p/tesseract-ocr/downloads/list
|
||||
after downloading the tesseract-ocr-3.02.eng.tar.gz extract the tar file and put
|
||||
tessdata folder where you have kept ccextractor executable
|
||||
|
||||
Copy the tesseract and leptonica dll in the folder of executable or in system32.
|
||||
Copy the tesseract and leptonica dll from lib folder downloaded from above link to folder of executable or in system32.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
ccextractor, 0.75
|
||||
ccextractor, 0.79
|
||||
-----------------
|
||||
Authors: Carlos Fernández (cfsmp3), Volker Quetschke.
|
||||
Maintainer: cfsmp3
|
||||
@@ -18,6 +18,15 @@ Google Summer of Code 2014 students
|
||||
- Ruslan KuchumoV
|
||||
- Anshul Maheshwari
|
||||
|
||||
Google Summer of Code 2015 students
|
||||
- Willem van iseghem
|
||||
- Ruslan Kuchumov
|
||||
- Anshul Maheshwari
|
||||
- Nurendra Choudhary
|
||||
- Oleg Kiselev
|
||||
- Vasanth Kalingeri
|
||||
|
||||
|
||||
License
|
||||
-------
|
||||
GPL 2.0.
|
||||
|
||||
@@ -24,5 +24,8 @@ Step 5) Use CCextractor as you would like
|
||||
If you want to build CCExtractor with FFMpeg you need to pass
|
||||
cmake -DWITH_FFMPEG=ON ../src/
|
||||
|
||||
If you want to build CCExtractor with OCR you need to pass
|
||||
cmake -DWITH_OCR=ON ../src/
|
||||
|
||||
Hint for looking all the things you want to set from outside
|
||||
cmake -LAH ../src/
|
||||
|
||||
@@ -42,7 +42,7 @@ INSTLALL_PROGRAM = $(INSTLALL)
|
||||
DESTDIR = /usr/bin
|
||||
|
||||
ifeq ($(ENABLE_OCR),yes)
|
||||
CFLAGS+=-DENABLE_OCR
|
||||
CFLAGS+=-DENABLE_OCR -DPNG_NO_CONFIG_H
|
||||
TESS_LDFLAGS+= $(shell pkg-config --libs tesseract)
|
||||
LEPT_LDFLAGS+= $(shell pkg-config --libs lept)
|
||||
|
||||
@@ -93,12 +93,15 @@ $(OBJS_DIR)/%.o: %.c
|
||||
$(OBJS_DIR)/%.o: %.cpp
|
||||
$(CC) -c $(ALL_FLAGS) $(INCLUDE) $(CFLAGS) $< -o $@ -I../src/gpacmp4
|
||||
|
||||
$(OBJS_DIR)/ccextractor.o: ccextractor.c
|
||||
$(CC) -c $(ALL_FLAGS) $(INCLUDE) $(CFLAGS) -O0 $< -o $@
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
rm $(TARGET) 2>/dev/null || true
|
||||
rm $(OBJS_CCX) $(OBJS_PNG) $(OBJS_ZLIB) $(OBJS_GPACMP4) $(OBJS) 2>/dev/null || true
|
||||
rm -rd $(OBJS_DIR) 2>/dev/null || true
|
||||
rm .depend 2>/dev/null || true
|
||||
rm -rf $(TARGET) 2>/dev/null || true
|
||||
rm -rf $(OBJS_CCX) $(OBJS_PNG) $(OBJS_ZLIB) $(OBJS_GPACMP4) $(OBJS) 2>/dev/null || true
|
||||
rm -rdf $(OBJS_DIR) 2>/dev/null || true
|
||||
rm -rf .depend 2>/dev/null || true
|
||||
|
||||
.PHONY: install
|
||||
install: $(TARGET)
|
||||
@@ -110,7 +113,7 @@ uninstall:
|
||||
|
||||
.PHONY: depend dep
|
||||
depend dep:
|
||||
$(CC) $(CFLAGS) -E -MM $(SRCS_C) $(SRCS_PNG) $(SRCS_ZLIB) $(SRCS_CXX) \
|
||||
$(CC) $(CFLAGS) $(INCLUDE) -E -MM $(SRCS_C) $(SRCS_PNG) $(SRCS_ZLIB) $(SRCS_CCX) \
|
||||
$(SRCS_GPACMP4_C) $(SRCS_GPACMP4_CPP) |\
|
||||
sed 's/^[a-zA-Z_0-9]*.o/$(OBJS_DIR)\/&/' > .depend
|
||||
|
||||
|
||||
11
linux/build
11
linux/build
@@ -1,2 +1,11 @@
|
||||
#!/bin/bash
|
||||
gcc -std=gnu99 -Wno-write-strings -DGPAC_CONFIG_LINUX -D_FILE_OFFSET_BITS=64 -I../src/lib_ccx/ -I../src/gpacmp4/ -I../src/libpng/ -I../src/zlib/ -o ccextractor $(find ../src/ -name '*.cpp') $(find ../src/ -name '*.c') -lm -zmuldefs
|
||||
BLD_FLAGS="-std=gnu99 -Wno-write-strings -DGPAC_CONFIG_LINUX -D_FILE_OFFSET_BITS=64"
|
||||
BLD_INCLUDE="-I../src/lib_ccx/ -I../src/gpacmp4/ -I../src/libpng/ -I../src/zlib/"
|
||||
SRC_LIBPNG="$(find ../src/libpng/ -name '*.c')"
|
||||
SRC_ZLIB="$(find ../src/zlib/ -name '*.c')"
|
||||
SRC_CCX="$(find ../src/lib_ccx/ -name '*.c')"
|
||||
SRC_GPAC="$(find ../src/gpacmp4/ -name '*.c')"
|
||||
BLD_SOURCES="../src/ccextractor.c $SRC_CCX $SRC_GPAC $SRC_ZLIB $SRC_LIBPNG"
|
||||
BLD_LINKER="-lm -zmuldefs"
|
||||
|
||||
gcc $BLD_FLAGS $BLD_INCLUDE -o ccextractor $BLD_SOURCES $BLD_LINKER
|
||||
@@ -1,2 +1,11 @@
|
||||
#!/bin/bash
|
||||
gcc -g -std=gnu99 -Wno-write-strings -DGPAC_CONFIG_LINUX -D_FILE_OFFSET_BITS=64 -I../src/gpacmp4/ -I../src/libpng/ -I../src/zlib/ -o ccextractor $(find ../src/ -name '*.cpp') $(find ../src/ -name '*.c') -lm -zmuldefs
|
||||
BLD_FLAGS="-g -std=gnu99 -Wno-write-strings -DGPAC_CONFIG_LINUX -D_FILE_OFFSET_BITS=64"
|
||||
BLD_INCLUDE="-I../src/lib_ccx/ -I../src/gpacmp4/ -I../src/libpng/ -I../src/zlib/"
|
||||
SRC_LIBPNG="$(find ../src/libpng/ -name '*.c')"
|
||||
SRC_ZLIB="$(find ../src/zlib/ -name '*.c')"
|
||||
SRC_CCX="$(find ../src/lib_ccx/ -name '*.c')"
|
||||
SRC_GPAC="$(find ../src/gpacmp4/ -name '*.c')"
|
||||
BLD_SOURCES="../src/ccextractor.c $SRC_CCX $SRC_GPAC $SRC_ZLIB $SRC_LIBPNG"
|
||||
BLD_LINKER="-lm -zmuldefs"
|
||||
|
||||
gcc $BLD_FLAGS $BLD_INCLUDE -o ccextractor $BLD_SOURCES $BLD_LINKER
|
||||
@@ -1 +1,2 @@
|
||||
gcc -std=gnu99 -Wno-write-strings -Dfopen64=fopen -Dopen64=open -Dlseek64=lseek -I ../src/gpacmp4 -I ../src/lib_ccx -I ../src/libpng -I ../src/zlib -o ccextractor $(find ../src/ -name '*.cpp' \! -name 'win_*') $(find ../src/ -name '*.c' \! -name 'win_*') -liconv
|
||||
gcc -std=gnu99 -Wno-write-strings -DGPAC_CONFIG_DARWIN -D_FILE_OFFSET_BITS=64 -Dfopen64=fopen -Dopen64=open -Dlseek64=lseek -I ../src/gpacmp4 -I ../src/lib_ccx -I ../src/libpng -I ../src/zlib -o ccextractor $(find ../src/ -name '*.cpp' \! -name 'win_*') $(find ../src/ -name '*.c' \! -name 'win_*') -liconv
|
||||
|
||||
|
||||
@@ -2,10 +2,12 @@ cmake_minimum_required (VERSION 3.0.2)
|
||||
|
||||
project (CCExtractor)
|
||||
|
||||
option(WITH_FFMPEG "Build using FFmpeg demuxer and decoder" OFF)
|
||||
option (WITH_FFMPEG "Build using FFmpeg demuxer and decoder" OFF)
|
||||
option (WITH_OCR "Build with OCR (Optical Character Recognition) feature" OFF)
|
||||
|
||||
#Version number
|
||||
set (CCEXTRACTOR_VERSION_MAJOR 0)
|
||||
set (CCEXTRACTOR_VERSION_MINOR 75)
|
||||
set (CCEXTRACTOR_VERSION_MINOR 77)
|
||||
|
||||
# configure a header file to pass some of the CMake settings
|
||||
# to the source code
|
||||
@@ -18,37 +20,69 @@ configure_file (
|
||||
include_directories ("${PROJECT_SOURCE_DIR}")
|
||||
include_directories ("${PROJECT_SOURCE_DIR}/lib_ccx")
|
||||
include_directories ("${PROJECT_SOURCE_DIR}/gpacmp4/")
|
||||
include_directories ("${PROJECT_SOURCE_DIR}/libccx_common/")
|
||||
|
||||
#Adding some platform specific library path
|
||||
LINK_DIRECTORIES(/opt/local/lib)
|
||||
LINK_DIRECTORIES(/usr/local/lib)
|
||||
link_directories (/opt/local/lib)
|
||||
link_directories (/usr/local/lib)
|
||||
|
||||
SET (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O0 -Wall -g -std=gnu99 -Wno-write-strings -D_FILE_OFFSET_BITS=64")
|
||||
set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O0 -Wall -g -std=gnu99 -Wno-write-strings -D_FILE_OFFSET_BITS=64")
|
||||
add_subdirectory (lib_ccx)
|
||||
|
||||
AUX_SOURCE_DIRECTORY(${PROJECT_SOURCE_DIR} SOURCEFILE)
|
||||
aux_source_directory (${PROJECT_SOURCE_DIR} SOURCEFILE)
|
||||
set (EXTRA_LIBS ${EXTRA_LIBS} ccx)
|
||||
set (EXTRA_LIBS ${EXTRA_LIBS} png)
|
||||
set (EXTRA_LIBS ${EXTRA_LIBS} m)
|
||||
|
||||
IF(${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
|
||||
set (EXTRA_LIBS ${EXTRA_LIBS} iconv)
|
||||
ENDIF(${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
|
||||
|
||||
|
||||
find_package (PkgConfig)
|
||||
if(PKG_CONFIG_FOUND)
|
||||
pkg_check_modules(PNG libpng )
|
||||
if(PNG_FOUND)
|
||||
set (EXTRA_LIBS ${EXTRA_LIBS} png)
|
||||
else (PNG_FOUND)
|
||||
include_directories ("${PROJECT_SOURCE_DIR}/libpng/")
|
||||
aux_source_directory ("${PROJECT_SOURCE_DIR}/libpng/" SOURCEFILE)
|
||||
aux_source_directory ("${PROJECT_SOURCE_DIR}/zlib/" SOURCEFILE)
|
||||
endif(PNG_FOUND)
|
||||
endif (PKG_CONFIG_FOUND)
|
||||
|
||||
########################################################
|
||||
# Build using FFmpeg libraries
|
||||
#
|
||||
if (WITH_FFMPEG)
|
||||
########################################################
|
||||
|
||||
find_package(PkgConfig)
|
||||
pkg_check_modules(AVFORMAT REQUIRED libavformat)
|
||||
pkg_check_modules(AVUTIL REQUIRED libavutil)
|
||||
pkg_check_modules(AVCODEC REQUIRED libavcodec)
|
||||
set (EXTRA_LIBS ${EXTRA_LIBS} ${AVFORMAT_STATIC_LIBRARIES} )
|
||||
set (EXTRA_LIBS ${EXTRA_LIBS} ${AVUTIL_STATIC_LIBRARIES} )
|
||||
set (EXTRA_LIBS ${EXTRA_LIBS} ${AVCODEC_STATIC_LIBRARIES} )
|
||||
if (PKG_CONFIG_FOUND AND WITH_FFMPEG)
|
||||
|
||||
SET (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DENABLE_FFMPEG")
|
||||
endif (WITH_FFMPEG)
|
||||
pkg_check_modules (AVFORMAT REQUIRED libavformat)
|
||||
pkg_check_modules (AVUTIL REQUIRED libavutil)
|
||||
pkg_check_modules (AVCODEC REQUIRED libavcodec)
|
||||
|
||||
add_executable(ccextractor ${SOURCEFILE})
|
||||
set (EXTRA_LIBS ${EXTRA_LIBS} ${AVFORMAT_STATIC_LIBRARIES})
|
||||
set (EXTRA_LIBS ${EXTRA_LIBS} ${AVUTIL_STATIC_LIBRARIES})
|
||||
set (EXTRA_LIBS ${EXTRA_LIBS} ${AVCODEC_STATIC_LIBRARIES})
|
||||
|
||||
set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DENABLE_FFMPEG")
|
||||
endif (PKG_CONFIG_FOUND AND WITH_FFMPEG)
|
||||
|
||||
########################################################
|
||||
# Build with OCR using leptonica and tesseract libraries
|
||||
########################################################
|
||||
|
||||
if (WITH_OCR)
|
||||
find_package(PkgConfig)
|
||||
|
||||
pkg_check_modules (TESSERACT REQUIRED tesseract)
|
||||
pkg_check_modules (LEPTONICA REQUIRED lept)
|
||||
|
||||
set (EXTRA_LIBS ${EXTRA_LIBS} ${TESSERACT_STATIC_LIBRARIES})
|
||||
set (EXTRA_LIBS ${EXTRA_LIBS} ${LEPTONICA_STATIC_LIBRARIES})
|
||||
|
||||
set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DENABLE_OCR ${TESSERACT_CFLAGS} ${LEPTONICA_CFLAGS}")
|
||||
endif (WITH_OCR)
|
||||
|
||||
add_executable (ccextractor ${SOURCEFILE})
|
||||
target_link_libraries (ccextractor ${EXTRA_LIBS})
|
||||
|
||||
install (TARGETS ccextractor DESTINATION bin)
|
||||
|
||||
@@ -10,8 +10,7 @@ License: GPL 2.0
|
||||
#include <signal.h>
|
||||
#include "ffmpeg_intgr.h"
|
||||
#include "ccx_common_option.h"
|
||||
|
||||
void xds_cea608_test();
|
||||
#include "ccx_mp4.h"
|
||||
|
||||
struct lib_ccx_ctx *signal_ctx;
|
||||
void sigint_handler()
|
||||
@@ -22,118 +21,49 @@ void sigint_handler()
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
|
||||
struct ccx_s_options ccx_options;
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
char *c;
|
||||
struct encoder_ctx enc_ctx[2];
|
||||
struct cc_subtitle dec_sub;
|
||||
#ifdef ENABLE_FFMPEG
|
||||
void *ffmpeg_ctx = NULL;
|
||||
#endif
|
||||
struct lib_ccx_ctx *ctx;
|
||||
struct lib_cc_decode *dec_ctx = NULL;
|
||||
|
||||
int ret = 0;
|
||||
enum ccx_stream_mode_enum stream_mode;
|
||||
|
||||
init_options (&ccx_options);
|
||||
|
||||
parse_configuration(&ccx_options);
|
||||
parse_parameters (&ccx_options, argc, argv);
|
||||
|
||||
// Initialize libraries
|
||||
ctx = init_libraries(&ccx_options);
|
||||
dec_ctx = ctx->dec_ctx;
|
||||
|
||||
|
||||
// Prepare write structures
|
||||
init_write(&ctx->wbout1,ccx_options.wbout1.filename);
|
||||
init_write(&ctx->wbout2,ccx_options.wbout2.filename);
|
||||
|
||||
|
||||
int show_myth_banner = 0;
|
||||
|
||||
memset (&cea708services[0],0,CCX_DECODERS_708_MAX_SERVICES*sizeof (int)); // Cannot (yet) be moved because it's needed in parse_parameters.
|
||||
memset (&dec_sub, 0,sizeof(dec_sub));
|
||||
|
||||
|
||||
if (ctx->num_input_files==0 && ccx_options.input_source==CCX_DS_FILE)
|
||||
ret = parse_parameters (&ccx_options, argc, argv);
|
||||
if (ret == EXIT_NO_INPUT_FILES)
|
||||
{
|
||||
usage ();
|
||||
fatal (EXIT_NO_INPUT_FILES, "(This help screen was shown because there were no input files)\n");
|
||||
}
|
||||
if (ctx->num_input_files>1 && ccx_options.live_stream)
|
||||
else if (ret == EXIT_WITH_HELP)
|
||||
{
|
||||
fatal(EXIT_TOO_MANY_INPUT_FILES, "Live stream mode accepts only one input file.\n");
|
||||
return EXIT_OK;
|
||||
}
|
||||
if (ctx->num_input_files && ccx_options.input_source==CCX_DS_NETWORK)
|
||||
else if (ret != EXIT_OK)
|
||||
{
|
||||
fatal(EXIT_TOO_MANY_INPUT_FILES, "UDP mode is not compatible with input files.\n");
|
||||
}
|
||||
if (ccx_options.input_source==CCX_DS_NETWORK || ccx_options.input_source==CCX_DS_TCP)
|
||||
{
|
||||
ccx_options.buffer_input=1; // Mandatory, because each datagram must be read complete.
|
||||
}
|
||||
if (ctx->num_input_files && ccx_options.input_source==CCX_DS_TCP)
|
||||
{
|
||||
fatal(EXIT_TOO_MANY_INPUT_FILES, "TCP mode is not compatible with input files.\n");
|
||||
exit(ret);
|
||||
}
|
||||
// Initialize libraries
|
||||
ctx = init_libraries(&ccx_options);
|
||||
if (!ctx && errno == ENOMEM)
|
||||
fatal (EXIT_NOT_ENOUGH_MEMORY, "Not enough memory\n");
|
||||
else if (!ctx && errno == EINVAL)
|
||||
fatal (CCX_COMMON_EXIT_BUG_BUG, "Invalid option to CCextractor Library\n");
|
||||
else if (!ctx && errno == EPERM)
|
||||
fatal (CCX_COMMON_EXIT_FILE_CREATION_FAILED, "Unable to create Output File\n");
|
||||
else if (!ctx && errno == EACCES)
|
||||
fatal (CCX_COMMON_EXIT_FILE_CREATION_FAILED, "Unable to create Output File\n");
|
||||
else if (!ctx)
|
||||
fatal (EXIT_NOT_CLASSIFIED, "Unable to create Library Context %d\n",errno);
|
||||
|
||||
if (ctx->num_input_files > 0)
|
||||
{
|
||||
ctx->wbout1.multiple_files = 1;
|
||||
ctx->wbout1.first_input_file = ctx->inputfile[0];
|
||||
ctx->wbout2.multiple_files = 1;
|
||||
ctx->wbout2.first_input_file = ctx->inputfile[0];
|
||||
}
|
||||
int show_myth_banner = 0;
|
||||
|
||||
// teletext page number out of range
|
||||
if ((tlt_config.page != 0) && ((tlt_config.page < 100) || (tlt_config.page > 899))) {
|
||||
fatal (EXIT_NOT_CLASSIFIED, "Teletext page number could not be lower than 100 or higher than 899\n");
|
||||
}
|
||||
|
||||
if (ccx_options.output_filename!=NULL)
|
||||
{
|
||||
// Use the given output file name for the field specified by
|
||||
// the -1, -2 switch. If -12 is used, the filename is used for
|
||||
// field 1.
|
||||
if (ccx_options.extract==2)
|
||||
ctx->wbout2.filename=ccx_options.output_filename;
|
||||
else
|
||||
ctx->wbout1.filename=ccx_options.output_filename;
|
||||
}
|
||||
|
||||
switch (ccx_options.write_format)
|
||||
{
|
||||
case CCX_OF_RAW:
|
||||
ctx->extension = ".raw";
|
||||
break;
|
||||
case CCX_OF_SRT:
|
||||
ctx->extension = ".srt";
|
||||
break;
|
||||
case CCX_OF_SAMI:
|
||||
ctx->extension = ".smi";
|
||||
break;
|
||||
case CCX_OF_SMPTETT:
|
||||
ctx->extension = ".ttml";
|
||||
break;
|
||||
case CCX_OF_TRANSCRIPT:
|
||||
ctx->extension = ".txt";
|
||||
break;
|
||||
case CCX_OF_RCWT:
|
||||
ctx->extension = ".bin";
|
||||
break;
|
||||
case CCX_OF_SPUPNG:
|
||||
ctx->extension = ".xml";
|
||||
break;
|
||||
case CCX_OF_NULL:
|
||||
ctx->extension = "";
|
||||
break;
|
||||
case CCX_OF_DVDRAW:
|
||||
ctx->extension = ".dvdraw";
|
||||
break;
|
||||
default:
|
||||
fatal (CCX_COMMON_EXIT_BUG_BUG, "write_format doesn't have any legal value, this is a bug.\n");
|
||||
}
|
||||
params_dump(ctx);
|
||||
|
||||
// default teletext page
|
||||
@@ -142,227 +72,6 @@ int main(int argc, char *argv[])
|
||||
tlt_config.page = ((tlt_config.page / 100) << 8) | (((tlt_config.page / 10) % 10) << 4) | (tlt_config.page % 10);
|
||||
}
|
||||
|
||||
if (ctx->auto_stream==CCX_SM_MCPOODLESRAW && ccx_options.write_format==CCX_OF_RAW)
|
||||
{
|
||||
fatal (EXIT_INCOMPATIBLE_PARAMETERS, "-in=raw can only be used if the output is a subtitle file.\n");
|
||||
}
|
||||
if (ctx->auto_stream==CCX_SM_RCWT && ccx_options.write_format==CCX_OF_RCWT && ccx_options.output_filename==NULL)
|
||||
{
|
||||
fatal (EXIT_INCOMPATIBLE_PARAMETERS,
|
||||
"CCExtractor's binary format can only be used simultaneously for input and\noutput if the output file name is specified given with -o.\n");
|
||||
}
|
||||
|
||||
subline = (unsigned char *) malloc (SUBLINESIZE);
|
||||
|
||||
switch (ccx_options.input_source)
|
||||
{
|
||||
case CCX_DS_FILE:
|
||||
ctx->basefilename = (char *) malloc (strlen (ctx->inputfile[0])+1);
|
||||
break;
|
||||
case CCX_DS_STDIN:
|
||||
ctx->basefilename = (char *) malloc (strlen (ctx->basefilename_for_stdin)+1);
|
||||
break;
|
||||
case CCX_DS_NETWORK:
|
||||
case CCX_DS_TCP:
|
||||
ctx->basefilename = (char *) malloc (strlen (ctx->basefilename_for_network)+1);
|
||||
break;
|
||||
}
|
||||
if (ctx->basefilename == NULL)
|
||||
fatal (EXIT_NOT_ENOUGH_MEMORY, "Not enough memory\n");
|
||||
switch (ccx_options.input_source)
|
||||
{
|
||||
case CCX_DS_FILE:
|
||||
strcpy (ctx->basefilename, ctx->inputfile[0]);
|
||||
break;
|
||||
case CCX_DS_STDIN:
|
||||
strcpy (ctx->basefilename, ctx->basefilename_for_stdin);
|
||||
break;
|
||||
case CCX_DS_NETWORK:
|
||||
case CCX_DS_TCP:
|
||||
strcpy (ctx->basefilename, ctx->basefilename_for_network);
|
||||
break;
|
||||
}
|
||||
|
||||
for (c = ctx->basefilename + strlen(ctx->basefilename) - 1; c>ctx->basefilename &&
|
||||
*c!='.'; c--) {;} // Get last .
|
||||
if (*c=='.')
|
||||
*c=0;
|
||||
|
||||
if (ctx->wbout1.filename==NULL)
|
||||
{
|
||||
ctx->wbout1.filename = (char *) malloc (strlen (ctx->basefilename)+3+strlen (ctx->extension));
|
||||
ctx->wbout1.filename[0]=0;
|
||||
}
|
||||
if (ctx->wbout2.filename==NULL)
|
||||
{
|
||||
ctx->wbout2.filename = (char *) malloc (strlen (ctx->basefilename)+3+strlen (ctx->extension));
|
||||
ctx->wbout2.filename[0]=0;
|
||||
}
|
||||
if (ctx->buffer == NULL || ctx->pesheaderbuf==NULL ||
|
||||
ctx->wbout1.filename == NULL || ctx->wbout2.filename == NULL ||
|
||||
subline==NULL || init_file_buffer() )
|
||||
{
|
||||
fatal (EXIT_NOT_ENOUGH_MEMORY, "Not enough memory\n");
|
||||
}
|
||||
|
||||
if (ccx_options.send_to_srv)
|
||||
{
|
||||
connect_to_srv(ccx_options.srv_addr, ccx_options.srv_port, ccx_options.tcp_desc);
|
||||
}
|
||||
|
||||
if (ccx_options.write_format!=CCX_OF_NULL)
|
||||
{
|
||||
/* # DVD format uses one raw file for both fields, while Broadcast requires 2 */
|
||||
if (ccx_options.write_format==CCX_OF_DVDRAW)
|
||||
{
|
||||
if (ctx->wbout1.filename[0]==0)
|
||||
{
|
||||
strcpy (ctx->wbout1.filename,ctx->basefilename);
|
||||
strcat (ctx->wbout1.filename,".raw");
|
||||
}
|
||||
if (ctx->cc_to_stdout)
|
||||
{
|
||||
ctx->wbout1.fh=STDOUT_FILENO;
|
||||
mprint ("Sending captions to stdout.\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
mprint ("Creating %s\n", ctx->wbout1.filename);
|
||||
ctx->wbout1.fh=open (ctx->wbout1.filename, O_RDWR | O_CREAT | O_TRUNC | O_BINARY, S_IREAD | S_IWRITE);
|
||||
if (ctx->wbout1.fh==-1)
|
||||
{
|
||||
fatal(CCX_COMMON_EXIT_FILE_CREATION_FAILED, "Failed\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (ctx->cc_to_stdout && ccx_options.extract==12)
|
||||
fatal (EXIT_INCOMPATIBLE_PARAMETERS, "You can't extract both fields to stdout at the same time in broadcast mode.");
|
||||
|
||||
if (ccx_options.write_format == CCX_OF_SPUPNG && ctx->cc_to_stdout)
|
||||
fatal (EXIT_INCOMPATIBLE_PARAMETERS, "You cannot use -out=spupng with -stdout.");
|
||||
|
||||
if (ccx_options.extract!=2)
|
||||
{
|
||||
if (ctx->cc_to_stdout)
|
||||
{
|
||||
ctx->wbout1.fh=STDOUT_FILENO;
|
||||
mprint ("Sending captions to stdout.\n");
|
||||
}
|
||||
else if (!ccx_options.send_to_srv)
|
||||
{
|
||||
if (ctx->wbout1.filename[0]==0)
|
||||
{
|
||||
strcpy (ctx->wbout1.filename,ctx->basefilename);
|
||||
if (ccx_options.extract==12) // _1 only added if there's two files
|
||||
strcat (ctx->wbout1.filename,"_1");
|
||||
strcat (ctx->wbout1.filename,(const char *) ctx->extension);
|
||||
}
|
||||
mprint ("Creating %s\n", ctx->wbout1.filename);
|
||||
ctx->wbout1.fh=open (ctx->wbout1.filename, O_RDWR | O_CREAT | O_TRUNC | O_BINARY, S_IREAD | S_IWRITE);
|
||||
if (ctx->wbout1.fh==-1)
|
||||
{
|
||||
fatal(CCX_COMMON_EXIT_FILE_CREATION_FAILED, "Failed (errno=%d)\n", errno);
|
||||
}
|
||||
}
|
||||
switch (ccx_options.write_format)
|
||||
{
|
||||
case CCX_OF_RAW:
|
||||
init_encoder(enc_ctx, &ctx->wbout1);
|
||||
writeraw(BROADCAST_HEADER, sizeof(BROADCAST_HEADER), &ctx->wbout1);
|
||||
break;
|
||||
case CCX_OF_DVDRAW:
|
||||
break;
|
||||
case CCX_OF_RCWT:
|
||||
if (init_encoder(enc_ctx, &ctx->wbout1))
|
||||
fatal(EXIT_NOT_ENOUGH_MEMORY, "Not enough memory\n");
|
||||
set_encoder_subs_delay(enc_ctx, ctx->subs_delay);
|
||||
set_encoder_last_displayed_subs_ms(enc_ctx, ctx->last_displayed_subs_ms);
|
||||
set_encoder_startcredits_displayed(enc_ctx, ctx->startcredits_displayed);
|
||||
break;
|
||||
default:
|
||||
if (!ccx_options.no_bom){
|
||||
if (ccx_options.encoding == CCX_ENC_UTF_8){ // Write BOM
|
||||
writeraw(UTF8_BOM, sizeof(UTF8_BOM), &ctx->wbout1);
|
||||
}
|
||||
if (ccx_options.encoding == CCX_ENC_UNICODE){ // Write BOM
|
||||
writeraw(LITTLE_ENDIAN_BOM, sizeof(LITTLE_ENDIAN_BOM), &ctx->wbout1);
|
||||
}
|
||||
}
|
||||
if (init_encoder(enc_ctx, &ctx->wbout1)){
|
||||
fatal(EXIT_NOT_ENOUGH_MEMORY, "Not enough memory\n");
|
||||
}
|
||||
set_encoder_subs_delay(enc_ctx, ctx->subs_delay);
|
||||
set_encoder_last_displayed_subs_ms(enc_ctx, ctx->last_displayed_subs_ms);
|
||||
set_encoder_startcredits_displayed(enc_ctx, ctx->startcredits_displayed);
|
||||
}
|
||||
}
|
||||
if (ccx_options.extract == 12 && ccx_options.write_format != CCX_OF_RAW)
|
||||
mprint (" and \n");
|
||||
if (ccx_options.extract!=1)
|
||||
{
|
||||
if (ctx->cc_to_stdout)
|
||||
{
|
||||
ctx->wbout1.fh=STDOUT_FILENO;
|
||||
mprint ("Sending captions to stdout.\n");
|
||||
}
|
||||
else if(ccx_options.write_format == CCX_OF_RAW
|
||||
&& ccx_options.extract == 12)
|
||||
{
|
||||
memcpy(&ctx->wbout2, &ctx->wbout1,sizeof(ctx->wbout1));
|
||||
}
|
||||
else if (!ccx_options.send_to_srv)
|
||||
{
|
||||
if (ctx->wbout2.filename[0]==0)
|
||||
{
|
||||
strcpy (ctx->wbout2.filename,ctx->basefilename);
|
||||
if (ccx_options.extract==12) // _ only added if there's two files
|
||||
strcat (ctx->wbout2.filename,"_2");
|
||||
strcat (ctx->wbout2.filename,(const char *) ctx->extension);
|
||||
}
|
||||
mprint ("Creating %s\n", ctx->wbout2.filename);
|
||||
ctx->wbout2.fh=open (ctx->wbout2.filename, O_RDWR | O_CREAT | O_TRUNC | O_BINARY, S_IREAD | S_IWRITE);
|
||||
if (ctx->wbout2.fh==-1)
|
||||
{
|
||||
fatal(CCX_COMMON_EXIT_FILE_CREATION_FAILED, "Failed\n");
|
||||
}
|
||||
if(ccx_options.write_format == CCX_OF_RAW)
|
||||
writeraw (BROADCAST_HEADER,sizeof (BROADCAST_HEADER),&ctx->wbout2);
|
||||
}
|
||||
|
||||
switch (ccx_options.write_format)
|
||||
{
|
||||
case CCX_OF_RAW:
|
||||
case CCX_OF_DVDRAW:
|
||||
break;
|
||||
case CCX_OF_RCWT:
|
||||
if( init_encoder(enc_ctx+1,&ctx->wbout2) )
|
||||
fatal (EXIT_NOT_ENOUGH_MEMORY, "Not enough memory\n");
|
||||
set_encoder_subs_delay(enc_ctx+1, ctx->subs_delay);
|
||||
set_encoder_last_displayed_subs_ms(enc_ctx+1, ctx->last_displayed_subs_ms);
|
||||
set_encoder_startcredits_displayed(enc_ctx+1, ctx->startcredits_displayed);
|
||||
break;
|
||||
default:
|
||||
if (!ccx_options.no_bom){
|
||||
if (ccx_options.encoding == CCX_ENC_UTF_8){ // Write BOM
|
||||
writeraw(UTF8_BOM, sizeof(UTF8_BOM), &ctx->wbout2);
|
||||
}
|
||||
if (ccx_options.encoding == CCX_ENC_UNICODE){ // Write BOM
|
||||
writeraw(LITTLE_ENDIAN_BOM, sizeof(LITTLE_ENDIAN_BOM), &ctx->wbout2);
|
||||
}
|
||||
}
|
||||
if (init_encoder(enc_ctx + 1, &ctx->wbout2)){
|
||||
fatal(EXIT_NOT_ENOUGH_MEMORY, "Not enough memory\n");
|
||||
}
|
||||
set_encoder_subs_delay(enc_ctx+1, ctx->subs_delay);
|
||||
set_encoder_last_displayed_subs_ms(enc_ctx+1, ctx->last_displayed_subs_ms);
|
||||
set_encoder_startcredits_displayed(enc_ctx+1, ctx->startcredits_displayed);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ccx_options.transcript_settings.xds)
|
||||
{
|
||||
if (ccx_options.write_format != CCX_OF_TRANSCRIPT)
|
||||
@@ -372,34 +81,10 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
}
|
||||
|
||||
if (ccx_options.teletext_mode == CCX_TXT_IN_USE) // Here, it would mean it was forced by user
|
||||
telxcc_init(ctx);
|
||||
|
||||
ctx->fh_out_elementarystream = NULL;
|
||||
if (ccx_options.out_elementarystream_filename!=NULL)
|
||||
{
|
||||
if ((ctx->fh_out_elementarystream = fopen (ccx_options.out_elementarystream_filename,"wb"))==NULL)
|
||||
{
|
||||
fatal(CCX_COMMON_EXIT_FILE_CREATION_FAILED, "Unable to open clean file: %s\n", ccx_options.out_elementarystream_filename);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Initialize HDTV caption buffer
|
||||
init_hdcc();
|
||||
|
||||
if (ccx_options.line_terminator_lf)
|
||||
encoded_crlf_length = encode_line(encoded_crlf, (unsigned char *) "\n");
|
||||
else
|
||||
encoded_crlf_length = encode_line(encoded_crlf, (unsigned char *) "\r\n");
|
||||
|
||||
encoded_br_length = encode_line(encoded_br, (unsigned char *) "<br>");
|
||||
|
||||
|
||||
time_t start, final;
|
||||
time(&start);
|
||||
|
||||
dec_ctx->processed_enough=0;
|
||||
if (ccx_options.binary_concat)
|
||||
{
|
||||
ctx->total_inputsize=gettotalfilessize(ctx);
|
||||
@@ -412,7 +97,7 @@ int main(int argc, char *argv[])
|
||||
m_signal(SIGINT, sigint_handler);
|
||||
#endif
|
||||
|
||||
while (switch_to_next_file(ctx, 0) && !dec_ctx->processed_enough)
|
||||
while (switch_to_next_file(ctx, 0))
|
||||
{
|
||||
prepare_for_new_file(ctx);
|
||||
#ifdef ENABLE_FFMPEG
|
||||
@@ -442,11 +127,11 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
else
|
||||
cc_count = len/3;
|
||||
ret = process_cc_data(dec_ctx, bptr, cc_count, &dec_sub);
|
||||
if(ret >= 0 && dec_sub.got_output)
|
||||
ret = process_cc_data(dec_ctx, bptr, cc_count, &dec_ctx->dec_sub);
|
||||
if(ret >= 0 && dec_ctx->dec_sub.got_output)
|
||||
{
|
||||
encode_sub(enc_ctx, &dec_sub);
|
||||
dec_sub.got_output = 0;
|
||||
encode_sub(enc_ctx, &dec_ctx->dec_sub);
|
||||
dec_ctx->dec_sub.got_output = 0;
|
||||
}
|
||||
}while(1);
|
||||
continue;
|
||||
@@ -456,103 +141,29 @@ int main(int argc, char *argv[])
|
||||
mprint ("\rFailed to initialized ffmpeg falling back to legacy\n");
|
||||
}
|
||||
#endif
|
||||
if (ctx->auto_stream == CCX_SM_AUTODETECT)
|
||||
{
|
||||
detect_stream_type(ctx);
|
||||
switch (ctx->stream_mode)
|
||||
{
|
||||
case CCX_SM_ELEMENTARY_OR_NOT_FOUND:
|
||||
mprint ("\rFile seems to be an elementary stream, enabling ES mode\n");
|
||||
break;
|
||||
case CCX_SM_TRANSPORT:
|
||||
mprint ("\rFile seems to be a transport stream, enabling TS mode\n");
|
||||
break;
|
||||
case CCX_SM_PROGRAM:
|
||||
mprint ("\rFile seems to be a program stream, enabling PS mode\n");
|
||||
break;
|
||||
case CCX_SM_ASF:
|
||||
mprint ("\rFile seems to be an ASF, enabling DVR-MS mode\n");
|
||||
break;
|
||||
case CCX_SM_WTV:
|
||||
mprint ("\rFile seems to be a WTV, enabling WTV mode\n");
|
||||
break;
|
||||
case CCX_SM_MCPOODLESRAW:
|
||||
mprint ("\rFile seems to be McPoodle raw data\n");
|
||||
break;
|
||||
case CCX_SM_RCWT:
|
||||
mprint ("\rFile seems to be a raw caption with time data\n");
|
||||
break;
|
||||
case CCX_SM_MP4:
|
||||
mprint ("\rFile seems to be a MP4\n");
|
||||
break;
|
||||
#ifdef WTV_DEBUG
|
||||
case CCX_SM_HEX_DUMP:
|
||||
mprint ("\rFile seems to be an hexadecimal dump\n");
|
||||
break;
|
||||
#endif
|
||||
case CCX_SM_MYTH:
|
||||
case CCX_SM_AUTODETECT:
|
||||
fatal(CCX_COMMON_EXIT_BUG_BUG, "Cannot be reached!");
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ctx->stream_mode=ctx->auto_stream;
|
||||
}
|
||||
|
||||
/* -----------------------------------------------------------------
|
||||
MAIN LOOP
|
||||
----------------------------------------------------------------- */
|
||||
|
||||
// The myth loop autodetect will only be used with ES or PS streams
|
||||
switch (ccx_options.auto_myth)
|
||||
{
|
||||
case 0:
|
||||
// Use whatever stream mode says
|
||||
break;
|
||||
case 1:
|
||||
// Force stream mode to myth
|
||||
ctx->stream_mode=CCX_SM_MYTH;
|
||||
break;
|
||||
case 2:
|
||||
// autodetect myth files, but only if it does not conflict with
|
||||
// the current stream mode
|
||||
switch (ctx->stream_mode)
|
||||
{
|
||||
case CCX_SM_ELEMENTARY_OR_NOT_FOUND:
|
||||
case CCX_SM_PROGRAM:
|
||||
if ( detect_myth(ctx) )
|
||||
{
|
||||
ctx->stream_mode=CCX_SM_MYTH;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
// Keep stream_mode
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
stream_mode = ctx->demux_ctx->get_stream_mode(ctx->demux_ctx);
|
||||
// Disable sync check for raw formats - they have the right timeline.
|
||||
// Also true for bin formats, but -nosync might have created a
|
||||
// broken timeline for debug purposes.
|
||||
// Disable too in MP4, specs doesn't say that there can't be a jump
|
||||
switch (ctx->stream_mode)
|
||||
switch (stream_mode)
|
||||
{
|
||||
case CCX_SM_MCPOODLESRAW:
|
||||
case CCX_SM_RCWT:
|
||||
case CCX_SM_MP4:
|
||||
case CCX_SM_MCPOODLESRAW:
|
||||
case CCX_SM_RCWT:
|
||||
case CCX_SM_MP4:
|
||||
#ifdef WTV_DEBUG
|
||||
case CCX_SM_HEX_DUMP:
|
||||
case CCX_SM_HEX_DUMP:
|
||||
#endif
|
||||
ccx_common_timing_settings.disable_sync_check = 1;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
ccx_common_timing_settings.disable_sync_check = 1;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
switch (ctx->stream_mode)
|
||||
|
||||
/* -----------------------------------------------------------------
|
||||
MAIN LOOP
|
||||
----------------------------------------------------------------- */
|
||||
switch (stream_mode)
|
||||
{
|
||||
case CCX_SM_ELEMENTARY_OR_NOT_FOUND:
|
||||
if (!ccx_options.use_gop_as_pts) // If !0 then the user selected something
|
||||
@@ -565,25 +176,27 @@ int main(int argc, char *argv[])
|
||||
if (!ccx_options.use_gop_as_pts) // If !0 then the user selected something
|
||||
ccx_options.use_gop_as_pts = 0;
|
||||
mprint ("\rAnalyzing data in general mode\n");
|
||||
general_loop(ctx, &enc_ctx);
|
||||
general_loop(ctx);
|
||||
break;
|
||||
case CCX_SM_MCPOODLESRAW:
|
||||
mprint ("\rAnalyzing data in McPoodle raw mode\n");
|
||||
raw_loop(ctx, &enc_ctx);
|
||||
raw_loop(ctx);
|
||||
break;
|
||||
case CCX_SM_RCWT:
|
||||
mprint ("\rAnalyzing data in CCExtractor's binary format\n");
|
||||
rcwt_loop(ctx, &enc_ctx);
|
||||
rcwt_loop(ctx);
|
||||
break;
|
||||
case CCX_SM_MYTH:
|
||||
mprint ("\rAnalyzing data in MythTV mode\n");
|
||||
show_myth_banner = 1;
|
||||
myth_loop(ctx, &enc_ctx);
|
||||
myth_loop(ctx);
|
||||
break;
|
||||
case CCX_SM_MP4:
|
||||
case CCX_SM_MP4:
|
||||
mprint ("\rAnalyzing data with GPAC (MP4 library)\n");
|
||||
close_input_file(ctx); // No need to have it open. GPAC will do it for us
|
||||
processmp4 (ctx, ctx->inputfile[0],&enc_ctx);
|
||||
processmp4 (ctx, &ctx->mp4_cfg, ctx->inputfile[0]);
|
||||
if (ccx_options.print_file_reports)
|
||||
print_file_report(ctx);
|
||||
break;
|
||||
#ifdef WTV_DEBUG
|
||||
case CCX_SM_HEX_DUMP:
|
||||
@@ -596,39 +209,7 @@ int main(int argc, char *argv[])
|
||||
break;
|
||||
}
|
||||
|
||||
mprint("\n");
|
||||
dbg_print(CCX_DMT_DECODER_608, "\nTime stamps after last caption block was written:\n");
|
||||
dbg_print(CCX_DMT_DECODER_608, "Last time stamps: PTS: %s (%+2dF) ",
|
||||
print_mstime( (LLONG) (sync_pts/(MPEG_CLOCK_FREQ/1000)
|
||||
+frames_since_ref_time*1000.0/current_fps) ),
|
||||
frames_since_ref_time);
|
||||
dbg_print(CCX_DMT_DECODER_608, "GOP: %s \n", print_mstime(gop_time.ms) );
|
||||
|
||||
// Blocks since last PTS/GOP time stamp.
|
||||
dbg_print(CCX_DMT_DECODER_608, "Calc. difference: PTS: %s (%+3lldms incl.) ",
|
||||
print_mstime( (LLONG) ((sync_pts-min_pts)/(MPEG_CLOCK_FREQ/1000)
|
||||
+ fts_offset + frames_since_ref_time*1000.0/current_fps)),
|
||||
fts_offset + (LLONG) (frames_since_ref_time*1000.0/current_fps) );
|
||||
dbg_print(CCX_DMT_DECODER_608, "GOP: %s (%+3dms incl.)\n",
|
||||
print_mstime((LLONG)(gop_time.ms
|
||||
-first_gop_time.ms
|
||||
+get_fts_max()-fts_at_gop_start)),
|
||||
(int)(get_fts_max()-fts_at_gop_start));
|
||||
// When padding is active the CC block time should be within
|
||||
// 1000/29.97 us of the differences.
|
||||
dbg_print(CCX_DMT_DECODER_608, "Max. FTS: %s (without caption blocks since then)\n",
|
||||
print_mstime(get_fts_max()));
|
||||
|
||||
if (ctx->stat_hdtv)
|
||||
{
|
||||
mprint ("\rCC type 0: %d (%s)\n", dec_ctx->cc_stats[0], cc_types[0]);
|
||||
mprint ("CC type 1: %d (%s)\n", dec_ctx->cc_stats[1], cc_types[1]);
|
||||
mprint ("CC type 2: %d (%s)\n", dec_ctx->cc_stats[2], cc_types[2]);
|
||||
mprint ("CC type 3: %d (%s)\n", dec_ctx->cc_stats[3], cc_types[3]);
|
||||
}
|
||||
mprint ("\nTotal frames time: %s (%u frames at %.2ffps)\n",
|
||||
print_mstime( (LLONG)(total_frames_count*1000/current_fps) ),
|
||||
total_frames_count, current_fps);
|
||||
#if 0
|
||||
if (ctx->total_pulldownframes)
|
||||
mprint ("incl. pulldown frames: %s (%u frames at %.2ffps)\n",
|
||||
print_mstime( (LLONG)(ctx->total_pulldownframes*1000/current_fps) ),
|
||||
@@ -648,7 +229,7 @@ int main(int argc, char *argv[])
|
||||
- min_pts/(MPEG_CLOCK_FREQ/1000) + fts_offset ));
|
||||
}
|
||||
// dvr-ms files have invalid GOPs
|
||||
if (gop_time.inited && first_gop_time.inited && ctx->stream_mode != CCX_SM_ASF)
|
||||
if (gop_time.inited && first_gop_time.inited && stream_mode != CCX_SM_ASF)
|
||||
{
|
||||
mprint ("\nInitial GOP time: %s\n",
|
||||
print_mstime(first_gop_time.ms));
|
||||
@@ -688,76 +269,71 @@ int main(int argc, char *argv[])
|
||||
mprint("caption is not well understood!\n\n");
|
||||
mprint("Please submit samples to the developers.\n\n\n");
|
||||
}
|
||||
#endif
|
||||
|
||||
// Add one frame as fts_max marks the beginning of the last frame,
|
||||
// but we need the end.
|
||||
fts_global += fts_max + (LLONG) (1000.0/current_fps);
|
||||
// CFS: At least in Hauppage mode, cb_field can be responsible for ALL the
|
||||
// timing (cb_fields having a huge number and fts_now and fts_global being 0 all
|
||||
// the time), so we need to take that into account in fts_global before resetting
|
||||
// counters.
|
||||
if (cb_field1!=0)
|
||||
fts_global += cb_field1*1001/3;
|
||||
else
|
||||
fts_global += cb_field2*1001/3;
|
||||
// Reset counters - This is needed if some captions are still buffered
|
||||
// and need to be written after the last file is processed.
|
||||
cb_field1 = 0; cb_field2 = 0; cb_708 = 0;
|
||||
fts_now = 0;
|
||||
fts_max = 0;
|
||||
list_for_each_entry(dec_ctx, &ctx->dec_ctx_head, list, struct lib_cc_decode)
|
||||
{
|
||||
mprint("\n");
|
||||
dbg_print(CCX_DMT_DECODER_608, "\nTime stamps after last caption block was written:\n");
|
||||
dbg_print(CCX_DMT_DECODER_608, "GOP: %s \n", print_mstime(gop_time.ms) );
|
||||
|
||||
dbg_print(CCX_DMT_DECODER_608, "GOP: %s (%+3dms incl.)\n",
|
||||
print_mstime((LLONG)(gop_time.ms
|
||||
-first_gop_time.ms
|
||||
+get_fts_max(dec_ctx->timing)-fts_at_gop_start)),
|
||||
(int)(get_fts_max(dec_ctx->timing)-fts_at_gop_start));
|
||||
// When padding is active the CC block time should be within
|
||||
// 1000/29.97 us of the differences.
|
||||
dbg_print(CCX_DMT_DECODER_608, "Max. FTS: %s (without caption blocks since then)\n",
|
||||
print_mstime(get_fts_max(dec_ctx->timing)));
|
||||
|
||||
if (dec_ctx->codec == CCX_CODEC_ATSC_CC)
|
||||
{
|
||||
mprint ("\nTotal frames time: %s (%u frames at %.2ffps)\n",
|
||||
print_mstime( (LLONG)(total_frames_count*1000/current_fps) ),
|
||||
total_frames_count, current_fps);
|
||||
}
|
||||
|
||||
if (ctx->stat_hdtv)
|
||||
{
|
||||
mprint ("\rCC type 0: %d (%s)\n", dec_ctx->cc_stats[0], cc_types[0]);
|
||||
mprint ("CC type 1: %d (%s)\n", dec_ctx->cc_stats[1], cc_types[1]);
|
||||
mprint ("CC type 2: %d (%s)\n", dec_ctx->cc_stats[2], cc_types[2]);
|
||||
mprint ("CC type 3: %d (%s)\n", dec_ctx->cc_stats[3], cc_types[3]);
|
||||
}
|
||||
// Add one frame as fts_max marks the beginning of the last frame,
|
||||
// but we need the end.
|
||||
dec_ctx->timing->fts_global += dec_ctx->timing->fts_max + (LLONG) (1000.0/current_fps);
|
||||
// CFS: At least in Hauppage mode, cb_field can be responsible for ALL the
|
||||
// timing (cb_fields having a huge number and fts_now and fts_global being 0 all
|
||||
// the time), so we need to take that into account in fts_global before resetting
|
||||
// counters.
|
||||
if (cb_field1!=0)
|
||||
dec_ctx->timing->fts_global += cb_field1*1001/3;
|
||||
else if (cb_field2!=0)
|
||||
dec_ctx->timing->fts_global += cb_field2*1001/3;
|
||||
else
|
||||
dec_ctx->timing->fts_global += cb_708*1001/3;
|
||||
// Reset counters - This is needed if some captions are still buffered
|
||||
// and need to be written after the last file is processed.
|
||||
cb_field1 = 0; cb_field2 = 0; cb_708 = 0;
|
||||
dec_ctx->timing->fts_now = 0;
|
||||
dec_ctx->timing->fts_max = 0;
|
||||
}
|
||||
|
||||
if(is_decoder_processed_enough(ctx) == CCX_TRUE)
|
||||
break;
|
||||
} // file loop
|
||||
close_input_file(ctx);
|
||||
|
||||
if (ctx->fh_out_elementarystream!=NULL)
|
||||
fclose (ctx->fh_out_elementarystream);
|
||||
|
||||
flushbuffer (ctx, &ctx->wbout1, false);
|
||||
flushbuffer (ctx, &ctx->wbout2, false);
|
||||
|
||||
prepare_for_new_file (ctx); // To reset counters used by handle_end_of_data()
|
||||
|
||||
telxcc_close(ctx);
|
||||
if (ctx->wbout1.fh!=-1)
|
||||
{
|
||||
if (ccx_options.write_format==CCX_OF_SMPTETT || ccx_options.write_format==CCX_OF_SAMI ||
|
||||
ccx_options.write_format==CCX_OF_SRT || ccx_options.write_format==CCX_OF_TRANSCRIPT
|
||||
|| ccx_options.write_format==CCX_OF_SPUPNG )
|
||||
{
|
||||
handle_end_of_data(dec_ctx->context_cc608_field_1, &dec_sub);
|
||||
if (dec_sub.got_output)
|
||||
{
|
||||
encode_sub(enc_ctx,&dec_sub);
|
||||
dec_sub.got_output = 0;
|
||||
}
|
||||
}
|
||||
else if(ccx_options.write_format==CCX_OF_RCWT)
|
||||
{
|
||||
// Write last header and data
|
||||
writercwtdata (dec_ctx, NULL);
|
||||
}
|
||||
dinit_encoder(enc_ctx);
|
||||
}
|
||||
if (ctx->wbout2.fh!=-1)
|
||||
{
|
||||
if (ccx_options.write_format==CCX_OF_SMPTETT || ccx_options.write_format==CCX_OF_SAMI ||
|
||||
ccx_options.write_format==CCX_OF_SRT || ccx_options.write_format==CCX_OF_TRANSCRIPT
|
||||
|| ccx_options.write_format==CCX_OF_SPUPNG )
|
||||
{
|
||||
handle_end_of_data(dec_ctx->context_cc608_field_2, &dec_sub);
|
||||
if (dec_sub.got_output)
|
||||
{
|
||||
encode_sub(enc_ctx,&dec_sub);
|
||||
dec_sub.got_output = 0;
|
||||
}
|
||||
}
|
||||
dinit_encoder(enc_ctx+1);
|
||||
}
|
||||
flushbuffer (ctx, &ctx->wbout1,true);
|
||||
flushbuffer (ctx, &ctx->wbout2,true);
|
||||
|
||||
time (&final);
|
||||
|
||||
long proc_time=(long) (final-start);
|
||||
mprint ("\rDone, processing time = %ld seconds\n", proc_time);
|
||||
#if 0
|
||||
if (proc_time>0)
|
||||
{
|
||||
LLONG ratio=(get_fts_max()/10)/proc_time;
|
||||
@@ -766,21 +342,14 @@ int main(int argc, char *argv[])
|
||||
mprint ("Performance (real length/process time) = %u.%02u\n",
|
||||
s1, s2);
|
||||
}
|
||||
dbg_print(CCX_DMT_708, "The 708 decoder was reset [%d] times.\n",resets_708);
|
||||
if (ccx_options.teletext_mode == CCX_TXT_IN_USE)
|
||||
mprint ( "Teletext decoder: %"PRIu32" packets processed, %"PRIu32" SRT frames written.\n", tlt_packet_counter, tlt_frames_produced);
|
||||
#endif
|
||||
dbg_print(CCX_DMT_708, "[CEA-708] The 708 decoder was reset [%d] times.\n", ctx->freport.data_from_708->reset_count);
|
||||
|
||||
if (dec_ctx->processed_enough)
|
||||
if (is_decoder_processed_enough(ctx) == CCX_TRUE)
|
||||
{
|
||||
mprint ("\rNote: Processing was cancelled before all data was processed because\n");
|
||||
mprint ("\rone or more user-defined limits were reached.\n");
|
||||
}
|
||||
if (ccblocks_in_avc_lost>0)
|
||||
{
|
||||
mprint ("Total caption blocks received: %d\n", ccblocks_in_avc_total);
|
||||
mprint ("Total caption blocks lost: %d\n", ccblocks_in_avc_lost);
|
||||
}
|
||||
|
||||
mprint ("This is beta software. Report issues to carlos at ccextractor org...\n");
|
||||
if (show_myth_banner)
|
||||
{
|
||||
|
||||
@@ -6159,6 +6159,12 @@ static void gf_isom_check_sample_desc(GF_TrackBox *trak)
|
||||
u32 i;
|
||||
|
||||
i=0;
|
||||
if(!trak->Media->information->sampleTable->SampleDescription)
|
||||
{
|
||||
printf("\nCCEXTRACTOR: Ignore table without description\n");
|
||||
return;
|
||||
}
|
||||
|
||||
while ((a = (GF_UnknownBox*)gf_list_enum(trak->Media->information->sampleTable->SampleDescription->boxList, &i))) {
|
||||
switch (a->type) {
|
||||
case GF_ISOM_BOX_TYPE_MP4S:
|
||||
|
||||
@@ -227,7 +227,8 @@ enum
|
||||
GF_ISOM_SUBTYPE_LSR1 = GF_4CC( 'l', 's', 'r', '1' ),
|
||||
|
||||
/* CAPTIONS */
|
||||
GF_ISOM_SUBTYPE_C608 = GF_4CC ('c', '6', '0', '8' )
|
||||
GF_ISOM_SUBTYPE_C608 = GF_4CC( 'c', '6', '0', '8' ),
|
||||
GF_ISOM_SUBTYPE_C708 = GF_4CC( 'c', '7', '0', '8' )
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -1736,15 +1736,15 @@ GF_Err gf_isom_get_chunks_infos(GF_ISOFile *movie, u32 trackNumber, u32 *dur_min
|
||||
chunk_dur += dur;
|
||||
stbl_GetSampleSize(trak->Media->information->sampleTable->SampleSize, k+sample_idx, &size);
|
||||
chunk_size += size;
|
||||
|
||||
|
||||
}
|
||||
if (dmin>chunk_dur) dmin = chunk_dur;
|
||||
if (dmax<chunk_dur) dmax = chunk_dur;
|
||||
davg += chunk_dur;
|
||||
if (smin>chunk_size) smin = chunk_size;
|
||||
if (smax<chunk_size) smax = chunk_size;
|
||||
savg += chunk_dur;
|
||||
|
||||
savg += chunk_size;
|
||||
|
||||
tot_chunks ++;
|
||||
sample_idx += stsc->entries[i].samplesPerChunk;
|
||||
if (i+1==stsc->nb_entries) break;
|
||||
@@ -1752,8 +1752,10 @@ GF_Err gf_isom_get_chunks_infos(GF_ISOFile *movie, u32 trackNumber, u32 *dur_min
|
||||
if (stsc->entries[i].firstChunk + nb_chunk == stsc->entries[i+1].firstChunk) break;
|
||||
}
|
||||
}
|
||||
if (tot_chunks) davg /= tot_chunks;
|
||||
|
||||
if (tot_chunks) {
|
||||
davg /= tot_chunks;
|
||||
savg /= tot_chunks;
|
||||
}
|
||||
if (dur_min) *dur_min = dmin;
|
||||
if (dur_avg) *dur_avg = (u32) davg;
|
||||
if (dur_max) *dur_max = dmax;
|
||||
|
||||
@@ -333,10 +333,12 @@ GF_Err Media_GetSample(GF_MediaBox *mdia, u32 sampleNumber, GF_ISOSample **samp,
|
||||
//divided into the original and the edition files
|
||||
if (mdia->mediaTrack->moov->mov->openMode == GF_ISOM_OPEN_READ) {
|
||||
//same as last call in read mode
|
||||
if (!mdia->information->dataHandler || (mdia->information->dataEntryIndex != dataRefIndex)) {
|
||||
if (!mdia->information->dataHandler) {
|
||||
e = gf_isom_datamap_open(mdia, dataRefIndex, isEdited);
|
||||
if (e) return e;
|
||||
}
|
||||
if (mdia->information->dataEntryIndex != dataRefIndex)
|
||||
mdia->information->dataEntryIndex = dataRefIndex;
|
||||
} else {
|
||||
e = gf_isom_datamap_open(mdia, dataRefIndex, isEdited);
|
||||
if (e) return e;
|
||||
|
||||
@@ -7,9 +7,9 @@
|
||||
#include "utility.h"
|
||||
#include "ccx_encoders_common.h"
|
||||
#include "ccx_common_option.h"
|
||||
|
||||
void do_NAL (struct lib_ccx_ctx *ctx, unsigned char *NALstart, LLONG NAL_length, struct cc_subtitle *sub);
|
||||
void set_fts(void); // From timing.c
|
||||
#include "ccx_mp4.h"
|
||||
#include "activity.h"
|
||||
#include "ccx_dtvcc.h"
|
||||
|
||||
static short bswap16(short v)
|
||||
{
|
||||
@@ -32,11 +32,12 @@ static int process_avc_sample(struct lib_ccx_ctx *ctx, u32 timescale, GF_AVCConf
|
||||
int status = 0;
|
||||
u32 i;
|
||||
s32 signed_cts=(s32) s->CTS_Offset; // Convert from unsigned to signed. GPAC uses u32 but unsigned values are legal.
|
||||
current_pts=(LLONG )(s->DTS + signed_cts)*MPEG_CLOCK_FREQ/timescale ; // Convert frequency to official one
|
||||
struct lib_cc_decode *dec_ctx = NULL;
|
||||
|
||||
if (pts_set==0)
|
||||
pts_set=1;
|
||||
set_fts();
|
||||
dec_ctx = update_decoder_list(ctx);
|
||||
|
||||
set_current_pts(dec_ctx->timing, (s->DTS + signed_cts)*MPEG_CLOCK_FREQ/timescale);
|
||||
set_fts(dec_ctx->timing);
|
||||
|
||||
for(i = 0; i < s->dataLength; )
|
||||
{
|
||||
@@ -62,7 +63,7 @@ static int process_avc_sample(struct lib_ccx_ctx *ctx, u32 timescale, GF_AVCConf
|
||||
temp_debug=0;
|
||||
|
||||
if (nal_length>0)
|
||||
do_NAL (ctx, (unsigned char *) &(s->data[i]) ,nal_length, sub);
|
||||
do_NAL (dec_ctx, (unsigned char *) &(s->data[i]) ,nal_length, sub);
|
||||
i += nal_length;
|
||||
} // outer for
|
||||
assert(i == s->dataLength);
|
||||
@@ -72,8 +73,11 @@ static int process_avc_sample(struct lib_ccx_ctx *ctx, u32 timescale, GF_AVCConf
|
||||
static int process_xdvb_track(struct lib_ccx_ctx *ctx, const char* basename, GF_ISOFile* f, u32 track, struct cc_subtitle *sub)
|
||||
{
|
||||
u32 timescale, i, sample_count;
|
||||
|
||||
int status;
|
||||
|
||||
struct lib_cc_decode *dec_ctx = NULL;
|
||||
|
||||
dec_ctx = update_decoder_list(ctx);
|
||||
if((sample_count = gf_isom_get_sample_count(f, track)) < 1)
|
||||
{
|
||||
return 0;
|
||||
@@ -91,24 +95,22 @@ static int process_xdvb_track(struct lib_ccx_ctx *ctx, const char* basename, GF_
|
||||
if (s!=NULL)
|
||||
{
|
||||
s32 signed_cts=(s32) s->CTS_Offset; // Convert from unsigned to signed. GPAC uses u32 but unsigned values are legal.
|
||||
current_pts=(LLONG )(s->DTS + signed_cts)*MPEG_CLOCK_FREQ/timescale ; // Convert frequency to official one
|
||||
if (pts_set==0)
|
||||
pts_set=1;
|
||||
set_fts();
|
||||
set_current_pts(dec_ctx->timing, (s->DTS + signed_cts)*MPEG_CLOCK_FREQ/timescale);
|
||||
set_fts(dec_ctx->timing);
|
||||
|
||||
process_m2v (ctx, (unsigned char *) s->data,s->dataLength, sub);
|
||||
process_m2v (dec_ctx, (unsigned char *) s->data,s->dataLength, sub);
|
||||
gf_isom_sample_del(&s);
|
||||
}
|
||||
|
||||
int progress = (int) ((i*100) / sample_count);
|
||||
if (ctx->last_reported_progress != progress)
|
||||
{
|
||||
int cur_sec = (int) (get_fts() / 1000);
|
||||
int cur_sec = (int) (get_fts(dec_ctx->timing, dec_ctx->current_field) / 1000);
|
||||
activity_progress(progress, cur_sec/60, cur_sec%60);
|
||||
ctx->last_reported_progress = progress;
|
||||
}
|
||||
}
|
||||
int cur_sec = (int) (get_fts() / 1000);
|
||||
int cur_sec = (int) (get_fts(dec_ctx->timing, dec_ctx->current_field) / 1000);
|
||||
activity_progress(100, cur_sec/60, cur_sec%60);
|
||||
|
||||
return status;
|
||||
@@ -119,6 +121,9 @@ static int process_avc_track(struct lib_ccx_ctx *ctx, const char* basename, GF_I
|
||||
u32 timescale, i, sample_count, last_sdi = 0;
|
||||
int status;
|
||||
GF_AVCConfig* c = NULL;
|
||||
struct lib_cc_decode *dec_ctx = NULL;
|
||||
|
||||
dec_ctx = update_decoder_list(ctx);
|
||||
|
||||
if((sample_count = gf_isom_get_sample_count(f, track)) < 1)
|
||||
{
|
||||
@@ -168,12 +173,12 @@ static int process_avc_track(struct lib_ccx_ctx *ctx, const char* basename, GF_I
|
||||
int progress = (int) ((i*100) / sample_count);
|
||||
if (ctx->last_reported_progress != progress)
|
||||
{
|
||||
int cur_sec = (int) (get_fts() / 1000);
|
||||
int cur_sec = (int) (get_fts(dec_ctx->timing, dec_ctx->current_field) / 1000);
|
||||
activity_progress(progress, cur_sec/60, cur_sec%60);
|
||||
ctx->last_reported_progress = progress;
|
||||
}
|
||||
}
|
||||
int cur_sec = (int) (get_fts() / 1000);
|
||||
int cur_sec = (int) (get_fts(dec_ctx->timing, dec_ctx->current_field) / 1000);
|
||||
activity_progress(100, cur_sec/60, cur_sec%60);
|
||||
|
||||
if(c != NULL)
|
||||
@@ -185,6 +190,87 @@ static int process_avc_track(struct lib_ccx_ctx *ctx, const char* basename, GF_I
|
||||
return status;
|
||||
}
|
||||
|
||||
unsigned char * ccdp_find_data(unsigned char * ccdp_atom_content, unsigned int len, unsigned int *cc_count)
|
||||
{
|
||||
unsigned char *data = ccdp_atom_content;
|
||||
|
||||
if (len < 4)
|
||||
{
|
||||
dbg_print(CCX_DMT_PARSE, "mp4-708-cdp: unexpected size of cdp\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
unsigned int cdp_id = (data[0] << 8) | data[1];
|
||||
if (cdp_id != 0x9669)
|
||||
{
|
||||
dbg_print(CCX_DMT_PARSE, "mp4-708-cdp: unexpected header %hhX %hhX\n", data[0], data[1]);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
data += 2;
|
||||
len -= 2;
|
||||
|
||||
unsigned int cdp_data_count = data[0];
|
||||
unsigned int cdp_frame_rate = data[1] >> 4; //frequency could be calculated
|
||||
if (cdp_data_count != len + 2)
|
||||
{
|
||||
dbg_print(CCX_DMT_PARSE, "mp4-708-cdp: unexpected data length %u %u\n", cdp_data_count, len + 2);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
data += 2;
|
||||
len -= 2;
|
||||
|
||||
unsigned int cdp_flags = data[0];
|
||||
unsigned int cdp_counter = (data[1] << 8) | data[2];
|
||||
|
||||
data += 3;
|
||||
len -= 3;
|
||||
|
||||
unsigned int cdp_timecode_added = (cdp_flags & 0x80) >> 7;
|
||||
unsigned int cdp_data_added = (cdp_flags & 0x40) >> 6;
|
||||
|
||||
if (!cdp_data_added)
|
||||
{
|
||||
dbg_print(CCX_DMT_PARSE, "mp4-708-cdp: packet without data\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (cdp_timecode_added)
|
||||
{
|
||||
data += 4;
|
||||
len -= 4;
|
||||
}
|
||||
|
||||
if (data[0] != CDP_SECTION_DATA)
|
||||
{
|
||||
dbg_print(CCX_DMT_PARSE, "mp4-708-cdp: cdp_data_section byte not found\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
*cc_count = (unsigned int) (data[1] & 0x1F);
|
||||
|
||||
if (*cc_count != 10 && *cc_count != 20 && *cc_count != 25 && *cc_count != 30)
|
||||
{
|
||||
dbg_print(CCX_DMT_PARSE, "mp4-708-cdp: unexpected cc_count %u\n", *cc_count);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
data += 2;
|
||||
len -= 2;
|
||||
|
||||
if ((*cc_count) * 3 > len)
|
||||
{
|
||||
dbg_print(CCX_DMT_PARSE, "mp4-708-cdp: not enough bytes left (%u) to carry %u*3 bytes\n", len, *cc_count);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
(void)(cdp_counter);
|
||||
(void)(cdp_frame_rate);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
/*
|
||||
Here is application algorithm described in some C-like pseudo code:
|
||||
main(){
|
||||
@@ -198,11 +284,15 @@ static int process_avc_track(struct lib_ccx_ctx *ctx, const char* basename, GF_I
|
||||
}
|
||||
|
||||
*/
|
||||
int processmp4 (struct lib_ccx_ctx *ctx, char *file,void *enc_ctx)
|
||||
int processmp4 (struct lib_ccx_ctx *ctx,struct ccx_s_mp4Cfg *cfg, char *file)
|
||||
{
|
||||
GF_ISOFile* f;
|
||||
u32 i, j, track_count, avc_track_count, cc_track_count;
|
||||
struct cc_subtitle dec_sub;
|
||||
struct lib_cc_decode *dec_ctx = NULL;
|
||||
struct encoder_ctx *enc_ctx = update_encoder_list(ctx);
|
||||
|
||||
dec_ctx = update_decoder_list(ctx);
|
||||
|
||||
memset(&dec_sub,0,sizeof(dec_sub));
|
||||
mprint("opening \'%s\': ", file);
|
||||
@@ -231,20 +321,23 @@ int processmp4 (struct lib_ccx_ctx *ctx, char *file,void *enc_ctx)
|
||||
(unsigned char) ((type>>16)%0x100),(unsigned char) ((type>>8)%0x100),(unsigned char) (type%0x100),
|
||||
(unsigned char) (subtype>>24%0x100),
|
||||
(unsigned char) ((subtype>>16)%0x100),(unsigned char) ((subtype>>8)%0x100),(unsigned char) (subtype%0x100));
|
||||
if (type == GF_ISOM_MEDIA_CAPTIONS && subtype == GF_ISOM_SUBTYPE_C608)
|
||||
cc_track_count++;
|
||||
if( type == GF_ISOM_MEDIA_VISUAL && subtype == GF_ISOM_SUBTYPE_AVC_H264)
|
||||
if ((type == GF_ISOM_MEDIA_CAPTIONS && subtype == GF_ISOM_SUBTYPE_C608) ||
|
||||
(type == GF_ISOM_MEDIA_CAPTIONS && subtype == GF_ISOM_SUBTYPE_C708))
|
||||
cc_track_count++;
|
||||
if (type == GF_ISOM_MEDIA_VISUAL && subtype == GF_ISOM_SUBTYPE_AVC_H264)
|
||||
avc_track_count++;
|
||||
}
|
||||
|
||||
mprint("mp4: found %u tracks: %u avc and %u cc\n", track_count, avc_track_count, cc_track_count);
|
||||
|
||||
for(i = 0; i < track_count; i++)
|
||||
{
|
||||
const u32 type = gf_isom_get_media_type(f, i + 1);
|
||||
const u32 subtype = gf_isom_get_media_subtype(f, i + 1, 1);
|
||||
|
||||
|
||||
if ( type == GF_ISOM_MEDIA_VISUAL && subtype == GF_ISOM_SUBTYPE_XDVB)
|
||||
{
|
||||
if (cc_track_count && !ccx_options.mp4vidtrack)
|
||||
if (cc_track_count && !cfg->mp4vidtrack)
|
||||
continue;
|
||||
if(process_xdvb_track(ctx, file, f, i + 1, &dec_sub) != 0)
|
||||
{
|
||||
@@ -259,8 +352,8 @@ int processmp4 (struct lib_ccx_ctx *ctx, char *file,void *enc_ctx)
|
||||
}
|
||||
|
||||
if( type == GF_ISOM_MEDIA_VISUAL && subtype == GF_ISOM_SUBTYPE_AVC_H264)
|
||||
{
|
||||
if (cc_track_count && !ccx_options.mp4vidtrack)
|
||||
{
|
||||
if (cc_track_count && !cfg->mp4vidtrack)
|
||||
continue;
|
||||
GF_AVCConfig *cnf = gf_isom_avc_config_get(f,i+1,1);
|
||||
if (cnf!=NULL)
|
||||
@@ -268,7 +361,7 @@ int processmp4 (struct lib_ccx_ctx *ctx, char *file,void *enc_ctx)
|
||||
for (j=0; j<gf_list_count(cnf->sequenceParameterSets);j++)
|
||||
{
|
||||
GF_AVCConfigSlot* seqcnf=(GF_AVCConfigSlot* )gf_list_get(cnf->sequenceParameterSets,j);
|
||||
do_NAL (ctx, (unsigned char *) seqcnf->data, seqcnf->size, &dec_sub);
|
||||
do_NAL (dec_ctx, (unsigned char *) seqcnf->data, seqcnf->size, &dec_sub);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -282,22 +375,21 @@ int processmp4 (struct lib_ccx_ctx *ctx, char *file,void *enc_ctx)
|
||||
encode_sub(enc_ctx, &dec_sub);
|
||||
dec_sub.got_output = 0;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
if (type == GF_ISOM_MEDIA_CAPTIONS && subtype == GF_ISOM_SUBTYPE_C608)
|
||||
{
|
||||
if (avc_track_count && ccx_options.mp4vidtrack)
|
||||
if (type == GF_ISOM_MEDIA_CAPTIONS &&
|
||||
(subtype == GF_ISOM_SUBTYPE_C608 || subtype == GF_ISOM_SUBTYPE_C708))
|
||||
{
|
||||
if (avc_track_count && cfg->mp4vidtrack)
|
||||
continue;
|
||||
|
||||
#ifdef MP4_DEBUG
|
||||
unsigned num_streams = gf_isom_get_sample_description_count (f,i+1);
|
||||
#endif
|
||||
unsigned num_samples = gf_isom_get_sample_count (f,i+1);
|
||||
|
||||
|
||||
u32 ProcessingStreamDescriptionIndex = 0; // Current track we are processing, 0 = we don't know yet
|
||||
u32 timescale = gf_isom_get_media_timescale(f,i+1);
|
||||
#ifdef MP$DEBUG
|
||||
#ifdef MP4_DEBUG
|
||||
u64 duration = gf_isom_get_media_duration(f,i+1);
|
||||
mprint ("%u streams\n",num_streams);
|
||||
mprint ("%u sample counts\n",num_samples);
|
||||
@@ -306,7 +398,7 @@ int processmp4 (struct lib_ccx_ctx *ctx, char *file,void *enc_ctx)
|
||||
#endif
|
||||
for (unsigned k = 0; k <num_samples; k++)
|
||||
{
|
||||
u32 StreamDescriptionIndex;
|
||||
u32 StreamDescriptionIndex;
|
||||
GF_ISOSample *sample= gf_isom_get_sample(f, i+1, k+1, &StreamDescriptionIndex);
|
||||
if (ProcessingStreamDescriptionIndex && ProcessingStreamDescriptionIndex!=StreamDescriptionIndex)
|
||||
{
|
||||
@@ -322,10 +414,8 @@ int processmp4 (struct lib_ccx_ctx *ctx, char *file,void *enc_ctx)
|
||||
mprint ("Data length: %lu\n",sample->dataLength);
|
||||
const LLONG timestamp = (LLONG )((sample->DTS + sample->CTS_Offset) * 1000) / timescale;
|
||||
#endif
|
||||
current_pts=(LLONG )(sample->DTS + sample->CTS_Offset)*MPEG_CLOCK_FREQ/timescale ; // Convert frequency to official one
|
||||
if (pts_set==0)
|
||||
pts_set=1;
|
||||
set_fts();
|
||||
set_current_pts(dec_ctx->timing, (sample->DTS + sample->CTS_Offset)*MPEG_CLOCK_FREQ/timescale);
|
||||
set_fts(dec_ctx->timing);
|
||||
|
||||
int atomStart = 0;
|
||||
// process Atom by Atom
|
||||
@@ -333,47 +423,116 @@ int processmp4 (struct lib_ccx_ctx *ctx, char *file,void *enc_ctx)
|
||||
{
|
||||
char *data = sample->data + atomStart;
|
||||
unsigned int atomLength = RB32(data);
|
||||
if(atomLength < 8 || atomLength > sample->dataLength)
|
||||
if (atomLength < 8 || atomLength > sample->dataLength)
|
||||
{
|
||||
mprint ("Invalid atom.\n");
|
||||
mprint ("Invalid atom length. Atom length: %u, should be: %u\n", atomLength, sample->dataLength);
|
||||
break;
|
||||
}
|
||||
|
||||
data += 4;
|
||||
if (!strncmp(data, "cdat", 4) || !strncmp(data, "cdt2", 4))
|
||||
{
|
||||
int ret = 0;
|
||||
int len = atomLength - 8;
|
||||
data += 4;
|
||||
#ifdef MP4_DEBUG
|
||||
dump(256, (unsigned char *)data, atomLength - 8, 0, 1);
|
||||
dump(256, (unsigned char *)data, atomLength - 8, 0, 1);
|
||||
#endif
|
||||
do
|
||||
data += 4;
|
||||
int is_ccdp = !strncmp(data, "ccdp", 4);
|
||||
|
||||
if (!strncmp(data, "cdat", 4) || !strncmp(data, "cdt2", 4) || is_ccdp)
|
||||
{
|
||||
if (subtype == GF_ISOM_SUBTYPE_C708)
|
||||
{
|
||||
ret = process608((unsigned char*)data, len, ctx->dec_ctx->context_cc608_field_1, &dec_sub);
|
||||
len -= ret;
|
||||
data += ret;
|
||||
if(dec_sub.got_output)
|
||||
if (!is_ccdp)
|
||||
{
|
||||
encode_sub(enc_ctx, &dec_sub);
|
||||
dec_sub.got_output = 0;
|
||||
mprint("Your video file seems to be an interesting sample for us\n");
|
||||
mprint("We haven't met c708 subtitle not in a \'ccdp\' atom before\n");
|
||||
mprint("Please, report\n");
|
||||
break;
|
||||
}
|
||||
} while (len > 0);
|
||||
|
||||
unsigned int cc_count;
|
||||
data += 4;
|
||||
unsigned char *cc_data = ccdp_find_data((unsigned char *) data, sample->dataLength - 8, &cc_count);
|
||||
|
||||
if (!cc_data)
|
||||
{
|
||||
dbg_print(CCX_DMT_PARSE, "mp4-708: no cc data found in ccdp\n");
|
||||
break;
|
||||
}
|
||||
|
||||
ctx->dec_global_setting->settings_dtvcc->enabled = 1;
|
||||
unsigned char temp[4];
|
||||
for (int cc_i = 0; cc_i < cc_count; cc_i++, cc_data += 3)
|
||||
{
|
||||
unsigned char cc_info = cc_data[0];
|
||||
unsigned char cc_valid = (unsigned char) ((cc_info & 4) >> 2);
|
||||
unsigned char cc_type = (unsigned char) (cc_info & 3);
|
||||
|
||||
if (cc_info == CDP_SECTION_SVC_INFO || cc_info == CDP_SECTION_FOOTER)
|
||||
{
|
||||
dbg_print(CCX_DMT_PARSE, "mp4-708: premature end of sample (0x73 or 0x74)\n");
|
||||
break;
|
||||
}
|
||||
|
||||
if ((cc_info == 0xFA || cc_info == 0xFC || cc_info == 0xFD)
|
||||
&& (cc_data[1] & 0x7F) == 0 && (cc_data[2] & 0x7F) == 0)
|
||||
{
|
||||
dbg_print(CCX_DMT_PARSE, "mp4-708: skipped (zero cc data)\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
temp[0] = cc_valid;
|
||||
temp[1] = cc_type;
|
||||
temp[2] = cc_data[1];
|
||||
temp[3] = cc_data[2];
|
||||
|
||||
if (cc_type < 2)
|
||||
{
|
||||
dbg_print(CCX_DMT_PARSE, "mp4-708: atom skipped (cc_type < 2)\n");
|
||||
continue;
|
||||
}
|
||||
dec_ctx->dtvcc->encoder = (void *)enc_ctx; //WARN: otherwise cea-708 will not work
|
||||
//TODO is it really always 4-bytes long?
|
||||
ccx_dtvcc_process_data(dec_ctx, (unsigned char *) temp, 4);
|
||||
cb_708++;
|
||||
}
|
||||
atomStart = sample->dataLength;
|
||||
}
|
||||
else //subtype == GF_ISOM_SUBTYPE_C608
|
||||
{
|
||||
if (is_ccdp)
|
||||
{
|
||||
mprint("Your video file seems to be an interesting sample for us\n");
|
||||
mprint("We haven't met c608 subtitle in a \'ccdp\' atom before\n");
|
||||
mprint("Please, report\n");
|
||||
break;
|
||||
}
|
||||
|
||||
int ret = 0;
|
||||
int len = atomLength - 8;
|
||||
data += 4;
|
||||
|
||||
do {
|
||||
ret = process608((unsigned char *) data, len, dec_ctx,
|
||||
&dec_sub);
|
||||
len -= ret;
|
||||
data += ret;
|
||||
if (dec_sub.got_output) {
|
||||
encode_sub(enc_ctx, &dec_sub);
|
||||
dec_sub.got_output = 0;
|
||||
}
|
||||
} while (len > 0);
|
||||
}
|
||||
}
|
||||
atomStart += atomLength;
|
||||
|
||||
}
|
||||
|
||||
// End of change
|
||||
int progress = (int) ((k*100) / num_samples);
|
||||
if (ctx->last_reported_progress != progress)
|
||||
{
|
||||
int cur_sec = (int) (get_fts() / 1000);
|
||||
int cur_sec = (int) (get_fts(dec_ctx->timing, dec_ctx->current_field) / 1000);
|
||||
activity_progress(progress, cur_sec/60, cur_sec%60);
|
||||
ctx->last_reported_progress = progress;
|
||||
}
|
||||
}
|
||||
int cur_sec = (int) (get_fts() / 1000);
|
||||
int cur_sec = (int) (get_fts(dec_ctx->timing, dec_ctx->current_field) / 1000);
|
||||
activity_progress(100, cur_sec/60, cur_sec%60);
|
||||
}
|
||||
}
|
||||
@@ -398,8 +557,6 @@ int processmp4 (struct lib_ccx_ctx *ctx, char *file,void *enc_ctx)
|
||||
mprint ("found no dedicated CC track(s).\n");
|
||||
|
||||
ctx->freport.mp4_cc_track_cnt = cc_track_count;
|
||||
if (ccx_options.print_file_reports)
|
||||
print_file_report(ctx);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1,204 +0,0 @@
|
||||
#include "lib_ccx.h"
|
||||
#include "ccx_common_option.h"
|
||||
#include "ccx_encoders_common.h"
|
||||
#include "png.h"
|
||||
#include "spupng_encoder.h"
|
||||
#include "ocr.h"
|
||||
#include "utility.h"
|
||||
|
||||
void write_stringz_as_sami(char *string, struct encoder_ctx *context, LLONG ms_start, LLONG ms_end)
|
||||
{
|
||||
int used;
|
||||
sprintf ((char *) str,
|
||||
"<SYNC start=%llu><P class=\"UNKNOWNCC\">\r\n",(unsigned long long)ms_start);
|
||||
if (ccx_options.encoding!=CCX_ENC_UNICODE)
|
||||
{
|
||||
dbg_print(CCX_DMT_DECODER_608, "\r%s\n", str);
|
||||
}
|
||||
|
||||
used = encode_line(context->buffer,(unsigned char *) str);
|
||||
write (context->out->fh, context->buffer, used);
|
||||
int len=strlen (string);
|
||||
unsigned char *unescaped= (unsigned char *) malloc (len+1);
|
||||
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_sami() - not enough memory.\n");
|
||||
int pos_r=0;
|
||||
int pos_w=0;
|
||||
// Scan for \n in the string and replace it with a 0
|
||||
while (pos_r<len)
|
||||
{
|
||||
if (string[pos_r]=='\\' && string[pos_r+1]=='n')
|
||||
{
|
||||
unescaped[pos_w]=0;
|
||||
pos_r+=2;
|
||||
}
|
||||
else
|
||||
{
|
||||
unescaped[pos_w]=string[pos_r];
|
||||
pos_r++;
|
||||
}
|
||||
pos_w++;
|
||||
}
|
||||
unescaped[pos_w]=0;
|
||||
// Now read the unescaped string (now several string'z and write them)
|
||||
unsigned char *begin=unescaped;
|
||||
while (begin<unescaped+len)
|
||||
{
|
||||
unsigned int u = encode_line (el, begin);
|
||||
if (ccx_options.encoding!=CCX_ENC_UNICODE)
|
||||
{
|
||||
dbg_print(CCX_DMT_DECODER_608, "\r");
|
||||
dbg_print(CCX_DMT_DECODER_608, "%s\n",subline);
|
||||
}
|
||||
write(context->out->fh, el, u);
|
||||
write(context->out->fh, encoded_br, encoded_br_length);
|
||||
|
||||
write(context->out->fh, encoded_crlf, encoded_crlf_length);
|
||||
begin+= strlen ((const char *) begin)+1;
|
||||
}
|
||||
|
||||
sprintf ((char *) str,"</P></SYNC>\r\n");
|
||||
if (ccx_options.encoding!=CCX_ENC_UNICODE)
|
||||
{
|
||||
dbg_print(CCX_DMT_DECODER_608, "\r%s\n", str);
|
||||
}
|
||||
used=encode_line (context->buffer,(unsigned char *) str);
|
||||
write(context->out->fh, context->buffer, used);
|
||||
sprintf ((char *) str,
|
||||
"<SYNC start=%llu><P class=\"UNKNOWNCC\"> </P></SYNC>\r\n\r\n",
|
||||
(unsigned long long)ms_end);
|
||||
if (ccx_options.encoding!=CCX_ENC_UNICODE)
|
||||
{
|
||||
dbg_print(CCX_DMT_DECODER_608, "\r%s\n", str);
|
||||
}
|
||||
write(context->out->fh, context->buffer, used);
|
||||
free(el);
|
||||
free(unescaped);
|
||||
}
|
||||
|
||||
|
||||
int write_cc_bitmap_as_sami(struct cc_subtitle *sub, struct encoder_ctx *context)
|
||||
{
|
||||
int ret = 0;
|
||||
struct cc_bitmap* rect;
|
||||
LLONG ms_start, ms_end;
|
||||
|
||||
if (context->prev_start != -1 && (sub->flags & SUB_EOD_MARKER))
|
||||
{
|
||||
ms_start = context->prev_start + context->subs_delay;
|
||||
ms_end = sub->start_time - 1;
|
||||
}
|
||||
else if ( !(sub->flags & SUB_EOD_MARKER))
|
||||
{
|
||||
ms_start = sub->start_time + context->subs_delay;
|
||||
ms_end = sub->end_time - 1;
|
||||
}
|
||||
else if ( context->prev_start == -1 && (sub->flags & SUB_EOD_MARKER) )
|
||||
{
|
||||
ms_start = 1 + context->subs_delay;
|
||||
ms_end = sub->start_time - 1;
|
||||
}
|
||||
|
||||
if(sub->nb_data == 0 )
|
||||
return 0;
|
||||
rect = sub->data;
|
||||
|
||||
if ( sub->flags & SUB_EOD_MARKER )
|
||||
context->prev_start = sub->start_time;
|
||||
|
||||
#if ENABLE_OCR
|
||||
if (rect[0].ocr_text && *(rect[0].ocr_text))
|
||||
{
|
||||
if (context->prev_start != -1 || !(sub->flags & SUB_EOD_MARKER))
|
||||
{
|
||||
char *token = NULL;
|
||||
char *buf = (char*)context->buffer;
|
||||
sprintf(buf,
|
||||
"<SYNC start=%llu><P class=\"UNKNOWNCC\">\r\n"
|
||||
,(unsigned long long)ms_start);
|
||||
write(context->out->fh, buf, strlen(buf));
|
||||
token = strtok(rect[0].ocr_text,"\r\n");
|
||||
while (token)
|
||||
{
|
||||
|
||||
sprintf(buf, "%s", token);
|
||||
token = strtok(NULL,"\r\n");
|
||||
if(token)
|
||||
strcat(buf, "<br>\n");
|
||||
else
|
||||
strcat(buf, "\n");
|
||||
write(context->out->fh, buf, strlen(buf));
|
||||
|
||||
}
|
||||
sprintf(buf,
|
||||
"<SYNC start=%llu><P class=\"UNKNOWNCC\"> </P></SYNC>\r\n\r\n"
|
||||
,(unsigned long long)ms_end);
|
||||
write(context->out->fh, buf, strlen(buf));
|
||||
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
sub->nb_data = 0;
|
||||
freep(&sub->data);
|
||||
return ret;
|
||||
|
||||
}
|
||||
int write_cc_buffer_as_sami(struct eia608_screen *data, struct encoder_ctx *context)
|
||||
{
|
||||
int used;
|
||||
LLONG startms, endms;
|
||||
int wrote_something=0;
|
||||
startms = data->start_time;
|
||||
|
||||
startms+=context->subs_delay;
|
||||
if (startms<0) // Drop screens that because of subs_delay start too early
|
||||
return 0;
|
||||
|
||||
endms = data->end_time;
|
||||
endms--; // To prevent overlapping with next line.
|
||||
sprintf ((char *) str,
|
||||
"<SYNC start=%llu><P class=\"UNKNOWNCC\">\r\n",
|
||||
(unsigned long long)startms);
|
||||
if (ccx_options.encoding!=CCX_ENC_UNICODE)
|
||||
{
|
||||
dbg_print(CCX_DMT_DECODER_608, "\r%s\n", str);
|
||||
}
|
||||
used = encode_line(context->buffer,(unsigned char *) str);
|
||||
write (context->out->fh, context->buffer, used);
|
||||
for (int i=0;i<15;i++)
|
||||
{
|
||||
if (data->row_used[i])
|
||||
{
|
||||
int length = get_decoder_line_encoded (subline, i, data);
|
||||
if (ccx_options.encoding!=CCX_ENC_UNICODE)
|
||||
{
|
||||
dbg_print(CCX_DMT_DECODER_608, "\r");
|
||||
dbg_print(CCX_DMT_DECODER_608, "%s\n",subline);
|
||||
}
|
||||
write (context->out->fh, subline, length);
|
||||
wrote_something=1;
|
||||
if (i!=14)
|
||||
write (context->out->fh, encoded_br, encoded_br_length);
|
||||
write (context->out->fh,encoded_crlf, encoded_crlf_length);
|
||||
}
|
||||
}
|
||||
sprintf ((char *) str,"</P></SYNC>\r\n");
|
||||
if (ccx_options.encoding!=CCX_ENC_UNICODE)
|
||||
{
|
||||
dbg_print(CCX_DMT_DECODER_608, "\r%s\n", str);
|
||||
}
|
||||
used = encode_line(context->buffer,(unsigned char *) str);
|
||||
write (context->out->fh, context->buffer, used);
|
||||
sprintf ((char *) str,
|
||||
"<SYNC start=%llu><P class=\"UNKNOWNCC\"> </P></SYNC>\r\n\r\n",
|
||||
(unsigned long long)endms);
|
||||
if (ccx_options.encoding!=CCX_ENC_UNICODE)
|
||||
{
|
||||
dbg_print(CCX_DMT_DECODER_608, "\r%s\n", str);
|
||||
}
|
||||
used = encode_line(context->buffer,(unsigned char *) str);
|
||||
write (context->out->fh, context->buffer, used);
|
||||
return wrote_something;
|
||||
}
|
||||
@@ -1,225 +0,0 @@
|
||||
#include "lib_ccx.h"
|
||||
#include "ccx_common_option.h"
|
||||
#include "ccx_encoders_common.h"
|
||||
#include "png.h"
|
||||
#include "spupng_encoder.h"
|
||||
#include "ocr.h"
|
||||
#include "utility.h"
|
||||
|
||||
// Produces minimally-compliant SMPTE Timed Text (W3C TTML)
|
||||
// format-compatible output
|
||||
|
||||
// See http://www.w3.org/TR/ttaf1-dfxp/ and
|
||||
// https://www.smpte.org/sites/default/files/st2052-1-2010.pdf
|
||||
|
||||
// Copyright (C) 2012 John Kemp
|
||||
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License
|
||||
// as published by the Free Software Foundation; either version 2
|
||||
// of the License, or (at your option) any later version.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
void write_stringz_as_smptett(char *string, struct encoder_ctx *context, LLONG ms_start, LLONG ms_end)
|
||||
{
|
||||
int used;
|
||||
unsigned h1,m1,s1,ms1;
|
||||
unsigned h2,m2,s2,ms2;
|
||||
|
||||
mstotime (ms_start,&h1,&m1,&s1,&ms1);
|
||||
mstotime (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);
|
||||
if (ccx_options.encoding!=CCX_ENC_UNICODE)
|
||||
{
|
||||
dbg_print(CCX_DMT_DECODER_608, "\r%s\n", str);
|
||||
}
|
||||
used = encode_line(context->buffer,(unsigned char *) str);
|
||||
write (context->out->fh, context->buffer, used);
|
||||
int len=strlen (string);
|
||||
unsigned char *unescaped= (unsigned char *) malloc (len+1);
|
||||
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_sami() - not enough memory.\n");
|
||||
int pos_r=0;
|
||||
int pos_w=0;
|
||||
// Scan for \n in the string and replace it with a 0
|
||||
while (pos_r<len)
|
||||
{
|
||||
if (string[pos_r]=='\\' && string[pos_r+1]=='n')
|
||||
{
|
||||
unescaped[pos_w]=0;
|
||||
pos_r+=2;
|
||||
}
|
||||
else
|
||||
{
|
||||
unescaped[pos_w]=string[pos_r];
|
||||
pos_r++;
|
||||
}
|
||||
pos_w++;
|
||||
}
|
||||
unescaped[pos_w]=0;
|
||||
// Now read the unescaped string (now several string'z and write them)
|
||||
unsigned char *begin=unescaped;
|
||||
while (begin<unescaped+len)
|
||||
{
|
||||
unsigned int u = encode_line (el, begin);
|
||||
if (ccx_options.encoding!=CCX_ENC_UNICODE)
|
||||
{
|
||||
dbg_print(CCX_DMT_DECODER_608, "\r");
|
||||
dbg_print(CCX_DMT_DECODER_608, "%s\n",subline);
|
||||
}
|
||||
write(context->out->fh, el, u);
|
||||
//write (wb->fh, encoded_br, encoded_br_length);
|
||||
|
||||
write(context->out->fh, encoded_crlf, encoded_crlf_length);
|
||||
begin+= strlen ((const char *) begin)+1;
|
||||
}
|
||||
|
||||
sprintf ((char *) str,"</p>\n");
|
||||
if (ccx_options.encoding!=CCX_ENC_UNICODE)
|
||||
{
|
||||
dbg_print(CCX_DMT_DECODER_608, "\r%s\n", str);
|
||||
}
|
||||
used = encode_line(context->buffer,(unsigned char *) str);
|
||||
write(context->out->fh, context->buffer, used);
|
||||
sprintf ((char *) str,"<p begin=\"%02u:%02u:%02u.%03u\">\n\n",h2,m2,s2,ms2);
|
||||
if (ccx_options.encoding!=CCX_ENC_UNICODE)
|
||||
{
|
||||
dbg_print(CCX_DMT_DECODER_608, "\r%s\n", str);
|
||||
}
|
||||
used = encode_line(context->buffer,(unsigned char *) str);
|
||||
write (context->out->fh, context->buffer, used);
|
||||
sprintf ((char *) str,"</p>\n");
|
||||
free(el);
|
||||
free(unescaped);
|
||||
}
|
||||
|
||||
|
||||
int write_cc_bitmap_as_smptett(struct cc_subtitle *sub, struct encoder_ctx *context)
|
||||
{
|
||||
int ret = 0;
|
||||
struct cc_bitmap* rect;
|
||||
LLONG ms_start, ms_end;
|
||||
//char timeline[128];
|
||||
int len = 0;
|
||||
|
||||
|
||||
if (context->prev_start != -1 && (sub->flags & SUB_EOD_MARKER))
|
||||
{
|
||||
ms_start = context->prev_start + context->subs_delay;
|
||||
ms_end = sub->start_time - 1;
|
||||
}
|
||||
else if ( !(sub->flags & SUB_EOD_MARKER))
|
||||
{
|
||||
ms_start = sub->start_time + context->subs_delay;
|
||||
ms_end = sub->end_time - 1;
|
||||
}
|
||||
else if (context->prev_start == -1 && (sub->flags & SUB_EOD_MARKER))
|
||||
{
|
||||
ms_start = 1 + context->subs_delay;
|
||||
ms_end = sub->start_time - 1;
|
||||
}
|
||||
|
||||
if(sub->nb_data == 0 )
|
||||
return 0;
|
||||
rect = sub->data;
|
||||
|
||||
if ( sub->flags & SUB_EOD_MARKER )
|
||||
context->prev_start = sub->start_time;
|
||||
|
||||
#ifdef ENABLE_OCR
|
||||
if (rect[0].ocr_text && *(rect[0].ocr_text))
|
||||
{
|
||||
if (context->prev_start != -1 || !(sub->flags & SUB_EOD_MARKER))
|
||||
{
|
||||
char *buf = (char *) context->buffer;
|
||||
unsigned h1, m1, s1, ms1;
|
||||
unsigned h2, m2, s2, ms2;
|
||||
mstotime (ms_start,&h1,&m1,&s1,&ms1);
|
||||
mstotime (ms_end-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 (context->out->fh, buf,strlen(buf) );
|
||||
len = strlen(rect[0].ocr_text);
|
||||
write (context->out->fh, rect[0].ocr_text, len);
|
||||
write (context->out->fh, encoded_crlf, encoded_crlf_length);
|
||||
sprintf ( buf,"</p>\n");
|
||||
write (context->out->fh, buf,strlen(buf) );
|
||||
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
sub->nb_data = 0;
|
||||
freep(&sub->data);
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
||||
int write_cc_buffer_as_smptett(struct eia608_screen *data, struct encoder_ctx *context)
|
||||
{
|
||||
int used;
|
||||
unsigned h1,m1,s1,ms1;
|
||||
unsigned h2,m2,s2,ms2;
|
||||
LLONG endms;
|
||||
int wrote_something=0;
|
||||
LLONG startms = data->start_time;
|
||||
|
||||
startms+=context->subs_delay;
|
||||
if (startms<0) // Drop screens that because of subs_delay start too early
|
||||
return 0;
|
||||
|
||||
endms = data->end_time;
|
||||
endms--; // To prevent overlapping with next line.
|
||||
mstotime (startms,&h1,&m1,&s1,&ms1);
|
||||
mstotime (endms-1,&h2,&m2,&s2,&ms2);
|
||||
|
||||
sprintf ((char *) str,"<p begin=\"%02u:%02u:%02u.%03u\" end=\"%02u:%02u:%02u.%03u\">\n",h1,m1,s1,ms1, h2,m2,s2,ms2);
|
||||
|
||||
if (ccx_options.encoding!=CCX_ENC_UNICODE)
|
||||
{
|
||||
dbg_print(CCX_DMT_DECODER_608, "\r%s\n", str);
|
||||
}
|
||||
used = encode_line(context->buffer,(unsigned char *) str);
|
||||
write (context->out->fh, context->buffer, used);
|
||||
for (int i=0;i<15;i++)
|
||||
{
|
||||
if (data->row_used[i])
|
||||
{
|
||||
int length = get_decoder_line_encoded (subline, i, data);
|
||||
if (ccx_options.encoding!=CCX_ENC_UNICODE)
|
||||
{
|
||||
dbg_print(CCX_DMT_DECODER_608, "\r");
|
||||
dbg_print(CCX_DMT_DECODER_608, "%s\n",subline);
|
||||
}
|
||||
write(context->out->fh, subline, length);
|
||||
wrote_something=1;
|
||||
|
||||
write(context->out->fh, encoded_crlf, encoded_crlf_length);
|
||||
}
|
||||
}
|
||||
sprintf ((char *) str,"</p>\n");
|
||||
if (ccx_options.encoding!=CCX_ENC_UNICODE)
|
||||
{
|
||||
dbg_print(CCX_DMT_DECODER_608, "\r%s\n", str);
|
||||
}
|
||||
used = encode_line(context->buffer,(unsigned char *) str);
|
||||
write (context->out->fh, context->buffer, used);
|
||||
|
||||
if (ccx_options.encoding!=CCX_ENC_UNICODE)
|
||||
{
|
||||
dbg_print(CCX_DMT_DECODER_608, "\r%s\n", str);
|
||||
}
|
||||
used = encode_line(context->buffer,(unsigned char *) str);
|
||||
//write (wb->fh, enc_buffer,enc_buffer_used);
|
||||
|
||||
return wrote_something;
|
||||
}
|
||||
@@ -1,278 +0,0 @@
|
||||
#include <assert.h>
|
||||
#include <sys/stat.h>
|
||||
#include "608_spupng.h"
|
||||
|
||||
void
|
||||
draw_row(struct eia608_screen* data, int row, uint8_t * canvas, int rowstride)
|
||||
{
|
||||
int column;
|
||||
int unicode = 0;
|
||||
uint8_t pen[2];
|
||||
uint8_t* cell;
|
||||
int first = -1;
|
||||
int last = 0;
|
||||
|
||||
pen[0] = COL_BLACK;
|
||||
|
||||
for (column = 0; column < COLUMNS ; column++) {
|
||||
|
||||
if (COL_TRANSPARENT != data->colors[row][column])
|
||||
{
|
||||
cell = canvas + ((column+1) * CCW);
|
||||
get_char_in_unicode((unsigned char*)&unicode, data->characters[row][column]);
|
||||
pen[1] = data->colors[row][column];
|
||||
|
||||
int attr = data->fonts[row][column];
|
||||
draw_char_indexed(cell, rowstride, pen, unicode, (attr & FONT_ITALICS) != 0, (attr & FONT_UNDERLINED) != 0);
|
||||
if (first < 0)
|
||||
{
|
||||
// draw a printable space before the first non-space char
|
||||
first = column;
|
||||
if (unicode != 0x20)
|
||||
{
|
||||
cell = canvas + ((first) * CCW);
|
||||
draw_char_indexed(cell, rowstride, pen, 0x20, 0, 0);
|
||||
}
|
||||
}
|
||||
last = column;
|
||||
}
|
||||
}
|
||||
// draw a printable space after the last non-space char
|
||||
// unicode should still contain the last character
|
||||
// check whether it is a space
|
||||
if (unicode != 0x20)
|
||||
{
|
||||
cell = canvas + ((last+2) * CCW);
|
||||
draw_char_indexed(cell, rowstride, pen, 0x20, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static png_color palette[10] =
|
||||
{
|
||||
{ 0xff, 0xff, 0xff }, // COL_WHITE = 0,
|
||||
{ 0x00, 0xff, 0x00 }, // COL_GREEN = 1,
|
||||
{ 0x00, 0x00, 0xff }, // COL_BLUE = 2,
|
||||
{ 0x00, 0xff, 0xff }, // COL_CYAN = 3,
|
||||
{ 0xff, 0x00, 0x00 }, // COL_RED = 4,
|
||||
{ 0xff, 0xff, 0x00 }, // COL_YELLOW = 5,
|
||||
{ 0xff, 0x00, 0xff }, // COL_MAGENTA = 6,
|
||||
{ 0xff, 0xff, 0xff }, // COL_USERDEFINED = 7,
|
||||
{ 0x00, 0x00, 0x00 }, // COL_BLACK = 8
|
||||
{ 0x00, 0x00, 0x00 } // COL_TRANSPARENT = 9
|
||||
};
|
||||
|
||||
static png_byte alpha[10] =
|
||||
{
|
||||
255,
|
||||
255,
|
||||
255,
|
||||
255,
|
||||
255,
|
||||
255,
|
||||
255,
|
||||
255,
|
||||
255,
|
||||
0
|
||||
};
|
||||
|
||||
int
|
||||
spupng_write_png(struct spupng_t *sp, struct eia608_screen* data,
|
||||
png_structp png_ptr, png_infop info_ptr,
|
||||
png_bytep image,
|
||||
png_bytep* row_pointer,
|
||||
unsigned int ww, unsigned int wh)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
if (setjmp(png_jmpbuf(png_ptr)))
|
||||
return 0;
|
||||
|
||||
png_init_io (png_ptr, sp->fppng);
|
||||
|
||||
png_set_IHDR (png_ptr,
|
||||
info_ptr,
|
||||
ww,
|
||||
wh,
|
||||
/* bit_depth */ 8,
|
||||
PNG_COLOR_TYPE_PALETTE,
|
||||
PNG_INTERLACE_NONE,
|
||||
PNG_COMPRESSION_TYPE_DEFAULT,
|
||||
PNG_FILTER_TYPE_DEFAULT);
|
||||
|
||||
png_set_PLTE (png_ptr, info_ptr, palette, sizeof(palette) / sizeof(palette[0]));
|
||||
png_set_tRNS (png_ptr, info_ptr, alpha, sizeof(alpha) / sizeof(alpha[0]), NULL);
|
||||
|
||||
png_set_gAMA (png_ptr, info_ptr, 1.0 / 2.2);
|
||||
|
||||
png_write_info (png_ptr, info_ptr);
|
||||
|
||||
for (i = 0; i < wh; i++)
|
||||
row_pointer[i] = image + i * ww;
|
||||
|
||||
png_write_image (png_ptr, row_pointer);
|
||||
|
||||
png_write_end (png_ptr, info_ptr);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int
|
||||
spupng_export_png(struct spupng_t *sp, struct eia608_screen* data)
|
||||
{
|
||||
png_structp png_ptr;
|
||||
png_infop info_ptr;
|
||||
png_bytep *row_pointer;
|
||||
png_bytep image;
|
||||
int ww, wh, rowstride, row_adv;
|
||||
int row;
|
||||
|
||||
assert ((sizeof(png_byte) == sizeof(uint8_t))
|
||||
&& (sizeof(*image) == sizeof(uint8_t)));
|
||||
|
||||
// Allow space at beginning and end of each row for a padding space
|
||||
ww = CCW * (COLUMNS+2);
|
||||
wh = CCH * ROWS;
|
||||
row_adv = (COLUMNS+2) * CCW * CCH;
|
||||
|
||||
rowstride = ww * sizeof(*image);
|
||||
|
||||
if (!(row_pointer = (png_bytep*)malloc(sizeof(*row_pointer) * wh))) {
|
||||
mprint("Unable to allocate %d byte buffer.\n",
|
||||
sizeof(*row_pointer) * wh);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!(image = (png_bytep)malloc(wh * ww * sizeof(*image)))) {
|
||||
mprint("Unable to allocate %d KB image buffer.",
|
||||
wh * ww * sizeof(*image) / 1024);
|
||||
free(row_pointer);
|
||||
return 0;
|
||||
}
|
||||
// Initialize image to transparent
|
||||
memset(image, COL_TRANSPARENT, wh * ww * sizeof(*image));
|
||||
|
||||
/* draw the image */
|
||||
|
||||
for (row = 0; row < ROWS; row++) {
|
||||
if (data->row_used[row])
|
||||
draw_row(data, row, image + row * row_adv, rowstride);
|
||||
}
|
||||
|
||||
/* Now save the image */
|
||||
|
||||
if (!(png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING,
|
||||
NULL, NULL, NULL)))
|
||||
goto unknown_error;
|
||||
|
||||
if (!(info_ptr = png_create_info_struct(png_ptr))) {
|
||||
png_destroy_write_struct(&png_ptr, (png_infopp) NULL);
|
||||
goto unknown_error;
|
||||
}
|
||||
|
||||
if (!spupng_write_png (sp, data, png_ptr, info_ptr, image, row_pointer, ww, wh)) {
|
||||
png_destroy_write_struct (&png_ptr, &info_ptr);
|
||||
goto write_error;
|
||||
}
|
||||
|
||||
png_destroy_write_struct (&png_ptr, &info_ptr);
|
||||
|
||||
free (row_pointer);
|
||||
|
||||
free (image);
|
||||
|
||||
return 1;
|
||||
|
||||
write_error:
|
||||
|
||||
unknown_error:
|
||||
free (row_pointer);
|
||||
|
||||
free (image);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
spupng_write_ccbuffer(struct spupng_t *sp, struct eia608_screen* data,
|
||||
struct encoder_ctx *context)
|
||||
{
|
||||
LLONG ms_start = data->start_time + context->subs_delay;
|
||||
if (ms_start < 0)
|
||||
{
|
||||
dbg_print(CCX_DMT_VERBOSE, "Negative start\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int row;
|
||||
int empty_buf = 1;
|
||||
char str[256] = "";
|
||||
for (row = 0; row < 15; row++)
|
||||
{
|
||||
if (data->row_used[row])
|
||||
{
|
||||
empty_buf = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (empty_buf)
|
||||
{
|
||||
dbg_print(CCX_DMT_VERBOSE, "Blank page\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
LLONG ms_end = data->end_time;
|
||||
|
||||
sprintf(sp->pngfile, "%s/sub%04d.png", sp->dirname, sp->fileIndex++);
|
||||
if ((sp->fppng = fopen(sp->pngfile, "wb")) == NULL)
|
||||
{
|
||||
fatal(CCX_COMMON_EXIT_FILE_CREATION_FAILED, "Cannot open %s: %s\n",
|
||||
sp->pngfile, strerror(errno));
|
||||
}
|
||||
if (!spupng_export_png(sp, data))
|
||||
{
|
||||
fatal(CCX_COMMON_EXIT_FILE_CREATION_FAILED, "Cannot write %s: %s\n",
|
||||
sp->pngfile, strerror(errno));
|
||||
}
|
||||
fclose(sp->fppng);
|
||||
write_sputag(sp,ms_start,ms_end);
|
||||
for (row = 0; row < ROWS; row++)
|
||||
{
|
||||
if (data->row_used[row])
|
||||
{
|
||||
int len = get_decoder_line_encoded(subline, row, data);
|
||||
// Check for characters that spumux won't parse
|
||||
// null chars will be changed to space
|
||||
// pairs of dashes will be changed to underscores
|
||||
for (unsigned char* ptr = subline; ptr < subline+len; ptr++)
|
||||
{
|
||||
switch (*ptr)
|
||||
{
|
||||
case 0:
|
||||
*ptr = ' ';
|
||||
break;
|
||||
case '-':
|
||||
if (*(ptr+1) == '-')
|
||||
{
|
||||
*ptr++ = '_';
|
||||
*ptr = '_';
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
strncat(str,(const char*)subline,256);
|
||||
strncat(str,"\n",256);
|
||||
}
|
||||
}
|
||||
|
||||
write_spucomment(sp,str);
|
||||
return 1;
|
||||
}
|
||||
int write_cc_buffer_as_spupng(struct eia608_screen *data,struct encoder_ctx *context)
|
||||
{
|
||||
if (0 != context->out->spupng_data)
|
||||
{
|
||||
struct spupng_t *sp = (struct spupng_t *) context->out->spupng_data;
|
||||
return spupng_write_ccbuffer(sp, data, context);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -1,18 +1,42 @@
|
||||
cmake_policy(SET CMP0037 NEW)
|
||||
cmake_policy (SET CMP0037 NEW)
|
||||
|
||||
SET (CMAKE_C_FLAGS "-O0 -Wall -g -std=gnu99")
|
||||
set (CMAKE_C_FLAGS "-O0 -Wall -g -std=gnu99")
|
||||
|
||||
if (WITH_FFMPEG)
|
||||
SET (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DENABLE_FFMPEG")
|
||||
set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DENABLE_FFMPEG")
|
||||
endif (WITH_FFMPEG)
|
||||
|
||||
AUX_SOURCE_DIRECTORY("${PROJECT_SOURCE_DIR}/lib_ccx/" SOURCEFILE)
|
||||
AUX_SOURCE_DIRECTORY("${PROJECT_SOURCE_DIR}/gpacmp4/" SOURCEFILE)
|
||||
#AUX_SOURCE_DIRECTORY("${PROJECT_SOURCE_DIR}/libpng/" SOURCEFILE)
|
||||
add_library(ccx ${SOURCEFILE})
|
||||
if (WITH_OCR)
|
||||
find_package(PkgConfig)
|
||||
|
||||
FILE(GLOB HeaderFiles *.h)
|
||||
file(WRITE ccx.pc "prefix=${CMAKE_INSTALL_PREFIX}\n"
|
||||
pkg_check_modules(TESSERACT REQUIRED tesseract)
|
||||
pkg_check_modules(LEPTONICA REQUIRED lept)
|
||||
|
||||
set (EXTRA_LIBS ${EXTRA_LIBS} ${TESSERACT_STATIC_LIBRARIES})
|
||||
set (EXTRA_LIBS ${EXTRA_LIBS} ${LEPTONICA_STATIC_LIBRARIES})
|
||||
set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DENABLE_OCR ${TESSERACT_CFLAGS} ${LEPTONICA_CFLAGS}")
|
||||
endif (WITH_OCR)
|
||||
|
||||
aux_source_directory ("${PROJECT_SOURCE_DIR}/lib_ccx/" SOURCEFILE)
|
||||
aux_source_directory ("${PROJECT_SOURCE_DIR}/gpacmp4/" SOURCEFILE)
|
||||
|
||||
add_library (ccx ${SOURCEFILE} ccx_dtvcc.h ccx_dtvcc.c)
|
||||
|
||||
if (MINGW OR CYGWIN)
|
||||
set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DGPAC_CONFIG_WIN32")
|
||||
endif (MINGW OR CYGWIN)
|
||||
|
||||
if (${CMAKE_SYSTEM_NAME} MATCHES "Linux")
|
||||
set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DGPAC_CONFIG_LINUX")
|
||||
endif (${CMAKE_SYSTEM_NAME} MATCHES "Linux")
|
||||
|
||||
if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
|
||||
set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DGPAC_CONFIG_DARWIN")
|
||||
endif (${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
|
||||
|
||||
|
||||
file (GLOB HeaderFiles *.h)
|
||||
file (WRITE ccx.pc "prefix=${CMAKE_INSTALL_PREFIX}\n"
|
||||
"includedir=\${prefix}/include\n"
|
||||
"libdir=\${prefix}/lib\n\n"
|
||||
"Name: ccx\n"
|
||||
@@ -20,8 +44,9 @@ file(WRITE ccx.pc "prefix=${CMAKE_INSTALL_PREFIX}\n"
|
||||
"Version: 0.75\n"
|
||||
"Cflags: -I\${includedir}/\n"
|
||||
"Libs: -L\${libdir} -lccx -lpng\n"
|
||||
"Libs.private: -lpng\n" )
|
||||
"Libs.private: -lpng\n"
|
||||
)
|
||||
|
||||
install (TARGETS ccx DESTINATION lib)
|
||||
install (FILES ${HeaderFiles} DESTINATION include)
|
||||
install (FILES ccx.pc DESTINATION lib/pkgconfig )
|
||||
install (FILES ccx.pc DESTINATION lib/pkgconfig)
|
||||
|
||||
@@ -7,53 +7,54 @@ relevant events. */
|
||||
static int credits_shown=0;
|
||||
unsigned long net_activity_gui=0;
|
||||
|
||||
/* Print current progress. For percentaje, -1 -> streaming mode */
|
||||
void activity_progress (int percentaje, int cur_min, int cur_sec)
|
||||
/* Print current progress. For percentage, -1 -> streaming mode */
|
||||
void activity_progress (int percentage, int cur_min, int cur_sec)
|
||||
{
|
||||
if (!ccx_options.no_progress_bar)
|
||||
{
|
||||
if (percentaje==-1)
|
||||
mprint ("\rStreaming | %02d:%02d", cur_min, cur_sec);
|
||||
else
|
||||
mprint ("\r%3d%% | %02d:%02d",percentaje, cur_min, cur_sec);
|
||||
}
|
||||
fflush (stdout);
|
||||
if (ccx_options.gui_mode_reports)
|
||||
{
|
||||
fprintf (stderr, "###PROGRESS#%d#%d#%d\n", percentaje, cur_min, cur_sec);
|
||||
fflush (stderr);
|
||||
}
|
||||
if (!ccx_options.no_progress_bar)
|
||||
{
|
||||
if (percentage==-1)
|
||||
mprint ("\rStreaming | %02d:%02d", cur_min, cur_sec);
|
||||
else
|
||||
mprint ("\r%3d%% | %02d:%02d",percentage, cur_min, cur_sec);
|
||||
}
|
||||
fflush (stdout);
|
||||
if (ccx_options.gui_mode_reports)
|
||||
{
|
||||
fprintf (stderr, "###PROGRESS#%d#%d#%d\n", percentage, cur_min, cur_sec);
|
||||
fflush (stderr);
|
||||
}
|
||||
}
|
||||
|
||||
void activity_input_file_open (const char *filename)
|
||||
{
|
||||
if (ccx_options.gui_mode_reports)
|
||||
{
|
||||
fprintf (stderr, "###INPUTFILEOPEN#%s\n", filename);
|
||||
fflush (stderr);
|
||||
}
|
||||
if (ccx_options.gui_mode_reports)
|
||||
{
|
||||
fprintf (stderr, "###INPUTFILEOPEN#%s\n", filename);
|
||||
fflush (stderr);
|
||||
}
|
||||
}
|
||||
|
||||
void activity_library_process(enum ccx_common_logging_gui message_type, ...){
|
||||
void activity_library_process(enum ccx_common_logging_gui message_type, ...)
|
||||
{
|
||||
if (ccx_options.gui_mode_reports){
|
||||
va_list args;
|
||||
va_start(args, message_type);
|
||||
switch (message_type)
|
||||
{
|
||||
case CCX_COMMON_LOGGING_GUI_XDS_CALL_LETTERS:
|
||||
vfprintf(stderr, "###XDSNETWORKCALLLETTERS#%s\n",args);
|
||||
break;
|
||||
case CCX_COMMON_LOGGING_GUI_XDS_PROGRAM_DESCRIPTION:
|
||||
vfprintf(stderr, "###XDSPROGRAMDESC#%d#%s\n", args);
|
||||
break;
|
||||
case CCX_COMMON_LOGGING_GUI_XDS_PROGRAM_ID_NR:
|
||||
vfprintf(stderr, "###XDSPROGRAMIDENTIFICATIONNUMBER#%u#%u#%u#%u\n", args);
|
||||
break;
|
||||
case CCX_COMMON_LOGGING_GUI_XDS_PROGRAM_NAME:
|
||||
vfprintf(stderr, "###XDSPROGRAMNAME#%s\n", args);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
case CCX_COMMON_LOGGING_GUI_XDS_CALL_LETTERS:
|
||||
vfprintf(stderr, "###XDSNETWORKCALLLETTERS#%s\n",args);
|
||||
break;
|
||||
case CCX_COMMON_LOGGING_GUI_XDS_PROGRAM_DESCRIPTION:
|
||||
vfprintf(stderr, "###XDSPROGRAMDESC#%d#%s\n", args);
|
||||
break;
|
||||
case CCX_COMMON_LOGGING_GUI_XDS_PROGRAM_ID_NR:
|
||||
vfprintf(stderr, "###XDSPROGRAMIDENTIFICATIONNUMBER#%u#%u#%u#%u\n", args);
|
||||
break;
|
||||
case CCX_COMMON_LOGGING_GUI_XDS_PROGRAM_NAME:
|
||||
vfprintf(stderr, "###XDSPROGRAMNAME#%s\n", args);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
fflush(stderr);
|
||||
va_end(args);
|
||||
@@ -61,65 +62,65 @@ void activity_library_process(enum ccx_common_logging_gui message_type, ...){
|
||||
}
|
||||
|
||||
void activity_video_info (int hor_size,int vert_size,
|
||||
const char *aspect_ratio, const char *framerate)
|
||||
const char *aspect_ratio, const char *framerate)
|
||||
{
|
||||
if (ccx_options.gui_mode_reports)
|
||||
{
|
||||
fprintf (stderr, "###VIDEOINFO#%u#%u#%s#%s\n",
|
||||
hor_size,vert_size, aspect_ratio, framerate);
|
||||
fflush (stderr);
|
||||
}
|
||||
if (ccx_options.gui_mode_reports)
|
||||
{
|
||||
fprintf (stderr, "###VIDEOINFO#%u#%u#%s#%s\n",
|
||||
hor_size,vert_size, aspect_ratio, framerate);
|
||||
fflush (stderr);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void activity_message (const char *fmt, ...)
|
||||
{
|
||||
if (ccx_options.gui_mode_reports)
|
||||
{
|
||||
va_list args;
|
||||
fprintf (stderr, "###MESSAGE#");
|
||||
va_start(args, fmt);
|
||||
fprintf(stderr, fmt, args);
|
||||
fprintf(stderr, "\n");
|
||||
va_end(args);
|
||||
fflush (stderr);
|
||||
}
|
||||
if (ccx_options.gui_mode_reports)
|
||||
{
|
||||
va_list args;
|
||||
fprintf (stderr, "###MESSAGE#");
|
||||
va_start(args, fmt);
|
||||
fprintf(stderr, fmt, args);
|
||||
fprintf(stderr, "\n");
|
||||
va_end(args);
|
||||
fflush (stderr);
|
||||
}
|
||||
}
|
||||
|
||||
void activity_input_file_closed (void)
|
||||
{
|
||||
if (ccx_options.gui_mode_reports)
|
||||
{
|
||||
fprintf (stderr, "###INPUTFILECLOSED\n");
|
||||
fflush (stderr);
|
||||
}
|
||||
if (ccx_options.gui_mode_reports)
|
||||
{
|
||||
fprintf (stderr, "###INPUTFILECLOSED\n");
|
||||
fflush (stderr);
|
||||
}
|
||||
}
|
||||
|
||||
void activity_program_number (unsigned program_number)
|
||||
{
|
||||
if (ccx_options.gui_mode_reports)
|
||||
{
|
||||
fprintf (stderr, "###TSPROGRAMNUMBER#%u\n",program_number);
|
||||
fprintf (stderr, "###TSPROGRAMNUMBER#%u\n", program_number);
|
||||
fflush (stderr);
|
||||
}
|
||||
}
|
||||
|
||||
void activity_report_version (void)
|
||||
{
|
||||
if (ccx_options.gui_mode_reports)
|
||||
{
|
||||
fprintf (stderr, "###VERSION#CCExtractor#%s\n",VERSION);
|
||||
fflush (stderr);
|
||||
}
|
||||
if (ccx_options.gui_mode_reports)
|
||||
{
|
||||
fprintf (stderr, "###VERSION#CCExtractor#%s\n", VERSION);
|
||||
fflush (stderr);
|
||||
}
|
||||
}
|
||||
|
||||
void activity_report_data_read (void)
|
||||
{
|
||||
if (ccx_options.gui_mode_reports)
|
||||
{
|
||||
fprintf (stderr, "###DATAREAD#%lu\n",net_activity_gui/1000);
|
||||
fflush (stderr);
|
||||
}
|
||||
if (ccx_options.gui_mode_reports)
|
||||
{
|
||||
fprintf (stderr, "###DATAREAD#%lu\n", net_activity_gui/1000);
|
||||
fflush (stderr);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -127,7 +128,7 @@ void activity_header (void)
|
||||
{
|
||||
if (!credits_shown)
|
||||
{
|
||||
credits_shown=1;
|
||||
credits_shown = 1;
|
||||
mprint ("CCExtractor %s, Carlos Fernandez Sanz, Volker Quetschke.\n", VERSION);
|
||||
mprint ("Teletext portions taken from Petr Kutalek's telxcc\n");
|
||||
mprint ("--------------------------------------------------------------------------\n");
|
||||
|
||||
17
src/lib_ccx/activity.h
Normal file
17
src/lib_ccx/activity.h
Normal file
@@ -0,0 +1,17 @@
|
||||
#ifndef ACTIVITY_H
|
||||
#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_library_process(enum ccx_common_logging_gui message_type, ...);
|
||||
void activity_report_data_read (void);
|
||||
|
||||
#endif
|
||||
@@ -85,10 +85,10 @@ typedef struct {
|
||||
// Payload parsing information
|
||||
int MultiplePayloads; // ASF
|
||||
int PacketLType; // ASF
|
||||
int ReplicatedLType; // ASF
|
||||
int OffsetMediaLType; // ASF
|
||||
int MediaNumberLType; // ASF
|
||||
int StreamNumberLType; // ASF
|
||||
int ReplicatedLType; // ASF
|
||||
int OffsetMediaLType; // ASF
|
||||
int MediaNumberLType; // ASF
|
||||
int StreamNumberLType; // ASF
|
||||
uint32_t PacketLength;
|
||||
uint32_t PaddingLength;
|
||||
} asf_data;
|
||||
} asf_data;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
55
src/lib_ccx/avc_functions.h
Normal file
55
src/lib_ccx/avc_functions.h
Normal file
@@ -0,0 +1,55 @@
|
||||
#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;
|
||||
int cc_buffer_saved; // Was the CC buffer saved after it was last updated?
|
||||
|
||||
int got_seq_para;
|
||||
unsigned nal_ref_idc;
|
||||
LLONG seq_parameter_set_id;
|
||||
int log2_max_frame_num;
|
||||
int pic_order_cnt_type;
|
||||
int log2_max_pic_order_cnt_lsb;
|
||||
int frame_mbs_only_flag;
|
||||
|
||||
// Use and throw stats for debug, remove this uglyness 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;
|
||||
|
||||
int ccblocks_in_avc_total;
|
||||
int ccblocks_in_avc_lost;
|
||||
|
||||
LLONG frame_num;
|
||||
LLONG lastframe_num;
|
||||
int currref;
|
||||
int maxidx;
|
||||
int lastmaxidx;
|
||||
|
||||
// Used to find tref zero in PTS mode
|
||||
int minidx;
|
||||
int lastminidx;
|
||||
|
||||
// Used to remember the max temporal reference number (poc mode)
|
||||
int maxtref;
|
||||
int last_gop_maxtref;
|
||||
|
||||
// Used for PTS ordering of CC blocks
|
||||
LLONG currefpts;
|
||||
LLONG last_pic_order_cnt_lsb;
|
||||
LLONG last_slice_pts;
|
||||
};
|
||||
|
||||
struct avc_ctx *init_avc(void);
|
||||
void dinit_avc(struct avc_ctx **ctx);
|
||||
void do_NAL (struct lib_cc_decode *ctx, unsigned char *NALstart, LLONG NAL_length, struct cc_subtitle *sub);
|
||||
size_t process_avc(struct lib_cc_decode *ctx, unsigned char *avcbuf, size_t avcbuflen, struct cc_subtitle *sub);
|
||||
#endif
|
||||
@@ -9,22 +9,22 @@
|
||||
// end is used to check that nothing is read at "end" or after it.
|
||||
struct bitstream
|
||||
{
|
||||
unsigned char *pos;
|
||||
int bpos;
|
||||
unsigned char *end;
|
||||
// Indicate how many bits are left in the stream after the previous
|
||||
// read call. A negative number indicates that a read after the
|
||||
// end of the stream was attempted.
|
||||
int64_t bitsleft;
|
||||
// Indicate an error occured while parsing the bitstream.
|
||||
// This is meant to store high level syntax errors, i.e a function
|
||||
// using the bitstream functions found a syntax error.
|
||||
int error;
|
||||
// Internal (private) variable - used to store the the bitstream
|
||||
// position until it is decided if the bitstream pointer will be
|
||||
// increased by the calling function, or not.
|
||||
unsigned char *_i_pos;
|
||||
int _i_bpos;
|
||||
unsigned char *pos;
|
||||
int bpos;
|
||||
unsigned char *end;
|
||||
// Indicate how many bits are left in the stream after the previous
|
||||
// read call. A negative number indicates that a read after the
|
||||
// end of the stream was attempted.
|
||||
int64_t bitsleft;
|
||||
// Indicate an error occured while parsing the bitstream.
|
||||
// This is meant to store high level syntax errors, i.e a function
|
||||
// using the bitstream functions found a syntax error.
|
||||
int error;
|
||||
// Internal (private) variable - used to store the the bitstream
|
||||
// position until it is decided if the bitstream pointer will be
|
||||
// increased by the calling function, or not.
|
||||
unsigned char *_i_pos;
|
||||
int _i_bpos;
|
||||
};
|
||||
|
||||
#define read_u8(bstream) (uint8_t)bitstream_get_num(bstream,1,1)
|
||||
|
||||
@@ -14,17 +14,17 @@
|
||||
int init_bitstream(struct bitstream *bstr, unsigned char *start, unsigned char *end)
|
||||
{
|
||||
bstr->pos = start;
|
||||
bstr->bpos = 8;
|
||||
bstr->end = end;
|
||||
bstr->bitsleft = (bstr->end - bstr->pos)*8;
|
||||
bstr->error = 0;
|
||||
bstr->_i_pos = NULL;
|
||||
bstr->_i_bpos = 0;
|
||||
bstr->bpos = 8;
|
||||
bstr->end = end;
|
||||
bstr->bitsleft = (bstr->end - bstr->pos)*8;
|
||||
bstr->error = 0;
|
||||
bstr->_i_pos = NULL;
|
||||
bstr->_i_bpos = 0;
|
||||
|
||||
if(bstr->bitsleft < 0)
|
||||
if(bstr->bitsleft < 0)
|
||||
{
|
||||
// See if we can somehow recover of this disaster by reporting the problem instead of terminating.
|
||||
mprint ( "init_bitstream: bitstream has negative length!");
|
||||
mprint ( "init_bitstream: bitstream has negative length!");
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
@@ -37,70 +37,70 @@ int init_bitstream(struct bitstream *bstr, unsigned char *start, unsigned char *
|
||||
// there are not enough bits left in the bitstream.
|
||||
uint64_t next_bits(struct bitstream *bstr, unsigned bnum)
|
||||
{
|
||||
uint64_t res = 0;
|
||||
uint64_t res = 0;
|
||||
|
||||
if(bnum > 64)
|
||||
fatal(CCX_COMMON_EXIT_BUG_BUG, "next_bits: 64 is maximum bit number, argument: %u!", bnum);
|
||||
if(bnum > 64)
|
||||
fatal(CCX_COMMON_EXIT_BUG_BUG, "next_bits: 64 is maximum bit number, argument: %u!", bnum);
|
||||
|
||||
// Sanity check
|
||||
if(bstr->end - bstr->pos < 0)
|
||||
fatal(CCX_COMMON_EXIT_BUG_BUG, "next_bits: bitstream has negative length!");
|
||||
// Sanity check
|
||||
if(bstr->end - bstr->pos < 0)
|
||||
fatal(CCX_COMMON_EXIT_BUG_BUG, "next_bits: bitstream has negative length!");
|
||||
|
||||
// Keep a negative bitstream.bitsleft, but correct it.
|
||||
if (bstr->bitsleft <= 0)
|
||||
{
|
||||
bstr->bitsleft -= bnum;
|
||||
return 0;
|
||||
}
|
||||
// Keep a negative bitstream.bitsleft, but correct it.
|
||||
if (bstr->bitsleft <= 0)
|
||||
{
|
||||
bstr->bitsleft -= bnum;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Calculate the remaining number of bits in bitstream after reading.
|
||||
bstr->bitsleft = 0LL + (bstr->end - bstr->pos - 1)*8 + bstr->bpos - bnum;
|
||||
if (bstr->bitsleft < 0)
|
||||
return 0;
|
||||
// Calculate the remaining number of bits in bitstream after reading.
|
||||
bstr->bitsleft = 0LL + (bstr->end - bstr->pos - 1)*8 + bstr->bpos - bnum;
|
||||
if (bstr->bitsleft < 0)
|
||||
return 0;
|
||||
|
||||
// Special case for reading zero bits. Return zero
|
||||
if(bnum == 0)
|
||||
return 0;
|
||||
// Special case for reading zero bits. Return zero
|
||||
if(bnum == 0)
|
||||
return 0;
|
||||
|
||||
int vbit = bstr->bpos;
|
||||
unsigned char *vpos = bstr->pos;
|
||||
int vbit = bstr->bpos;
|
||||
unsigned char *vpos = bstr->pos;
|
||||
|
||||
if(vbit < 1 || vbit > 8)
|
||||
{
|
||||
fatal(CCX_COMMON_EXIT_BUG_BUG, "next_bits: Illegal bit position value %d!", vbit);
|
||||
}
|
||||
if(vbit < 1 || vbit > 8)
|
||||
{
|
||||
fatal(CCX_COMMON_EXIT_BUG_BUG, "next_bits: Illegal bit position value %d!", vbit);
|
||||
}
|
||||
|
||||
while( 1 )
|
||||
{
|
||||
if(vpos >= bstr->end)
|
||||
{
|
||||
// We should not get here ...
|
||||
fatal(CCX_COMMON_EXIT_BUG_BUG, "next_bits: Reading after end of data ...");
|
||||
}
|
||||
while( 1 )
|
||||
{
|
||||
if(vpos >= bstr->end)
|
||||
{
|
||||
// We should not get here ...
|
||||
fatal(CCX_COMMON_EXIT_BUG_BUG, "next_bits: Reading after end of data ...");
|
||||
}
|
||||
|
||||
res |= (*vpos & (0x01 << (vbit-1)) ? 1 : 0);
|
||||
vbit--;
|
||||
bnum--;
|
||||
res |= (*vpos & (0x01 << (vbit-1)) ? 1 : 0);
|
||||
vbit--;
|
||||
bnum--;
|
||||
|
||||
if(vbit == 0)
|
||||
{
|
||||
vpos++;
|
||||
vbit = 8;
|
||||
}
|
||||
if(vbit == 0)
|
||||
{
|
||||
vpos++;
|
||||
vbit = 8;
|
||||
}
|
||||
|
||||
if(bnum)
|
||||
{
|
||||
res <<= 1;
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
if(bnum)
|
||||
{
|
||||
res <<= 1;
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
// Remember the bitstream position
|
||||
bstr->_i_bpos = vbit;
|
||||
bstr->_i_pos = vpos;
|
||||
// Remember the bitstream position
|
||||
bstr->_i_bpos = vbit;
|
||||
bstr->_i_pos = vpos;
|
||||
|
||||
return res;
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
@@ -108,18 +108,18 @@ uint64_t next_bits(struct bitstream *bstr, unsigned bnum)
|
||||
// bit read first. A 64 bit unsigned integer is returned.
|
||||
uint64_t read_bits(struct bitstream *bstr, unsigned bnum)
|
||||
{
|
||||
uint64_t res = next_bits(bstr, bnum);
|
||||
uint64_t res = next_bits(bstr, bnum);
|
||||
|
||||
// Special case for reading zero bits. Also abort when not enough
|
||||
// bits are left. Return zero
|
||||
if(bnum == 0 || bstr->bitsleft < 0)
|
||||
return 0;
|
||||
// Special case for reading zero bits. Also abort when not enough
|
||||
// bits are left. Return zero
|
||||
if(bnum == 0 || bstr->bitsleft < 0)
|
||||
return 0;
|
||||
|
||||
// Advance the bitstream
|
||||
bstr->bpos = bstr->_i_bpos;
|
||||
bstr->pos = bstr->_i_pos;
|
||||
// Advance the bitstream
|
||||
bstr->bpos = bstr->_i_bpos;
|
||||
bstr->pos = bstr->_i_pos;
|
||||
|
||||
return res;
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
@@ -128,35 +128,35 @@ uint64_t read_bits(struct bitstream *bstr, unsigned bnum)
|
||||
// Return TRUE when successfull, otherwise FALSE
|
||||
int skip_bits(struct bitstream *bstr, unsigned bnum)
|
||||
{
|
||||
// Sanity check
|
||||
if(bstr->end - bstr->pos < 0)
|
||||
fatal(CCX_COMMON_EXIT_BUG_BUG, "skip_bits: bitstream has negative length!");
|
||||
// Sanity check
|
||||
if(bstr->end - bstr->pos < 0)
|
||||
fatal(CCX_COMMON_EXIT_BUG_BUG, "skip_bits: bitstream has negative length!");
|
||||
|
||||
// Keep a negative bstr->bitsleft, but correct it.
|
||||
if (bstr->bitsleft < 0)
|
||||
{
|
||||
bstr->bitsleft -= bnum;
|
||||
return 0;
|
||||
}
|
||||
// Keep a negative bstr->bitsleft, but correct it.
|
||||
if (bstr->bitsleft < 0)
|
||||
{
|
||||
bstr->bitsleft -= bnum;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Calculate the remaining number of bits in bitstream after reading.
|
||||
bstr->bitsleft = 0LL + (bstr->end - bstr->pos - 1)*8 + bstr->bpos - bnum;
|
||||
if (bstr->bitsleft < 0)
|
||||
return 0;
|
||||
// Calculate the remaining number of bits in bitstream after reading.
|
||||
bstr->bitsleft = 0LL + (bstr->end - bstr->pos - 1)*8 + bstr->bpos - bnum;
|
||||
if (bstr->bitsleft < 0)
|
||||
return 0;
|
||||
|
||||
// Special case for reading zero bits. Return zero
|
||||
if(bnum == 0)
|
||||
return 1;
|
||||
// Special case for reading zero bits. Return zero
|
||||
if(bnum == 0)
|
||||
return 1;
|
||||
|
||||
bstr->bpos -= bnum%8;
|
||||
bstr->pos += bnum/8;
|
||||
bstr->bpos -= bnum%8;
|
||||
bstr->pos += bnum/8;
|
||||
|
||||
if (bstr->bpos < 1)
|
||||
{
|
||||
bstr->bpos += 8;
|
||||
bstr->pos += 1;
|
||||
}
|
||||
return 1;
|
||||
if (bstr->bpos < 1)
|
||||
{
|
||||
bstr->bpos += 8;
|
||||
bstr->pos += 1;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
@@ -165,55 +165,55 @@ int skip_bits(struct bitstream *bstr, unsigned bnum)
|
||||
// a byte, otherwise return FALSE
|
||||
int is_byte_aligned(struct bitstream *bstr)
|
||||
{
|
||||
// Sanity check
|
||||
if(bstr->end - bstr->pos < 0)
|
||||
fatal(CCX_COMMON_EXIT_BUG_BUG, "is_byte_aligned: bitstream has negative length!");
|
||||
// Sanity check
|
||||
if(bstr->end - bstr->pos < 0)
|
||||
fatal(CCX_COMMON_EXIT_BUG_BUG, "is_byte_aligned: bitstream has negative length!");
|
||||
|
||||
int vbit = bstr->bpos;
|
||||
int vbit = bstr->bpos;
|
||||
|
||||
if(vbit == 0 || vbit > 8)
|
||||
{
|
||||
fatal(CCX_COMMON_EXIT_BUG_BUG, "is_byte_aligned: Illegal bit position value %d!\n", vbit);
|
||||
}
|
||||
if(vbit == 0 || vbit > 8)
|
||||
{
|
||||
fatal(CCX_COMMON_EXIT_BUG_BUG, "is_byte_aligned: Illegal bit position value %d!\n", vbit);
|
||||
}
|
||||
|
||||
if (vbit == 8)
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
if (vbit == 8)
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
// Move bitstream to next byte border. Adjust bitsleft.
|
||||
void make_byte_aligned(struct bitstream *bstr)
|
||||
{
|
||||
// Sanity check
|
||||
if(bstr->end - bstr->pos < 0)
|
||||
fatal(CCX_COMMON_EXIT_BUG_BUG, "make_byte_aligned: bitstream has negative length!");
|
||||
// Sanity check
|
||||
if(bstr->end - bstr->pos < 0)
|
||||
fatal(CCX_COMMON_EXIT_BUG_BUG, "make_byte_aligned: bitstream has negative length!");
|
||||
|
||||
int vbit = bstr->bpos;
|
||||
int vbit = bstr->bpos;
|
||||
|
||||
if(vbit == 0 || vbit > 8)
|
||||
{
|
||||
fatal(CCX_COMMON_EXIT_BUG_BUG, "make_byte_aligned: Illegal bit position value %d!\n", vbit);
|
||||
}
|
||||
if(vbit == 0 || vbit > 8)
|
||||
{
|
||||
fatal(CCX_COMMON_EXIT_BUG_BUG, "make_byte_aligned: Illegal bit position value %d!\n", vbit);
|
||||
}
|
||||
|
||||
// Keep a negative bstr->bitsleft, but correct it.
|
||||
if (bstr->bitsleft < 0)
|
||||
{
|
||||
// Pay attention to the bit alignment
|
||||
bstr->bitsleft = (bstr->bitsleft-7)/8 *8;
|
||||
return;
|
||||
}
|
||||
// Keep a negative bstr->bitsleft, but correct it.
|
||||
if (bstr->bitsleft < 0)
|
||||
{
|
||||
// Pay attention to the bit alignment
|
||||
bstr->bitsleft = (bstr->bitsleft-7)/8 *8;
|
||||
return;
|
||||
}
|
||||
|
||||
if(bstr->bpos != 8)
|
||||
{
|
||||
bstr->bpos = 8;
|
||||
bstr->pos += 1;
|
||||
}
|
||||
// Reset, in case a next_???() function was used before
|
||||
bstr->bitsleft = 0LL + 8*(bstr->end-bstr->pos-1)+bstr->bpos;
|
||||
if(bstr->bpos != 8)
|
||||
{
|
||||
bstr->bpos = 8;
|
||||
bstr->pos += 1;
|
||||
}
|
||||
// Reset, in case a next_???() function was used before
|
||||
bstr->bitsleft = 0LL + 8*(bstr->end-bstr->pos-1)+bstr->bpos;
|
||||
|
||||
return;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -224,27 +224,27 @@ void make_byte_aligned(struct bitstream *bstr)
|
||||
// This function does not advance the bitstream pointer.
|
||||
unsigned char *next_bytes(struct bitstream *bstr, unsigned bynum)
|
||||
{
|
||||
// Sanity check
|
||||
if(bstr->end - bstr->pos < 0)
|
||||
fatal(CCX_COMMON_EXIT_BUG_BUG, "next_bytes: bitstream has negative length!");
|
||||
// Sanity check
|
||||
if(bstr->end - bstr->pos < 0)
|
||||
fatal(CCX_COMMON_EXIT_BUG_BUG, "next_bytes: bitstream has negative length!");
|
||||
|
||||
// Keep a negative bstr->bitsleft, but correct it.
|
||||
if (bstr->bitsleft < 0)
|
||||
{
|
||||
bstr->bitsleft -= bynum*8;
|
||||
return NULL;
|
||||
}
|
||||
// Keep a negative bstr->bitsleft, but correct it.
|
||||
if (bstr->bitsleft < 0)
|
||||
{
|
||||
bstr->bitsleft -= bynum*8;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bstr->bitsleft = 0LL + (bstr->end - bstr->pos - 1)*8 + bstr->bpos - bynum*8;
|
||||
bstr->bitsleft = 0LL + (bstr->end - bstr->pos - 1)*8 + bstr->bpos - bynum*8;
|
||||
|
||||
if (!is_byte_aligned(bstr) || bstr->bitsleft < 0 || bynum < 1)
|
||||
return NULL;
|
||||
if (!is_byte_aligned(bstr) || bstr->bitsleft < 0 || bynum < 1)
|
||||
return NULL;
|
||||
|
||||
// Remember the bitstream position
|
||||
bstr->_i_bpos = 8;
|
||||
bstr->_i_pos = bstr->pos + bynum;
|
||||
// Remember the bitstream position
|
||||
bstr->_i_bpos = 8;
|
||||
bstr->_i_pos = bstr->pos + bynum;
|
||||
|
||||
return bstr->pos;
|
||||
return bstr->pos;
|
||||
}
|
||||
|
||||
|
||||
@@ -255,15 +255,15 @@ unsigned char *next_bytes(struct bitstream *bstr, unsigned bynum)
|
||||
// This function does advance the bitstream pointer.
|
||||
unsigned char *read_bytes(struct bitstream *bstr, unsigned bynum)
|
||||
{
|
||||
unsigned char *res = next_bytes(bstr, bynum);
|
||||
unsigned char *res = next_bytes(bstr, bynum);
|
||||
|
||||
// Advance the bitstream when a read was possible
|
||||
if(res)
|
||||
{
|
||||
bstr->bpos = bstr->_i_bpos;
|
||||
bstr->pos = bstr->_i_pos;
|
||||
}
|
||||
return res;
|
||||
// Advance the bitstream when a read was possible
|
||||
if(res)
|
||||
{
|
||||
bstr->bpos = bstr->_i_bpos;
|
||||
bstr->pos = bstr->_i_pos;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
@@ -275,19 +275,19 @@ unsigned char *read_bytes(struct bitstream *bstr, unsigned bynum)
|
||||
// little-endian and big-endian CPUs.
|
||||
uint64_t bitstream_get_num(struct bitstream *bstr, unsigned bytes, int advance)
|
||||
{
|
||||
void *bpos;
|
||||
uint64_t rval=0;
|
||||
void *bpos;
|
||||
uint64_t rval=0;
|
||||
|
||||
if (advance)
|
||||
bpos = read_bytes(bstr, bytes);
|
||||
else
|
||||
bpos = next_bytes(bstr, bytes);
|
||||
if (advance)
|
||||
bpos = read_bytes(bstr, bytes);
|
||||
else
|
||||
bpos = next_bytes(bstr, bytes);
|
||||
|
||||
if (!bpos)
|
||||
return 0;
|
||||
if (!bpos)
|
||||
return 0;
|
||||
|
||||
switch (bytes)
|
||||
{
|
||||
switch (bytes)
|
||||
{
|
||||
case 1:
|
||||
case 2:
|
||||
case 4:
|
||||
@@ -295,47 +295,47 @@ uint64_t bitstream_get_num(struct bitstream *bstr, unsigned bytes, int advance)
|
||||
break;
|
||||
default:
|
||||
fatal (CCX_COMMON_EXIT_BUG_BUG, "bitstream_get_num: Illegal precision value [%u]!",
|
||||
bytes);
|
||||
bytes);
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (unsigned i=0;i<bytes;i++)
|
||||
{
|
||||
unsigned char *ucpos=((unsigned char *)bpos) +bytes-i-1; // Read backwards
|
||||
unsigned char uc=*ucpos;
|
||||
rval=(rval<<8) + uc;
|
||||
}
|
||||
return rval;
|
||||
return rval;
|
||||
}
|
||||
|
||||
|
||||
// Read unsigned Exp-Golomb code from bitstream
|
||||
uint64_t ue(struct bitstream *bstr)
|
||||
{
|
||||
uint64_t res = 0;
|
||||
int zeros=0;
|
||||
uint64_t res = 0;
|
||||
int zeros=0;
|
||||
|
||||
while(!read_bits(bstr,1))
|
||||
zeros++;
|
||||
while(!read_bits(bstr,1))
|
||||
zeros++;
|
||||
|
||||
res = (0x01 << zeros) - 1 + read_bits(bstr,zeros);
|
||||
res = (0x01 << zeros) - 1 + read_bits(bstr,zeros);
|
||||
|
||||
return res;
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
// Read signed Exp-Golomb code from bitstream
|
||||
int64_t se(struct bitstream *bstr)
|
||||
{
|
||||
int64_t res = 0;
|
||||
int64_t res = 0;
|
||||
|
||||
res = ue(bstr);
|
||||
res = ue(bstr);
|
||||
|
||||
// The following function might truncate when res+1 overflows
|
||||
//res = (res+1)/2 * (res % 2 ? 1 : -1);
|
||||
// Use this:
|
||||
res = (res/2+(res%2 ? 1 : 0)) * (res % 2 ? 1 : -1);
|
||||
// The following function might truncate when res+1 overflows
|
||||
//res = (res+1)/2 * (res % 2 ? 1 : -1);
|
||||
// Use this:
|
||||
res = (res/2+(res%2 ? 1 : 0)) * (res % 2 ? 1 : -1);
|
||||
|
||||
return res;
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
@@ -343,33 +343,33 @@ int64_t se(struct bitstream *bstr)
|
||||
// alias for read_bits().
|
||||
uint64_t u(struct bitstream *bstr, unsigned bnum)
|
||||
{
|
||||
return read_bits(bstr, bnum);
|
||||
return read_bits(bstr, bnum);
|
||||
}
|
||||
|
||||
|
||||
// Read signed integer with bnum bits length.
|
||||
int64_t i(struct bitstream *bstr, unsigned bnum)
|
||||
{
|
||||
uint64_t res = read_bits(bstr, bnum);
|
||||
uint64_t res = read_bits(bstr, bnum);
|
||||
|
||||
// Special case for reading zero bits. Return zero
|
||||
if(bnum == 0)
|
||||
return 0;
|
||||
// Special case for reading zero bits. Return zero
|
||||
if(bnum == 0)
|
||||
return 0;
|
||||
|
||||
return (0xFFFFFFFFFFFFFFFFULL << bnum) | res;
|
||||
return (0xFFFFFFFFFFFFFFFFULL << bnum) | res;
|
||||
}
|
||||
|
||||
|
||||
// Return the value with the bit order reversed.
|
||||
uint8_t reverse8(uint8_t data)
|
||||
{
|
||||
uint8_t res = 0;
|
||||
uint8_t res = 0;
|
||||
|
||||
for (int k=0;k<8;k++)
|
||||
{
|
||||
res <<= 1;
|
||||
res |= (data & (0x01 << k) ? 1 : 0);
|
||||
}
|
||||
for (int k=0;k<8;k++)
|
||||
{
|
||||
res <<= 1;
|
||||
res |= (data & (0x01 << k) ? 1 : 0);
|
||||
}
|
||||
|
||||
return res;
|
||||
return res;
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,7 @@
|
||||
#include "ccx_common_common.h"
|
||||
|
||||
int cc608_parity_table[256];
|
||||
|
||||
/* printf() for fd instead of FILE*, since dprintf is not portable */
|
||||
void fdprintf(int fd, const char *fmt, ...)
|
||||
{
|
||||
@@ -36,7 +38,8 @@ void fdprintf(int fd, const char *fmt, ...)
|
||||
free(p);
|
||||
return;
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
p = np;
|
||||
}
|
||||
}
|
||||
@@ -63,3 +66,63 @@ void freep(void *arg)
|
||||
*ptr = NULL;
|
||||
|
||||
}
|
||||
|
||||
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 e_type)
|
||||
{
|
||||
if (sub->nb_data)
|
||||
{
|
||||
for(;sub->next;sub = sub->next);
|
||||
sub->next = malloc(sizeof(struct cc_subtitle));
|
||||
if(!sub->next)
|
||||
return -1;
|
||||
sub->next->prev = sub;
|
||||
sub = sub->next;
|
||||
}
|
||||
|
||||
sub->type = CC_TEXT;
|
||||
sub->enc_type = e_type;
|
||||
sub->data = strdup(str);
|
||||
sub->nb_data = strlen(str);
|
||||
sub->start_time = start_time;
|
||||
sub->end_time = end_time;
|
||||
if(info)
|
||||
strncpy(sub->info, info, 4);
|
||||
if(mode)
|
||||
strncpy(sub->mode, mode, 4);
|
||||
sub->got_output = 1;
|
||||
sub->next = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cc608_parity(unsigned int byte)
|
||||
{
|
||||
int ones = 0;
|
||||
|
||||
for (int i = 0; i < 7; i++)
|
||||
{
|
||||
if (byte & (1 << i))
|
||||
ones++;
|
||||
}
|
||||
|
||||
return ones & 1;
|
||||
}
|
||||
|
||||
void cc608_build_parity_table(int *parity_table)
|
||||
{
|
||||
unsigned int byte;
|
||||
int parity_v;
|
||||
for (byte = 0; byte <= 127; byte++)
|
||||
{
|
||||
parity_v = cc608_parity(byte);
|
||||
/* CC uses odd parity (i.e., # of 1's in byte is odd.) */
|
||||
parity_table[byte] = parity_v;
|
||||
parity_table[byte | 0x80] = !parity_v;
|
||||
}
|
||||
}
|
||||
|
||||
void build_parity_table (void)
|
||||
{
|
||||
cc608_build_parity_table(cc608_parity_table);
|
||||
}
|
||||
|
||||
@@ -2,12 +2,41 @@
|
||||
#define _CC_COMMON_COMMON
|
||||
|
||||
#include "ccx_common_platform.h"
|
||||
#include "ccx_common_structs.h"
|
||||
|
||||
// Define possible exit codes that will be passed on to the fatal function
|
||||
#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
|
||||
/* Exit codes. Take this seriously as the GUI depends on them.
|
||||
0 means OK as usual,
|
||||
<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_WITH_HELP 9
|
||||
#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_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
|
||||
|
||||
// Declarations
|
||||
void fdprintf(int fd, const char *fmt, ...);
|
||||
@@ -15,6 +44,8 @@ void mstotime(LLONG milli, unsigned *hours, unsigned *minutes,unsigned *seconds,
|
||||
void freep(void *arg);
|
||||
void dbg_print(LLONG mask, const char *fmt, ...);
|
||||
unsigned char *debug_608toASC(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);
|
||||
|
||||
extern int cc608_parity_table[256]; // From myth
|
||||
#endif
|
||||
|
||||
@@ -24,104 +24,104 @@ const unsigned char lc6[1]={0xfe};
|
||||
|
||||
const double framerates_values[16]=
|
||||
{
|
||||
0,
|
||||
24000.0/1001, /* 23.976 */
|
||||
24.0,
|
||||
25.0,
|
||||
30000.0/1001, /* 29.97 */
|
||||
30.0,
|
||||
50.0,
|
||||
60000.0/1001, /* 59.94 */
|
||||
60.0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0
|
||||
0,
|
||||
24000.0/1001, /* 23.976 */
|
||||
24.0,
|
||||
25.0,
|
||||
30000.0/1001, /* 29.97 */
|
||||
30.0,
|
||||
50.0,
|
||||
60000.0/1001, /* 59.94 */
|
||||
60.0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0
|
||||
};
|
||||
|
||||
const char *framerates_types[16]=
|
||||
{
|
||||
"00 - forbidden",
|
||||
"01 - 23.976",
|
||||
"02 - 24",
|
||||
"03 - 25",
|
||||
"04 - 29.97",
|
||||
"05 - 30",
|
||||
"06 - 50",
|
||||
"07 - 59.94",
|
||||
"08 - 60",
|
||||
"09 - reserved",
|
||||
"10 - reserved",
|
||||
"11 - reserved",
|
||||
"12 - reserved",
|
||||
"13 - reserved",
|
||||
"14 - reserved",
|
||||
"15 - reserved"
|
||||
"00 - forbidden",
|
||||
"01 - 23.976",
|
||||
"02 - 24",
|
||||
"03 - 25",
|
||||
"04 - 29.97",
|
||||
"05 - 30",
|
||||
"06 - 50",
|
||||
"07 - 59.94",
|
||||
"08 - 60",
|
||||
"09 - reserved",
|
||||
"10 - reserved",
|
||||
"11 - reserved",
|
||||
"12 - reserved",
|
||||
"13 - reserved",
|
||||
"14 - reserved",
|
||||
"15 - reserved"
|
||||
};
|
||||
|
||||
const char *aspect_ratio_types[16]=
|
||||
{
|
||||
"00 - forbidden",
|
||||
"01 - 1:1",
|
||||
"02 - 4:3",
|
||||
"03 - 16:9",
|
||||
"04 - 2.21:1",
|
||||
"05 - reserved",
|
||||
"06 - reserved",
|
||||
"07 - reserved",
|
||||
"08 - reserved",
|
||||
"09 - reserved",
|
||||
"10 - reserved",
|
||||
"11 - reserved",
|
||||
"12 - reserved",
|
||||
"13 - reserved",
|
||||
"14 - reserved",
|
||||
"15 - reserved"
|
||||
"00 - forbidden",
|
||||
"01 - 1:1",
|
||||
"02 - 4:3",
|
||||
"03 - 16:9",
|
||||
"04 - 2.21:1",
|
||||
"05 - reserved",
|
||||
"06 - reserved",
|
||||
"07 - reserved",
|
||||
"08 - reserved",
|
||||
"09 - reserved",
|
||||
"10 - reserved",
|
||||
"11 - reserved",
|
||||
"12 - reserved",
|
||||
"13 - reserved",
|
||||
"14 - reserved",
|
||||
"15 - reserved"
|
||||
};
|
||||
|
||||
|
||||
const char *pict_types[8]=
|
||||
{
|
||||
"00 - ilegal (0)",
|
||||
"01 - I",
|
||||
"02 - P",
|
||||
"03 - B",
|
||||
"04 - ilegal (D)",
|
||||
"05 - ilegal (5)",
|
||||
"06 - ilegal (6)",
|
||||
"07 - ilegal (7)"
|
||||
"00 - ilegal (0)",
|
||||
"01 - I",
|
||||
"02 - P",
|
||||
"03 - B",
|
||||
"04 - ilegal (D)",
|
||||
"05 - ilegal (5)",
|
||||
"06 - ilegal (6)",
|
||||
"07 - ilegal (7)"
|
||||
};
|
||||
|
||||
|
||||
const char *slice_types[10]=
|
||||
{
|
||||
"0 - P",
|
||||
"1 - B",
|
||||
"2 - I",
|
||||
"3 - SP",
|
||||
"4 - SI",
|
||||
"5 - P",
|
||||
"6 - B",
|
||||
"7 - I",
|
||||
"8 - SP",
|
||||
"9 - SI"
|
||||
"0 - P",
|
||||
"1 - B",
|
||||
"2 - I",
|
||||
"3 - SP",
|
||||
"4 - SI",
|
||||
"5 - P",
|
||||
"6 - B",
|
||||
"7 - I",
|
||||
"8 - SP",
|
||||
"9 - SI"
|
||||
};
|
||||
|
||||
const char *cc_types[4] =
|
||||
{
|
||||
"NTSC line 21 field 1 closed captions",
|
||||
"NTSC line 21 field 2 closed captions",
|
||||
"DTVCC Channel Packet Data",
|
||||
"DTVCC Channel Packet Start"
|
||||
"NTSC line 21 field 1 closed captions",
|
||||
"NTSC line 21 field 2 closed captions",
|
||||
"DTVCC Channel Packet Data",
|
||||
"DTVCC Channel Packet Start"
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
NTSC_CC_f1 = 0,
|
||||
NTSC_CC_f2 = 1,
|
||||
DTVCC_PACKET_DATA = 2,
|
||||
DTVCC_PACKET_START = 3,
|
||||
NTSC_CC_f1 = 0,
|
||||
NTSC_CC_f2 = 1,
|
||||
DTVCC_PACKET_DATA = 2,
|
||||
DTVCC_PACKET_START = 3,
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -36,6 +36,7 @@ extern unsigned char rcwt_header[11];
|
||||
#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()
|
||||
|
||||
@@ -56,6 +57,7 @@ enum ccx_debug_message_types
|
||||
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_DUMPDEF=0x2000 // Dump defective TS packets
|
||||
};
|
||||
|
||||
// AVC NAL types
|
||||
@@ -98,12 +100,11 @@ enum ccx_avc_nal_types
|
||||
// MPEG-2 TS stream types
|
||||
enum ccx_stream_type
|
||||
{
|
||||
CCX_STREAM_TYPE_UNKNOWNSTREAM = 0,
|
||||
|
||||
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_UNKNOWNSTREAM = 0,
|
||||
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,
|
||||
@@ -113,13 +114,13 @@ enum ccx_stream_type
|
||||
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_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_AC3 = 0x81,
|
||||
CCX_STREAM_TYPE_AUDIO_HDMV_DTS = 0x82,
|
||||
CCX_STREAM_TYPE_AUDIO_DTS = 0x8a,
|
||||
};
|
||||
|
||||
enum ccx_mpeg_descriptor
|
||||
@@ -131,6 +132,7 @@ enum ccx_mpeg_descriptor
|
||||
CCX_MPEG_DSC_VBI_TELETEXT_DESCRIPTOR = 0x46,
|
||||
CCX_MPEG_DSC_TELETEXT_DESCRIPTOR = 0x56,
|
||||
CCX_MPEG_DSC_DVB_SUBTITLE = 0x59,
|
||||
CCX_MPEG_DSC_CAPTION_SERVICE = 0x86,
|
||||
CCX_MPEG_DESC_DATA_COMP = 0xfd,
|
||||
};
|
||||
|
||||
@@ -152,15 +154,18 @@ enum ccx_datasource
|
||||
|
||||
enum ccx_output_format
|
||||
{
|
||||
CCX_OF_RAW = 0,
|
||||
CCX_OF_SRT = 1,
|
||||
CCX_OF_SAMI = 2,
|
||||
CCX_OF_TRANSCRIPT = 3,
|
||||
CCX_OF_RCWT = 4,
|
||||
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_SPUPNG = 7,
|
||||
CCX_OF_DVDRAW = 8, // See -d at http://www.geocities.com/mcpoodle43/SCC_TOOLS/DOCS/SCC_TOOLS.HTML#CCExtract
|
||||
CCX_OF_WEBVTT = 9,
|
||||
CCX_OF_SIMPLE_XML = 10,
|
||||
CCX_OF_G608 = 11,
|
||||
};
|
||||
|
||||
enum ccx_output_date_format
|
||||
@@ -174,47 +179,49 @@ enum ccx_output_date_format
|
||||
|
||||
enum ccx_stream_mode_enum
|
||||
{
|
||||
CCX_SM_ELEMENTARY_OR_NOT_FOUND=0,
|
||||
CCX_SM_TRANSPORT=1,
|
||||
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_ELEMENTARY_OR_NOT_FOUND=0,
|
||||
CCX_SM_TRANSPORT=1,
|
||||
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-
|
||||
#ifdef WTV_DEBUG
|
||||
CCX_SM_HEX_DUMP = 8, // Hexadecimal dump generated by wtvccdump
|
||||
#endif
|
||||
CCX_SM_WTV = 9,
|
||||
CCX_SM_AUTODETECT = 16
|
||||
CCX_SM_WTV = 9,
|
||||
CCX_SM_AUTODETECT = 16
|
||||
};
|
||||
|
||||
enum ccx_encoding_type
|
||||
{
|
||||
CCX_ENC_UNICODE = 0,
|
||||
CCX_ENC_LATIN_1 = 1,
|
||||
CCX_ENC_UTF_8 = 2
|
||||
CCX_ENC_UNICODE = 0,
|
||||
CCX_ENC_LATIN_1 = 1,
|
||||
CCX_ENC_UTF_8 = 2,
|
||||
CCX_ENC_ASCII = 3
|
||||
};
|
||||
|
||||
enum ccx_bufferdata_type
|
||||
{
|
||||
CCX_UNKNOWN = 0,
|
||||
CCX_PES = 1,
|
||||
CCX_RAW = 2,
|
||||
CCX_H264 = 3,
|
||||
CCX_UNKNOWN = 0,
|
||||
CCX_PES = 1,
|
||||
CCX_RAW = 2,
|
||||
CCX_H264 = 3,
|
||||
CCX_HAUPPAGE = 4,
|
||||
CCX_TELETEXT = 5,
|
||||
CCX_PRIVATE_MPEG2_CC = 6,
|
||||
CCX_DVB_SUBTITLE = 7,
|
||||
CCX_ISDB_SUBTITLE = 8,
|
||||
};
|
||||
|
||||
enum ccx_frame_type
|
||||
{
|
||||
CCX_FRAME_TYPE_RESET_OR_UNKNOWN = 0,
|
||||
CCX_FRAME_TYPE_I_FRAME = 1,
|
||||
CCX_FRAME_TYPE_P_FRAME = 2,
|
||||
CCX_FRAME_TYPE_B_FRAME = 3,
|
||||
CCX_FRAME_TYPE_D_FRAME = 4
|
||||
CCX_FRAME_TYPE_RESET_OR_UNKNOWN = 0,
|
||||
CCX_FRAME_TYPE_I_FRAME = 1,
|
||||
CCX_FRAME_TYPE_P_FRAME = 2,
|
||||
CCX_FRAME_TYPE_B_FRAME = 3,
|
||||
CCX_FRAME_TYPE_D_FRAME = 4
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
@@ -229,8 +236,17 @@ enum ccx_code_type
|
||||
CCX_CODEC_TELETEXT,
|
||||
CCX_CODEC_DVB,
|
||||
CCX_CODEC_ISDB_CC,
|
||||
CCX_CODEC_ATSC_CC,
|
||||
CCX_CODEC_NONE,
|
||||
};
|
||||
|
||||
enum cdp_section_type
|
||||
{
|
||||
CDP_SECTION_DATA = 0x72,
|
||||
CDP_SECTION_SVC_INFO = 0x73,
|
||||
CDP_SECTION_FOOTER = 0x74
|
||||
};
|
||||
|
||||
/*
|
||||
* This Macro check whether descriptor tag is valid for teletext
|
||||
* codec or not.
|
||||
@@ -242,15 +258,15 @@ enum ccx_code_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 ) )
|
||||
( (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
|
||||
* should parse f_sel subtitle codec type or not
|
||||
*
|
||||
* @param u_sel pass the codec selected by user to be searched in
|
||||
* all elementry stream, we ignore the not to be selected stream
|
||||
* all elementary stream, we ignore the not to be selected stream
|
||||
* if we find stream this is selected stream. since setting
|
||||
* selected stream and not selected to same codec does not
|
||||
* make ay sense.
|
||||
@@ -263,10 +279,18 @@ enum ccx_code_type
|
||||
* 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 autodetected, or forced, etc
|
||||
#define CCX_TXT_FORBIDDEN 0 // Ignore teletext packets
|
||||
#define CCX_TXT_AUTO_NOT_YET_FOUND 1
|
||||
#define CCX_TXT_IN_USE 2 // Positive autodetected, or forced, etc
|
||||
|
||||
#define NB_LANGUAGE 5
|
||||
extern const char *language[NB_LANGUAGE];
|
||||
|
||||
#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"
|
||||
#endif
|
||||
|
||||
@@ -1,22 +1,22 @@
|
||||
#include "ccx_common_option.h"
|
||||
#include "ccx_encoders_common.h"
|
||||
#include "ccx_decoders_708.h"
|
||||
#include "utility.h"
|
||||
|
||||
struct ccx_s_options ccx_options;
|
||||
extern ccx_encoders_transcript_format ccx_encoders_default_transcript_settings;
|
||||
/* Parameters */
|
||||
void init_options (struct ccx_s_options *options)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
options->buffer_input = 1; // In Windows buffering seems to help
|
||||
options->no_bom = 0;
|
||||
#else
|
||||
options->buffer_input = 0; // In linux, not so much.
|
||||
options->no_bom = 1;
|
||||
#endif
|
||||
options->nofontcolor=0; // 1 = don't put <font color> tags
|
||||
options->notypesetting=0; // 1 = Don't put <i>, <u>, etc typesetting tags
|
||||
options->no_rollup = 0;
|
||||
options->noscte20 = 0;
|
||||
|
||||
options->no_bom = 0; // Use BOM by default.
|
||||
|
||||
options->settings_608.direct_rollup = 0;
|
||||
options->settings_608.no_rollup = 0;
|
||||
@@ -24,22 +24,15 @@ void init_options (struct ccx_s_options *options)
|
||||
options->settings_608.screens_to_process = -1;
|
||||
options->settings_608.default_color = COL_TRANSPARENT; // Defaults to transparant/no-color.
|
||||
|
||||
/* Select subtitle codec */
|
||||
options->codec = CCX_CODEC_ANY;
|
||||
options->nocodec = CCX_CODEC_NONE;
|
||||
|
||||
/* Credit stuff */
|
||||
options->start_credits_text=NULL;
|
||||
options->end_credits_text=NULL;
|
||||
options->extract = 1; // Extract 1st field only (primary language)
|
||||
options->cc_channel = 1; // Channel we want to dump in srt mode
|
||||
options->binary_concat=1; // Disabled by -ve or --videoedited
|
||||
options->use_gop_as_pts = 0; // Use GOP instead of PTS timing (0=do as needed, 1=always, -1=never)
|
||||
options->fix_padding = 0; // Replace 0000 with 8080 in HDTV (needed for some cards)
|
||||
options->trim_subs=0; // " Remove spaces at sides? "
|
||||
options->gui_mode_reports=0; // If 1, output in stderr progress updates so the GUI can grab them
|
||||
options->no_progress_bar=0; // If 1, suppress the output of the progress to stdout
|
||||
options->sentence_cap =0 ; // FIX CASE? = Fix case?
|
||||
options->enc_cfg.sentence_cap =0 ; // FIX CASE? = Fix case?
|
||||
options->sentence_cap_file=NULL; // Extra words file?
|
||||
options->live_stream=0; // 0 -> A regular file
|
||||
options->messages_target=1; // 1=stdout
|
||||
@@ -58,30 +51,19 @@ void init_options (struct ccx_s_options *options)
|
||||
options->mp4vidtrack=0; // Process the video track even if a CC dedicated track exists.
|
||||
/* General stuff */
|
||||
options->usepicorder = 0; // Force the use of pic_order_cnt_lsb in AVC/H.264 data streams
|
||||
options->autodash=0; // Add dashes (-) before each speaker automatically?
|
||||
options->xmltv=0; // 1 = full output. 2 = live output. 3 = both
|
||||
options->xmltvliveinterval=10; // interval in seconds between writting xmltv output files in live mode
|
||||
options->xmltvoutputinterval=0; // interval in seconds between writting xmltv full file output
|
||||
options->xmltvonlycurrent=0; // 0 off 1 on
|
||||
options->teletext_mode=CCX_TXT_AUTO_NOT_YET_FOUND; // 0=Disabled, 1 = Not found, 2=Found
|
||||
|
||||
options->transcript_settings = ccx_encoders_default_transcript_settings;
|
||||
options->millis_separator=',';
|
||||
|
||||
options->encoding = CCX_ENC_UTF_8;
|
||||
options->write_format=CCX_OF_SRT; // 0=Raw, 1=srt, 2=SMI
|
||||
options->date_format=ODF_NONE;
|
||||
options->output_filename=NULL;
|
||||
options->out_elementarystream_filename=NULL;
|
||||
options->output_filename = NULL;
|
||||
options->debug_mask=CCX_DMT_GENERIC_NOTICES; // dbg_print will use this mask to print or ignore different types
|
||||
options->debug_mask_on_debug=CCX_DMT_VERBOSE; // If we're using temp_debug to enable/disable debug "live", this is the mask when temp_debug=1
|
||||
options->ts_autoprogram =0; // Try to find a stream with captions automatically (no -pn needed)
|
||||
options->ts_cappid = 0; // PID for stream that holds caption information
|
||||
options->ts_forced_cappid = 0; // If 1, never mess with the selected PID
|
||||
options->ts_forced_program=0; // Specific program to process in TS files, if ts_forced_program_selected==1
|
||||
options->ts_forced_program_selected=0;
|
||||
options->ts_datastreamtype = -1; // User WANTED stream type (i.e. use the stream that has this type)
|
||||
options->ts_forced_streamtype=CCX_STREAM_TYPE_UNKNOWNSTREAM; // User selected (forced) stream type
|
||||
/* Networking */
|
||||
options->udpaddr = NULL;
|
||||
options->udpport=0; // Non-zero => Listen for UDP packets on this port, no files.
|
||||
@@ -91,20 +73,62 @@ void init_options (struct ccx_s_options *options)
|
||||
options->tcp_desc = NULL;
|
||||
options->srv_addr = NULL;
|
||||
options->srv_port = NULL;
|
||||
options->line_terminator_lf=0; // 0 = CRLF
|
||||
options->noautotimeref=0; // Do NOT set time automatically?
|
||||
options->input_source=CCX_DS_FILE; // Files, stdin or network
|
||||
options->auto_stream = CCX_SM_AUTODETECT;
|
||||
options->m2ts = 0;
|
||||
options->multiprogram = 0;
|
||||
options->out_interval = -1;
|
||||
|
||||
options->subs_delay = 0;
|
||||
|
||||
/* Select subtitle codec */
|
||||
options->demux_cfg.codec = CCX_CODEC_ANY;
|
||||
options->demux_cfg.nocodec = CCX_CODEC_NONE;
|
||||
|
||||
options->demux_cfg.auto_stream = CCX_SM_AUTODETECT;
|
||||
options->demux_cfg.m2ts = 0;
|
||||
options->demux_cfg.out_elementarystream_filename=NULL;
|
||||
options->demux_cfg.ts_autoprogram =0; // Try to find a stream with captions automatically (no -pn needed)
|
||||
options->demux_cfg.ts_cappids[0] = 0; // PID for stream that holds caption information
|
||||
options->demux_cfg.nb_ts_cappid = 0;
|
||||
options->demux_cfg.ts_forced_program = -1; // Specific program to process in TS files, if ts_forced_program_selected==1
|
||||
options->demux_cfg.ts_forced_program_selected=0;
|
||||
options->demux_cfg.ts_datastreamtype = CCX_STREAM_TYPE_UNKNOWNSTREAM; // User WANTED stream type (i.e. use the stream that has this type)
|
||||
options->demux_cfg.ts_forced_streamtype=CCX_STREAM_TYPE_UNKNOWNSTREAM; // User selected (forced) stream type
|
||||
|
||||
options->enc_cfg.autodash=0; // Add dashes (-) before each speaker automatically?
|
||||
options->enc_cfg.trim_subs=0; // " Remove spaces at sides? "
|
||||
options->enc_cfg.in_format = 1;
|
||||
options->enc_cfg.line_terminator_lf=0; // 0 = CRLF
|
||||
options->enc_cfg.start_credits_text=NULL;
|
||||
options->enc_cfg.end_credits_text=NULL;
|
||||
options->enc_cfg.encoding = CCX_ENC_UTF_8;
|
||||
options->enc_cfg.no_bom = 0; // Use BOM by default.
|
||||
options->enc_cfg.services_charsets = NULL;
|
||||
options->enc_cfg.all_services_charset = NULL;
|
||||
|
||||
options->settings_dtvcc.enabled = 0;
|
||||
options->settings_dtvcc.active_services_count = 0;
|
||||
options->settings_dtvcc.print_file_reports = 1;
|
||||
options->settings_dtvcc.no_rollup = 0;
|
||||
options->settings_dtvcc.report = NULL;
|
||||
memset(options->settings_dtvcc.services_enabled, 0, CCX_DTVCC_MAX_SERVICES);
|
||||
|
||||
// Prepare time structures
|
||||
init_boundary_time (&options->extraction_start);
|
||||
init_boundary_time (&options->extraction_end);
|
||||
init_boundary_time (&options->startcreditsnotbefore);
|
||||
init_boundary_time (&options->startcreditsnotafter);
|
||||
init_boundary_time (&options->startcreditsforatleast);
|
||||
init_boundary_time (&options->startcreditsforatmost);
|
||||
init_boundary_time (&options->endcreditsforatleast);
|
||||
init_boundary_time (&options->endcreditsforatmost);
|
||||
init_boundary_time (&options->enc_cfg.startcreditsnotbefore);
|
||||
init_boundary_time (&options->enc_cfg.startcreditsnotafter);
|
||||
init_boundary_time (&options->enc_cfg.startcreditsforatleast);
|
||||
init_boundary_time (&options->enc_cfg.startcreditsforatmost);
|
||||
init_boundary_time (&options->enc_cfg.endcreditsforatleast);
|
||||
init_boundary_time (&options->enc_cfg.endcreditsforatmost);
|
||||
|
||||
|
||||
// Sensible default values for credits
|
||||
stringztoms (DEF_VAL_STARTCREDITSNOTBEFORE, &options->enc_cfg.startcreditsnotbefore);
|
||||
stringztoms (DEF_VAL_STARTCREDITSNOTAFTER, &options->enc_cfg.startcreditsnotafter);
|
||||
stringztoms (DEF_VAL_STARTCREDITSFORATLEAST, &options->enc_cfg.startcreditsforatleast);
|
||||
stringztoms (DEF_VAL_STARTCREDITSFORATMOST, &options->enc_cfg.startcreditsforatmost);
|
||||
stringztoms (DEF_VAL_ENDCREDITSFORATLEAST, &options->enc_cfg.endcreditsforatleast);
|
||||
stringztoms (DEF_VAL_ENDCREDITSFORATMOST, &options->enc_cfg.endcreditsforatmost);
|
||||
}
|
||||
|
||||
@@ -4,36 +4,91 @@
|
||||
#include "ccx_common_timing.h"
|
||||
#include "ccx_decoders_608.h"
|
||||
#include "ccx_encoders_structs.h"
|
||||
struct ccx_s_options // Options from user parameters
|
||||
#include "list.h"
|
||||
|
||||
struct demuxer_cfg
|
||||
{
|
||||
int extract; // Extract 1st, 2nd or both fields
|
||||
int cc_channel; // Channel we want to dump in srt mode
|
||||
int buffer_input;
|
||||
int nofontcolor;
|
||||
int notypesetting;
|
||||
struct ccx_boundary_time extraction_start, extraction_end; // Segment we actually process
|
||||
int print_file_reports;
|
||||
|
||||
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!
|
||||
|
||||
ccx_decoder_608_settings settings_608; // Contains the settings for the 608 decoder.
|
||||
int m2ts; // Regular TS or M2TS
|
||||
enum ccx_stream_mode_enum auto_stream;
|
||||
char *out_elementarystream_filename;
|
||||
|
||||
/* subtitle codec type */
|
||||
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_allprogram;
|
||||
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_program_selected;
|
||||
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
|
||||
char *output_filename;
|
||||
enum ccx_output_format write_format; // 0=Raw, 1=srt, 2=SMI
|
||||
|
||||
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 sentence_cap ; // FIX CASE? = Fix case?
|
||||
/* 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 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!
|
||||
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 program_number;
|
||||
unsigned char in_format;
|
||||
|
||||
//CEA-708
|
||||
int services_enabled[CCX_DTVCC_MAX_SERVICES];
|
||||
char** services_charsets;
|
||||
char* all_services_charset;
|
||||
};
|
||||
struct ccx_s_options // Options from user parameters
|
||||
{
|
||||
int extract; // Extract 1st, 2nd or both fields
|
||||
int no_rollup;
|
||||
int noscte20;
|
||||
int cc_channel; // Channel we want to dump in srt mode
|
||||
int buffer_input;
|
||||
int nofontcolor;
|
||||
int nohtmlescape;
|
||||
int notypesetting;
|
||||
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
|
||||
|
||||
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 trim_subs; // " Remove spaces at sides? "
|
||||
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
|
||||
int sentence_cap ; // FIX CASE? = Fix case?
|
||||
char *sentence_cap_file; // Extra words file?
|
||||
int live_stream; /* -1 -> Not a complete file but a live stream, without timeout
|
||||
0 -> A regular file
|
||||
@@ -44,7 +99,7 @@ struct ccx_s_options // Options from user parameters
|
||||
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 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 wtvconvertfix; // Fix broken Windows 7 conversion
|
||||
int wtvmpeg2;
|
||||
int auto_myth; // Use myth-tv mpeg code? 0=no, 1=yes, 2=auto
|
||||
@@ -52,50 +107,38 @@ struct ccx_s_options // Options from user parameters
|
||||
unsigned mp4vidtrack; // Process the video track even if a CC dedicated track exists.
|
||||
/* General settings */
|
||||
int usepicorder; // Force the use of pic_order_cnt_lsb in AVC/H.264 data streams
|
||||
int autodash; // Add dashes (-) before each speaker automatically?
|
||||
int xmltv; // 1 = full output. 2 = live output. 3 = both
|
||||
int xmltvliveinterval; // interval in seconds between writting xmltv output files in live mode
|
||||
int xmltvoutputinterval; // interval in seconds between writting xmltv full file output
|
||||
int xmltvonlycurrent; // 0 off 1 on
|
||||
unsigned teletext_mode; // 0=Disabled, 1 = Not found, 2=Found
|
||||
|
||||
ccx_encoders_transcript_format transcript_settings; // Keeps the settings for generating transcript output files.
|
||||
char millis_separator;
|
||||
enum ccx_encoding_type encoding;
|
||||
enum ccx_output_format write_format; // 0=Raw, 1=srt, 2=SMI
|
||||
enum ccx_output_date_format date_format;
|
||||
char *output_filename;
|
||||
char *out_elementarystream_filename;
|
||||
unsigned send_to_srv;
|
||||
enum ccx_output_format write_format; // 0=Raw, 1=srt, 2=SMI
|
||||
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
|
||||
unsigned ts_autoprogram ; // Try to find a stream with captions automatically (no -pn needed)
|
||||
unsigned ts_cappid ; // PID for stream that holds caption information
|
||||
unsigned ts_forced_cappid ; // If 1, never mess with the selected PID
|
||||
unsigned 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)
|
||||
unsigned ts_forced_streamtype; // User selected (forced) stream type
|
||||
/* Networking */
|
||||
char *udpaddr;
|
||||
unsigned udpport; // Non-zero => Listen for UDP packets on this port, no files.
|
||||
unsigned send_to_srv;
|
||||
char *tcpport;
|
||||
char *tcp_password;
|
||||
char *tcp_desc;
|
||||
char *srv_addr;
|
||||
char *srv_port;
|
||||
int line_terminator_lf; // 0 = CRLF, 1=LF
|
||||
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?
|
||||
enum ccx_stream_mode_enum auto_stream;
|
||||
int m2ts; // Regular TS or M2TS
|
||||
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.
|
||||
// Output structures
|
||||
struct ccx_s_write wbout1;
|
||||
struct ccx_s_write wbout2;
|
||||
int multiprogram;
|
||||
int out_interval;
|
||||
};
|
||||
|
||||
extern struct ccx_s_options ccx_options;
|
||||
|
||||
@@ -14,6 +14,8 @@
|
||||
#define __STDC_FORMAT_MACROS
|
||||
|
||||
#ifdef _WIN32
|
||||
#define inline _inline
|
||||
#define typeof decltype
|
||||
#include <io.h>
|
||||
#include <ws2tcpip.h>
|
||||
#include <windows.h>
|
||||
@@ -110,4 +112,4 @@
|
||||
|
||||
typedef int64_t LLONG;
|
||||
|
||||
#endif // CCX_PLATFORM_H
|
||||
#endif // CCX_PLATFORM_H
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
#ifndef _CC_COMMON_STRUCTS
|
||||
#define _CC_COMMON_STRUCTS
|
||||
|
||||
#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
|
||||
@@ -9,7 +11,7 @@ enum ccx_common_logging_gui {
|
||||
};
|
||||
|
||||
struct ccx_common_logging_t {
|
||||
int debug_mask; // The debug mask that is used to determine if things should be printed or not.
|
||||
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.
|
||||
@@ -21,33 +23,52 @@ enum subtype
|
||||
{
|
||||
CC_BITMAP,
|
||||
CC_608,
|
||||
CC_708,
|
||||
CC_TEXT,
|
||||
CC_RAW,
|
||||
};
|
||||
|
||||
/**
|
||||
* 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
|
||||
* just now only struct cc_eia608_screen is placed here
|
||||
* @warn decoder cant output multiple types of data
|
||||
*/
|
||||
void *data;
|
||||
|
||||
/** number of data */
|
||||
unsigned int nb_data;
|
||||
|
||||
/** type of 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;
|
||||
|
||||
/* set only when all the data is to be displayed at same time */
|
||||
LLONG start_time;
|
||||
LLONG end_time;
|
||||
|
||||
/* flags */
|
||||
int flags;
|
||||
|
||||
/* index of language table */
|
||||
int lang_index;
|
||||
|
||||
/** flag to tell that decoder has given output */
|
||||
int got_output;
|
||||
|
||||
char mode[5];
|
||||
char info[4];
|
||||
|
||||
struct cc_subtitle *next;
|
||||
struct cc_subtitle *prev;
|
||||
};
|
||||
#endif
|
||||
|
||||
@@ -11,32 +11,18 @@
|
||||
// Count 608 (per field) and 708 blocks since last set_fts() call
|
||||
int cb_field1, cb_field2, cb_708;
|
||||
|
||||
int pts_set; //0 = No, 1 = received, 2 = min_pts set
|
||||
int MPEG_CLOCK_FREQ = 90000; // This "constant" is part of the standard
|
||||
|
||||
LLONG min_pts, max_pts, sync_pts;
|
||||
LLONG current_pts = 0;
|
||||
|
||||
int max_dif = 5;
|
||||
unsigned pts_big_change;
|
||||
|
||||
// PTS timing related stuff
|
||||
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_global = 0; // Duration of previous files (-ve mode), see c1global
|
||||
|
||||
enum ccx_frame_type current_picture_coding_type = CCX_FRAME_TYPE_RESET_OR_UNKNOWN;
|
||||
int current_tref = 0; // Store temporal reference of current frame
|
||||
double current_fps = (double) 30000.0 / 1001; /* 29.97 */ // TODO: Get from framerates_values[] instead
|
||||
|
||||
int frames_since_ref_time = 0;
|
||||
unsigned total_frames_count;
|
||||
|
||||
// Remember the current field for 608 decoding
|
||||
int current_field = 1; // 1 = field 1, 2 = field 2, 3 = 708
|
||||
|
||||
struct gop_time_code gop_time, first_gop_time, printed_gop;
|
||||
LLONG fts_at_gop_start = 0;
|
||||
int gop_rollover = 0;
|
||||
@@ -51,263 +37,331 @@ void ccx_common_timing_init(LLONG *file_position,int no_sync)
|
||||
ccx_common_timing_settings.no_sync = no_sync;
|
||||
}
|
||||
|
||||
void set_fts(void)
|
||||
void dinit_timing_ctx(struct ccx_common_timing_ctx **arg)
|
||||
{
|
||||
int pts_jump = 0;
|
||||
freep(arg);
|
||||
}
|
||||
struct ccx_common_timing_ctx *init_timing_ctx(struct ccx_common_timing_settings_t *cfg)
|
||||
{
|
||||
struct ccx_common_timing_ctx *ctx = malloc(sizeof(struct ccx_common_timing_ctx));
|
||||
if(!ctx)
|
||||
return NULL;
|
||||
|
||||
// ES don't have PTS unless GOP timing is used
|
||||
if (!pts_set && ccx_common_timing_settings.is_elementary_stream)
|
||||
return;
|
||||
ctx->pts_set = 0;
|
||||
ctx->current_tref = 0;
|
||||
ctx->current_pts = 0;
|
||||
ctx->current_picture_coding_type = CCX_FRAME_TYPE_RESET_OR_UNKNOWN;
|
||||
ctx->min_pts = 0x01FFFFFFFFLL; // 33 bit
|
||||
ctx->max_pts = 0;
|
||||
ctx->sync_pts = 0;
|
||||
ctx->minimum_fts = 0;
|
||||
|
||||
// First check for timeline jump (only when min_pts was set (implies sync_pts)).
|
||||
int dif = 0;
|
||||
if (pts_set == 2)
|
||||
{
|
||||
dif=(int) (current_pts-sync_pts)/MPEG_CLOCK_FREQ;
|
||||
ctx->fts_now = 0; // Time stamp of current file (w/ fts_offset, w/o fts_global)
|
||||
ctx->fts_offset = 0; // Time before first sync_pts
|
||||
ctx->fts_fc_offset = 0; // Time before first GOP
|
||||
ctx->fts_max = 0; // Remember the maximum fts that we saw in current file
|
||||
ctx->fts_global = 0; // Duration of previous files (-ve mode), see c1global
|
||||
|
||||
return ctx;
|
||||
}
|
||||
|
||||
void add_current_pts(struct ccx_common_timing_ctx *ctx, LLONG pts)
|
||||
{
|
||||
set_current_pts(ctx, ctx->current_pts + pts);
|
||||
}
|
||||
|
||||
void set_current_pts(struct ccx_common_timing_ctx *ctx, LLONG pts)
|
||||
{
|
||||
ctx->current_pts = pts;
|
||||
if(ctx->pts_set == 0)
|
||||
ctx->pts_set = 1;
|
||||
dbg_print(CCX_DMT_VIDES, "PTS: %s (%8u)", print_mstime(ctx->current_pts/(MPEG_CLOCK_FREQ/1000)),
|
||||
(unsigned) (ctx->current_pts));
|
||||
dbg_print(CCX_DMT_VIDES, " FTS: %s \n",print_mstime(ctx->fts_now));
|
||||
}
|
||||
|
||||
int set_fts(struct ccx_common_timing_ctx *ctx)
|
||||
{
|
||||
int pts_jump = 0;
|
||||
|
||||
// ES don't have PTS unless GOP timing is used
|
||||
if (!ctx->pts_set && ccx_common_timing_settings.is_elementary_stream)
|
||||
return CCX_OK;
|
||||
|
||||
// First check for timeline jump (only when min_pts was set (implies sync_pts)).
|
||||
int dif = 0;
|
||||
if (ctx->pts_set == 2)
|
||||
{
|
||||
dif=(int) (ctx->current_pts - ctx->sync_pts)/MPEG_CLOCK_FREQ;
|
||||
|
||||
if (ccx_common_timing_settings.disable_sync_check){
|
||||
// Disables sync check. Used for several input formats.
|
||||
dif = 0;
|
||||
}
|
||||
|
||||
if (dif < -0.2 || dif >= max_dif )
|
||||
{
|
||||
// ATSC specs: More than 3501 ms means missing component
|
||||
ccx_common_logging.log_ftn ("\nWarning: Reference clock has changed abruptly (%d seconds filepos=%lld), attempting to synchronize\n", (int) dif, *ccx_common_timing_settings.file_position);
|
||||
ccx_common_logging.log_ftn ("Last sync PTS value: %lld\n",sync_pts);
|
||||
ccx_common_logging.log_ftn ("Current PTS value: %lld\n",current_pts);
|
||||
pts_jump = 1;
|
||||
pts_big_change=1;
|
||||
if (dif < -0.2 || dif >= max_dif )
|
||||
{
|
||||
// ATSC specs: More than 3501 ms means missing component
|
||||
ccx_common_logging.log_ftn ("\nWarning: Reference clock has changed abruptly (%d seconds filepos=%lld), attempting to synchronize\n", (int) dif, *ccx_common_timing_settings.file_position);
|
||||
ccx_common_logging.log_ftn ("Last sync PTS value: %lld\n",ctx->sync_pts);
|
||||
ccx_common_logging.log_ftn ("Current PTS value: %lld\n",ctx->current_pts);
|
||||
pts_jump = 1;
|
||||
pts_big_change = 1;
|
||||
|
||||
// Discard the gap if it is not on an I-frame or temporal reference zero.
|
||||
if(current_tref != 0 && current_picture_coding_type != CCX_FRAME_TYPE_I_FRAME)
|
||||
{
|
||||
fts_now = fts_max;
|
||||
ccx_common_logging.log_ftn ("Change did not occur on first frame - probably a broken GOP\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Discard the gap if it is not on an I-frame or temporal reference zero.
|
||||
if(ctx->current_tref != 0 && ctx->current_picture_coding_type != CCX_FRAME_TYPE_I_FRAME)
|
||||
{
|
||||
ctx->fts_now = ctx->fts_max;
|
||||
ccx_common_logging.log_ftn ("Change did not occur on first frame - probably a broken GOP\n");
|
||||
return CCX_OK;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Set min_pts, fts_offset
|
||||
if (pts_set!=0)
|
||||
{
|
||||
pts_set=2;
|
||||
// Set min_pts, fts_offset
|
||||
if (ctx->pts_set != 0)
|
||||
{
|
||||
ctx->pts_set = 2;
|
||||
|
||||
// Use this part only the first time min_pts is set. Later treat
|
||||
// it as a reference clock change
|
||||
if (current_pts<min_pts && !pts_jump)
|
||||
{
|
||||
// If this is the first GOP, and seq 0 was not encountered yet
|
||||
// we might reset min_pts/fts_offset again
|
||||
// Use this part only the first time min_pts is set. Later treat
|
||||
// it as a reference clock change
|
||||
if (ctx->current_pts < ctx->min_pts && !pts_jump)
|
||||
{
|
||||
// If this is the first GOP, and seq 0 was not encountered yet
|
||||
// we might reset min_pts/fts_offset again
|
||||
|
||||
min_pts=current_pts;
|
||||
ctx->min_pts = ctx->current_pts;
|
||||
|
||||
// Avoid next async test
|
||||
sync_pts = (LLONG)(current_pts
|
||||
-current_tref*1000.0/current_fps
|
||||
*(MPEG_CLOCK_FREQ/1000));
|
||||
// Avoid next async test
|
||||
ctx->sync_pts = (LLONG)(ctx->current_pts
|
||||
-ctx->current_tref*1000.0/current_fps
|
||||
*(MPEG_CLOCK_FREQ/1000));
|
||||
|
||||
if(current_tref == 0)
|
||||
{ // Earliest time in GOP.
|
||||
fts_offset = 0;
|
||||
}
|
||||
else if ( total_frames_count-frames_since_ref_time == 0 )
|
||||
{ // If this is the first frame (PES) there cannot be an offset.
|
||||
// This part is also reached for dvr-ms/NTSC (RAW) as
|
||||
// total_frames_count = frames_since_ref_time = 0 when
|
||||
// this is called for the first time.
|
||||
fts_offset = 0;
|
||||
}
|
||||
else
|
||||
{ // It needs to be "+1" because the current frame is
|
||||
// not yet counted.
|
||||
fts_offset = (LLONG)((total_frames_count
|
||||
-frames_since_ref_time+1)
|
||||
*1000.0/current_fps);
|
||||
}
|
||||
if(ctx->current_tref == 0)
|
||||
{ // Earliest time in GOP.
|
||||
ctx->fts_offset = 0;
|
||||
}
|
||||
else if ( total_frames_count-frames_since_ref_time == 0 )
|
||||
{ // If this is the first frame (PES) there cannot be an offset.
|
||||
// This part is also reached for dvr-ms/NTSC (RAW) as
|
||||
// total_frames_count = frames_since_ref_time = 0 when
|
||||
// this is called for the first time.
|
||||
ctx->fts_offset = 0;
|
||||
}
|
||||
else
|
||||
{ // It needs to be "+1" because the current frame is
|
||||
// not yet counted.
|
||||
ctx->fts_offset = (LLONG)((total_frames_count
|
||||
-frames_since_ref_time+1)
|
||||
*1000.0/current_fps);
|
||||
}
|
||||
ccx_common_logging.debug_ftn(CCX_DMT_TIME, "\nFirst sync time PTS: %s %+lldms (time before this PTS)\n",
|
||||
print_mstime(min_pts/(MPEG_CLOCK_FREQ/1000)),
|
||||
fts_offset );
|
||||
ccx_common_logging.debug_ftn(CCX_DMT_TIME, "Total_frames_count %u frames_since_ref_time %u\n",
|
||||
total_frames_count, frames_since_ref_time);
|
||||
}
|
||||
print_mstime(ctx->min_pts/(MPEG_CLOCK_FREQ/1000)),
|
||||
ctx->fts_offset );
|
||||
ccx_common_logging.debug_ftn(CCX_DMT_TIME, "Total_frames_count %u frames_since_ref_time %u\n",
|
||||
total_frames_count, frames_since_ref_time);
|
||||
}
|
||||
|
||||
// -nosync disables syncing
|
||||
if (pts_jump && !ccx_common_timing_settings.no_sync)
|
||||
{
|
||||
// The current time in the old time base is calculated using
|
||||
// sync_pts (set at the beginning of the last GOP) plus the
|
||||
// time of the frames since then.
|
||||
fts_offset = fts_offset
|
||||
+ (sync_pts-min_pts)/(MPEG_CLOCK_FREQ/1000)
|
||||
+ (LLONG) (frames_since_ref_time*1000/current_fps);
|
||||
fts_max = fts_offset;
|
||||
// -nosync disables syncing
|
||||
if (pts_jump && !ccx_common_timing_settings.no_sync)
|
||||
{
|
||||
// The current time in the old time base is calculated using
|
||||
// sync_pts (set at the beginning of the last GOP) plus the
|
||||
// time of the frames since then.
|
||||
ctx->fts_offset = ctx->fts_offset
|
||||
+ (ctx->sync_pts - ctx->min_pts)/(MPEG_CLOCK_FREQ/1000)
|
||||
+ (LLONG) (frames_since_ref_time*1000/current_fps);
|
||||
ctx->fts_max = ctx->fts_offset;
|
||||
|
||||
// Start counting again from here
|
||||
pts_set=1; // Force min to be set again
|
||||
// Start counting again from here
|
||||
ctx->pts_set = 1; // Force min to be set again
|
||||
|
||||
// Avoid next async test - the gap might have occured on
|
||||
// current_tref != 0.
|
||||
sync_pts = (LLONG) (current_pts
|
||||
-current_tref*1000.0/current_fps
|
||||
*(MPEG_CLOCK_FREQ/1000));
|
||||
// Set min_pts = sync_pts as this is used for fts_now
|
||||
min_pts = sync_pts;
|
||||
// Avoid next async test - the gap might have occured on
|
||||
// current_tref != 0.
|
||||
ctx->sync_pts = (LLONG) (ctx->current_pts
|
||||
-ctx->current_tref*1000.0/current_fps
|
||||
*(MPEG_CLOCK_FREQ/1000));
|
||||
// Set min_pts = sync_pts as this is used for fts_now
|
||||
ctx->min_pts = ctx->sync_pts;
|
||||
|
||||
ccx_common_logging.debug_ftn(CCX_DMT_TIME, "\nNew min PTS time: %s %+lldms (time before this PTS)\n",
|
||||
print_mstime(min_pts/(MPEG_CLOCK_FREQ/1000)),
|
||||
fts_offset );
|
||||
}
|
||||
}
|
||||
ccx_common_logging.debug_ftn(CCX_DMT_TIME, "\nNew min PTS time: %s %+lldms (time before this PTS)\n",
|
||||
print_mstime(ctx->min_pts/(MPEG_CLOCK_FREQ/1000)),
|
||||
ctx->fts_offset );
|
||||
}
|
||||
}
|
||||
|
||||
// Set sync_pts, fts_offset
|
||||
if(current_tref == 0)
|
||||
sync_pts = current_pts;
|
||||
// Set sync_pts, fts_offset
|
||||
if(ctx->current_tref == 0)
|
||||
ctx->sync_pts = ctx->current_pts;
|
||||
|
||||
// Reset counters
|
||||
cb_field1 = 0;
|
||||
cb_field2 = 0;
|
||||
cb_708 = 0;
|
||||
// Reset counters
|
||||
cb_field1 = 0;
|
||||
cb_field2 = 0;
|
||||
cb_708 = 0;
|
||||
|
||||
// Avoid wrong "Calc. difference" and "Asynchronous by" numbers
|
||||
// for uninitialized min_pts
|
||||
// Avoid wrong "Calc. difference" and "Asynchronous by" numbers
|
||||
// for uninitialized min_pts
|
||||
if (1) // CFS: Remove or think decent condition
|
||||
{
|
||||
if ( pts_set )
|
||||
if ( ctx->pts_set )
|
||||
{
|
||||
// If pts_set is TRUE we have min_pts
|
||||
fts_now = (LLONG)((current_pts-min_pts)/(MPEG_CLOCK_FREQ/1000)
|
||||
+ fts_offset);
|
||||
ctx->fts_now = (LLONG)((ctx->current_pts - ctx->min_pts)/(MPEG_CLOCK_FREQ/1000)
|
||||
+ ctx->fts_offset);
|
||||
}
|
||||
else
|
||||
{
|
||||
// No PTS info at all!!
|
||||
ccx_common_logging.fatal_ftn(CCX_COMMON_EXIT_BUG_BUG,
|
||||
"No PTS info. Please write bug report.");
|
||||
ccx_common_logging.log_ftn("Set PTS called without any global timestamp set\n");
|
||||
return CCX_EINVAL;
|
||||
}
|
||||
}
|
||||
if ( fts_now > fts_max )
|
||||
{
|
||||
fts_max = fts_now;
|
||||
}
|
||||
if ( ctx->fts_now > ctx->fts_max )
|
||||
{
|
||||
ctx->fts_max = ctx->fts_now;
|
||||
}
|
||||
return CCX_OK;
|
||||
}
|
||||
|
||||
|
||||
LLONG get_fts(void)
|
||||
LLONG get_fts(struct ccx_common_timing_ctx *ctx, int current_field)
|
||||
{
|
||||
LLONG fts;
|
||||
LLONG fts;
|
||||
|
||||
switch (current_field)
|
||||
{
|
||||
case 1:
|
||||
fts = fts_now + fts_global + cb_field1*1001/30;
|
||||
break;
|
||||
case 2:
|
||||
fts = fts_now + fts_global + cb_field2*1001/30;
|
||||
break;
|
||||
case 3:
|
||||
fts = fts_now + fts_global + cb_708*1001/30;
|
||||
break;
|
||||
default:
|
||||
ccx_common_logging.fatal_ftn(CCX_COMMON_EXIT_BUG_BUG, "Cannot be reached!");
|
||||
}
|
||||
|
||||
return fts;
|
||||
switch (current_field)
|
||||
{
|
||||
case 1:
|
||||
fts = ctx->fts_now + ctx->fts_global + cb_field1*1001/30;
|
||||
break;
|
||||
case 2:
|
||||
fts = ctx->fts_now + ctx->fts_global + cb_field2*1001/30;
|
||||
break;
|
||||
case 3:
|
||||
fts = ctx->fts_now + ctx->fts_global + cb_708*1001/30;
|
||||
break;
|
||||
default:
|
||||
ccx_common_logging.fatal_ftn(CCX_COMMON_EXIT_BUG_BUG, "get_fts: unhandled branch");
|
||||
}
|
||||
// ccx_common_logging.debug_ftn(CCX_DMT_TIME, "[FTS] "
|
||||
// "fts: %llu, fts_now: %llu, fts_global: %llu, current_field: %llu, cb_708: %llu\n",
|
||||
// fts, fts_now, fts_global, current_field, cb_708);
|
||||
return fts;
|
||||
}
|
||||
|
||||
LLONG get_fts_max(void)
|
||||
LLONG get_fts_max(struct ccx_common_timing_ctx *ctx)
|
||||
{
|
||||
// This returns the maximum FTS that belonged to a frame. Caption block
|
||||
// counters are not applicable.
|
||||
return fts_max + fts_global;
|
||||
// This returns the maximum FTS that belonged to a frame. Caption block
|
||||
// counters are not applicable.
|
||||
return ctx->fts_max + ctx->fts_global;
|
||||
}
|
||||
|
||||
/* Fill a static buffer with a time string (hh:mm:ss:ms) corresponding
|
||||
to the microsecond value in mstime. */
|
||||
to the microsecond value in mstime. */
|
||||
char *print_mstime2buf( LLONG mstime , char *buf )
|
||||
{
|
||||
unsigned hh,mm,ss,ms;
|
||||
int signoffset = (mstime < 0 ? 1 : 0);
|
||||
unsigned hh,mm,ss,ms;
|
||||
int signoffset = (mstime < 0 ? 1 : 0);
|
||||
|
||||
if (mstime<0) // Avoid loss of data warning with abs()
|
||||
mstime=-mstime;
|
||||
hh = (unsigned) (mstime/1000/60/60);
|
||||
mm = (unsigned) (mstime/1000/60 - 60*hh);
|
||||
ss = (unsigned) (mstime/1000 - 60*(mm + 60*hh));
|
||||
ms = (int) (mstime - 1000*(ss + 60*(mm + 60*hh)));
|
||||
if (mstime<0) // Avoid loss of data warning with abs()
|
||||
mstime=-mstime;
|
||||
hh = (unsigned) (mstime/1000/60/60);
|
||||
mm = (unsigned) (mstime/1000/60 - 60*hh);
|
||||
ss = (unsigned) (mstime/1000 - 60*(mm + 60*hh));
|
||||
ms = (int) (mstime - 1000*(ss + 60*(mm + 60*hh)));
|
||||
|
||||
buf[0]='-';
|
||||
sprintf (buf+signoffset, "%02u:%02u:%02u:%03u",hh,mm,ss,ms);
|
||||
buf[0]='-';
|
||||
sprintf (buf+signoffset, "%02u:%02u:%02u:%03u",hh,mm,ss,ms);
|
||||
|
||||
return buf;
|
||||
return buf;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fill buffer with a time string using specified format
|
||||
* @param fmt has to contain 4 format specifiers for h, m, s and ms respectively
|
||||
*/
|
||||
size_t mstime_sprintf(LLONG mstime, char *fmt, char *buf)
|
||||
{
|
||||
unsigned hh, mm, ss, ms;
|
||||
int signoffset = (mstime < 0 ? 1 : 0);
|
||||
|
||||
if (mstime < 0) // Avoid loss of data warning with abs()
|
||||
mstime = -mstime;
|
||||
|
||||
hh = (unsigned) (mstime / 1000 / 60 / 60);
|
||||
mm = (unsigned) (mstime / 1000 / 60 - 60 * hh);
|
||||
ss = (unsigned) (mstime / 1000 - 60 * (mm + 60 * hh));
|
||||
ms = (unsigned) (mstime - 1000 * (ss + 60 * (mm + 60 * hh)));
|
||||
|
||||
buf[0] = '-';
|
||||
return (size_t) sprintf(buf + signoffset, fmt, hh, mm, ss, ms);
|
||||
}
|
||||
|
||||
|
||||
/* Fill a static buffer with a time string (hh:mm:ss:ms) corresponding
|
||||
to the microsecond value in mstime. */
|
||||
to the microsecond value in mstime. */
|
||||
char *print_mstime( LLONG mstime )
|
||||
{
|
||||
static char buf[15]; // 14 should be long enough
|
||||
static char buf[15]; // 14 should be long enough
|
||||
return print_mstime2buf (mstime, buf);
|
||||
}
|
||||
|
||||
/* Helper function for to display debug timing info. */
|
||||
void print_debug_timing( void )
|
||||
void print_debug_timing(struct ccx_common_timing_ctx *ctx)
|
||||
{
|
||||
// Avoid wrong "Calc. difference" and "Asynchronous by" numbers
|
||||
// for uninitialized min_pts
|
||||
LLONG tempmin_pts = (min_pts==0x01FFFFFFFFLL ? sync_pts : min_pts);
|
||||
// Avoid wrong "Calc. difference" and "Asynchronous by" numbers
|
||||
// for uninitialized min_pts
|
||||
LLONG tempmin_pts = (ctx->min_pts==0x01FFFFFFFFLL ? ctx->sync_pts : ctx->min_pts);
|
||||
|
||||
ccx_common_logging.log_ftn("Sync time stamps: PTS: %s ",
|
||||
print_mstime((sync_pts)/(MPEG_CLOCK_FREQ/1000)) );
|
||||
ccx_common_logging.log_ftn("GOP: %s \n", print_mstime(gop_time.ms));
|
||||
ccx_common_logging.log_ftn("Sync time stamps: PTS: %s ",
|
||||
print_mstime((ctx->sync_pts)/(MPEG_CLOCK_FREQ/1000)) );
|
||||
ccx_common_logging.log_ftn("GOP: %s \n", print_mstime(gop_time.ms));
|
||||
|
||||
// Length first GOP to last GOP
|
||||
LLONG goplenms = (LLONG) (gop_time.ms - first_gop_time.ms);
|
||||
// Length at last sync point
|
||||
LLONG ptslenms = (unsigned)((sync_pts-tempmin_pts)/(MPEG_CLOCK_FREQ/1000)
|
||||
+ fts_offset);
|
||||
// Length first GOP to last GOP
|
||||
LLONG goplenms = (LLONG) (gop_time.ms - first_gop_time.ms);
|
||||
// Length at last sync point
|
||||
LLONG ptslenms = (unsigned)((ctx->sync_pts-tempmin_pts)/(MPEG_CLOCK_FREQ/1000)
|
||||
+ ctx->fts_offset);
|
||||
|
||||
ccx_common_logging.log_ftn("Last FTS: %s",
|
||||
print_mstime(get_fts_max()));
|
||||
ccx_common_logging.log_ftn(" GOP start FTS: %s\n",
|
||||
print_mstime(fts_at_gop_start));
|
||||
ccx_common_logging.log_ftn("Last FTS: %s",
|
||||
print_mstime(get_fts_max(ctx)));
|
||||
ccx_common_logging.log_ftn(" GOP start FTS: %s\n",
|
||||
print_mstime(fts_at_gop_start));
|
||||
|
||||
// Times are based on last GOP and/or sync time
|
||||
ccx_common_logging.log_ftn("Max FTS diff. to PTS: %6lldms GOP: %6lldms\n\n",
|
||||
get_fts_max()+(LLONG)(1000.0/current_fps)-ptslenms,
|
||||
get_fts_max()+(LLONG)(1000.0/current_fps)-goplenms);
|
||||
// Times are based on last GOP and/or sync time
|
||||
ccx_common_logging.log_ftn("Max FTS diff. to PTS: %6lldms GOP: %6lldms\n\n",
|
||||
get_fts_max(ctx)+(LLONG)(1000.0/current_fps)-ptslenms,
|
||||
get_fts_max(ctx)+(LLONG)(1000.0/current_fps)-goplenms);
|
||||
}
|
||||
|
||||
void calculate_ms_gop_time (struct gop_time_code *g)
|
||||
{
|
||||
int seconds=(g->time_code_hours*3600)+(g->time_code_minutes*60)+g->time_code_seconds;
|
||||
g->ms = (LLONG)( 1000*(seconds + g->time_code_pictures/current_fps) );
|
||||
if (gop_rollover)
|
||||
g->ms += 24*60*60*1000;
|
||||
int seconds=(g->time_code_hours*3600)+(g->time_code_minutes*60)+g->time_code_seconds;
|
||||
g->ms = (LLONG)( 1000*(seconds + g->time_code_pictures/current_fps) );
|
||||
if (gop_rollover)
|
||||
g->ms += 24*60*60*1000;
|
||||
}
|
||||
|
||||
int gop_accepted(struct gop_time_code* g )
|
||||
{
|
||||
if (! ((g->time_code_hours <= 23)
|
||||
&& (g->time_code_minutes <= 59)
|
||||
&& (g->time_code_seconds <= 59)
|
||||
&& (g->time_code_pictures <= 59)))
|
||||
return 0;
|
||||
if (! ((g->time_code_hours <= 23)
|
||||
&& (g->time_code_minutes <= 59)
|
||||
&& (g->time_code_seconds <= 59)
|
||||
&& (g->time_code_pictures <= 59)))
|
||||
return 0;
|
||||
|
||||
if (gop_time.time_code_hours==23 && gop_time.time_code_minutes==59 &&
|
||||
g->time_code_hours==0 && g->time_code_minutes==0)
|
||||
{
|
||||
gop_rollover = 1;
|
||||
return 1;
|
||||
}
|
||||
if (gop_time.inited)
|
||||
{
|
||||
if (gop_time.ms > g->ms)
|
||||
{
|
||||
// We are going back in time but it's not a complete day rollover
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
if (gop_time.time_code_hours==23 && gop_time.time_code_minutes==59 &&
|
||||
g->time_code_hours==0 && g->time_code_minutes==0)
|
||||
{
|
||||
gop_rollover = 1;
|
||||
return 1;
|
||||
}
|
||||
if (gop_time.inited)
|
||||
{
|
||||
if (gop_time.ms > g->ms)
|
||||
{
|
||||
// We are going back in time but it's not a complete day rollover
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
#ifndef __Timing_H__
|
||||
#define __Timing_H__
|
||||
|
||||
#include "ccx_common_platform.h"
|
||||
|
||||
#include "ccx_common_constants.h"
|
||||
struct gop_time_code
|
||||
{
|
||||
int drop_frame_flag;
|
||||
@@ -14,7 +15,8 @@ struct gop_time_code
|
||||
LLONG ms;
|
||||
};
|
||||
|
||||
struct ccx_common_timing_settings_t {
|
||||
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 is_elementary_stream; // Needs to be set, as it's used in set_fts.
|
||||
@@ -29,44 +31,55 @@ struct ccx_boundary_time
|
||||
int set;
|
||||
};
|
||||
|
||||
struct ccx_common_timing_ctx
|
||||
{
|
||||
int pts_set; //0 = No, 1 = received, 2 = min_pts set
|
||||
LLONG current_pts;
|
||||
enum ccx_frame_type current_picture_coding_type;
|
||||
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_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_global; // Duration of previous files (-ve mode)
|
||||
};
|
||||
// Count 608 (per field) and 708 blocks since last set_fts() call
|
||||
extern int cb_field1, cb_field2, cb_708;
|
||||
|
||||
extern int pts_set; //0 = No, 1 = received, 2 = min_pts set
|
||||
extern int MPEG_CLOCK_FREQ; // This is part of the standard
|
||||
|
||||
extern LLONG min_pts, max_pts, sync_pts, current_pts;
|
||||
extern int max_dif;
|
||||
extern unsigned pts_big_change;
|
||||
|
||||
extern LLONG fts_now; // Time stamp of current file (w/ fts_offset, w/o fts_global)
|
||||
extern LLONG fts_offset; // Time before first sync_pts
|
||||
extern LLONG fts_fc_offset; // Time before first GOP
|
||||
extern LLONG fts_max; // Remember the maximum fts that we saw in current file
|
||||
extern LLONG fts_global; // Duration of previous files (-ve mode)
|
||||
|
||||
extern enum ccx_frame_type current_picture_coding_type;
|
||||
extern int current_tref; // Store temporal reference of current frame
|
||||
extern double current_fps;
|
||||
extern int frames_since_ref_time;
|
||||
extern unsigned total_frames_count;
|
||||
|
||||
extern int current_field;
|
||||
|
||||
extern struct gop_time_code gop_time, first_gop_time, printed_gop;
|
||||
extern LLONG fts_at_gop_start;
|
||||
extern int gop_rollover;
|
||||
|
||||
void ccx_common_timing_init(LLONG *file_position, int no_sync);
|
||||
|
||||
void set_fts(void);
|
||||
LLONG get_fts(void);
|
||||
LLONG get_fts_max(void);
|
||||
void dinit_timing_ctx(struct ccx_common_timing_ctx **arg);
|
||||
struct ccx_common_timing_ctx *init_timing_ctx(struct ccx_common_timing_settings_t *cfg);
|
||||
|
||||
void set_current_pts(struct ccx_common_timing_ctx *ctx, LLONG pts);
|
||||
void add_current_pts(struct ccx_common_timing_ctx *ctx, LLONG pts);
|
||||
int set_fts(struct ccx_common_timing_ctx *ctx);
|
||||
LLONG get_fts(struct ccx_common_timing_ctx *ctx, int current_field);
|
||||
LLONG get_fts_max(struct ccx_common_timing_ctx *ctx);
|
||||
char *print_mstime(LLONG mstime);
|
||||
char *print_mstime2buf(LLONG mstime, char *buf);
|
||||
void print_debug_timing(void);
|
||||
size_t mstime_sprintf(LLONG mstime, char *fmt, char *buf);
|
||||
void print_debug_timing(struct ccx_common_timing_ctx *ctx);
|
||||
int gop_accepted(struct gop_time_code* g);
|
||||
void calculate_ms_gop_time(struct gop_time_code *g);
|
||||
|
||||
#define __Timing_H__
|
||||
#endif
|
||||
|
||||
@@ -10,7 +10,7 @@ static const int rowdata[] = {11,-1,1,2,3,4,12,13,14,15,5,6,7,8,9,10};
|
||||
// Relationship between the first PAC byte and the row number
|
||||
int in_xds_mode=0;
|
||||
|
||||
unsigned char str[2048]; // Another generic general purpose buffer
|
||||
//unsigned char str[2048]; // Another generic general purpose buffer
|
||||
|
||||
const unsigned char pac2_attribs[][3] = // Color, font, ident
|
||||
{
|
||||
@@ -48,8 +48,6 @@ const unsigned char pac2_attribs[][3] = // Color, font, ident
|
||||
{ COL_WHITE, FONT_UNDERLINED, 28 } // 0x5f || 0x7f
|
||||
};
|
||||
|
||||
unsigned char *subline; // Temp storage for .srt lines
|
||||
int new_sentence=1; // Capitalize next letter?
|
||||
|
||||
static const char *command_type[] =
|
||||
{
|
||||
@@ -88,7 +86,8 @@ static const char *cc_modes_text[]=
|
||||
"Pop-Up captions"
|
||||
};
|
||||
#endif
|
||||
const char *color_text[][2]=
|
||||
|
||||
const char *color_text[MAX_COLOR][2]=
|
||||
{
|
||||
{"white",""},
|
||||
{"green","<font color=\"#00ff00\">"},
|
||||
@@ -109,7 +108,7 @@ void clear_eia608_cc_buffer(ccx_decoder_608_context *context, struct eia608_scre
|
||||
{
|
||||
memset(data->characters[i], ' ', CCX_DECODER_608_SCREEN_WIDTH);
|
||||
data->characters[i][CCX_DECODER_608_SCREEN_WIDTH] = 0;
|
||||
memset(data->colors[i], context->settings.default_color, CCX_DECODER_608_SCREEN_WIDTH + 1);
|
||||
memset(data->colors[i], context->settings->default_color, CCX_DECODER_608_SCREEN_WIDTH + 1);
|
||||
memset(data->fonts[i], FONT_REGULAR, CCX_DECODER_608_SCREEN_WIDTH + 1);
|
||||
data->row_used[i]=0;
|
||||
}
|
||||
@@ -120,11 +119,10 @@ void ccx_decoder_608_dinit_library(void **ctx)
|
||||
{
|
||||
freep(ctx);
|
||||
}
|
||||
ccx_decoder_608_context* ccx_decoder_608_init_library(ccx_decoder_608_settings settings, int channel,
|
||||
int field, int trim_subs,
|
||||
enum ccx_encoding_type encoding, int *halt,
|
||||
int cc_to_stdout, LLONG subs_delay,
|
||||
enum ccx_output_format output_format)
|
||||
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 *data = NULL;
|
||||
|
||||
@@ -148,19 +146,18 @@ ccx_decoder_608_context* ccx_decoder_608_init_library(ccx_decoder_608_settings s
|
||||
data->bytes_processed_608 = 0;
|
||||
data->my_field = field;
|
||||
data->my_channel = channel;
|
||||
data->out = NULL;
|
||||
data->have_cursor_position = 0;
|
||||
|
||||
data->trim_subs = trim_subs;
|
||||
data->encoding = encoding;
|
||||
data->output_format = output_format;
|
||||
data->cc_to_stdout = cc_to_stdout;
|
||||
data->textprinted = 0;
|
||||
data->ts_start_of_current_line = 0;
|
||||
|
||||
data->halt = halt;
|
||||
data->cc_to_stdout = cc_to_stdout;
|
||||
data->subs_delay = subs_delay;
|
||||
data->output_format = output_format;
|
||||
|
||||
data->settings = settings;
|
||||
data->current_color = data->settings.default_color;
|
||||
data->current_color = data->settings->default_color;
|
||||
data->report = settings->report;
|
||||
data->timing = timing;
|
||||
|
||||
clear_eia608_cc_buffer(data, &data->buffer1);
|
||||
clear_eia608_cc_buffer(data, &data->buffer2);
|
||||
@@ -207,7 +204,7 @@ void delete_to_end_of_row(ccx_decoder_608_context *context)
|
||||
// TODO: This can change the 'used' situation of a column, so we'd
|
||||
// need to check and correct.
|
||||
use_buffer->characters[context->cursor_row][i] = ' ';
|
||||
use_buffer->colors[context->cursor_row][i] = context->settings.default_color;
|
||||
use_buffer->colors[context->cursor_row][i] = context->settings->default_color;
|
||||
use_buffer->fonts[context->cursor_row][i] = context->font;
|
||||
}
|
||||
}
|
||||
@@ -229,15 +226,15 @@ void write_char(const unsigned char c, ccx_decoder_608_context *context)
|
||||
if (use_buffer->empty)
|
||||
{
|
||||
if (MODE_POPON != context->mode)
|
||||
context->current_visible_start_ms = get_visible_start();
|
||||
context->current_visible_start_ms = get_visible_start(context->timing, context->my_field);
|
||||
}
|
||||
use_buffer->empty=0;
|
||||
|
||||
if (context->cursor_column<CCX_DECODER_608_SCREEN_WIDTH - 1)
|
||||
context->cursor_column++;
|
||||
if (context->ts_start_of_current_line == -1)
|
||||
context->ts_start_of_current_line = get_fts();
|
||||
context->ts_last_char_received = get_fts();
|
||||
context->ts_start_of_current_line = get_fts(context->timing, context->my_field);
|
||||
context->ts_last_char_received = get_fts(context->timing, context->my_field);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -289,8 +286,8 @@ int write_cc_buffer(ccx_decoder_608_context *context, struct cc_subtitle *sub)
|
||||
LLONG end_time;
|
||||
|
||||
|
||||
if (context->settings.screens_to_process != -1 &&
|
||||
context->screenfuls_counter >= context->settings.screens_to_process)
|
||||
if (context->settings->screens_to_process != -1 &&
|
||||
context->screenfuls_counter >= context->settings->screens_to_process)
|
||||
{
|
||||
// We are done.
|
||||
*context->halt=1;
|
||||
@@ -304,7 +301,7 @@ int write_cc_buffer(ccx_decoder_608_context *context, struct cc_subtitle *sub)
|
||||
context->current_visible_start_ms = context->ts_start_of_current_line;
|
||||
|
||||
start_time = context->current_visible_start_ms;
|
||||
end_time = get_visible_end() + context->subs_delay;
|
||||
end_time = get_visible_end(context->timing, context->my_field);
|
||||
sub->type = CC_608;
|
||||
data->format = SFORMAT_CC_SCREEN;
|
||||
data->start_time = 0;
|
||||
@@ -313,7 +310,7 @@ int write_cc_buffer(ccx_decoder_608_context *context, struct cc_subtitle *sub)
|
||||
data->channel = context->channel;
|
||||
data->my_field = context->my_field;
|
||||
|
||||
if (!data->empty)
|
||||
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)
|
||||
@@ -357,11 +354,10 @@ int write_cc_line(ccx_decoder_608_context *context, struct cc_subtitle *sub)
|
||||
LLONG end_time;
|
||||
int i = 0;
|
||||
int wrote_something=0;
|
||||
int ret = 0;
|
||||
data = get_current_visible_buffer(context);
|
||||
|
||||
start_time = context->ts_start_of_current_line + context->subs_delay;
|
||||
end_time = get_fts() + context->subs_delay;
|
||||
start_time = context->ts_start_of_current_line;
|
||||
end_time = get_fts(context->timing, context->my_field);
|
||||
sub->type = CC_608;
|
||||
data->format = SFORMAT_CC_LINE;
|
||||
data->start_time = 0;
|
||||
@@ -370,9 +366,7 @@ int write_cc_line(ccx_decoder_608_context *context, struct cc_subtitle *sub)
|
||||
data->channel = context->channel;
|
||||
data->my_field = context->my_field;
|
||||
|
||||
//TODO need to put below functionality in encoder context
|
||||
ret = get_decoder_line_basic (subline, context->cursor_row, data,context->trim_subs,context->encoding);
|
||||
if( ret > 0 )
|
||||
if (!data->empty)
|
||||
{
|
||||
sub->data = (struct eia608_screen *) realloc(sub->data,(sub->nb_data +1) * sizeof(*data));
|
||||
if (!sub->data)
|
||||
@@ -534,13 +528,13 @@ int roll_up(ccx_decoder_608_context *context)
|
||||
for (int j = 0; j<(1 + context->cursor_row - keep_lines); j++)
|
||||
{
|
||||
memset(use_buffer->characters[j], ' ', CCX_DECODER_608_SCREEN_WIDTH);
|
||||
memset(use_buffer->colors[j], context->settings.default_color, CCX_DECODER_608_SCREEN_WIDTH);
|
||||
memset(use_buffer->colors[j], context->settings->default_color, CCX_DECODER_608_SCREEN_WIDTH);
|
||||
memset(use_buffer->fonts[j], FONT_REGULAR, CCX_DECODER_608_SCREEN_WIDTH);
|
||||
use_buffer->characters[j][CCX_DECODER_608_SCREEN_WIDTH] = 0;
|
||||
use_buffer->row_used[j]=0;
|
||||
}
|
||||
memset(use_buffer->characters[lastrow], ' ', CCX_DECODER_608_SCREEN_WIDTH);
|
||||
memset(use_buffer->colors[lastrow], context->settings.default_color, CCX_DECODER_608_SCREEN_WIDTH);
|
||||
memset(use_buffer->colors[lastrow], context->settings->default_color, CCX_DECODER_608_SCREEN_WIDTH);
|
||||
memset(use_buffer->fonts[lastrow], FONT_REGULAR, CCX_DECODER_608_SCREEN_WIDTH);
|
||||
|
||||
use_buffer->characters[lastrow][CCX_DECODER_608_SCREEN_WIDTH] = 0;
|
||||
@@ -599,7 +593,7 @@ int is_current_row_empty(ccx_decoder_608_context *context)
|
||||
}
|
||||
|
||||
/* Process GLOBAL CODES */
|
||||
void handle_command(/*const */ unsigned char c1, const unsigned char c2, ccx_decoder_608_context *context, struct cc_subtitle *sub)
|
||||
void handle_command(unsigned char c1, const unsigned char c2, ccx_decoder_608_context *context, struct cc_subtitle *sub)
|
||||
{
|
||||
int changes=0;
|
||||
|
||||
@@ -646,12 +640,12 @@ void handle_command(/*const */ unsigned char c1, const unsigned char c2, ccx_dec
|
||||
if ((c1==0x14 || c1==0x1C) && c2==0x2b)
|
||||
command = COM_RESUMETEXTDISPLAY;
|
||||
|
||||
if ((command == COM_ROLLUP2 || command == COM_ROLLUP3 || command == COM_ROLLUP4) && context->settings.force_rollup == 1)
|
||||
if ((command == COM_ROLLUP2 || command == COM_ROLLUP3 || command == COM_ROLLUP4) && context->settings->force_rollup == 1)
|
||||
command=COM_FAKE_RULLUP1;
|
||||
|
||||
if ((command == COM_ROLLUP3 || command == COM_ROLLUP4) && context->settings.force_rollup == 2)
|
||||
if ((command == COM_ROLLUP3 || command == COM_ROLLUP4) && context->settings->force_rollup == 2)
|
||||
command=COM_ROLLUP2;
|
||||
else if (command == COM_ROLLUP4 && context->settings.force_rollup == 3)
|
||||
else if (command == COM_ROLLUP4 && context->settings->force_rollup == 3)
|
||||
command=COM_ROLLUP3;
|
||||
|
||||
ccx_common_logging.debug_ftn(CCX_DMT_DECODER_608, "\rCommand begin: %02X %02X (%s)\n", c1, c2, command_type[command]);
|
||||
@@ -756,14 +750,14 @@ void handle_command(/*const */ unsigned char c1, const unsigned char c2, ccx_dec
|
||||
{
|
||||
if (write_cc_buffer(context, sub))
|
||||
context->screenfuls_counter++;
|
||||
if (context->settings.no_rollup)
|
||||
if (context->settings->no_rollup)
|
||||
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.
|
||||
if (changes)
|
||||
context->current_visible_start_ms = get_visible_start();
|
||||
context->current_visible_start_ms = get_visible_start(context->timing, context->my_field);
|
||||
context->cursor_column = 0;
|
||||
break;
|
||||
case COM_ERASENONDISPLAYEDMEMORY:
|
||||
@@ -791,7 +785,7 @@ void handle_command(/*const */ unsigned char c1, const unsigned char c2, ccx_dec
|
||||
context->screenfuls_counter++;
|
||||
}
|
||||
erase_memory(context, true);
|
||||
context->current_visible_start_ms = get_visible_start();
|
||||
context->current_visible_start_ms = get_visible_start(context->timing, context->my_field);
|
||||
break;
|
||||
case COM_ENDOFCAPTION: // Switch buffers
|
||||
// The currently *visible* buffer is leaving, so now we know its ending
|
||||
@@ -799,10 +793,10 @@ void handle_command(/*const */ unsigned char c1, const unsigned char c2, ccx_dec
|
||||
if (write_cc_buffer(context, sub))
|
||||
context->screenfuls_counter++;
|
||||
context->visible_buffer = (context->visible_buffer == 1) ? 2 : 1;
|
||||
context->current_visible_start_ms = get_visible_start();
|
||||
context->current_visible_start_ms = get_visible_start(context->timing, context->my_field);
|
||||
context->cursor_column = 0;
|
||||
context->cursor_row = 0;
|
||||
context->current_color = context->settings.default_color;
|
||||
context->current_color = context->settings->default_color;
|
||||
context->font = FONT_REGULAR;
|
||||
context->mode = MODE_POPON;
|
||||
break;
|
||||
@@ -828,7 +822,7 @@ void handle_command(/*const */ unsigned char c1, const unsigned char c2, ccx_dec
|
||||
|
||||
}
|
||||
|
||||
void handle_end_of_data(ccx_decoder_608_context *context, struct cc_subtitle *sub)
|
||||
void flush_608_context(ccx_decoder_608_context *context, struct cc_subtitle *sub)
|
||||
{
|
||||
// We issue a EraseDisplayedMemory here so if there's any captions pending
|
||||
// they get written to Subtitle.
|
||||
@@ -926,7 +920,7 @@ void handle_pac(unsigned char c1, unsigned char c2, ccx_decoder_608_context *con
|
||||
int indent=pac2_attribs[c2][2];
|
||||
ccx_common_logging.debug_ftn(CCX_DMT_DECODER_608, " -- Position: %d:%d, color: %s, font: %s\n", row,
|
||||
indent, color_text[context->current_color][0], font_text[context->font]);
|
||||
if (context->settings.default_color == COL_USERDEFINED && (context->current_color == COL_WHITE || context->current_color == COL_TRANSPARENT))
|
||||
if (context->settings->default_color == COL_USERDEFINED && (context->current_color == COL_WHITE || context->current_color == COL_TRANSPARENT))
|
||||
context->current_color = COL_USERDEFINED;
|
||||
if (context->mode != MODE_TEXT)
|
||||
{
|
||||
@@ -949,7 +943,7 @@ void handle_pac(unsigned char c1, unsigned char c2, ccx_decoder_608_context *con
|
||||
if (use_buffer->row_used[j])
|
||||
{
|
||||
memset(use_buffer->characters[j], ' ', CCX_DECODER_608_SCREEN_WIDTH);
|
||||
memset(use_buffer->colors[j], context->settings.default_color, CCX_DECODER_608_SCREEN_WIDTH);
|
||||
memset(use_buffer->colors[j], context->settings->default_color, CCX_DECODER_608_SCREEN_WIDTH);
|
||||
memset(use_buffer->fonts[j], FONT_REGULAR, CCX_DECODER_608_SCREEN_WIDTH);
|
||||
use_buffer->characters[j][CCX_DECODER_608_SCREEN_WIDTH] = 0;
|
||||
use_buffer->row_used[j] = 0;
|
||||
@@ -977,10 +971,10 @@ void erase_both_memories(ccx_decoder_608_context *context, struct cc_subtitle *s
|
||||
// time. Time to actually write it to file.
|
||||
if (write_cc_buffer(context, sub))
|
||||
context->screenfuls_counter++;
|
||||
context->current_visible_start_ms = get_visible_start();
|
||||
context->current_visible_start_ms = get_visible_start(context->timing, context->my_field);
|
||||
context->cursor_column = 0;
|
||||
context->cursor_row = 0;
|
||||
context->current_color = context->settings.default_color;
|
||||
context->current_color = context->settings->default_color;
|
||||
context->font = FONT_REGULAR;
|
||||
|
||||
erase_memory(context, true);
|
||||
@@ -1075,15 +1069,29 @@ int disCommand(unsigned char hi, unsigned char lo, ccx_decoder_608_context *cont
|
||||
return wrote_to_screen;
|
||||
}
|
||||
|
||||
/* If wb is NULL, then only XDS will be processed */
|
||||
int process608(const unsigned char *data, int length, ccx_decoder_608_context *context, struct cc_subtitle *sub)
|
||||
/* If private data is NULL, then only XDS will be processed */
|
||||
int process608(const unsigned char *data, int length, void *private_data, struct cc_subtitle *sub)
|
||||
{
|
||||
struct ccx_decoder_608_report *report = NULL;
|
||||
static int textprinted = 0;
|
||||
struct lib_cc_decode *dec_ctx = private_data;
|
||||
struct ccx_decoder_608_context *context;
|
||||
int i;
|
||||
|
||||
if(dec_ctx->current_field == 1)
|
||||
{
|
||||
context = dec_ctx->context_cc608_field_1;
|
||||
}
|
||||
else if (dec_ctx->current_field == 2 && dec_ctx->extract == 1)
|
||||
{
|
||||
context = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
context = dec_ctx->context_cc608_field_2;
|
||||
}
|
||||
if (context)
|
||||
{
|
||||
report = &context->report;
|
||||
report = context->report;
|
||||
context->bytes_processed_608 += length;
|
||||
}
|
||||
if (!data)
|
||||
@@ -1118,16 +1126,16 @@ int process608(const unsigned char *data, int length, ccx_decoder_608_context *c
|
||||
context->channel = 3;
|
||||
if (!in_xds_mode)
|
||||
{
|
||||
ts_start_of_xds=get_fts();
|
||||
in_xds_mode=1;
|
||||
ts_start_of_xds = get_fts(dec_ctx->timing, dec_ctx->current_field);
|
||||
in_xds_mode = 1;
|
||||
}
|
||||
if(report)
|
||||
report->xds=1;
|
||||
report->xds = 1;
|
||||
}
|
||||
if (hi == 0x0F && in_xds_mode && (context == NULL || context->my_field == 2)) // End of XDS block
|
||||
{
|
||||
in_xds_mode=0;
|
||||
do_end_of_xds (sub, lo);
|
||||
do_end_of_xds (sub, dec_ctx->xds_ctx, lo);
|
||||
if (context)
|
||||
context->channel = context->new_channel; // Switch from channel 3
|
||||
continue;
|
||||
@@ -1136,17 +1144,19 @@ int process608(const unsigned char *data, int length, ccx_decoder_608_context *c
|
||||
// http://www.geocities.com/mcpoodle43/SCC_TOOLS/DOCS/CC_CODES.HTML
|
||||
// http://www.geocities.com/mcpoodle43/SCC_TOOLS/DOCS/CC_CHARS.HTML
|
||||
{
|
||||
// We were writing characters before, start a new line for
|
||||
// diagnostic output from disCommand()
|
||||
if (textprinted == 1 )
|
||||
{
|
||||
ccx_common_logging.debug_ftn(CCX_DMT_DECODER_608, "\n");
|
||||
textprinted = 0;
|
||||
}
|
||||
if (!context || context->my_field == 2)
|
||||
in_xds_mode=0; // Back to normal (CEA 608-8.6.2)
|
||||
if (!context) // Not XDS and we don't have a writebuffer, nothing else would have an effect
|
||||
continue;
|
||||
|
||||
// We were writing characters before, start a new line for
|
||||
// diagnostic output from disCommand()
|
||||
if (context->textprinted == 1 )
|
||||
{
|
||||
ccx_common_logging.debug_ftn(CCX_DMT_DECODER_608, "\n");
|
||||
context->textprinted = 0;
|
||||
}
|
||||
|
||||
if (context->last_c1 == hi && context->last_c2 == lo)
|
||||
{
|
||||
// Duplicate dual code, discard. Correct to do it only in
|
||||
@@ -1167,7 +1177,7 @@ int process608(const unsigned char *data, int length, ccx_decoder_608_context *c
|
||||
{
|
||||
if (in_xds_mode && (context == NULL || context->my_field == 2))
|
||||
{
|
||||
process_xds_bytes (hi,lo);
|
||||
process_xds_bytes (dec_ctx->xds_ctx, hi, lo);
|
||||
continue;
|
||||
}
|
||||
if (!context) // No XDS code after this point, and user doesn't want captions.
|
||||
@@ -1182,10 +1192,10 @@ int process608(const unsigned char *data, int length, ccx_decoder_608_context *c
|
||||
if (context->channel != context->my_channel)
|
||||
continue;
|
||||
|
||||
if( textprinted == 0 )
|
||||
if( context->textprinted == 0 )
|
||||
{
|
||||
ccx_common_logging.debug_ftn(CCX_DMT_DECODER_608, "\n");
|
||||
textprinted = 1;
|
||||
context->textprinted = 1;
|
||||
}
|
||||
|
||||
handle_single(hi, context);
|
||||
@@ -1195,16 +1205,16 @@ int process608(const unsigned char *data, int length, ccx_decoder_608_context *c
|
||||
context->last_c2 = 0;
|
||||
}
|
||||
|
||||
if (!textprinted && context->channel == context->my_channel)
|
||||
if (!context->textprinted && context->channel == context->my_channel)
|
||||
{ // Current FTS information after the characters are shown
|
||||
ccx_common_logging.debug_ftn(CCX_DMT_DECODER_608, "Current FTS: %s\n", print_mstime(get_fts()));
|
||||
ccx_common_logging.debug_ftn(CCX_DMT_DECODER_608, "Current FTS: %s\n", print_mstime(get_fts(dec_ctx->timing, context->my_field)));
|
||||
//printf(" N:%u", unsigned(fts_now) );
|
||||
//printf(" G:%u", unsigned(fts_global) );
|
||||
//printf(" F:%d %d %d %d\n",
|
||||
// current_field, cb_field1, cb_field2, cb_708 );
|
||||
}
|
||||
|
||||
if (wrote_to_screen && context->settings.direct_rollup && // If direct_rollup is enabled and
|
||||
if (wrote_to_screen && context->settings->direct_rollup && // If direct_rollup is enabled and
|
||||
(context->mode == MODE_FAKE_ROLLUP_1 || // we are in rollup mode, write now.
|
||||
context->mode == MODE_ROLLUP_2 ||
|
||||
context->mode == MODE_ROLLUP_3 ||
|
||||
@@ -1212,7 +1222,7 @@ int process608(const unsigned char *data, int length, ccx_decoder_608_context *c
|
||||
{
|
||||
// We don't increase screenfuls_counter here.
|
||||
write_cc_buffer(context, sub);
|
||||
context->current_visible_start_ms = get_visible_start();
|
||||
context->current_visible_start_ms = get_visible_start(context->timing, context->my_field);
|
||||
}
|
||||
}
|
||||
if (wrote_to_screen && context->cc_to_stdout)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#ifndef __608_H__
|
||||
|
||||
#ifndef CCX_DECODER_608_H
|
||||
#define CCX_DECODER_608_H
|
||||
#include "ccx_common_platform.h"
|
||||
#include "ccx_common_structs.h"
|
||||
#include "ccx_decoders_structs.h"
|
||||
@@ -7,14 +7,14 @@
|
||||
extern LLONG ts_start_of_xds;
|
||||
|
||||
/*
|
||||
This variable (ccx_decoder_608_report) holds data on the cc channels & xds packets that are encountered during file parse.
|
||||
This can be interesting if you just want to know what kind of data a file holds that has 608 packets. CCExtractor uses it
|
||||
for the report functionality.
|
||||
*/
|
||||
This variable (ccx_decoder_608_report) holds data on the cc channels & xds packets that are encountered during file parse.
|
||||
This can be interesting if you just want to know what kind of data a file holds that has 608 packets. CCExtractor uses it
|
||||
for the report functionality.
|
||||
*/
|
||||
struct ccx_decoder_608_report
|
||||
{
|
||||
unsigned xds : 1;
|
||||
unsigned cc_channels[4];
|
||||
uint8_t xds : 1;
|
||||
uint8_t cc_channels[4];
|
||||
};
|
||||
|
||||
typedef struct ccx_decoder_608_settings
|
||||
@@ -24,11 +24,12 @@ typedef struct ccx_decoder_608_settings
|
||||
int no_rollup; // If 1, write one line at a time
|
||||
unsigned char default_color; // Default color to use.
|
||||
int screens_to_process; // How many screenfuls we want? Use -1 for unlimited
|
||||
struct ccx_decoder_608_report *report;
|
||||
} ccx_decoder_608_settings;
|
||||
|
||||
typedef struct ccx_decoder_608_context
|
||||
{
|
||||
ccx_decoder_608_settings settings;
|
||||
ccx_decoder_608_settings *settings;
|
||||
eia608_screen buffer1;
|
||||
eia608_screen buffer2;
|
||||
int cursor_row, cursor_column;
|
||||
@@ -47,65 +48,60 @@ typedef struct ccx_decoder_608_context
|
||||
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
|
||||
struct ccx_s_write *out;
|
||||
int have_cursor_position;
|
||||
|
||||
int trim_subs;
|
||||
enum ccx_encoding_type encoding;
|
||||
|
||||
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;
|
||||
struct ccx_decoder_608_report *report;
|
||||
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;
|
||||
|
||||
extern unsigned char *enc_buffer;
|
||||
extern unsigned char str[2048];
|
||||
extern unsigned enc_buffer_used;
|
||||
extern unsigned enc_buffer_capacity;
|
||||
|
||||
extern int new_sentence;
|
||||
extern const char *color_text[][2];
|
||||
#define MAX_COLOR 10
|
||||
extern const char *color_text[MAX_COLOR][2];
|
||||
|
||||
typedef enum ccx_decoder_608_color_code
|
||||
{
|
||||
COL_WHITE = 0,
|
||||
COL_GREEN = 1,
|
||||
COL_BLUE = 2,
|
||||
COL_CYAN = 3,
|
||||
COL_RED = 4,
|
||||
COL_YELLOW = 5,
|
||||
COL_MAGENTA = 6,
|
||||
COL_USERDEFINED = 7,
|
||||
COL_BLACK = 8,
|
||||
COL_TRANSPARENT = 9
|
||||
COL_WHITE = 0,
|
||||
COL_GREEN = 1,
|
||||
COL_BLUE = 2,
|
||||
COL_CYAN = 3,
|
||||
COL_RED = 4,
|
||||
COL_YELLOW = 5,
|
||||
COL_MAGENTA = 6,
|
||||
COL_USERDEFINED = 7,
|
||||
COL_BLACK = 8,
|
||||
COL_TRANSPARENT = 9
|
||||
} ccx_decoder_608_color_code;
|
||||
|
||||
|
||||
enum font_bits
|
||||
{
|
||||
FONT_REGULAR = 0,
|
||||
FONT_ITALICS = 1,
|
||||
FONT_UNDERLINED = 2,
|
||||
FONT_UNDERLINED_ITALICS = 3
|
||||
FONT_REGULAR = 0,
|
||||
FONT_ITALICS = 1,
|
||||
FONT_UNDERLINED = 2,
|
||||
FONT_UNDERLINED_ITALICS = 3
|
||||
};
|
||||
|
||||
enum command_code
|
||||
{
|
||||
COM_UNKNOWN = 0,
|
||||
COM_ERASEDISPLAYEDMEMORY = 1,
|
||||
COM_RESUMECAPTIONLOADING = 2,
|
||||
COM_ENDOFCAPTION = 3,
|
||||
COM_TABOFFSET1 = 4,
|
||||
COM_TABOFFSET2 = 5,
|
||||
COM_TABOFFSET3 = 6,
|
||||
COM_ROLLUP2 = 7,
|
||||
COM_ROLLUP3 = 8,
|
||||
COM_ROLLUP4 = 9,
|
||||
COM_CARRIAGERETURN = 10,
|
||||
COM_ERASENONDISPLAYEDMEMORY = 11,
|
||||
COM_BACKSPACE = 12,
|
||||
COM_UNKNOWN = 0,
|
||||
COM_ERASEDISPLAYEDMEMORY = 1,
|
||||
COM_RESUMECAPTIONLOADING = 2,
|
||||
COM_ENDOFCAPTION = 3,
|
||||
COM_TABOFFSET1 = 4,
|
||||
COM_TABOFFSET2 = 5,
|
||||
COM_TABOFFSET3 = 6,
|
||||
COM_ROLLUP2 = 7,
|
||||
COM_ROLLUP3 = 8,
|
||||
COM_ROLLUP4 = 9,
|
||||
COM_CARRIAGERETURN = 10,
|
||||
COM_ERASENONDISPLAYEDMEMORY = 11,
|
||||
COM_BACKSPACE = 12,
|
||||
COM_RESUMETEXTDISPLAY = 13,
|
||||
COM_ALARMOFF =14,
|
||||
COM_ALARMON = 15,
|
||||
@@ -121,18 +117,17 @@ void ccx_decoder_608_dinit_library(void **ctx);
|
||||
/*
|
||||
*
|
||||
*/
|
||||
ccx_decoder_608_context* ccx_decoder_608_init_library(ccx_decoder_608_settings settings, int channel,
|
||||
int field, int trim_subs,
|
||||
enum ccx_encoding_type encoding, int *halt,
|
||||
int cc_to_stdout, LLONG subs_delay,
|
||||
enum ccx_output_format output_format);
|
||||
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
|
||||
*
|
||||
* @param length length of data passed
|
||||
*
|
||||
* @param context context of cc608 where important information related to 608
|
||||
* @param private_data context of cc608 where important information related to 608
|
||||
* are stored.
|
||||
*
|
||||
* @param sub pointer to subtitle should be memset to 0 when passed first time
|
||||
@@ -140,15 +135,14 @@ ccx_decoder_608_context* ccx_decoder_608_init_library(ccx_decoder_608_settings s
|
||||
*
|
||||
* @return number of bytes used from data, -1 when any error is encountered
|
||||
*/
|
||||
int process608(const unsigned char *data, int length, ccx_decoder_608_context *context, struct cc_subtitle *sub);
|
||||
int process608(const unsigned char *data, int length, void *private_data, struct cc_subtitle *sub);
|
||||
|
||||
/**
|
||||
* Issue a EraseDisplayedMemory here so if there's any captions pending
|
||||
* they get written to cc_subtitle
|
||||
*/
|
||||
void handle_end_of_data(ccx_decoder_608_context *context, struct cc_subtitle *sub);
|
||||
void flush_608_context(ccx_decoder_608_context *context, struct cc_subtitle *sub);
|
||||
|
||||
int write_cc_buffer(ccx_decoder_608_context *context, struct cc_subtitle *sub);
|
||||
|
||||
#define __608_H__
|
||||
#endif
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -2,312 +2,371 @@
|
||||
#define _INCLUDE_708_
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include "ccx_decoders_common.h"
|
||||
#include "ccx_common_platform.h"
|
||||
#include "ccx_common_constants.h"
|
||||
#include "ccx_common_structs.h"
|
||||
|
||||
#define MAX_708_PACKET_LENGTH 128
|
||||
#define CCX_DECODERS_708_MAX_SERVICES 63
|
||||
#define CCX_DTVCC_MAX_PACKET_LENGTH 128 //According to EIA-708B, part 5
|
||||
#define CCX_DTVCC_MAX_SERVICES 63
|
||||
|
||||
#define I708_MAX_ROWS 15
|
||||
#define I708_MAX_COLUMNS 42
|
||||
#define CCX_DTVCC_MAX_ROWS 15
|
||||
/**
|
||||
* 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 I708_SCREENGRID_ROWS 75
|
||||
#define I708_SCREENGRID_COLUMNS 210
|
||||
#define CCX_DTVCC_SCREENGRID_ROWS 75
|
||||
#define CCX_DTVCC_SCREENGRID_COLUMNS 210
|
||||
|
||||
#define I708_MAX_WINDOWS 8
|
||||
#define CCX_DTVCC_MAX_WINDOWS 8
|
||||
|
||||
/*
|
||||
This variable (ccx_decoder_708_report) holds data on the cc channels & xds packets that are encountered during file parse.
|
||||
This can be interesting if you just want to know what kind of data a file holds that has 608 packets. CCExtractor uses it
|
||||
for the report functionality.
|
||||
*/
|
||||
struct ccx_decoder_708_report_t
|
||||
#define CCX_DTVCC_FILENAME_TEMPLATE ".p%u.svc%02u"
|
||||
|
||||
#define CCX_DTVCC_NO_LAST_SEQUENCE -1
|
||||
|
||||
enum CCX_DTVCC_COMMANDS_C0_CODES
|
||||
{
|
||||
unsigned services[CCX_DECODERS_708_MAX_SERVICES];
|
||||
};
|
||||
extern struct ccx_decoder_708_report_t ccx_decoder_708_report;
|
||||
|
||||
enum COMMANDS_C0_CODES
|
||||
{
|
||||
NUL=0,
|
||||
ETX=3,
|
||||
BS=8,
|
||||
FF=0xC,
|
||||
CR=0xD,
|
||||
HCR=0xE,
|
||||
EXT1=0x10,
|
||||
P16=0x18
|
||||
CCX_DTVCC_C0_NUL = 0x00,
|
||||
CCX_DTVCC_C0_ETX = 0x03,
|
||||
CCX_DTVCC_C0_BS = 0x08,
|
||||
CCX_DTVCC_C0_FF = 0x0c,
|
||||
CCX_DTVCC_C0_CR = 0x0d,
|
||||
CCX_DTVCC_C0_HCR = 0x0e,
|
||||
CCX_DTVCC_C0_EXT1 = 0x10,
|
||||
CCX_DTVCC_C0_P16 = 0x18
|
||||
};
|
||||
|
||||
enum COMMANDS_C1_CODES
|
||||
enum CCX_DTVCC_COMMANDS_C1_CODES
|
||||
{
|
||||
CW0=0x80,
|
||||
CW1=0x81,
|
||||
CW2=0x82,
|
||||
CW3=0x83,
|
||||
CW4=0x84,
|
||||
CW5=0x85,
|
||||
CW6=0x86,
|
||||
CW7=0x87,
|
||||
CLW=0x88,
|
||||
DSW=0x89,
|
||||
HDW=0x8A,
|
||||
TGW=0x8B,
|
||||
DLW=0x8C,
|
||||
DLY=0x8D,
|
||||
DLC=0x8E,
|
||||
RST=0x8F,
|
||||
SPA=0x90,
|
||||
SPC=0x91,
|
||||
SPL=0x92,
|
||||
RSV93=0x93,
|
||||
RSV94=0x94,
|
||||
RSV95=0x95,
|
||||
RSV96=0x96,
|
||||
SWA=0x97,
|
||||
DF0=0x98,
|
||||
DF1=0x99,
|
||||
DF2=0x9A,
|
||||
DF3=0x9B,
|
||||
DF4=0x9C,
|
||||
DF5=0x9D,
|
||||
DF6=0x9E,
|
||||
DF7=0x9F
|
||||
CCX_DTVCC_C1_CW0 = 0x80,
|
||||
CCX_DTVCC_C1_CW1 = 0x81,
|
||||
CCX_DTVCC_C1_CW2 = 0x82,
|
||||
CCX_DTVCC_C1_CW3 = 0x83,
|
||||
CCX_DTVCC_C1_CW4 = 0x84,
|
||||
CCX_DTVCC_C1_CW5 = 0x85,
|
||||
CCX_DTVCC_C1_CW6 = 0x86,
|
||||
CCX_DTVCC_C1_CW7 = 0x87,
|
||||
CCX_DTVCC_C1_CLW = 0x88,
|
||||
CCX_DTVCC_C1_DSW = 0x89,
|
||||
CCX_DTVCC_C1_HDW = 0x8A,
|
||||
CCX_DTVCC_C1_TGW = 0x8B,
|
||||
CCX_DTVCC_C1_DLW = 0x8C,
|
||||
CCX_DTVCC_C1_DLY = 0x8D,
|
||||
CCX_DTVCC_C1_DLC = 0x8E,
|
||||
CCX_DTVCC_C1_RST = 0x8F,
|
||||
CCX_DTVCC_C1_SPA = 0x90,
|
||||
CCX_DTVCC_C1_SPC = 0x91,
|
||||
CCX_DTVCC_C1_SPL = 0x92,
|
||||
CCX_DTVCC_C1_RSV93 = 0x93,
|
||||
CCX_DTVCC_C1_RSV94 = 0x94,
|
||||
CCX_DTVCC_C1_RSV95 = 0x95,
|
||||
CCX_DTVCC_C1_RSV96 = 0x96,
|
||||
CCX_DTVCC_C1_SWA = 0x97,
|
||||
CCX_DTVCC_C1_DF0 = 0x98,
|
||||
CCX_DTVCC_C1_DF1 = 0x99,
|
||||
CCX_DTVCC_C1_DF2 = 0x9A,
|
||||
CCX_DTVCC_C1_DF3 = 0x9B,
|
||||
CCX_DTVCC_C1_DF4 = 0x9C,
|
||||
CCX_DTVCC_C1_DF5 = 0x9D,
|
||||
CCX_DTVCC_C1_DF6 = 0x9E,
|
||||
CCX_DTVCC_C1_DF7 = 0x9F
|
||||
};
|
||||
|
||||
struct S_COMMANDS_C1
|
||||
struct CCX_DTVCC_S_COMMANDS_C1
|
||||
{
|
||||
int code;
|
||||
const char *name;
|
||||
const char *description;
|
||||
int length;
|
||||
int code;
|
||||
const char *name;
|
||||
const char *description;
|
||||
int length;
|
||||
};
|
||||
|
||||
|
||||
enum eWindowsAttribJustify
|
||||
enum ccx_dtvcc_window_justify
|
||||
{
|
||||
left=0,
|
||||
right=1,
|
||||
center=2,
|
||||
full=3
|
||||
CCX_DTVCC_WINDOW_JUSTIFY_LEFT = 0,
|
||||
CCX_DTVCC_WINDOW_JUSTIFY_RIGHT = 1,
|
||||
CCX_DTVCC_WINDOW_JUSTIFY_CENTER = 2,
|
||||
CCX_DTVCC_WINDOW_JUSTIFY_FULL = 3
|
||||
};
|
||||
|
||||
enum eWindowsAttribPrintDirection
|
||||
enum ccx_dtvcc_window_pd //Print Direction
|
||||
{
|
||||
pd_left_to_right=0,
|
||||
pd_right_to_left=1,
|
||||
pd_top_to_bottom=2,
|
||||
pd_bottom_to_top=3
|
||||
CCX_DTVCC_WINDOW_PD_LEFT_RIGHT = 0, //left -> right
|
||||
CCX_DTVCC_WINDOW_PD_RIGHT_LEFT = 1,
|
||||
CCX_DTVCC_WINDOW_PD_TOP_BOTTOM = 2,
|
||||
CCX_DTVCC_WINDOW_PD_BOTTOM_TOP = 3
|
||||
};
|
||||
|
||||
enum eWindowsAttribScrollDirection
|
||||
enum ccx_dtvcc_window_sd //Scroll Direction
|
||||
{
|
||||
sd_left_to_right=0,
|
||||
sd_right_to_left=1,
|
||||
sd_top_to_bottom=2,
|
||||
sd_bottom_to_top=3
|
||||
CCX_DTVCC_WINDOW_SD_LEFT_RIGHT = 0,
|
||||
CCX_DTVCC_WINDOW_SD_RIGHT_LEFT = 1,
|
||||
CCX_DTVCC_WINDOW_SD_TOP_BOTTOM = 2,
|
||||
CCX_DTVCC_WINDOW_SD_BOTTOM_TOP = 3
|
||||
};
|
||||
|
||||
enum eWindowsAttribScrollDisplayEffect
|
||||
enum ccx_dtvcc_window_sde //Scroll Display Effect
|
||||
{
|
||||
snap=0,
|
||||
fade=1,
|
||||
wipe=2
|
||||
CCX_DTVCC_WINDOW_SDE_SNAP = 0,
|
||||
CCX_DTVCC_WINDOW_SDE_FADE = 1,
|
||||
CCX_DTVCC_WINDOW_SDE_WIPE = 2
|
||||
};
|
||||
|
||||
enum eWindowsAttribEffectDirection
|
||||
enum ccx_dtvcc_window_ed //Effect Direction
|
||||
{
|
||||
left_to_right=0,
|
||||
right_to_left=1,
|
||||
top_to_bottom=2,
|
||||
bottom_to_top=3
|
||||
CCX_DTVCC_WINDOW_ED_LEFT_RIGHT = 0,
|
||||
CCX_DTVCC_WINDOW_ED_RIGHT_LEFT = 1,
|
||||
CCX_DTVCC_WINDOW_ED_TOP_BOTTOM = 2,
|
||||
CCX_DTVCC_WINDOW_ED_BOTTOM_TOP = 3
|
||||
};
|
||||
|
||||
enum eWindowsAttribFillOpacity
|
||||
enum ccx_dtvcc_window_fo //Fill Opacity
|
||||
{
|
||||
solid=0,
|
||||
flash=1,
|
||||
traslucent=2,
|
||||
transparent=3
|
||||
CCX_DTVCC_WINDOW_FO_SOLID = 0,
|
||||
CCX_DTVCC_WINDOW_FO_FLASH = 1,
|
||||
CCX_DTVCC_WINDOW_FO_TRANSLUCENT = 2,
|
||||
CCX_DTVCC_WINDOW_FO_TRANSPARENT = 3
|
||||
};
|
||||
|
||||
enum eWindowsAttribBorderType
|
||||
enum ccx_dtvcc_window_border
|
||||
{
|
||||
none=0,
|
||||
raised=1,
|
||||
depressed=2,
|
||||
uniform=3,
|
||||
shadow_left=4,
|
||||
shadow_right=5
|
||||
CCX_DTVCC_WINDOW_BORDER_NONE = 0,
|
||||
CCX_DTVCC_WINDOW_BORDER_RAISED = 1,
|
||||
CCX_DTVCC_WINDOW_BORDER_DEPRESSED = 2,
|
||||
CCX_DTVCC_WINDOW_BORDER_UNIFORM = 3,
|
||||
CCX_DTVCC_WINDOW_BORDER_SHADOW_LEFT = 4,
|
||||
CCX_DTVCC_WINDOW_BORDER_SHADOW_RIGHT = 5
|
||||
};
|
||||
|
||||
enum ePenAttribSize
|
||||
enum ccx_dtvcc_pen_size
|
||||
{
|
||||
pensize_small=0,
|
||||
pensize_standard=1,
|
||||
pensize_large=2
|
||||
CCX_DTVCC_PEN_SIZE_SMALL = 0,
|
||||
CCX_DTVCC_PEN_SIZE_STANDART = 1,
|
||||
CCX_DTVCC_PEN_SIZE_LARGE = 2
|
||||
};
|
||||
|
||||
enum ePenAttribFontStyle
|
||||
enum ccx_dtvcc_pen_font_style
|
||||
{
|
||||
fontstyle_default_or_undefined=0,
|
||||
monospaced_with_serifs=1,
|
||||
proportionally_spaced_with_serifs=2,
|
||||
monospaced_without_serifs=3,
|
||||
proportionally_spaced_without_serifs=4,
|
||||
casual_font_type=5,
|
||||
cursive_font_type=6,
|
||||
small_capitals=7
|
||||
CCX_DTVCC_PEN_FONT_STYLE_DEFAULT_OR_UNDEFINED = 0,
|
||||
CCX_DTVCC_PEN_FONT_STYLE_MONOSPACED_WITH_SERIFS = 1,
|
||||
CCX_DTVCC_PEN_FONT_STYLE_PROPORTIONALLY_SPACED_WITH_SERIFS = 2,
|
||||
CCX_DTVCC_PEN_FONT_STYLE_MONOSPACED_WITHOUT_SERIFS = 3,
|
||||
CCX_DTVCC_PEN_FONT_STYLE_PROPORTIONALLY_SPACED_WITHOUT_SERIFS = 4,
|
||||
CCX_DTVCC_PEN_FONT_STYLE_CASUAL_FONT_TYPE = 5,
|
||||
CCX_DTVCC_PEN_FONT_STYLE_CURSIVE_FONT_TYPE = 6,
|
||||
CCX_DTVCC_PEN_FONT_STYLE_SMALL_CAPITALS = 7
|
||||
};
|
||||
|
||||
enum ePanAttribTextTag
|
||||
enum ccx_dtvcc_pen_text_tag
|
||||
{
|
||||
texttag_dialog=0,
|
||||
texttag_source_or_speaker_id=1,
|
||||
texttag_electronic_voice=2,
|
||||
texttag_foreign_language=3,
|
||||
texttag_voiceover=4,
|
||||
texttag_audible_translation=5,
|
||||
texttag_subtitle_translation=6,
|
||||
texttag_voice_quality_description=7,
|
||||
texttag_song_lyrics=8,
|
||||
texttag_sound_effect_description=9,
|
||||
texttag_musical_score_description=10,
|
||||
texttag_expletitive=11,
|
||||
texttag_undefined_12=12,
|
||||
texttag_undefined_13=13,
|
||||
texttag_undefined_14=14,
|
||||
texttag_not_to_be_displayed=15
|
||||
CCX_DTVCC_PEN_TEXT_TAG_DIALOG = 0,
|
||||
CCX_DTVCC_PEN_TEXT_TAG_SOURCE_OR_SPEAKER_ID = 1,
|
||||
CCX_DTVCC_PEN_TEXT_TAG_ELECTRONIC_VOICE = 2,
|
||||
CCX_DTVCC_PEN_TEXT_TAG_FOREIGN_LANGUAGE = 3,
|
||||
CCX_DTVCC_PEN_TEXT_TAG_VOICEOVER = 4,
|
||||
CCX_DTVCC_PEN_TEXT_TAG_AUDIBLE_TRANSLATION = 5,
|
||||
CCX_DTVCC_PEN_TEXT_TAG_SUBTITLE_TRANSLATION = 6,
|
||||
CCX_DTVCC_PEN_TEXT_TAG_VOICE_QUALITY_DESCRIPTION = 7,
|
||||
CCX_DTVCC_PEN_TEXT_TAG_SONG_LYRICS = 8,
|
||||
CCX_DTVCC_PEN_TEXT_TAG_SOUND_EFFECT_DESCRIPTION = 9,
|
||||
CCX_DTVCC_PEN_TEXT_TAG_MUSICAL_SCORE_DESCRIPTION = 10,
|
||||
CCX_DTVCC_PEN_TEXT_TAG_EXPLETIVE = 11,
|
||||
CCX_DTVCC_PEN_TEXT_TAG_UNDEFINED_12 = 12,
|
||||
CCX_DTVCC_PEN_TEXT_TAG_UNDEFINED_13 = 13,
|
||||
CCX_DTVCC_PEN_TEXT_TAG_UNDEFINED_14 = 14,
|
||||
CCX_DTVCC_PEN_TEXT_TAG_NOT_TO_BE_DISPLAYED = 15
|
||||
};
|
||||
|
||||
enum ePanAttribOffset
|
||||
enum ccx_dtvcc_pen_offset
|
||||
{
|
||||
offset_subscript=0,
|
||||
offset_normal=1,
|
||||
offset_superscript=2
|
||||
CCX_DTVCC_PEN_OFFSET_SUBSCRIPT = 0,
|
||||
CCX_DTVCC_PEN_OFFSET_NORMAL = 1,
|
||||
CCX_DTVCC_PEN_OFFSET_SUPERSCRIPT = 2
|
||||
};
|
||||
|
||||
enum ePanAttribEdgeType
|
||||
enum ccx_dtvcc_pen_edge
|
||||
{
|
||||
edgetype_none=0,
|
||||
edgetype_raised=1,
|
||||
edgetype_depressed=2,
|
||||
edgetype_uniform=3,
|
||||
edgetype_left_drop_shadow=4,
|
||||
edgetype_right_drop_shadow=5
|
||||
CCX_DTVCC_PEN_EDGE_NONE = 0,
|
||||
CCX_DTVCC_PEN_EDGE_RAISED = 1,
|
||||
CCX_DTVCC_PEN_EDGE_DEPRESSED = 2,
|
||||
CCX_DTVCC_PEN_EDGE_UNIFORM = 3,
|
||||
CCX_DTVCC_PEN_EDGE_LEFT_DROP_SHADOW = 4,
|
||||
CCX_DTVCC_PEN_EDGE_RIGHT_DROP_SHADOW = 5
|
||||
};
|
||||
|
||||
enum eAnchorPoints
|
||||
enum ccx_dtvcc_pen_anchor_point
|
||||
{
|
||||
anchorpoint_top_left = 0,
|
||||
anchorpoint_top_center = 1,
|
||||
anchorpoint_top_right =2,
|
||||
anchorpoint_middle_left = 3,
|
||||
anchorpoint_middle_center = 4,
|
||||
anchorpoint_middle_right = 5,
|
||||
anchorpoint_bottom_left = 6,
|
||||
anchorpoint_bottom_center = 7,
|
||||
anchorpoint_bottom_right = 8
|
||||
CCX_DTVCC_ANCHOR_POINT_TOP_LEFT = 0,
|
||||
CCX_DTVCC_ANCHOR_POINT_TOP_CENTER = 1,
|
||||
CCX_DTVCC_ANCHOR_POINT_TOP_RIGHT = 2,
|
||||
CCX_DTVCC_ANCHOR_POINT_MIDDLE_LEFT = 3,
|
||||
CCX_DTVCC_ANCHOR_POINT_MIDDLE_CENTER = 4,
|
||||
CCX_DTVCC_ANCHOR_POINT_MIDDLE_RIGHT = 5,
|
||||
CCX_DTVCC_ANCHOR_POINT_BOTTOM_LEFT = 6,
|
||||
CCX_DTVCC_ANCHOR_POINT_BOTTOM_CENTER = 7,
|
||||
CCX_DTVCC_ANCHOR_POINT_BOTTOM_RIGHT = 8
|
||||
};
|
||||
|
||||
typedef struct e708Pen_color
|
||||
typedef struct ccx_dtvcc_pen_color
|
||||
{
|
||||
int fg_color;
|
||||
int fg_opacity;
|
||||
int bg_color;
|
||||
int bg_opacity;
|
||||
int edge_color;
|
||||
} e708Pen_color;
|
||||
int fg_color;
|
||||
int fg_opacity;
|
||||
int bg_color;
|
||||
int bg_opacity;
|
||||
int edge_color;
|
||||
} ccx_dtvcc_pen_color;
|
||||
|
||||
typedef struct e708Pen_attribs
|
||||
typedef struct ccx_dtvcc_pen_attribs
|
||||
{
|
||||
int pen_size;
|
||||
int offset;
|
||||
int text_tag;
|
||||
int font_tag;
|
||||
int edge_type;
|
||||
int underline;
|
||||
int italic;
|
||||
} e708Pen_attribs;
|
||||
int pen_size;
|
||||
int offset;
|
||||
int text_tag;
|
||||
int font_tag;
|
||||
int edge_type;
|
||||
int underline;
|
||||
int italic;
|
||||
} ccx_dtvcc_pen_attribs;
|
||||
|
||||
typedef struct e708Window_attribs
|
||||
typedef struct ccx_dtvcc_window_attribs
|
||||
{
|
||||
int fill_color;
|
||||
int fill_opacity;
|
||||
int border_color;
|
||||
int border_type01;
|
||||
int justify;
|
||||
int scroll_dir;
|
||||
int print_dir;
|
||||
int word_wrap;
|
||||
int border_type;
|
||||
int display_eff;
|
||||
int effect_dir;
|
||||
int effect_speed;
|
||||
} e708Window_attribs;
|
||||
int justify;
|
||||
int print_direction;
|
||||
int scroll_direction;
|
||||
int word_wrap;
|
||||
int display_effect;
|
||||
int effect_direction;
|
||||
int effect_speed;
|
||||
int fill_color;
|
||||
int fill_opacity;
|
||||
int border_type;
|
||||
int border_color;
|
||||
} ccx_dtvcc_window_attribs;
|
||||
|
||||
typedef struct e708Window
|
||||
/**
|
||||
* Since 1-byte and 2-byte symbols could appear in captions and
|
||||
* since we have to keep symbols alignment and several windows could appear on a screen at one time,
|
||||
* we use special structure for holding symbols
|
||||
*/
|
||||
typedef struct ccx_dtvcc_symbol
|
||||
{
|
||||
int is_defined;
|
||||
int number; // Handy, in case we only have a pointer to the window
|
||||
int priority;
|
||||
int col_lock;
|
||||
int row_lock;
|
||||
int visible;
|
||||
int anchor_vertical;
|
||||
int relative_pos;
|
||||
int anchor_horizontal;
|
||||
int row_count;
|
||||
int anchor_point;
|
||||
int col_count;
|
||||
int pen_style;
|
||||
int win_style;
|
||||
unsigned char commands[6]; // Commands used to create this window
|
||||
e708Window_attribs attribs;
|
||||
e708Pen_attribs pen;
|
||||
e708Pen_color pen_color;
|
||||
int pen_row;
|
||||
int pen_column;
|
||||
unsigned char *rows[I708_MAX_ROWS+1]; // Max is 15, but we define an extra one for convenience
|
||||
int memory_reserved;
|
||||
unsigned short sym; //symbol itself, at least 16 bit
|
||||
unsigned char len; //length. could be 1 or 2
|
||||
} ccx_dtvcc_symbol;
|
||||
|
||||
#define CCX_DTVCC_SYM_SET(x, c) {x.len = 1; x.sym = c;}
|
||||
#define CCX_DTVCC_SYM_SET_16(x, c1, c2) {x.len = 2; x.sym = (c1 << 8) | c2;}
|
||||
#define CCX_DTVCC_SYM_IS_16(x) (x.len == 2)
|
||||
#define CCX_DTVCC_SYM(x) ((unsigned char)(x.sym))
|
||||
#define CCX_DTVCC_SYM_16_FIRST(x) ((unsigned char)(x.sym >> 8))
|
||||
#define CCX_DTVCC_SYM_16_SECOND(x) ((unsigned char)(x.sym & 0xff))
|
||||
#define CCX_DTVCC_SYM_IS_EMPTY(x) (x.len == 0)
|
||||
#define CCX_DTVCC_SYM_IS_SET(x) (x.len > 0)
|
||||
|
||||
typedef struct ccx_dtvcc_window
|
||||
{
|
||||
int is_defined;
|
||||
int number;
|
||||
int priority;
|
||||
int col_lock;
|
||||
int row_lock;
|
||||
int visible;
|
||||
int anchor_vertical;
|
||||
int relative_pos;
|
||||
int anchor_horizontal;
|
||||
int row_count;
|
||||
int anchor_point;
|
||||
int col_count;
|
||||
int pen_style;
|
||||
int win_style;
|
||||
unsigned char commands[6]; // Commands used to create this window
|
||||
ccx_dtvcc_window_attribs attribs;
|
||||
int pen_row;
|
||||
int pen_column;
|
||||
ccx_dtvcc_symbol *rows[CCX_DTVCC_MAX_ROWS];
|
||||
ccx_dtvcc_pen_color pen_colors[CCX_DTVCC_MAX_ROWS];
|
||||
ccx_dtvcc_pen_attribs pen_attribs[CCX_DTVCC_MAX_ROWS];
|
||||
int memory_reserved;
|
||||
int is_empty;
|
||||
} e708Window;
|
||||
LLONG time_ms_show;
|
||||
LLONG time_ms_hide;
|
||||
} ccx_dtvcc_window;
|
||||
|
||||
typedef struct tvscreen
|
||||
typedef struct dtvcc_tv_screen
|
||||
{
|
||||
unsigned char chars[I708_SCREENGRID_ROWS][I708_SCREENGRID_COLUMNS+1];
|
||||
}
|
||||
tvscreen;
|
||||
ccx_dtvcc_symbol chars[CCX_DTVCC_SCREENGRID_ROWS][CCX_DTVCC_SCREENGRID_COLUMNS];
|
||||
ccx_dtvcc_pen_color pen_colors[CCX_DTVCC_SCREENGRID_ROWS];
|
||||
ccx_dtvcc_pen_attribs pen_attribs[CCX_DTVCC_SCREENGRID_ROWS];
|
||||
LLONG time_ms_show;
|
||||
LLONG time_ms_hide;
|
||||
unsigned int cc_count;
|
||||
int service_number;
|
||||
} dtvcc_tv_screen;
|
||||
|
||||
typedef struct cc708_service_decoder
|
||||
/**
|
||||
* Holds data on the CEA 708 services that are encountered during file parse
|
||||
* This can be interesting, so CCExtractor uses it for the report functionality.
|
||||
*/
|
||||
typedef struct ccx_decoder_dtvcc_report
|
||||
{
|
||||
e708Window windows[I708_MAX_WINDOWS];
|
||||
int current_window;
|
||||
int inited;
|
||||
LLONG current_visible_start_ms;
|
||||
tvscreen tv1, tv2; // Current and previous "screenfuls", note that we switch between them
|
||||
int is_empty_tv1, is_empty_tv2;
|
||||
int cur_tv; // 1 or 2 rather than 0 or 1, to at least be consistent with the decoder
|
||||
tvscreen *tv; // Pointer to the current TV buffer
|
||||
char *filename; // Where we are going to write our output
|
||||
int fh; // Handle to output file. -1 if not yet open
|
||||
int srt_counter;
|
||||
enum ccx_output_format output_format; // What kind of output format should be used?
|
||||
LLONG subs_delay; // ms to delay (or advance) subs
|
||||
}
|
||||
cc708_service_decoder;
|
||||
int reset_count;
|
||||
unsigned services[CCX_DTVCC_MAX_SERVICES];
|
||||
} ccx_decoder_dtvcc_report;
|
||||
|
||||
extern int do_cea708; // Process 708 data?
|
||||
extern int cea708services[]; // [] -> 1 for services to be processed
|
||||
typedef struct ccx_dtvcc_service_decoder
|
||||
{
|
||||
ccx_dtvcc_window windows[CCX_DTVCC_MAX_WINDOWS];
|
||||
int current_window;
|
||||
dtvcc_tv_screen *tv;
|
||||
int cc_count;
|
||||
} ccx_dtvcc_service_decoder;
|
||||
|
||||
extern int resets_708;
|
||||
typedef struct ccx_decoder_dtvcc_settings
|
||||
{
|
||||
int enabled;
|
||||
int print_file_reports;
|
||||
int no_rollup;
|
||||
ccx_decoder_dtvcc_report *report;
|
||||
int active_services_count;
|
||||
int services_enabled[CCX_DTVCC_MAX_SERVICES];
|
||||
struct ccx_common_timing_ctx *timing;
|
||||
} ccx_decoder_dtvcc_settings;
|
||||
|
||||
void do_708 (struct lib_cc_decode* ctx, const unsigned char *data, int datalength);
|
||||
/**
|
||||
* TODO
|
||||
* solution requires "sink" or "writer" entity to write captions to output file
|
||||
* decoders have to know nothing about output files
|
||||
*/
|
||||
|
||||
unsigned char get_internal_from_G0 (unsigned char g0_char);
|
||||
unsigned char get_internal_from_G1 (unsigned char g1_char);
|
||||
unsigned char get_internal_from_G2 (unsigned char g2_char);
|
||||
unsigned char get_internal_from_G3 (unsigned char g3_char);
|
||||
void process_character (cc708_service_decoder *decoder, unsigned char internal_char);
|
||||
typedef struct ccx_dtvcc_ctx
|
||||
{
|
||||
int is_active;
|
||||
int active_services_count;
|
||||
int services_active[CCX_DTVCC_MAX_SERVICES]; //0 - inactive, 1 - active
|
||||
int report_enabled;
|
||||
|
||||
ccx_decoder_dtvcc_report *report;
|
||||
|
||||
ccx_dtvcc_service_decoder decoders[CCX_DTVCC_MAX_SERVICES];
|
||||
|
||||
unsigned char current_packet[CCX_DTVCC_MAX_PACKET_LENGTH];
|
||||
int current_packet_length;
|
||||
|
||||
int last_sequence;
|
||||
|
||||
void *encoder; //we can't include header, so keeping it this way
|
||||
int no_rollup;
|
||||
struct ccx_common_timing_ctx *timing;
|
||||
} ccx_dtvcc_ctx;
|
||||
|
||||
|
||||
void ccx_dtvcc_clear_packet(ccx_dtvcc_ctx *ctx);
|
||||
void ccx_dtvcc_windows_reset(ccx_dtvcc_service_decoder *decoder);
|
||||
void ccx_dtvcc_decoder_flush(ccx_dtvcc_ctx *dtvcc, ccx_dtvcc_service_decoder *decoder);
|
||||
|
||||
void ccx_dtvcc_process_current_packet(ccx_dtvcc_ctx *dtvcc);
|
||||
void ccx_dtvcc_process_service_block(ccx_dtvcc_ctx *dtvcc,
|
||||
ccx_dtvcc_service_decoder *decoder,
|
||||
unsigned char *data,
|
||||
int data_length);
|
||||
|
||||
void ccx_decoders_708_init_library(char *basefilename,const char *extension, int report);
|
||||
#endif
|
||||
|
||||
@@ -12,34 +12,34 @@ EIA-708, SO INTERNALLY WE USE THIS TABLE (FOR CONVENIENCE)
|
||||
A0-FF -> Group G1 as is - non-English characters and symbols
|
||||
*/
|
||||
|
||||
unsigned char get_internal_from_G0 (unsigned char g0_char)
|
||||
unsigned char dtvcc_get_internal_from_G0(unsigned char g0_char)
|
||||
{
|
||||
return g0_char;
|
||||
return g0_char;
|
||||
}
|
||||
|
||||
unsigned char get_internal_from_G1 (unsigned char g1_char)
|
||||
unsigned char dtvcc_get_internal_from_G1(unsigned char g1_char)
|
||||
{
|
||||
return g1_char;
|
||||
return g1_char;
|
||||
}
|
||||
|
||||
// TODO: Probably not right
|
||||
// G2: Extended Control Code Set 1
|
||||
unsigned char get_internal_from_G2 (unsigned char g2_char)
|
||||
unsigned char dtvcc_get_internal_from_G2(unsigned char g2_char)
|
||||
{
|
||||
if (g2_char>=0x20 && g2_char<=0x3F)
|
||||
return g2_char-0x20;
|
||||
if (g2_char>=0x60 && g2_char<=0x7F)
|
||||
return g2_char+0x20;
|
||||
// Rest unmapped, so we return a blank space
|
||||
return 0x20;
|
||||
if (g2_char >= 0x20 && g2_char <= 0x3F)
|
||||
return g2_char - (unsigned char)0x20;
|
||||
if (g2_char >= 0x60 && g2_char <= 0x7F)
|
||||
return g2_char + (unsigned char)0x20;
|
||||
// Rest unmapped, so we return a blank space
|
||||
return 0x20;
|
||||
}
|
||||
|
||||
// TODO: Probably not right
|
||||
// G3: Future Characters and Icon Expansion
|
||||
unsigned char get_internal_from_G3 (unsigned char g3_char)
|
||||
unsigned char dtvcc_get_internal_from_G3(unsigned char g3_char)
|
||||
{
|
||||
if (g3_char==0xa0) // The "CC" (closed captions) sign
|
||||
return 0x06;
|
||||
// Rest unmapped, so we return a blank space
|
||||
return 0x20;
|
||||
if (g3_char == 0xa0) // The "CC" (closed captions) sign
|
||||
return 0x06;
|
||||
// Rest unmapped, so we return a blank space
|
||||
return 0x20;
|
||||
}
|
||||
9
src/lib_ccx/ccx_decoders_708_encoding.h
Normal file
9
src/lib_ccx/ccx_decoders_708_encoding.h
Normal file
@@ -0,0 +1,9 @@
|
||||
#ifndef _CCX_DECODERS_708_ENCODING_H_
|
||||
#define _CCX_DECODERS_708_ENCODING_H_
|
||||
|
||||
unsigned char dtvcc_get_internal_from_G0(unsigned char g0_char);
|
||||
unsigned char dtvcc_get_internal_from_G1(unsigned char g1_char);
|
||||
unsigned char dtvcc_get_internal_from_G2(unsigned char g2_char);
|
||||
unsigned char dtvcc_get_internal_from_G3(unsigned char g3_char);
|
||||
|
||||
#endif /*_CCX_DECODERS_708_ENCODING_H_*/
|
||||
425
src/lib_ccx/ccx_decoders_708_output.c
Normal file
425
src/lib_ccx/ccx_decoders_708_output.c
Normal file
@@ -0,0 +1,425 @@
|
||||
#include "ccx_decoders_708_output.h"
|
||||
#include "ccx_encoders_common.h"
|
||||
#include "utility.h"
|
||||
#include "ccx_common_common.h"
|
||||
|
||||
int _dtvcc_is_row_empty(dtvcc_tv_screen *tv, int row_index)
|
||||
{
|
||||
for (int j = 0; j < CCX_DTVCC_SCREENGRID_COLUMNS; j++)
|
||||
{
|
||||
if (CCX_DTVCC_SYM_IS_SET(tv->chars[row_index][j]))
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
int _dtvcc_is_screen_empty(dtvcc_tv_screen *tv)
|
||||
{
|
||||
for (int i = 0; i < CCX_DTVCC_SCREENGRID_ROWS; i++)
|
||||
{
|
||||
if (!_dtvcc_is_row_empty(tv, i))
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
void _dtvcc_get_write_interval(dtvcc_tv_screen *tv, int row_index, int *first, int *last)
|
||||
{
|
||||
for (*first = 0; *first < CCX_DTVCC_SCREENGRID_COLUMNS; (*first)++)
|
||||
if (CCX_DTVCC_SYM_IS_SET(tv->chars[row_index][*first]))
|
||||
break;
|
||||
for (*last = CCX_DTVCC_SCREENGRID_COLUMNS - 1; *last > 0; (*last)--)
|
||||
if (CCX_DTVCC_SYM_IS_SET(tv->chars[row_index][*last]))
|
||||
break;
|
||||
}
|
||||
|
||||
void _dtvcc_color_to_hex(int color, unsigned *hR, unsigned *hG, unsigned *hB)
|
||||
{
|
||||
*hR = (unsigned) (color >> 4);
|
||||
*hG = (unsigned) ((color >> 2) & 0x3);
|
||||
*hB = (unsigned) (color & 0x3);
|
||||
ccx_common_logging.debug_ftn(CCX_DMT_708, "[CEA-708] Color: %d [%06x] %u %u %u\n",
|
||||
color, color, *hR, *hG, *hB);
|
||||
}
|
||||
|
||||
void _dtvcc_write_tag_open(dtvcc_tv_screen *tv, struct encoder_ctx *encoder, int row_index)
|
||||
{
|
||||
char *buf = (char *) encoder->buffer;
|
||||
size_t buf_len = 0;
|
||||
|
||||
if (tv->pen_attribs[row_index].italic)
|
||||
buf_len += sprintf(buf + buf_len, "<i>");
|
||||
|
||||
if (tv->pen_attribs[row_index].underline)
|
||||
buf_len += sprintf(buf + buf_len, "<u>");
|
||||
|
||||
if (tv->pen_colors[row_index].fg_color != 0x3f && !encoder->no_font_color) //assuming white is default
|
||||
{
|
||||
unsigned red, green, blue;
|
||||
_dtvcc_color_to_hex(tv->pen_colors[row_index].fg_color, &red, &green, &blue);
|
||||
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);
|
||||
}
|
||||
write(encoder->dtvcc_writers[tv->service_number - 1].fd, buf, buf_len);
|
||||
}
|
||||
|
||||
void _dtvcc_write_tag_close(dtvcc_tv_screen *tv, struct encoder_ctx *encoder, int row_index)
|
||||
{
|
||||
char *buf = (char *) encoder->buffer;
|
||||
size_t buf_len = 0;
|
||||
|
||||
if (tv->pen_colors[row_index].fg_color != 0x3f && !encoder->no_font_color)
|
||||
buf_len += sprintf(buf + buf_len, "</font>");
|
||||
|
||||
if (tv->pen_attribs[row_index].underline)
|
||||
buf_len += sprintf(buf + buf_len, "</u>");
|
||||
|
||||
if (tv->pen_attribs[row_index].italic)
|
||||
buf_len += sprintf(buf + buf_len, "</i>");
|
||||
|
||||
write(encoder->dtvcc_writers[tv->service_number - 1].fd, buf, buf_len);
|
||||
}
|
||||
|
||||
void _dtvcc_write_row(ccx_dtvcc_writer_ctx *writer, dtvcc_tv_screen *tv, int row_index, struct encoder_ctx *encoder)
|
||||
{
|
||||
char *buf = (char *) encoder->buffer;
|
||||
size_t buf_len = 0;
|
||||
memset(buf, 0, INITIAL_ENC_BUFFER_CAPACITY * sizeof(char));
|
||||
int first, last;
|
||||
|
||||
_dtvcc_get_write_interval(tv, row_index, &first, &last);
|
||||
for (int j = first; j <= last; j++)
|
||||
{
|
||||
if (CCX_DTVCC_SYM_IS_16(tv->chars[row_index][j]))
|
||||
{
|
||||
buf[buf_len++] = CCX_DTVCC_SYM_16_FIRST(tv->chars[row_index][j]);
|
||||
buf[buf_len++] = CCX_DTVCC_SYM_16_SECOND(tv->chars[row_index][j]);
|
||||
}
|
||||
else
|
||||
{
|
||||
buf[buf_len++] = CCX_DTVCC_SYM(tv->chars[row_index][j]);
|
||||
}
|
||||
}
|
||||
|
||||
int fd = encoder->dtvcc_writers[tv->service_number - 1].fd;
|
||||
|
||||
if (writer->cd != (iconv_t) -1)
|
||||
{
|
||||
char *encoded_buf = calloc(INITIAL_ENC_BUFFER_CAPACITY, sizeof(char));
|
||||
if (!encoded_buf)
|
||||
ccx_common_logging.fatal_ftn(EXIT_NOT_ENOUGH_MEMORY, "_dtvcc_write_row");
|
||||
|
||||
char *encoded_buf_start = encoded_buf;
|
||||
|
||||
size_t in_bytes_left = buf_len;
|
||||
size_t out_bytes_left = INITIAL_ENC_BUFFER_CAPACITY * sizeof(char);
|
||||
|
||||
size_t result = iconv(writer->cd, &buf, &in_bytes_left, &encoded_buf, &out_bytes_left);
|
||||
|
||||
if (result == -1)
|
||||
ccx_common_logging.log_ftn("[CEA-708] _dtvcc_write_row: "
|
||||
"conversion failed: %s\n", strerror(errno));
|
||||
|
||||
write(fd, encoded_buf_start, encoded_buf - encoded_buf_start);
|
||||
|
||||
free(encoded_buf_start);
|
||||
}
|
||||
else
|
||||
{
|
||||
write(fd, buf, buf_len);
|
||||
}
|
||||
}
|
||||
|
||||
void ccx_dtvcc_write_srt(ccx_dtvcc_writer_ctx *writer, dtvcc_tv_screen *tv, struct encoder_ctx *encoder)
|
||||
{
|
||||
if (_dtvcc_is_screen_empty(tv))
|
||||
return;
|
||||
|
||||
if (tv->time_ms_show + encoder->subs_delay < 0)
|
||||
return;
|
||||
|
||||
char *buf = (char *) encoder->buffer;
|
||||
memset(buf, 0, INITIAL_ENC_BUFFER_CAPACITY);
|
||||
|
||||
sprintf(buf, "%u%s", tv->cc_count, encoder->encoded_crlf);
|
||||
mstime_sprintf(tv->time_ms_show + encoder->subs_delay,
|
||||
"%02u:%02u:%02u,%03u", buf + strlen(buf));
|
||||
sprintf(buf + strlen(buf), " --> ");
|
||||
mstime_sprintf(tv->time_ms_hide + encoder->subs_delay,
|
||||
"%02u:%02u:%02u,%03u", buf + strlen(buf));
|
||||
sprintf(buf + strlen(buf), (char *) encoder->encoded_crlf);
|
||||
|
||||
write(encoder->dtvcc_writers[tv->service_number - 1].fd, buf, strlen(buf));
|
||||
|
||||
for (int i = 0; i < CCX_DTVCC_SCREENGRID_ROWS; i++)
|
||||
{
|
||||
if (!_dtvcc_is_row_empty(tv, i))
|
||||
{
|
||||
_dtvcc_write_tag_open(tv, encoder, i);
|
||||
_dtvcc_write_row(writer, tv, i, encoder);
|
||||
_dtvcc_write_tag_close(tv, encoder, i);
|
||||
write(encoder->dtvcc_writers[tv->service_number - 1].fd,
|
||||
encoder->encoded_crlf, encoder->encoded_crlf_length);
|
||||
}
|
||||
}
|
||||
write(encoder->dtvcc_writers[tv->service_number - 1].fd,
|
||||
encoder->encoded_crlf, encoder->encoded_crlf_length);
|
||||
}
|
||||
|
||||
void ccx_dtvcc_write_debug(dtvcc_tv_screen *tv)
|
||||
{
|
||||
char tbuf1[SUBLINESIZE],
|
||||
tbuf2[SUBLINESIZE];
|
||||
|
||||
print_mstime2buf(tv->time_ms_show, tbuf1);
|
||||
print_mstime2buf(tv->time_ms_hide, tbuf2);
|
||||
|
||||
ccx_common_logging.debug_ftn(CCX_DMT_GENERIC_NOTICES, "\r%s --> %s\n", tbuf1, tbuf2);
|
||||
for (int i = 0; i < CCX_DTVCC_SCREENGRID_ROWS; i++)
|
||||
{
|
||||
if (!_dtvcc_is_row_empty(tv, i))
|
||||
{
|
||||
int first, last;
|
||||
_dtvcc_get_write_interval(tv, i, &first, &last);
|
||||
for (int j = first; j <= last; j++)
|
||||
ccx_common_logging.debug_ftn(CCX_DMT_GENERIC_NOTICES, "%c", tv->chars[i][j]);
|
||||
ccx_common_logging.debug_ftn(CCX_DMT_GENERIC_NOTICES, "\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ccx_dtvcc_write_transcript(ccx_dtvcc_writer_ctx *writer, dtvcc_tv_screen *tv, struct encoder_ctx *encoder)
|
||||
{
|
||||
if (_dtvcc_is_screen_empty(tv))
|
||||
return;
|
||||
|
||||
if (tv->time_ms_show + encoder->subs_delay < 0) // Drop screens that because of subs_delay start too early
|
||||
return;
|
||||
|
||||
char *buf = (char *) encoder->buffer;
|
||||
|
||||
for (int i = 0; i < CCX_DTVCC_SCREENGRID_ROWS; i++)
|
||||
{
|
||||
if (!_dtvcc_is_row_empty(tv, i))
|
||||
{
|
||||
buf[0] = 0;
|
||||
|
||||
if (encoder->transcript_settings->showStartTime)
|
||||
mstime_sprintf(tv->time_ms_show + encoder->subs_delay,
|
||||
"%02u:%02u:%02u,%03u|", buf + strlen(buf));
|
||||
|
||||
if (encoder->transcript_settings->showEndTime)
|
||||
mstime_sprintf(tv->time_ms_hide + encoder->subs_delay,
|
||||
"%02u:%02u:%02u,%03u|", buf + strlen(buf));
|
||||
|
||||
if (encoder->transcript_settings->showCC)
|
||||
sprintf(buf + strlen(buf), "CC1|"); //always CC1 because CEA-708 is field-independent
|
||||
|
||||
if (encoder->transcript_settings->showMode)
|
||||
sprintf(buf + strlen(buf), "POP|"); //TODO caption mode(pop, rollup, etc.)
|
||||
|
||||
if (strlen(buf))
|
||||
write(encoder->dtvcc_writers[tv->service_number - 1].fd, buf, strlen(buf));
|
||||
|
||||
_dtvcc_write_row(writer, tv, i, encoder);
|
||||
write(encoder->dtvcc_writers[tv->service_number - 1].fd,
|
||||
encoder->encoded_crlf, encoder->encoded_crlf_length);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
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);
|
||||
|
||||
write(encoder->dtvcc_writers[tv->service_number - 1].fd, buf, buf_len);
|
||||
}
|
||||
|
||||
void _dtvcc_write_sami_footer(dtvcc_tv_screen *tv, struct encoder_ctx *encoder)
|
||||
{
|
||||
char *buf = (char *) encoder->buffer;
|
||||
sprintf(buf, "</body></sami>");
|
||||
write(encoder->dtvcc_writers[tv->service_number - 1].fd, buf, strlen(buf));
|
||||
write(encoder->dtvcc_writers[tv->service_number - 1].fd,
|
||||
encoder->encoded_crlf, encoder->encoded_crlf_length);
|
||||
}
|
||||
|
||||
void ccx_dtvcc_write_sami(ccx_dtvcc_writer_ctx *writer, dtvcc_tv_screen *tv, struct encoder_ctx *encoder)
|
||||
{
|
||||
if (_dtvcc_is_screen_empty(tv))
|
||||
return;
|
||||
|
||||
if (tv->time_ms_show + encoder->subs_delay < 0)
|
||||
return;
|
||||
|
||||
if (tv->cc_count == 1)
|
||||
_dtvcc_write_sami_header(tv, encoder);
|
||||
|
||||
char *buf = (char *) encoder->buffer;
|
||||
|
||||
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(encoder->dtvcc_writers[tv->service_number - 1].fd, buf, strlen(buf));
|
||||
|
||||
for (int i = 0; i < CCX_DTVCC_SCREENGRID_ROWS; i++)
|
||||
{
|
||||
if (!_dtvcc_is_row_empty(tv, i))
|
||||
{
|
||||
_dtvcc_write_tag_open(tv, encoder, i);
|
||||
_dtvcc_write_row(writer, tv, i, encoder);
|
||||
_dtvcc_write_tag_close(tv, encoder, i);
|
||||
write(encoder->dtvcc_writers[tv->service_number - 1].fd,
|
||||
encoder->encoded_br, encoder->encoded_br_length);
|
||||
write(encoder->dtvcc_writers[tv->service_number - 1].fd,
|
||||
encoder->encoded_crlf, encoder->encoded_crlf_length);
|
||||
}
|
||||
}
|
||||
|
||||
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(encoder->dtvcc_writers[tv->service_number - 1].fd, buf, strlen(buf));
|
||||
}
|
||||
|
||||
void _ccx_dtvcc_write(ccx_dtvcc_writer_ctx *writer, dtvcc_tv_screen *tv, struct encoder_ctx *encoder)
|
||||
{
|
||||
switch (encoder->write_format)
|
||||
{
|
||||
case CCX_OF_NULL:
|
||||
break;
|
||||
case CCX_OF_SRT:
|
||||
ccx_dtvcc_write_srt(writer, tv, encoder);
|
||||
break;
|
||||
case CCX_OF_TRANSCRIPT:
|
||||
ccx_dtvcc_write_transcript(writer, tv, encoder);
|
||||
break;
|
||||
case CCX_OF_SAMI:
|
||||
ccx_dtvcc_write_sami(writer, tv, encoder);
|
||||
break;
|
||||
default:
|
||||
ccx_dtvcc_write_debug(tv);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void ccx_dtvcc_write_done(dtvcc_tv_screen *tv, struct encoder_ctx *encoder)
|
||||
{
|
||||
switch (encoder->write_format)
|
||||
{
|
||||
case CCX_OF_SAMI:
|
||||
_dtvcc_write_sami_footer(tv, encoder);
|
||||
break;
|
||||
default:
|
||||
ccx_common_logging.debug_ftn(
|
||||
CCX_DMT_708, "[CEA-708] ccx_dtvcc_write_done: no handling required\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void ccx_dtvcc_writer_init(ccx_dtvcc_writer_ctx *writer,
|
||||
char *base_filename,
|
||||
int program_number,
|
||||
int service_number,
|
||||
enum ccx_output_format write_format,
|
||||
struct encoder_cfg *cfg)
|
||||
{
|
||||
ccx_common_logging.debug_ftn(CCX_DMT_708, "[CEA-708] ccx_dtvcc_writer_init\n");
|
||||
writer->fd = -1;
|
||||
writer->cd = (iconv_t) -1;
|
||||
if (write_format == CCX_OF_NULL)
|
||||
{
|
||||
writer->filename = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
ccx_common_logging.debug_ftn(CCX_DMT_708, "[CEA-708] ccx_dtvcc_writer_init: "
|
||||
"[%s][%d][%d]\n", base_filename, program_number, service_number);
|
||||
|
||||
char *ext = get_file_extension(write_format);
|
||||
char suffix[32];
|
||||
sprintf(suffix, CCX_DTVCC_FILENAME_TEMPLATE, program_number, service_number);
|
||||
|
||||
writer->filename = create_outfilename(base_filename, suffix, ext);
|
||||
if (!writer->filename)
|
||||
ccx_common_logging.fatal_ftn(
|
||||
EXIT_NOT_ENOUGH_MEMORY, "[CEA-708] _dtvcc_decoder_init_write: not enough memory");
|
||||
|
||||
ccx_common_logging.debug_ftn(CCX_DMT_708, "[CEA-708] ccx_dtvcc_writer_init: inited [%s]\n", writer->filename);
|
||||
|
||||
char *charset = cfg->all_services_charset ?
|
||||
cfg->all_services_charset :
|
||||
cfg->services_charsets[service_number - 1];
|
||||
|
||||
if (charset)
|
||||
{
|
||||
writer->cd = iconv_open("UTF-8", charset);
|
||||
if (writer->cd == (iconv_t) -1)
|
||||
{
|
||||
ccx_common_logging.fatal_ftn(EXIT_FAILURE, "[CEA-708] dtvcc_init: "
|
||||
"can't create iconv for charset \"%s\": %s\n",
|
||||
charset, strerror(errno));
|
||||
}
|
||||
}
|
||||
|
||||
free(ext);
|
||||
}
|
||||
|
||||
void ccx_dtvcc_writer_cleanup(ccx_dtvcc_writer_ctx *writer)
|
||||
{
|
||||
if (writer->fd >= 0 && writer->fd != STDOUT_FILENO)
|
||||
close(writer->fd);
|
||||
free(writer->filename);
|
||||
if (writer->cd == (iconv_t) -1)
|
||||
{
|
||||
//TODO nothing to do here
|
||||
}
|
||||
else
|
||||
iconv_close(writer->cd);
|
||||
}
|
||||
|
||||
void ccx_dtvcc_writer_output(ccx_dtvcc_writer_ctx *writer, dtvcc_tv_screen *tv, struct encoder_ctx *encoder)
|
||||
{
|
||||
ccx_common_logging.debug_ftn(CCX_DMT_708, "[CEA-708] ccx_dtvcc_writer_output: "
|
||||
"writing... [%s][%d]\n", writer->filename, writer->fd);
|
||||
|
||||
if (!writer->filename && writer->fd < 0)
|
||||
return;
|
||||
|
||||
if (writer->filename && writer->fd < 0) //first request to write
|
||||
{
|
||||
ccx_common_logging.debug_ftn(CCX_DMT_708, "[CEA-708] "
|
||||
"ccx_dtvcc_writer_output: creating %s\n", writer->filename);
|
||||
writer->fd = open(writer->filename, O_RDWR | O_CREAT | O_TRUNC | O_BINARY, S_IREAD | S_IWRITE);
|
||||
if (writer->fd == -1)
|
||||
{
|
||||
ccx_common_logging.fatal_ftn(
|
||||
CCX_COMMON_EXIT_FILE_CREATION_FAILED, "[CEA-708] Failed to open a file\n");
|
||||
}
|
||||
if (!encoder->no_bom)
|
||||
write(writer->fd, UTF8_BOM, sizeof(UTF8_BOM));
|
||||
}
|
||||
|
||||
_ccx_dtvcc_write(writer, tv, encoder);
|
||||
}
|
||||
19
src/lib_ccx/ccx_decoders_708_output.h
Normal file
19
src/lib_ccx/ccx_decoders_708_output.h
Normal file
@@ -0,0 +1,19 @@
|
||||
#ifndef _CCX_DECODERS_708_OUTPUT_H_
|
||||
#define _CCX_DECODERS_708_OUTPUT_H_
|
||||
|
||||
#include "ccx_decoders_708.h"
|
||||
#include "ccx_encoders_common.h"
|
||||
#include "ccx_common_option.h"
|
||||
|
||||
void ccx_dtvcc_write_done(dtvcc_tv_screen *tv, struct encoder_ctx *encoder);
|
||||
|
||||
void ccx_dtvcc_writer_init(ccx_dtvcc_writer_ctx *writer,
|
||||
char *base_filename,
|
||||
int program_number,
|
||||
int service_number,
|
||||
enum ccx_output_format write_format,
|
||||
struct encoder_cfg *cfg);
|
||||
void ccx_dtvcc_writer_cleanup(ccx_dtvcc_writer_ctx *writer);
|
||||
void ccx_dtvcc_writer_output(ccx_dtvcc_writer_ctx *writer, dtvcc_tv_screen *tv, struct encoder_ctx *encoder);
|
||||
|
||||
#endif /*_CCX_DECODERS_708_OUTPUT_H_*/
|
||||
@@ -8,96 +8,37 @@ made to reuse, not duplicate, as many functions as possible */
|
||||
#include "ccx_common_timing.h"
|
||||
#include "ccx_common_common.h"
|
||||
#include "lib_ccx.h"
|
||||
#include "ccx_decoders_608.h"
|
||||
#include "ccx_decoders_708.h"
|
||||
#include "ccx_decoders_xds.h"
|
||||
#include "ccx_dtvcc.h"
|
||||
|
||||
|
||||
uint64_t utc_refvalue = UINT64_MAX; /* _UI64_MAX means don't use UNIX, 0 = use current system time as reference, +1 use a specific reference */
|
||||
extern int in_xds_mode;
|
||||
|
||||
// Preencoded strings
|
||||
unsigned char encoded_crlf[16];
|
||||
unsigned int encoded_crlf_length;
|
||||
unsigned char encoded_br[16];
|
||||
unsigned int encoded_br_length;
|
||||
|
||||
LLONG minimum_fts = 0; // No screen should start before this FTS
|
||||
|
||||
/* 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 (void)
|
||||
LLONG get_visible_start (struct ccx_common_timing_ctx *ctx, int current_field)
|
||||
{
|
||||
LLONG fts = get_fts();
|
||||
if (fts <= minimum_fts)
|
||||
fts = minimum_fts+1;
|
||||
LLONG fts = get_fts(ctx, current_field);
|
||||
if (fts <= ctx->minimum_fts)
|
||||
fts = ctx->minimum_fts + 1;
|
||||
ccx_common_logging.debug_ftn(CCX_DMT_DECODER_608, "Visible Start time=%s\n", print_mstime(fts));
|
||||
return fts;
|
||||
}
|
||||
|
||||
/* This function returns the current FTS and saves it so it can be used by get_visible_start */
|
||||
LLONG get_visible_end (void)
|
||||
/* This function returns the current FTS and saves it so it can be used by ctxget_visible_start */
|
||||
LLONG get_visible_end (struct ccx_common_timing_ctx *ctx, int current_field)
|
||||
{
|
||||
LLONG fts = get_fts();
|
||||
if (fts>minimum_fts)
|
||||
minimum_fts=fts;
|
||||
LLONG fts = get_fts(ctx, current_field);
|
||||
if (fts > ctx->minimum_fts)
|
||||
ctx->minimum_fts = fts;
|
||||
ccx_common_logging.debug_ftn(CCX_DMT_DECODER_608, "Visible End time=%s\n", print_mstime(fts));
|
||||
return fts;
|
||||
}
|
||||
|
||||
void find_limit_characters(unsigned char *line, int *first_non_blank, int *last_non_blank)
|
||||
{
|
||||
*last_non_blank = -1;
|
||||
*first_non_blank = -1;
|
||||
for (int i = 0; i<CCX_DECODER_608_SCREEN_WIDTH; i++)
|
||||
{
|
||||
unsigned char c = line[i];
|
||||
if (c != ' ' && c != 0x89)
|
||||
{
|
||||
if (*first_non_blank == -1)
|
||||
*first_non_blank = i;
|
||||
*last_non_blank = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsigned get_decoder_line_basic(unsigned char *buffer, int line_num, struct eia608_screen *data, int trim_subs, enum ccx_encoding_type encoding)
|
||||
{
|
||||
unsigned char *line = data->characters[line_num];
|
||||
int last_non_blank = -1;
|
||||
int first_non_blank = -1;
|
||||
unsigned char *orig = buffer; // Keep for debugging
|
||||
find_limit_characters(line, &first_non_blank, &last_non_blank);
|
||||
if (!trim_subs)
|
||||
first_non_blank = 0;
|
||||
|
||||
if (first_non_blank == -1)
|
||||
{
|
||||
*buffer = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int bytes = 0;
|
||||
for (int i = first_non_blank; i <= last_non_blank; i++)
|
||||
{
|
||||
char c = line[i];
|
||||
switch (encoding)
|
||||
{
|
||||
case CCX_ENC_UTF_8:
|
||||
bytes = get_char_in_utf_8(buffer, c);
|
||||
break;
|
||||
case CCX_ENC_LATIN_1:
|
||||
get_char_in_latin_1(buffer, c);
|
||||
bytes = 1;
|
||||
break;
|
||||
case CCX_ENC_UNICODE:
|
||||
get_char_in_unicode(buffer, c);
|
||||
bytes = 2;
|
||||
break;
|
||||
}
|
||||
buffer += bytes;
|
||||
}
|
||||
*buffer = 0;
|
||||
return (unsigned)(buffer - orig); // Return length
|
||||
}
|
||||
|
||||
int process_cc_data (struct lib_cc_decode *ctx, unsigned char *cc_data, int cc_count, struct cc_subtitle *sub)
|
||||
{
|
||||
int ret = -1;
|
||||
@@ -139,20 +80,20 @@ int validate_cc_data_pair (unsigned char *cc_data_pair)
|
||||
}
|
||||
int do_cb (struct lib_cc_decode *ctx, unsigned char *cc_block, struct cc_subtitle *sub)
|
||||
{
|
||||
unsigned char cc_valid = (*cc_block & 4) >>2;
|
||||
unsigned char cc_type = *cc_block & 3;
|
||||
unsigned char cc_valid = (*cc_block & 4) >>2;
|
||||
unsigned char cc_type = *cc_block & 3;
|
||||
|
||||
int timeok = 1;
|
||||
int timeok = 1;
|
||||
|
||||
if ( ctx->fix_padding
|
||||
&& cc_valid==0 && cc_type <= 1 // Only fix NTSC packets
|
||||
&& cc_block[1]==0 && cc_block[2]==0 )
|
||||
{
|
||||
/* Padding */
|
||||
cc_valid=1;
|
||||
cc_block[1]=0x80;
|
||||
cc_block[2]=0x80;
|
||||
}
|
||||
if ( ctx->fix_padding
|
||||
&& cc_valid==0 && cc_type <= 1 // Only fix NTSC packets
|
||||
&& cc_block[1]==0 && cc_block[2]==0 )
|
||||
{
|
||||
/* Padding */
|
||||
cc_valid=1;
|
||||
cc_block[1]=0x80;
|
||||
cc_block[2]=0x80;
|
||||
}
|
||||
|
||||
if ( ctx->write_format!=CCX_OF_RAW && // In raw we cannot skip padding because timing depends on it
|
||||
ctx->write_format!=CCX_OF_DVDRAW &&
|
||||
@@ -160,116 +101,128 @@ int do_cb (struct lib_cc_decode *ctx, unsigned char *cc_block, struct cc_subtitl
|
||||
&& (cc_block[1]&0x7F)==0 && (cc_block[2]&0x7F)==0) // CFS: Skip non-data, makes debugging harder.
|
||||
return 1;
|
||||
|
||||
// Print raw data with FTS.
|
||||
dbg_print(CCX_DMT_CBRAW, "%s %d %02X:%c%c:%02X", print_mstime(fts_now + fts_global),in_xds_mode,
|
||||
cc_block[0], cc_block[1]&0x7f,cc_block[2]&0x7f, cc_block[2]);
|
||||
// Print raw data with FTS.
|
||||
dbg_print(CCX_DMT_CBRAW, "%s %d %02X:%c%c:%02X", print_mstime(ctx->timing->fts_now + ctx->timing->fts_global),in_xds_mode,
|
||||
cc_block[0], cc_block[1]&0x7f,cc_block[2]&0x7f, cc_block[2]);
|
||||
|
||||
/* In theory the writercwtdata() function could return early and not
|
||||
* go through the 608/708 cases below. We do that to get accurate
|
||||
* counts for cb_field1, cb_field2 and cb_708.
|
||||
* Note that printdata() and do_708() must not be called for
|
||||
* the CCX_OF_RCWT case. */
|
||||
/* In theory the writercwtdata() function could return early and not
|
||||
* go through the 608/708 cases below. We do that to get accurate
|
||||
* counts for cb_field1, cb_field2 and cb_708.
|
||||
* Note that printdata() and dtvcc_process_data() must not be called for
|
||||
* the CCX_OF_RCWT case. */
|
||||
|
||||
if (cc_valid || cc_type==3)
|
||||
{
|
||||
ctx->cc_stats[cc_type]++;
|
||||
if (cc_valid || cc_type==3)
|
||||
{
|
||||
ctx->cc_stats[cc_type]++;
|
||||
|
||||
switch (cc_type)
|
||||
{
|
||||
case 0:
|
||||
dbg_print(CCX_DMT_CBRAW, " %s .. ..\n", debug_608toASC( cc_block, 0));
|
||||
switch (cc_type)
|
||||
{
|
||||
case 0:
|
||||
dbg_print(CCX_DMT_CBRAW, " %s .. ..\n", debug_608toASC( cc_block, 0));
|
||||
|
||||
current_field=1;
|
||||
ctx->saw_caption_block = 1;
|
||||
ctx->current_field = 1;
|
||||
ctx->saw_caption_block = 1;
|
||||
|
||||
if (ctx->extraction_start.set &&
|
||||
get_fts() < ctx->extraction_start.time_in_ms)
|
||||
timeok = 0;
|
||||
if (ctx->extraction_end.set &&
|
||||
get_fts() > ctx->extraction_end.time_in_ms)
|
||||
{
|
||||
timeok = 0;
|
||||
ctx->processed_enough=1;
|
||||
}
|
||||
if (timeok)
|
||||
{
|
||||
if(ctx->write_format!=CCX_OF_RCWT)
|
||||
printdata (ctx, cc_block+1,2,0,0, sub);
|
||||
else
|
||||
writercwtdata(ctx, cc_block);
|
||||
}
|
||||
cb_field1++;
|
||||
break;
|
||||
case 1:
|
||||
dbg_print(CCX_DMT_CBRAW, " .. %s ..\n", debug_608toASC( cc_block, 1));
|
||||
if (ctx->extraction_start.set &&
|
||||
get_fts(ctx->timing, ctx->current_field) < ctx->extraction_start.time_in_ms)
|
||||
timeok = 0;
|
||||
if (ctx->extraction_end.set &&
|
||||
get_fts(ctx->timing, ctx->current_field) > ctx->extraction_end.time_in_ms)
|
||||
{
|
||||
timeok = 0;
|
||||
ctx->processed_enough=1;
|
||||
}
|
||||
if (timeok)
|
||||
{
|
||||
if(ctx->write_format!=CCX_OF_RCWT)
|
||||
printdata (ctx, cc_block+1,2,0,0, sub);
|
||||
else
|
||||
writercwtdata(ctx, cc_block, sub);
|
||||
}
|
||||
cb_field1++;
|
||||
break;
|
||||
case 1:
|
||||
dbg_print(CCX_DMT_CBRAW, " .. %s ..\n", debug_608toASC( cc_block, 1));
|
||||
|
||||
current_field=2;
|
||||
ctx->saw_caption_block = 1;
|
||||
ctx->current_field = 2;
|
||||
ctx->saw_caption_block = 1;
|
||||
|
||||
if (ctx->extraction_start.set &&
|
||||
get_fts() < ctx->extraction_start.time_in_ms)
|
||||
timeok = 0;
|
||||
if (ctx->extraction_end.set &&
|
||||
get_fts() > ctx->extraction_end.time_in_ms)
|
||||
{
|
||||
timeok = 0;
|
||||
ctx->processed_enough=1;
|
||||
}
|
||||
if (timeok)
|
||||
{
|
||||
if(ctx->write_format!=CCX_OF_RCWT)
|
||||
printdata (ctx, 0,0,cc_block+1,2, sub);
|
||||
else
|
||||
writercwtdata(ctx, cc_block);
|
||||
}
|
||||
cb_field2++;
|
||||
break;
|
||||
case 2: //EIA-708
|
||||
// DTVCC packet data
|
||||
// Fall through
|
||||
case 3: //EIA-708
|
||||
dbg_print(CCX_DMT_CBRAW, " .. .. DD\n");
|
||||
if (ctx->extraction_start.set &&
|
||||
get_fts(ctx->timing, ctx->current_field) < ctx->extraction_start.time_in_ms)
|
||||
timeok = 0;
|
||||
if (ctx->extraction_end.set &&
|
||||
get_fts(ctx->timing, ctx->current_field) > ctx->extraction_end.time_in_ms)
|
||||
{
|
||||
timeok = 0;
|
||||
ctx->processed_enough=1;
|
||||
}
|
||||
if (timeok)
|
||||
{
|
||||
if(ctx->write_format!=CCX_OF_RCWT)
|
||||
printdata (ctx, 0,0,cc_block+1,2, sub);
|
||||
else
|
||||
writercwtdata(ctx, cc_block, sub);
|
||||
}
|
||||
cb_field2++;
|
||||
break;
|
||||
case 2: //EIA-708
|
||||
// DTVCC packet data
|
||||
// Fall through
|
||||
case 3: //EIA-708
|
||||
dbg_print(CCX_DMT_CBRAW, " .. .. DD\n");
|
||||
|
||||
// DTVCC packet start
|
||||
current_field=3;
|
||||
// DTVCC packet start
|
||||
ctx->current_field = 3;
|
||||
|
||||
if (ctx->extraction_start.set &&
|
||||
get_fts() < ctx->extraction_start.time_in_ms)
|
||||
timeok = 0;
|
||||
if (ctx->extraction_end.set &&
|
||||
get_fts() > ctx->extraction_end.time_in_ms)
|
||||
{
|
||||
timeok = 0;
|
||||
ctx->processed_enough=1;
|
||||
}
|
||||
char temp[4];
|
||||
temp[0]=cc_valid;
|
||||
temp[1]=cc_type;
|
||||
temp[2]=cc_block[1];
|
||||
temp[3]=cc_block[2];
|
||||
if (timeok)
|
||||
{
|
||||
if(ctx->write_format!=CCX_OF_RCWT)
|
||||
do_708 (ctx,(const unsigned char *) temp, 4);
|
||||
else
|
||||
writercwtdata(ctx, cc_block);
|
||||
}
|
||||
cb_708++;
|
||||
// Check for bytes read
|
||||
// printf ("Warning: Losing EIA-708 data!\n");
|
||||
break;
|
||||
default:
|
||||
fatal(CCX_COMMON_EXIT_BUG_BUG, "Cannot be reached!");
|
||||
} // switch (cc_type)
|
||||
} // cc_valid
|
||||
else
|
||||
{
|
||||
dbg_print(CCX_DMT_CBRAW, " .. .. ..\n");
|
||||
dbg_print(CCX_DMT_VERBOSE, "Found !(cc_valid || cc_type==3) - ignore this block\n");
|
||||
}
|
||||
if (ctx->extraction_start.set &&
|
||||
get_fts(ctx->timing, ctx->current_field) < ctx->extraction_start.time_in_ms)
|
||||
timeok = 0;
|
||||
if (ctx->extraction_end.set &&
|
||||
get_fts(ctx->timing, ctx->current_field) > ctx->extraction_end.time_in_ms)
|
||||
{
|
||||
timeok = 0;
|
||||
ctx->processed_enough=1;
|
||||
}
|
||||
char temp[4];
|
||||
temp[0]=cc_valid;
|
||||
temp[1]=cc_type;
|
||||
temp[2]=cc_block[1];
|
||||
temp[3]=cc_block[2];
|
||||
if (timeok)
|
||||
{
|
||||
if (ctx->write_format != CCX_OF_RCWT)
|
||||
ccx_dtvcc_process_data(ctx, (const unsigned char *) temp, 4);
|
||||
else
|
||||
writercwtdata(ctx, cc_block, sub);
|
||||
}
|
||||
cb_708++;
|
||||
// Check for bytes read
|
||||
// printf ("Warning: Losing EIA-708 data!\n");
|
||||
break;
|
||||
default:
|
||||
fatal(CCX_COMMON_EXIT_BUG_BUG, "Cannot be reached!");
|
||||
} // switch (cc_type)
|
||||
} // cc_valid
|
||||
else
|
||||
{
|
||||
dbg_print(CCX_DMT_CBRAW, " .. .. ..\n");
|
||||
dbg_print(CCX_DMT_VERBOSE, "Found !(cc_valid || cc_type==3) - ignore this block\n");
|
||||
}
|
||||
|
||||
return 1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
void dinit_cc_decode(struct lib_cc_decode **ctx)
|
||||
{
|
||||
struct lib_cc_decode *lctx = *ctx;
|
||||
ccx_dtvcc_free(&lctx->dtvcc);
|
||||
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);
|
||||
freep(ctx);
|
||||
}
|
||||
|
||||
struct lib_cc_decode* init_cc_decode (struct ccx_decoders_common_settings_t *setting)
|
||||
{
|
||||
struct lib_cc_decode *ctx = NULL;
|
||||
@@ -278,42 +231,164 @@ struct lib_cc_decode* init_cc_decode (struct ccx_decoders_common_settings_t *set
|
||||
if(!ctx)
|
||||
return NULL;
|
||||
|
||||
// Prepare 608 context
|
||||
ctx->context_cc608_field_1 = ccx_decoder_608_init_library(
|
||||
ccx_options.settings_608,
|
||||
ccx_options.cc_channel,
|
||||
1,
|
||||
ccx_options.trim_subs,
|
||||
ccx_options.encoding,
|
||||
&ctx->processed_enough,
|
||||
setting->cc_to_stdout,
|
||||
setting->subs_delay,
|
||||
setting->output_format
|
||||
);
|
||||
ctx->context_cc608_field_2 = ccx_decoder_608_init_library(
|
||||
ccx_options.settings_608,
|
||||
ccx_options.cc_channel,
|
||||
2,
|
||||
ccx_options.trim_subs,
|
||||
ccx_options.encoding,
|
||||
&ctx->processed_enough,
|
||||
setting->cc_to_stdout,
|
||||
setting->subs_delay,
|
||||
setting->output_format
|
||||
);
|
||||
ctx->avc_ctx = init_avc();
|
||||
ctx->codec = setting->codec;
|
||||
ctx->timing = init_timing_ctx(&ccx_common_timing_settings);
|
||||
|
||||
setting->settings_dtvcc->timing = ctx->timing;
|
||||
|
||||
setting->settings_608->no_rollup = setting->no_rollup;
|
||||
setting->settings_dtvcc->no_rollup = setting->no_rollup;
|
||||
ctx->no_rollup = setting->no_rollup;
|
||||
ctx->noscte20 = setting->noscte20;
|
||||
|
||||
ctx->dtvcc = ccx_dtvcc_init(setting->settings_dtvcc);
|
||||
ctx->dtvcc->is_active = setting->settings_dtvcc->enabled;
|
||||
|
||||
if(setting->codec == CCX_CODEC_ATSC_CC)
|
||||
{
|
||||
// Prepare 608 context
|
||||
ctx->context_cc608_field_1 = ccx_decoder_608_init_library(
|
||||
setting->settings_608,
|
||||
setting->cc_channel,
|
||||
1,
|
||||
&ctx->processed_enough,
|
||||
setting->cc_to_stdout,
|
||||
setting->output_format,
|
||||
ctx->timing
|
||||
);
|
||||
ctx->context_cc608_field_2 = ccx_decoder_608_init_library(
|
||||
setting->settings_608,
|
||||
setting->cc_channel,
|
||||
2,
|
||||
&ctx->processed_enough,
|
||||
setting->cc_to_stdout,
|
||||
setting->output_format,
|
||||
ctx->timing
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
ctx->context_cc608_field_1 = NULL;
|
||||
ctx->context_cc608_field_2 = NULL;
|
||||
}
|
||||
ctx->current_field = 1;
|
||||
ctx->private_data = setting->private_data;
|
||||
ctx->fix_padding = setting->fix_padding;
|
||||
ctx->write_format = setting->output_format;
|
||||
ctx->subs_delay = setting->subs_delay;
|
||||
ctx->wbout1 = setting->wbout1;
|
||||
ctx->extract = setting->extract;
|
||||
ctx->fullbin = setting->fullbin;
|
||||
ctx->hauppauge_mode = setting->hauppauge_mode;
|
||||
ctx->program_number = setting->program_number;
|
||||
ctx->processed_enough = 0;
|
||||
ctx->max_gop_length = 0;
|
||||
ctx->has_ccdata_buffered = 0;
|
||||
ctx->in_bufferdatatype = CCX_UNKNOWN;
|
||||
ctx->frames_since_last_gop = 0;
|
||||
memcpy(&ctx->extraction_start, &setting->extraction_start,sizeof(struct ccx_boundary_time));
|
||||
memcpy(&ctx->extraction_end, &setting->extraction_end,sizeof(struct ccx_boundary_time));
|
||||
|
||||
if (setting->send_to_srv)
|
||||
ctx->writedata = net_send_cc;
|
||||
else if (setting->output_format==CCX_OF_RAW ||
|
||||
setting->output_format==CCX_OF_DVDRAW ||
|
||||
setting->output_format==CCX_OF_RCWT )
|
||||
ctx->writedata = writeraw;
|
||||
else if (setting->output_format==CCX_OF_SMPTETT ||
|
||||
setting->output_format==CCX_OF_SAMI ||
|
||||
setting->output_format==CCX_OF_SRT ||
|
||||
setting->output_format == CCX_OF_WEBVTT ||
|
||||
setting->output_format==CCX_OF_TRANSCRIPT ||
|
||||
setting->output_format==CCX_OF_SPUPNG ||
|
||||
setting->output_format==CCX_OF_SIMPLE_XML ||
|
||||
setting->output_format==CCX_OF_G608 ||
|
||||
setting->output_format==CCX_OF_NULL)
|
||||
ctx->writedata = process608;
|
||||
else
|
||||
fatal(CCX_COMMON_EXIT_BUG_BUG, "Invalid Write Format Selected");
|
||||
|
||||
memset (&ctx->dec_sub, 0,sizeof(ctx->dec_sub));
|
||||
|
||||
// Initialize HDTV caption buffer
|
||||
init_hdcc(ctx);
|
||||
|
||||
ctx->current_hor_size = 0;
|
||||
ctx->current_vert_size = 0;
|
||||
ctx->current_aspect_ratio = 0;
|
||||
ctx->current_frame_rate = 4; // Assume standard fps, 29.97
|
||||
|
||||
//Variables used while parsing elementry stream
|
||||
ctx->no_bitstream_error = 0;
|
||||
ctx->saw_seqgoppic = 0;
|
||||
ctx->in_pic_data = 0;
|
||||
|
||||
ctx->current_progressive_sequence = 2;
|
||||
ctx->current_pulldownfields = 32768;
|
||||
|
||||
ctx->temporal_reference = 0;
|
||||
ctx->maxtref = 0;
|
||||
ctx->picture_coding_type = CCX_FRAME_TYPE_RESET_OR_UNKNOWN;
|
||||
ctx->picture_structure = 0;
|
||||
ctx->top_field_first = 0;
|
||||
ctx->repeat_first_field = 0;
|
||||
ctx->progressive_frame = 0;
|
||||
ctx->pulldownfields = 0;
|
||||
//es parser related variable ends here
|
||||
|
||||
memset(ctx->cc_stats, 0, 4 * sizeof(int));
|
||||
|
||||
ctx->anchor_seq_number = -1;
|
||||
// Init XDS buffers
|
||||
if(setting->ignore_xds == CCX_TRUE)
|
||||
ctx->xds_ctx = NULL;
|
||||
else
|
||||
ctx->xds_ctx = ccx_decoders_xds_init_library(ctx->timing);
|
||||
//xds_cea608_test(ctx->xds_ctx);
|
||||
|
||||
return ctx;
|
||||
}
|
||||
void dinit_cc_decode(struct lib_cc_decode **ctx)
|
||||
|
||||
void flush_cc_decode(struct lib_cc_decode *ctx, struct cc_subtitle *sub)
|
||||
{
|
||||
struct lib_cc_decode *lctx = *ctx;
|
||||
ccx_decoder_608_dinit_library(&lctx->context_cc608_field_1);
|
||||
ccx_decoder_608_dinit_library(&lctx->context_cc608_field_2);
|
||||
freep(ctx);
|
||||
if(ctx->codec == CCX_CODEC_ATSC_CC)
|
||||
{
|
||||
if (ctx->extract != 2)
|
||||
{
|
||||
if (ctx->write_format==CCX_OF_SMPTETT || ctx->write_format==CCX_OF_SAMI ||
|
||||
ctx->write_format==CCX_OF_SRT || ctx->write_format==CCX_OF_TRANSCRIPT ||
|
||||
ctx->write_format == CCX_OF_WEBVTT || ctx->write_format == CCX_OF_SPUPNG)
|
||||
{
|
||||
flush_608_context(ctx->context_cc608_field_1, sub);
|
||||
}
|
||||
else if(ctx->write_format == CCX_OF_RCWT)
|
||||
{
|
||||
// Write last header and data
|
||||
writercwtdata (ctx, NULL, sub);
|
||||
}
|
||||
}
|
||||
if (ctx->extract != 1)
|
||||
{
|
||||
if (ctx->write_format == CCX_OF_SMPTETT || ctx->write_format == CCX_OF_SAMI ||
|
||||
ctx->write_format == CCX_OF_SRT || ctx->write_format == CCX_OF_TRANSCRIPT ||
|
||||
ctx->write_format == CCX_OF_WEBVTT || ctx->write_format == CCX_OF_SPUPNG)
|
||||
{
|
||||
flush_608_context(ctx->context_cc608_field_2, sub);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (ctx->dtvcc->is_active)
|
||||
{
|
||||
for (int i = 0; i < CCX_DTVCC_MAX_SERVICES; i++)
|
||||
{
|
||||
ccx_dtvcc_service_decoder *decoder = &ctx->dtvcc->decoders[i];
|
||||
if (!ctx->dtvcc->services_active[i])
|
||||
continue;
|
||||
if (decoder->cc_count > 0)
|
||||
{
|
||||
ctx->current_field = 3;
|
||||
ccx_dtvcc_decoder_flush(ctx->dtvcc, decoder);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,22 +5,15 @@
|
||||
#include "ccx_common_constants.h"
|
||||
#include "ccx_common_structs.h"
|
||||
#include "ccx_decoders_structs.h"
|
||||
|
||||
extern unsigned char encoded_crlf[16]; // We keep it encoded here so we don't have to do it many times
|
||||
extern unsigned int encoded_crlf_length;
|
||||
extern unsigned char encoded_br[16];
|
||||
extern unsigned int encoded_br_length;
|
||||
|
||||
extern LLONG minimum_fts; // No screen should start before this FTS
|
||||
#include "ccx_common_option.h"
|
||||
|
||||
extern uint64_t utc_refvalue; // UTC referential value
|
||||
|
||||
// Declarations
|
||||
LLONG get_visible_start(void);
|
||||
LLONG get_visible_end(void);
|
||||
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);
|
||||
|
||||
void find_limit_characters(unsigned char *line, int *first_non_blank, int *last_non_blank);
|
||||
unsigned get_decoder_line_basic(unsigned char *buffer, int line_num, struct eia608_screen *data, int trim_subs, enum ccx_encoding_type encoding);
|
||||
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);
|
||||
|
||||
@@ -31,4 +24,5 @@ void printdata (struct lib_cc_decode *ctx, const unsigned char *data1, int lengt
|
||||
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);
|
||||
#endif
|
||||
|
||||
1419
src/lib_ccx/ccx_decoders_isdb.c
Normal file
1419
src/lib_ccx/ccx_decoders_isdb.c
Normal file
File diff suppressed because it is too large
Load Diff
13
src/lib_ccx/ccx_decoders_isdb.h
Normal file
13
src/lib_ccx/ccx_decoders_isdb.h
Normal file
@@ -0,0 +1,13 @@
|
||||
#ifndef ISDB_H
|
||||
#define ISDB_H
|
||||
#include "ccx_common_platform.h"
|
||||
#include "ccx_common_structs.h"
|
||||
#include "ccx_decoders_structs.h"
|
||||
|
||||
int isdb_set_global_time(struct lib_cc_decode *dec_ctx, uint64_t timestamp);
|
||||
int isdbsub_decode(struct lib_cc_decode *dec_ctx, const uint8_t *buf, size_t buf_size, struct cc_subtitle *sub);
|
||||
void delete_isdb_decoder(void **isdb_ctx);
|
||||
void *init_isdb_decoder(void);
|
||||
|
||||
|
||||
#endif
|
||||
@@ -4,9 +4,14 @@
|
||||
#include "ccx_common_platform.h"
|
||||
#include "ccx_common_constants.h"
|
||||
#include "ccx_common_timing.h"
|
||||
|
||||
#include "ccx_common_structs.h"
|
||||
#include "list.h"
|
||||
#include "ccx_decoders_708.h"
|
||||
// Define max width in characters/columns on the screen
|
||||
#define CCX_DECODER_608_SCREEN_WIDTH 32
|
||||
#define MAXBFRAMES 50
|
||||
#define SORTBUF (2*MAXBFRAMES+1)
|
||||
|
||||
|
||||
/* flag raised when end of display marker arrives in Dvb Subtitle */
|
||||
#define SUB_EOD_MARKER (1 << 0 )
|
||||
@@ -81,9 +86,22 @@ struct ccx_decoders_common_settings_t
|
||||
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
|
||||
void *wbout1;
|
||||
int cc_to_stdout;
|
||||
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
|
||||
unsigned send_to_srv;
|
||||
unsigned int hauppauge_mode; // If 1, use PID=1003, process specially and so on
|
||||
int program_number;
|
||||
enum ccx_code_type codec;
|
||||
int ignore_xds;
|
||||
void *private_data;
|
||||
};
|
||||
|
||||
struct lib_cc_decode
|
||||
{
|
||||
int cc_stats[4];
|
||||
@@ -95,12 +113,76 @@ 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 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
|
||||
struct ccx_boundary_time extraction_start, extraction_end; // Segment we actually process
|
||||
void *wbout1;
|
||||
void *wbout2;
|
||||
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
|
||||
|
||||
int frames_since_last_gop;
|
||||
/* GOP-based timing */
|
||||
int saw_gop_header;
|
||||
/* Time info for timed-transcript */
|
||||
int max_gop_length; // (Maximum) length of a group of pictures
|
||||
int last_gop_length; // Length of the previous group of pictures
|
||||
unsigned total_pulldownfields;
|
||||
unsigned total_pulldownframes;
|
||||
int program_number;
|
||||
struct list_head list;
|
||||
struct ccx_common_timing_ctx *timing;
|
||||
enum ccx_code_type codec;
|
||||
// Set to true if data is buffered
|
||||
int has_ccdata_buffered;
|
||||
|
||||
struct avc_ctx *avc_ctx;
|
||||
void *private_data;
|
||||
|
||||
/* General video information */
|
||||
unsigned int current_hor_size;
|
||||
unsigned int current_vert_size;
|
||||
unsigned int current_aspect_ratio;
|
||||
unsigned int current_frame_rate; // Assume standard fps, 29.97
|
||||
|
||||
/* Reguired in es_function.c */
|
||||
int no_bitstream_error;
|
||||
int saw_seqgoppic;
|
||||
int in_pic_data;
|
||||
|
||||
unsigned int current_progressive_sequence;
|
||||
unsigned int current_pulldownfields ;
|
||||
|
||||
int temporal_reference;
|
||||
enum ccx_frame_type picture_coding_type;
|
||||
unsigned picture_structure;
|
||||
unsigned repeat_first_field;
|
||||
unsigned progressive_frame;
|
||||
unsigned pulldownfields;
|
||||
/* Reguired in es_function.c and es_userdata.c */
|
||||
unsigned top_field_first; // Needs to be global
|
||||
|
||||
ccx_dtvcc_ctx *dtvcc;
|
||||
int current_field;
|
||||
// Analyse/use the picture information
|
||||
int maxtref; // Use to remember the temporal reference number
|
||||
|
||||
int cc_data_count[SORTBUF];
|
||||
// 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
|
||||
|
||||
// The sequence number of the current anchor frame. All currently read
|
||||
// B-Frames belong to this I- or P-frame.
|
||||
int anchor_seq_number;
|
||||
struct ccx_decoders_xds_context *xds_ctx;
|
||||
|
||||
int (*writedata)(const unsigned char *data, int length, void *private_data, struct cc_subtitle *sub);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,15 +1,13 @@
|
||||
#ifndef _XDS_H
|
||||
#define _XDS_H
|
||||
#ifndef CCX_DECODER_XDS_H
|
||||
#define CCX_DECODER_XDS_H
|
||||
|
||||
#include "ccx_decoders_common.h"
|
||||
#include "ccx_encoders_common.h"
|
||||
|
||||
void process_xds_bytes(const unsigned char hi, int lo);
|
||||
void do_end_of_xds(struct cc_subtitle *sub, unsigned char expected_checksum);
|
||||
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 ccx_decoders_xds_init_library(ccx_encoders_transcript_format *transcriptSettings, LLONG subs_delay, char millis_separator);
|
||||
|
||||
void xds_write_transcript_line_suffix (struct ccx_s_write *wb);
|
||||
void xds_write_transcript_line_prefix (struct ccx_s_write *wb, LLONG start_time, LLONG end_time, int cur_xds_packet_class);
|
||||
struct ccx_decoders_xds_context *ccx_decoders_xds_init_library(struct ccx_common_timing_ctx *timing);
|
||||
|
||||
void xds_cea608_test();
|
||||
#endif
|
||||
|
||||
381
src/lib_ccx/ccx_demuxer.c
Normal file
381
src/lib_ccx/ccx_demuxer.c
Normal file
@@ -0,0 +1,381 @@
|
||||
#include "ccx_demuxer.h"
|
||||
#include "activity.h"
|
||||
#include "lib_ccx.h"
|
||||
#include "utility.h"
|
||||
|
||||
static void ccx_demuxer_reset(struct ccx_demuxer *ctx)
|
||||
{
|
||||
ctx->startbytes_pos=0;
|
||||
ctx->startbytes_avail=0;
|
||||
memset (ctx->PIDs_seen, 0, 65536*sizeof (int));
|
||||
memset (ctx->PIDs_programs, 0, 65536*sizeof (struct PMT_entry *));
|
||||
}
|
||||
|
||||
static void ccx_demuxer_close(struct ccx_demuxer *ctx)
|
||||
{
|
||||
ctx->past = 0;
|
||||
if (ctx->infd!=-1 && ccx_options.input_source==CCX_DS_FILE)
|
||||
{
|
||||
close (ctx->infd);
|
||||
ctx->infd=-1;
|
||||
activity_input_file_closed();
|
||||
}
|
||||
}
|
||||
|
||||
static int ccx_demuxer_isopen(struct ccx_demuxer *ctx)
|
||||
{
|
||||
return ctx->infd != -1;
|
||||
}
|
||||
static int ccx_demuxer_open(struct ccx_demuxer *ctx, const char *file)
|
||||
{
|
||||
init_file_buffer(ctx);
|
||||
if (ccx_options.input_source==CCX_DS_STDIN)
|
||||
{
|
||||
if (ctx->infd != -1) // Means we had already processed stdin. So we're done.
|
||||
{
|
||||
if (ccx_options.print_file_reports)
|
||||
print_file_report(ctx->parent);
|
||||
return -1;
|
||||
}
|
||||
ctx->infd = 0;
|
||||
mprint ("\n\r-----------------------------------------------------------------\n");
|
||||
mprint ("\rReading from standard input\n");
|
||||
}
|
||||
else if (ccx_options.input_source == CCX_DS_NETWORK)
|
||||
{
|
||||
if (ctx->infd != -1) // Means we have already bound a socket.
|
||||
{
|
||||
if (ccx_options.print_file_reports)
|
||||
print_file_report(ctx->parent);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
ctx->infd = start_upd_srv(ccx_options.udpaddr, ccx_options.udpport);
|
||||
if(ctx->infd < 0)
|
||||
{
|
||||
print_error(ccx_options.gui_mode_reports,"socket() failed.");
|
||||
return CCX_COMMON_EXIT_BUG_BUG;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
else if (ccx_options.input_source == CCX_DS_TCP)
|
||||
{
|
||||
if (ctx->infd != -1)
|
||||
{
|
||||
if (ccx_options.print_file_reports)
|
||||
print_file_report(ctx->parent);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
ctx->infd = start_tcp_srv(ccx_options.tcpport, ccx_options.tcp_password);
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef _WIN32
|
||||
ctx->infd = OPEN (file, O_RDONLY | O_BINARY);
|
||||
#else
|
||||
ctx->infd = OPEN (file, O_RDONLY);
|
||||
#endif
|
||||
if (ctx->infd < 0)
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ctx->auto_stream == CCX_SM_AUTODETECT)
|
||||
{
|
||||
detect_stream_type(ctx);
|
||||
switch (ctx->stream_mode)
|
||||
{
|
||||
case CCX_SM_ELEMENTARY_OR_NOT_FOUND:
|
||||
mprint ("\rFile seems to be an elementary stream, enabling ES mode\n");
|
||||
break;
|
||||
case CCX_SM_TRANSPORT:
|
||||
mprint ("\rFile seems to be a transport stream, enabling TS mode\n");
|
||||
break;
|
||||
case CCX_SM_PROGRAM:
|
||||
mprint ("\rFile seems to be a program stream, enabling PS mode\n");
|
||||
break;
|
||||
case CCX_SM_ASF:
|
||||
mprint ("\rFile seems to be an ASF, enabling DVR-MS mode\n");
|
||||
break;
|
||||
case CCX_SM_WTV:
|
||||
mprint ("\rFile seems to be a WTV, enabling WTV mode\n");
|
||||
break;
|
||||
case CCX_SM_MCPOODLESRAW:
|
||||
mprint ("\rFile seems to be McPoodle raw data\n");
|
||||
break;
|
||||
case CCX_SM_RCWT:
|
||||
mprint ("\rFile seems to be a raw caption with time data\n");
|
||||
break;
|
||||
case CCX_SM_MP4:
|
||||
mprint ("\rFile seems to be a MP4\n");
|
||||
break;
|
||||
#ifdef WTV_DEBUG
|
||||
case CCX_SM_HEX_DUMP:
|
||||
mprint ("\rFile seems to be an hexadecimal dump\n");
|
||||
break;
|
||||
#endif
|
||||
case CCX_SM_MYTH:
|
||||
case CCX_SM_AUTODETECT:
|
||||
fatal(CCX_COMMON_EXIT_BUG_BUG, "Cannot be reached!");
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ctx->stream_mode = ctx->auto_stream;
|
||||
}
|
||||
|
||||
// The myth loop autodetect will only be used with ES or PS streams
|
||||
switch (ccx_options.auto_myth)
|
||||
{
|
||||
case 0:
|
||||
// Use whatever stream mode says
|
||||
break;
|
||||
case 1:
|
||||
// Force stream mode to myth
|
||||
ctx->stream_mode=CCX_SM_MYTH;
|
||||
break;
|
||||
case 2:
|
||||
// autodetect myth files, but only if it does not conflict with
|
||||
// the current stream mode
|
||||
switch (ctx->stream_mode)
|
||||
{
|
||||
case CCX_SM_ELEMENTARY_OR_NOT_FOUND:
|
||||
case CCX_SM_PROGRAM:
|
||||
if ( detect_myth(ctx->parent) )
|
||||
{
|
||||
ctx->stream_mode=CCX_SM_MYTH;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
// Keep stream_mode
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
ctx->past = 0;
|
||||
ctx->min_global_timestamp = 0;
|
||||
ctx->global_timestamp_inited = 0;
|
||||
ctx->last_global_timestamp = 0;
|
||||
ctx->offset_global_timestamp = 0;
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
LLONG ccx_demuxer_getfilesize (struct ccx_demuxer *ctx)
|
||||
{
|
||||
LLONG ret = 0;
|
||||
int in = ctx->infd;
|
||||
LLONG current=LSEEK (in, 0, SEEK_CUR);
|
||||
LLONG length = LSEEK (in,0,SEEK_END);
|
||||
if(current < 0 ||length < 0)
|
||||
return -1;
|
||||
|
||||
ret = LSEEK (in, current, SEEK_SET);
|
||||
if (ret < 0)
|
||||
return -1;
|
||||
|
||||
return length;
|
||||
}
|
||||
|
||||
static int ccx_demuxer_get_stream_mode(struct ccx_demuxer *ctx)
|
||||
{
|
||||
return ctx->stream_mode;
|
||||
}
|
||||
|
||||
|
||||
static void ccx_demuxer_print_cfg(struct ccx_demuxer *ctx)
|
||||
{
|
||||
switch (ctx->auto_stream)
|
||||
{
|
||||
case CCX_SM_ELEMENTARY_OR_NOT_FOUND:
|
||||
mprint ("Elementary");
|
||||
break;
|
||||
case CCX_SM_TRANSPORT:
|
||||
mprint ("Transport");
|
||||
break;
|
||||
case CCX_SM_PROGRAM:
|
||||
mprint ("Program");
|
||||
break;
|
||||
case CCX_SM_ASF:
|
||||
mprint ("DVR-MS");
|
||||
break;
|
||||
case CCX_SM_WTV:
|
||||
mprint ("Windows Television (WTV)");
|
||||
break;
|
||||
case CCX_SM_MCPOODLESRAW:
|
||||
mprint ("McPoodle's raw");
|
||||
break;
|
||||
case CCX_SM_AUTODETECT:
|
||||
mprint ("Autodetect");
|
||||
break;
|
||||
case CCX_SM_RCWT:
|
||||
mprint ("BIN");
|
||||
break;
|
||||
case CCX_SM_MP4:
|
||||
mprint ("MP4");
|
||||
break;
|
||||
#ifdef WTV_DEBUG
|
||||
case CCX_SM_HEX_DUMP:
|
||||
mprint ("Hex");
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
fatal(CCX_COMMON_EXIT_BUG_BUG, "BUG: Unknown stream mode.\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
int ccx_demuxer_write_es(struct ccx_demuxer *ctx, unsigned char* buf, size_t len)
|
||||
{
|
||||
if (ctx->fh_out_elementarystream!=NULL)
|
||||
fwrite (buf, 1, len,ctx->fh_out_elementarystream);
|
||||
return CCX_OK;
|
||||
}
|
||||
|
||||
void ccx_demuxer_delete(struct ccx_demuxer **ctx)
|
||||
{
|
||||
struct ccx_demuxer *lctx = *ctx;
|
||||
int i;
|
||||
dinit_cap(lctx);
|
||||
freep(&lctx->last_pat_payload);
|
||||
for (i = 0; i < MAX_PSI_PID; i++)
|
||||
{
|
||||
if(lctx->PID_buffers[i]!=NULL && lctx->PID_buffers[i]->buffer!=NULL)
|
||||
{
|
||||
free(lctx->PID_buffers[i]->buffer);
|
||||
lctx->PID_buffers[i]->buffer=NULL;
|
||||
lctx->PID_buffers[i]->buffer_length=0;
|
||||
}
|
||||
freep(&lctx->PID_buffers[i]);
|
||||
}
|
||||
for (i = 0; i < MAX_PID; i++)
|
||||
{
|
||||
if( lctx->PIDs_programs[i])
|
||||
freep(lctx->PIDs_programs + i);
|
||||
}
|
||||
if (lctx->fh_out_elementarystream != NULL)
|
||||
fclose (lctx->fh_out_elementarystream);
|
||||
|
||||
freep(&lctx->filebuffer);
|
||||
freep(ctx);
|
||||
}
|
||||
|
||||
struct ccx_demuxer *init_demuxer(void *parent, struct demuxer_cfg *cfg)
|
||||
{
|
||||
int i;
|
||||
struct ccx_demuxer *ctx = malloc(sizeof(struct ccx_demuxer));
|
||||
if(!ctx)
|
||||
return NULL;
|
||||
|
||||
ctx->infd = -1;//Set to -1 to indicate no file is open.
|
||||
ctx->m2ts = cfg->m2ts;
|
||||
ctx->auto_stream = cfg->auto_stream;
|
||||
ctx->stream_mode = CCX_SM_ELEMENTARY_OR_NOT_FOUND;
|
||||
|
||||
ctx->ts_autoprogram = cfg->ts_autoprogram;
|
||||
ctx->ts_allprogram = cfg->ts_allprogram;
|
||||
ctx->ts_datastreamtype = cfg->ts_datastreamtype;
|
||||
ctx->nb_program = 0;
|
||||
ctx->multi_stream_per_prog = 0;
|
||||
|
||||
if(cfg->ts_forced_program != -1)
|
||||
{
|
||||
ctx->pinfo[ctx->nb_program].pid = CCX_UNKNOWN;
|
||||
ctx->pinfo[ctx->nb_program].program_number = cfg->ts_forced_program;
|
||||
ctx->flag_ts_forced_pn = CCX_TRUE;
|
||||
ctx->nb_program++;
|
||||
}
|
||||
else
|
||||
{
|
||||
ctx->flag_ts_forced_pn = CCX_FALSE;
|
||||
}
|
||||
|
||||
INIT_LIST_HEAD(&ctx->cinfo_tree.all_stream);
|
||||
INIT_LIST_HEAD(&ctx->cinfo_tree.sib_stream);
|
||||
INIT_LIST_HEAD(&ctx->cinfo_tree.pg_stream);
|
||||
|
||||
ctx->codec = cfg->codec;
|
||||
|
||||
ctx->flag_ts_forced_cappid = CCX_FALSE;
|
||||
for(i = 0; i < cfg->nb_ts_cappid; i++)
|
||||
{
|
||||
if(ctx->codec == CCX_CODEC_ANY)
|
||||
update_capinfo(ctx, cfg->ts_cappids[i], cfg->ts_datastreamtype, CCX_CODEC_NONE, 0, NULL);
|
||||
else
|
||||
update_capinfo(ctx, cfg->ts_cappids[i], cfg->ts_datastreamtype, ctx->codec, 0, NULL);
|
||||
}
|
||||
|
||||
ctx->flag_ts_forced_cappid = cfg->nb_ts_cappid ? CCX_TRUE : CCX_FALSE;
|
||||
ctx->nocodec = cfg->nocodec;
|
||||
|
||||
ctx->reset = ccx_demuxer_reset;
|
||||
ctx->close = ccx_demuxer_close;
|
||||
ctx->open = ccx_demuxer_open;
|
||||
ctx->is_open = ccx_demuxer_isopen;
|
||||
ctx->get_filesize = ccx_demuxer_getfilesize;
|
||||
ctx->get_stream_mode = ccx_demuxer_get_stream_mode;
|
||||
ctx->print_cfg = ccx_demuxer_print_cfg;
|
||||
ctx->write_es = ccx_demuxer_write_es;
|
||||
ctx->hauppauge_warning_shown = 0;
|
||||
ctx->parent = parent;
|
||||
ctx->last_pat_payload = NULL;
|
||||
ctx->last_pat_length = 0;
|
||||
|
||||
ctx->fh_out_elementarystream = NULL;
|
||||
ctx->warning_program_not_found_shown = CCX_FALSE;
|
||||
ctx->strangeheader = 0;
|
||||
memset(&ctx->freport, 0, sizeof(ctx->freport));
|
||||
if (cfg->out_elementarystream_filename != NULL)
|
||||
{
|
||||
if ((ctx->fh_out_elementarystream = fopen (cfg->out_elementarystream_filename,"wb"))==NULL)
|
||||
{
|
||||
print_error(CCX_COMMON_EXIT_FILE_CREATION_FAILED, "Unable to open clean file: %s\n", cfg->out_elementarystream_filename);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
for(i = 0; i < MAX_PSI_PID; i++)
|
||||
ctx->PID_buffers[i]=NULL;
|
||||
|
||||
init_ts(ctx);
|
||||
ctx->filebuffer = NULL;
|
||||
|
||||
return ctx;
|
||||
}
|
||||
|
||||
void delete_demuxer_data(struct demuxer_data *data)
|
||||
{
|
||||
free(data->buffer);
|
||||
free(data);
|
||||
}
|
||||
|
||||
struct demuxer_data* alloc_demuxer_data(void)
|
||||
{
|
||||
struct demuxer_data* data = malloc(sizeof(struct demuxer_data));
|
||||
|
||||
if(!data)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
data->buffer = (unsigned char *) malloc (BUFSIZE);
|
||||
if(!data->buffer)
|
||||
{
|
||||
free(data);
|
||||
return NULL;
|
||||
}
|
||||
data->len = 0;
|
||||
data->bufferdatatype = CCX_PES;
|
||||
|
||||
data->program_number = -1;
|
||||
data->stream_pid = -1;
|
||||
data->codec = CCX_CODEC_NONE;
|
||||
data->len = 0;
|
||||
data->pts = CCX_NOPTS;
|
||||
data->next_stream = 0;
|
||||
data->next_program = 0;
|
||||
return data;
|
||||
|
||||
}
|
||||
174
src/lib_ccx/ccx_demuxer.h
Normal file
174
src/lib_ccx/ccx_demuxer.h
Normal file
@@ -0,0 +1,174 @@
|
||||
#ifndef CCX_DEMUXER_H
|
||||
#define CCX_DEMUXER_H
|
||||
#include "ccx_common_constants.h"
|
||||
#include "ccx_common_option.h"
|
||||
#include "ts_functions.h"
|
||||
#include "list.h"
|
||||
#include "activity.h"
|
||||
|
||||
/* Report information */
|
||||
#define SUB_STREAMS_CNT 10
|
||||
#define MAX_PID 65536
|
||||
#define MAX_PSI_PID 8191
|
||||
#define TS_PMT_MAP_SIZE 128
|
||||
#define MAX_PROGRAM 128
|
||||
#define MAX_PROGRAM_NAME_LEN 128
|
||||
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;
|
||||
};
|
||||
|
||||
struct program_info
|
||||
{
|
||||
int pid;
|
||||
int program_number;
|
||||
uint8_t analysed_PMT_once:1;
|
||||
uint8_t version;
|
||||
uint8_t saved_section[1021];
|
||||
int32_t crc;
|
||||
uint8_t valid_crc:1;
|
||||
char name[MAX_PROGRAM_NAME_LEN];
|
||||
/**
|
||||
* -1 pid represent that pcr_pid is not available
|
||||
*/
|
||||
int16_t pcr_pid;
|
||||
};
|
||||
|
||||
struct cap_info
|
||||
{
|
||||
int pid;
|
||||
int program_number;
|
||||
enum ccx_stream_type stream;
|
||||
enum ccx_code_type codec;
|
||||
long capbufsize;
|
||||
unsigned char *capbuf;
|
||||
long capbuflen; // Bytes read in capbuf
|
||||
int saw_pesstart;
|
||||
int prev_counter;
|
||||
void *codec_private_data;
|
||||
int ignore;
|
||||
|
||||
/**
|
||||
List joining all stream in TS
|
||||
*/
|
||||
struct list_head all_stream;
|
||||
/**
|
||||
List joining all sibling Stream in Program
|
||||
*/
|
||||
struct list_head sib_head;
|
||||
struct list_head sib_stream;
|
||||
/**
|
||||
List joining all sibling Stream in Program
|
||||
*/
|
||||
struct list_head pg_stream;
|
||||
|
||||
};
|
||||
|
||||
struct ccx_demuxer
|
||||
{
|
||||
int m2ts;
|
||||
enum ccx_stream_mode_enum stream_mode;
|
||||
enum ccx_stream_mode_enum auto_stream;
|
||||
|
||||
// Small buffer to help us with the initial sync
|
||||
unsigned char startbytes[STARTBYTESLENGTH];
|
||||
unsigned int startbytes_pos;
|
||||
int startbytes_avail;
|
||||
|
||||
// User Specified Param
|
||||
int ts_autoprogram;
|
||||
int ts_allprogram;
|
||||
int flag_ts_forced_pn;
|
||||
int flag_ts_forced_cappid;
|
||||
int ts_datastreamtype;
|
||||
|
||||
|
||||
struct program_info pinfo[MAX_PROGRAM];
|
||||
int nb_program;
|
||||
/* subtitle codec type */
|
||||
enum ccx_code_type codec;
|
||||
enum ccx_code_type nocodec;
|
||||
struct cap_info cinfo_tree;
|
||||
|
||||
/* File handles */
|
||||
FILE *fh_out_elementarystream;
|
||||
int infd; // descriptor number to input.
|
||||
LLONG past; /* Position in file, if in sync same as ftell() */
|
||||
|
||||
// TODO relates to fts_global
|
||||
int64_t global_timestamp;
|
||||
int64_t min_global_timestamp;
|
||||
int64_t offset_global_timestamp;
|
||||
int64_t last_global_timestamp;
|
||||
int global_timestamp_inited;
|
||||
|
||||
struct PSI_buffer *PID_buffers[MAX_PSI_PID];
|
||||
int PIDs_seen[MAX_PID];
|
||||
|
||||
struct PMT_entry *PIDs_programs[MAX_PID];
|
||||
struct ccx_demux_report freport;
|
||||
|
||||
/* Hauppauge support */
|
||||
unsigned hauppauge_warning_shown; // Did we detect a possible Hauppauge capture and told the user already?
|
||||
|
||||
int multi_stream_per_prog;
|
||||
|
||||
unsigned char *last_pat_payload;
|
||||
unsigned last_pat_length;
|
||||
|
||||
unsigned char *filebuffer;
|
||||
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
|
||||
|
||||
int warning_program_not_found_shown;
|
||||
|
||||
// Remember if the last header was valid. Used to suppress too much output
|
||||
// and the expected unrecognized first header for TiVo files.
|
||||
int strangeheader;
|
||||
|
||||
void *parent;
|
||||
void (*print_cfg)(struct ccx_demuxer *ctx);
|
||||
void (*reset)(struct ccx_demuxer *ctx);
|
||||
void (*close)(struct ccx_demuxer *ctx);
|
||||
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);
|
||||
int (*write_es)(struct ccx_demuxer *ctx, unsigned char* buf, size_t len);
|
||||
};
|
||||
|
||||
struct demuxer_data
|
||||
{
|
||||
int program_number;
|
||||
int stream_pid;
|
||||
enum ccx_code_type codec;
|
||||
enum ccx_bufferdata_type bufferdatatype;
|
||||
unsigned char *buffer;
|
||||
size_t len;
|
||||
LLONG pts;
|
||||
struct demuxer_data *next_stream;
|
||||
struct demuxer_data *next_program;
|
||||
};
|
||||
|
||||
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);
|
||||
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);
|
||||
int need_capInfo(struct ccx_demuxer *ctx, int program_number);
|
||||
int need_capInfo_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);
|
||||
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);
|
||||
#endif
|
||||
147
src/lib_ccx/ccx_dtvcc.c
Normal file
147
src/lib_ccx/ccx_dtvcc.c
Normal file
@@ -0,0 +1,147 @@
|
||||
#include "ccx_dtvcc.h"
|
||||
#include "ccx_common_common.h"
|
||||
#include "ccx_encoders_common.h"
|
||||
#include "ccx_decoders_708_output.h"
|
||||
|
||||
void ccx_dtvcc_process_data(struct lib_cc_decode *ctx,
|
||||
const unsigned char *data,
|
||||
int data_length)
|
||||
{
|
||||
/*
|
||||
* Note: the data has following format:
|
||||
* 1 byte for cc_valid
|
||||
* 1 byte for cc_type
|
||||
* 2 bytes for the actual data
|
||||
*/
|
||||
|
||||
ccx_dtvcc_ctx *dtvcc = ctx->dtvcc;
|
||||
|
||||
if (!dtvcc->is_active && !dtvcc->report_enabled)
|
||||
return;
|
||||
|
||||
for (int i = 0; i < data_length; i += 4)
|
||||
{
|
||||
unsigned char cc_valid = data[i];
|
||||
unsigned char cc_type = data[i + 1];
|
||||
|
||||
switch (cc_type)
|
||||
{
|
||||
case 2:
|
||||
ccx_common_logging.debug_ftn (CCX_DMT_708, "[CEA-708] dtvcc_process_data: DTVCC Channel Packet Data\n");
|
||||
if (cc_valid == 0) // This ends the previous packet
|
||||
ccx_dtvcc_process_current_packet(dtvcc);
|
||||
else
|
||||
{
|
||||
if (dtvcc->current_packet_length > 253)
|
||||
{
|
||||
ccx_common_logging.debug_ftn(CCX_DMT_708, "[CEA-708] dtvcc_process_data: "
|
||||
"Warning: Legal packet size exceeded (1), data not added.\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
dtvcc->current_packet[dtvcc->current_packet_length++] = data[i + 2];
|
||||
dtvcc->current_packet[dtvcc->current_packet_length++] = data[i + 3];
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
ccx_common_logging.debug_ftn (CCX_DMT_708, "[CEA-708] dtvcc_process_data: DTVCC Channel Packet Start\n");
|
||||
ccx_dtvcc_process_current_packet(dtvcc);
|
||||
if (cc_valid)
|
||||
{
|
||||
if (dtvcc->current_packet_length > CCX_DTVCC_MAX_PACKET_LENGTH - 1)
|
||||
{
|
||||
ccx_common_logging.debug_ftn(CCX_DMT_708, "[CEA-708] dtvcc_process_data: "
|
||||
"Warning: Legal packet size exceeded (2), data not added.\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
dtvcc->current_packet[dtvcc->current_packet_length++] = data[i + 2];
|
||||
dtvcc->current_packet[dtvcc->current_packet_length++] = data[i + 3];
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
ccx_common_logging.fatal_ftn (CCX_COMMON_EXIT_BUG_BUG, "[CEA-708] dtvcc_process_data: "
|
||||
"shouldn't be here - cc_type: %d\n", cc_type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------
|
||||
|
||||
ccx_dtvcc_ctx *ccx_dtvcc_init(struct ccx_decoder_dtvcc_settings *opts)
|
||||
{
|
||||
ccx_common_logging.debug_ftn(CCX_DMT_708, "[CEA-708] initializing dtvcc decoder\n");
|
||||
ccx_dtvcc_ctx *ctx = (ccx_dtvcc_ctx *) malloc(sizeof(ccx_dtvcc_ctx));
|
||||
if (!ctx)
|
||||
{
|
||||
ccx_common_logging.fatal_ftn(EXIT_NOT_ENOUGH_MEMORY, "[CEA-708] ccx_dtvcc_init");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ctx->report = opts->report;
|
||||
ctx->report->reset_count = 0;
|
||||
ctx->is_active = 0;
|
||||
ctx->report_enabled = 0;
|
||||
ctx->no_rollup = opts->no_rollup;
|
||||
ctx->active_services_count = opts->active_services_count;
|
||||
|
||||
memcpy(ctx->services_active, opts->services_enabled, CCX_DTVCC_MAX_SERVICES * sizeof(int));
|
||||
|
||||
ccx_dtvcc_clear_packet(ctx);
|
||||
|
||||
ctx->last_sequence = CCX_DTVCC_NO_LAST_SEQUENCE;
|
||||
|
||||
ctx->report_enabled = opts->print_file_reports;
|
||||
ctx->timing = opts->timing;
|
||||
|
||||
ccx_common_logging.debug_ftn(CCX_DMT_708, "[CEA-708] initializing services\n");
|
||||
|
||||
for (int i = 0; i < CCX_DTVCC_MAX_SERVICES; i++)
|
||||
{
|
||||
if (!ctx->services_active[i])
|
||||
continue;
|
||||
|
||||
ccx_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, "ccx_dtvcc_init");
|
||||
|
||||
for (int j = 0; j < CCX_DTVCC_MAX_WINDOWS; j++)
|
||||
decoder->windows[j].memory_reserved = 0;
|
||||
|
||||
ccx_dtvcc_windows_reset(decoder);
|
||||
}
|
||||
|
||||
return ctx;
|
||||
}
|
||||
|
||||
void ccx_dtvcc_free(ccx_dtvcc_ctx **ctx_ptr)
|
||||
{
|
||||
ccx_common_logging.debug_ftn(CCX_DMT_708, "[CEA-708] dtvcc_free: cleaning up\n");
|
||||
|
||||
ccx_dtvcc_ctx *ctx = *ctx_ptr;
|
||||
|
||||
for (int i = 0; i < CCX_DTVCC_MAX_SERVICES; i++)
|
||||
{
|
||||
if (!ctx->services_active[i])
|
||||
continue;
|
||||
|
||||
ccx_dtvcc_service_decoder *decoder = &ctx->decoders[i];
|
||||
|
||||
for (int j = 0; j < CCX_DTVCC_MAX_WINDOWS; j++)
|
||||
if (decoder->windows[j].memory_reserved)
|
||||
{
|
||||
for (int k = 0; k < CCX_DTVCC_MAX_ROWS; k++)
|
||||
free(decoder->windows[j].rows[k]);
|
||||
decoder->windows[j].memory_reserved = 0;
|
||||
}
|
||||
|
||||
free(decoder->tv);
|
||||
}
|
||||
freep(ctx_ptr);
|
||||
}
|
||||
14
src/lib_ccx/ccx_dtvcc.h
Normal file
14
src/lib_ccx/ccx_dtvcc.h
Normal file
@@ -0,0 +1,14 @@
|
||||
#ifndef CCEXTRACTOR_CCX_DTVCC_H
|
||||
#define CCEXTRACTOR_CCX_DTVCC_H
|
||||
|
||||
#include "ccx_decoders_708.h"
|
||||
#include "ccx_common_option.h"
|
||||
|
||||
void ccx_dtvcc_process_data(struct lib_cc_decode *ctx,
|
||||
const unsigned char *data,
|
||||
int data_length);
|
||||
|
||||
ccx_dtvcc_ctx *ccx_dtvcc_init(ccx_decoder_dtvcc_settings *opts);
|
||||
void ccx_dtvcc_free(ccx_dtvcc_ctx **);
|
||||
|
||||
#endif //CCEXTRACTOR_CCX_DTVCC_H
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,17 +1,28 @@
|
||||
#ifndef _CC_ENCODER_COMMON_H
|
||||
#define _CC_ENCODER_COMMON_H
|
||||
|
||||
#ifdef WIN32
|
||||
#include "..\\win_iconv\\iconv.h"
|
||||
#else
|
||||
#include "iconv.h"
|
||||
#endif
|
||||
|
||||
#include "ccx_common_structs.h"
|
||||
#include "ccx_decoders_structs.h"
|
||||
#include "ccx_encoders_structs.h"
|
||||
#include "ccx_encoders_helpers.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, bailing out\n"); } \
|
||||
}
|
||||
|
||||
extern ccx_encoders_transcript_format ccx_encoders_default_transcript_settings;
|
||||
typedef struct ccx_dtvcc_writer_ctx
|
||||
{
|
||||
int fd;
|
||||
char *filename;
|
||||
iconv_t cd;
|
||||
} ccx_dtvcc_writer_ctx;
|
||||
|
||||
/**
|
||||
* Context of encoder, This structure gives single interface
|
||||
@@ -25,14 +36,64 @@ struct encoder_ctx
|
||||
unsigned int capacity;
|
||||
/* keep count of srt subtitle*/
|
||||
unsigned int srt_counter;
|
||||
/* output context */
|
||||
|
||||
/* Input outputs */
|
||||
/* Flag giving hint that output is send to server through network */
|
||||
unsigned int send_to_srv;
|
||||
/* Used only in Spupng output */
|
||||
int multiple_files;
|
||||
/* Used only in Spupng output and creating name of output file*/
|
||||
char *first_input_file;
|
||||
/* Its array with length of number of languages */
|
||||
struct ccx_s_write *out;
|
||||
/* 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
|
||||
|
||||
/* 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
|
||||
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 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 extract;
|
||||
|
||||
int dtvcc_extract; //1 or 0 depending if we have to handle dtvcc
|
||||
ccx_dtvcc_writer_ctx dtvcc_writers[CCX_DTVCC_MAX_SERVICES];
|
||||
|
||||
/* Timing related variables*/
|
||||
/* start time of previous sub */
|
||||
LLONG prev_start;
|
||||
|
||||
LLONG subs_delay;
|
||||
LLONG last_displayed_subs_ms;
|
||||
enum ccx_output_date_format date_format;
|
||||
char millis_separator;
|
||||
|
||||
/* Credit stuff */
|
||||
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 startcreditsforatleast, startcreditsforatmost; // How long to display them?
|
||||
struct ccx_boundary_time endcreditsforatleast, endcreditsforatmost;
|
||||
|
||||
// Preencoded strings
|
||||
unsigned char encoded_crlf[16];
|
||||
unsigned int encoded_crlf_length;
|
||||
unsigned char encoded_br[16];
|
||||
unsigned int encoded_br_length;
|
||||
|
||||
int new_sentence; // Capitalize next letter?
|
||||
|
||||
int program_number;
|
||||
struct list_head list;
|
||||
};
|
||||
|
||||
#define INITIAL_ENC_BUFFER_CAPACITY 2048
|
||||
@@ -42,12 +103,11 @@ struct encoder_ctx
|
||||
* write subtitle header to file refrenced by
|
||||
* output context
|
||||
*
|
||||
* @param ctx preallocated encoder ctx
|
||||
* @param out output context
|
||||
* @param cfg Option to initilaize encoder cfg params
|
||||
*
|
||||
* @return 0 on SUCESS, -1 on failure
|
||||
* @return Allocated and properly initilaized Encoder Context, NULL on failure
|
||||
*/
|
||||
int init_encoder(struct encoder_ctx *ctx,struct ccx_s_write *out);
|
||||
struct encoder_ctx *init_encoder(struct encoder_cfg *opt);
|
||||
|
||||
/**
|
||||
* try to add end credits in subtitle file and then write subtitle
|
||||
@@ -56,9 +116,11 @@ int init_encoder(struct encoder_ctx *ctx,struct ccx_s_write *out);
|
||||
* deallocate encoder ctx, so before using encoder_ctx again
|
||||
* after deallocating user need to allocate encoder ctx again
|
||||
*
|
||||
* @oaram ctx Initialized encoder ctx using init_encoder
|
||||
* @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 *ctx);
|
||||
void dinit_encoder(struct encoder_ctx **arg, LLONG current_fts);
|
||||
|
||||
/**
|
||||
* @param ctx encoder context
|
||||
@@ -66,18 +128,31 @@ void dinit_encoder(struct encoder_ctx *ctx);
|
||||
*/
|
||||
int encode_sub(struct encoder_ctx *ctx,struct cc_subtitle *sub);
|
||||
|
||||
int write_cc_buffer_as_g608(struct eia608_screen *data, struct encoder_ctx *context);
|
||||
|
||||
int write_cc_buffer_as_srt(struct eia608_screen *data, struct encoder_ctx *context);
|
||||
void write_stringz_as_srt(char *string, struct encoder_ctx *context, LLONG ms_start, LLONG ms_end);
|
||||
int write_cc_subtitle_as_srt(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_cc_buffer_as_webvtt(struct eia608_screen *data, struct encoder_ctx *context);
|
||||
int write_cc_subtitle_as_webvtt(struct cc_subtitle *sub, struct encoder_ctx *context);
|
||||
int write_stringz_as_webvtt(char *string, struct encoder_ctx *context, LLONG ms_start, LLONG ms_end);
|
||||
|
||||
int write_cc_buffer_as_sami(struct eia608_screen *data, struct encoder_ctx *context);
|
||||
void write_stringz_as_sami(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);
|
||||
int write_cc_subtitle_as_sami(struct cc_subtitle *sub, struct encoder_ctx *context);
|
||||
|
||||
int write_cc_buffer_as_smptett(struct eia608_screen *data, struct encoder_ctx *context);
|
||||
void write_stringz_as_smptett(char *string, struct encoder_ctx *context, LLONG ms_start, LLONG ms_end);
|
||||
int write_cc_subtitle_as_smptett(struct cc_subtitle *sub, struct encoder_ctx *context);
|
||||
|
||||
void write_cc_buffer_to_gui(struct eia608_screen *data, struct encoder_ctx *context);
|
||||
|
||||
int write_cc_bitmap_as_spupng(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_bitmap_as_srt(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);
|
||||
|
||||
@@ -85,4 +160,9 @@ int write_cc_bitmap_as_smptett(struct cc_subtitle *sub, struct encoder_ctx *cont
|
||||
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);
|
||||
void set_encoder_startcredits_displayed(struct encoder_ctx *ctx, int startcredits_displayed);
|
||||
void set_encoder_rcwt_fileformat(struct encoder_ctx *ctx, short int format);
|
||||
|
||||
void find_limit_characters(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);
|
||||
#endif
|
||||
|
||||
109
src/lib_ccx/ccx_encoders_g608.c
Normal file
109
src/lib_ccx/ccx_encoders_g608.c
Normal file
@@ -0,0 +1,109 @@
|
||||
#include "lib_ccx.h"
|
||||
#include "ccx_encoders_common.h"
|
||||
#include "ccx_encoders_helpers.h"
|
||||
|
||||
static unsigned int get_line_encoded(struct encoder_ctx *ctx, unsigned char *buffer, int line_num, struct eia608_screen *data)
|
||||
{
|
||||
unsigned char *orig = buffer; // Keep for debugging
|
||||
unsigned char *line = data->characters[line_num];
|
||||
for (int i = 0; i < 32; i++)
|
||||
{
|
||||
int bytes = 0;
|
||||
switch (ctx->encoding)
|
||||
{
|
||||
case CCX_ENC_UTF_8:
|
||||
bytes = get_char_in_utf_8(buffer, line[i]);
|
||||
break;
|
||||
case CCX_ENC_LATIN_1:
|
||||
get_char_in_latin_1(buffer, line[i]);
|
||||
bytes = 1;
|
||||
break;
|
||||
case CCX_ENC_UNICODE:
|
||||
get_char_in_unicode(buffer, line[i]);
|
||||
bytes = 2;
|
||||
case CCX_ENC_ASCII:
|
||||
*buffer = line[i];
|
||||
bytes = 1;
|
||||
break;
|
||||
}
|
||||
buffer += bytes;
|
||||
}
|
||||
return (unsigned int)(buffer - orig); // Return length
|
||||
}
|
||||
static unsigned int get_color_encoded(struct encoder_ctx *ctx, unsigned char *buffer, int line_num, struct eia608_screen *data)
|
||||
{
|
||||
unsigned char *orig = buffer; // Keep for debugging
|
||||
for (int i = 0; i < 32; i++)
|
||||
{
|
||||
if (data->colors[line_num][i] < 10)
|
||||
*buffer++ = data->colors[line_num][i] + '0';
|
||||
else
|
||||
*buffer++ = 'E';
|
||||
}
|
||||
*buffer = 0;
|
||||
return (unsigned)(buffer - orig); // Return length
|
||||
}
|
||||
static unsigned int get_font_encoded(struct encoder_ctx *ctx, unsigned char *buffer, int line_num, struct eia608_screen *data)
|
||||
{
|
||||
unsigned char *orig = buffer; // Keep for debugging
|
||||
for (int i = 0; i < 32; i++)
|
||||
{
|
||||
if(data->fonts[line_num][i] == FONT_REGULAR)
|
||||
*buffer++ = 'R';
|
||||
else if(data->fonts[line_num][i] == FONT_UNDERLINED_ITALICS)
|
||||
*buffer++ = 'B';
|
||||
else if(data->fonts[line_num][i] == FONT_UNDERLINED)
|
||||
*buffer++ = 'U';
|
||||
else if(data->fonts[line_num][i] == FONT_ITALICS)
|
||||
*buffer++ = 'I';
|
||||
else
|
||||
*buffer++ = 'E';
|
||||
}
|
||||
return (unsigned)(buffer - orig); // Return length
|
||||
}
|
||||
int write_cc_buffer_as_g608(struct eia608_screen *data, struct encoder_ctx *context)
|
||||
{
|
||||
int used;
|
||||
unsigned h1,m1,s1,ms1;
|
||||
unsigned h2,m2,s2,ms2;
|
||||
LLONG ms_start, ms_end;
|
||||
int wrote_something = 0;
|
||||
ms_start = data->start_time;
|
||||
|
||||
ms_start+=context->subs_delay;
|
||||
if (ms_start<0) // Drop screens that because of subs_delay start too early
|
||||
return 0;
|
||||
|
||||
ms_end = data->end_time;
|
||||
|
||||
mstotime (ms_start,&h1,&m1,&s1,&ms1);
|
||||
mstotime (ms_end-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);
|
||||
used = encode_line(context, context->buffer,(unsigned char *) timeline);
|
||||
write(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);
|
||||
used = encode_line(context, context->buffer,(unsigned char *) timeline);
|
||||
|
||||
|
||||
write (context->out->fh, context->buffer, used);
|
||||
|
||||
for (int i=0;i<15;i++)
|
||||
{
|
||||
int length = get_line_encoded (context, context->subline, i, data);
|
||||
write(context->out->fh, context->subline, length);
|
||||
|
||||
length = get_color_encoded (context, context->subline, i, data);
|
||||
write(context->out->fh, context->subline, length);
|
||||
|
||||
length = get_font_encoded (context, context->subline, i, data);
|
||||
write(context->out->fh, context->subline, length);
|
||||
write(context->out->fh, context->encoded_crlf, context->encoded_crlf_length);
|
||||
wrote_something=1;
|
||||
}
|
||||
|
||||
write (context->out->fh, context->encoded_crlf, context->encoded_crlf_length);
|
||||
return wrote_something;
|
||||
}
|
||||
@@ -3,6 +3,7 @@
|
||||
#include "ccx_common_constants.h"
|
||||
#include "ccx_common_structs.h"
|
||||
#include "ccx_decoders_common.h"
|
||||
#include "ccx_encoders_common.h"
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#define strcasecmp stricmp
|
||||
@@ -17,7 +18,6 @@ char **spell_lower = NULL;
|
||||
char **spell_correct = NULL;
|
||||
int spell_words = 0;
|
||||
int spell_capacity = 0;
|
||||
struct ccx_encoders_helpers_settings_t ccx_encoders_helpers_settings;
|
||||
// Some basic English words, so user-defined doesn't have to
|
||||
// include the common stuff
|
||||
static const char *spell_builtin[] =
|
||||
@@ -68,9 +68,9 @@ void correct_case(int line_num, struct eia608_screen *data)
|
||||
free(line);
|
||||
}
|
||||
|
||||
void capitalize(int line_num, struct eia608_screen *data)
|
||||
void capitalize(struct encoder_ctx *context, int line_num, struct eia608_screen *data)
|
||||
{
|
||||
for (int i = 0; i<CCX_DECODER_608_SCREEN_WIDTH; i++)
|
||||
for (int i = 0; i < CCX_DECODER_608_SCREEN_WIDTH; i++)
|
||||
{
|
||||
switch (data->characters[line_num][i])
|
||||
{
|
||||
@@ -82,14 +82,14 @@ void capitalize(int line_num, struct eia608_screen *data)
|
||||
case '?': // Fallthrough
|
||||
case '!':
|
||||
case ':':
|
||||
new_sentence = 1;
|
||||
context->new_sentence = 1;
|
||||
break;
|
||||
default:
|
||||
if (new_sentence)
|
||||
if (context->new_sentence)
|
||||
data->characters[line_num][i] = cctoupper(data->characters[line_num][i]);
|
||||
else
|
||||
data->characters[line_num][i] = cctolower(data->characters[line_num][i]);
|
||||
new_sentence = 0;
|
||||
context->new_sentence = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -98,12 +98,12 @@ void capitalize(int line_num, struct eia608_screen *data)
|
||||
// Encodes a generic string. Note that since we use the encoders for closed caption
|
||||
// data, text would have to be encoded as CCs... so using special characters here
|
||||
// it's a bad idea.
|
||||
unsigned encode_line(unsigned char *buffer, unsigned char *text)
|
||||
unsigned encode_line(struct encoder_ctx *ctx, unsigned char *buffer, unsigned char *text)
|
||||
{
|
||||
unsigned bytes = 0;
|
||||
while (*text)
|
||||
{
|
||||
switch (ccx_encoders_helpers_settings.encoding)
|
||||
switch (ctx->encoding)
|
||||
{
|
||||
case CCX_ENC_UTF_8:
|
||||
case CCX_ENC_LATIN_1:
|
||||
@@ -120,6 +120,7 @@ unsigned encode_line(unsigned char *buffer, unsigned char *text)
|
||||
}
|
||||
text++;
|
||||
}
|
||||
*buffer = 0;
|
||||
return bytes;
|
||||
}
|
||||
|
||||
@@ -128,7 +129,7 @@ unsigned get_decoder_line_encoded_for_gui(unsigned char *buffer, int line_num, s
|
||||
unsigned char *line = data->characters[line_num];
|
||||
unsigned char *orig = buffer; // Keep for debugging
|
||||
int first = 0, last = 31;
|
||||
find_limit_characters(line, &first, &last);
|
||||
find_limit_characters(line, &first, &last, CCX_DECODER_608_SCREEN_WIDTH);
|
||||
for (int i = first; i <= last; i++)
|
||||
{
|
||||
get_char_in_latin_1(buffer, line[i]);
|
||||
@@ -139,7 +140,7 @@ unsigned get_decoder_line_encoded_for_gui(unsigned char *buffer, int line_num, s
|
||||
|
||||
}
|
||||
|
||||
unsigned char *close_tag(unsigned char *buffer, char *tagstack, char tagtype, int *punderlined, int *pitalics, int *pchanged_font)
|
||||
unsigned char *close_tag(struct encoder_ctx *ctx, unsigned char *buffer, char *tagstack, char tagtype, int *punderlined, int *pitalics, int *pchanged_font)
|
||||
{
|
||||
for (int l = strlen(tagstack) - 1; l >= 0; l--)
|
||||
{
|
||||
@@ -147,15 +148,15 @@ unsigned char *close_tag(unsigned char *buffer, char *tagstack, char tagtype, in
|
||||
switch (cur)
|
||||
{
|
||||
case 'F':
|
||||
buffer += encode_line(buffer, (unsigned char *) "</font>");
|
||||
buffer += encode_line(ctx, buffer, (unsigned char *) "</font>");
|
||||
(*pchanged_font)--;
|
||||
break;
|
||||
case 'U':
|
||||
buffer += encode_line(buffer, (unsigned char *) "</u>");
|
||||
buffer += encode_line(ctx, buffer, (unsigned char *) "</u>");
|
||||
(*punderlined)--;
|
||||
break;
|
||||
case 'I':
|
||||
buffer += encode_line(buffer, (unsigned char *) "</i>");
|
||||
buffer += encode_line(ctx, buffer, (unsigned char *) "</i>");
|
||||
(*pitalics)--;
|
||||
break;
|
||||
}
|
||||
@@ -168,7 +169,7 @@ unsigned char *close_tag(unsigned char *buffer, char *tagstack, char tagtype, in
|
||||
return buffer;
|
||||
}
|
||||
|
||||
unsigned get_decoder_line_encoded(unsigned char *buffer, int line_num, struct eia608_screen *data)
|
||||
unsigned get_decoder_line_encoded(struct encoder_ctx *ctx, unsigned char *buffer, int line_num, struct eia608_screen *data)
|
||||
{
|
||||
int col = COL_WHITE;
|
||||
int underlined = 0;
|
||||
@@ -179,25 +180,33 @@ unsigned get_decoder_line_encoded(unsigned char *buffer, int line_num, struct ei
|
||||
unsigned char *line = data->characters[line_num];
|
||||
unsigned char *orig = buffer; // Keep for debugging
|
||||
int first = 0, last = 31;
|
||||
if (ccx_encoders_helpers_settings.trim_subs)
|
||||
find_limit_characters(line, &first, &last);
|
||||
if (ctx->trim_subs)
|
||||
find_limit_characters(line, &first, &last, CCX_DECODER_608_SCREEN_WIDTH);
|
||||
for (int i = first; i <= last; i++)
|
||||
{
|
||||
// Handle color
|
||||
int its_col = data->colors[line_num][i];
|
||||
if (its_col != col && !ccx_encoders_helpers_settings.no_font_color &&
|
||||
if (its_col != col && !ctx->no_font_color &&
|
||||
!(col == COL_USERDEFINED && its_col == COL_WHITE)) // Don't replace user defined with white
|
||||
{
|
||||
if (changed_font)
|
||||
buffer = close_tag(buffer, tagstack, 'F', &underlined, &italics, &changed_font);
|
||||
buffer = close_tag(ctx, buffer, tagstack, 'F', &underlined, &italics, &changed_font);
|
||||
|
||||
// Add new font tag
|
||||
buffer += encode_line(buffer, (unsigned char*)color_text[its_col][1]);
|
||||
if ( MAX_COLOR > its_col)
|
||||
buffer += encode_line(ctx, buffer, (unsigned char*)color_text[its_col][1]);
|
||||
else
|
||||
{
|
||||
ccx_common_logging.log_ftn("WARNING:get_decoder_line_encoded:Invalid Color index Selected %d\n", its_col);
|
||||
its_col = COL_WHITE;
|
||||
}
|
||||
|
||||
if (its_col == COL_USERDEFINED)
|
||||
{
|
||||
// The previous sentence doesn't copy the whole
|
||||
// <font> tag, just up to the quote before the color
|
||||
buffer += encode_line(buffer, (unsigned char*)usercolor_rgb);
|
||||
buffer += encode_line(buffer, (unsigned char*) "\">");
|
||||
buffer += encode_line(ctx, buffer, (unsigned char*)usercolor_rgb);
|
||||
buffer += encode_line(ctx, buffer, (unsigned char*) "\">");
|
||||
}
|
||||
if (color_text[its_col][1][0]) // That means a <font> was added to the buffer
|
||||
{
|
||||
@@ -208,30 +217,30 @@ unsigned get_decoder_line_encoded(unsigned char *buffer, int line_num, struct ei
|
||||
}
|
||||
// Handle underlined
|
||||
int is_underlined = data->fonts[line_num][i] & FONT_UNDERLINED;
|
||||
if (is_underlined && underlined == 0 && !ccx_encoders_helpers_settings.no_type_setting) // Open underline
|
||||
if (is_underlined && underlined == 0 && !ctx->no_type_setting) // Open underline
|
||||
{
|
||||
buffer += encode_line(buffer, (unsigned char *) "<u>");
|
||||
buffer += encode_line(ctx, buffer, (unsigned char *) "<u>");
|
||||
strcat(tagstack, "U");
|
||||
underlined++;
|
||||
}
|
||||
if (is_underlined == 0 && underlined && !ccx_encoders_helpers_settings.no_type_setting) // Close underline
|
||||
if (is_underlined == 0 && underlined && !ctx->no_type_setting) // Close underline
|
||||
{
|
||||
buffer = close_tag(buffer, tagstack, 'U', &underlined, &italics, &changed_font);
|
||||
buffer = close_tag(ctx, buffer, tagstack, 'U', &underlined, &italics, &changed_font);
|
||||
}
|
||||
// Handle italics
|
||||
int has_ita = data->fonts[line_num][i] & FONT_ITALICS;
|
||||
if (has_ita && italics == 0 && !ccx_encoders_helpers_settings.no_type_setting) // Open italics
|
||||
if (has_ita && italics == 0 && !ctx->no_type_setting) // Open italics
|
||||
{
|
||||
buffer += encode_line(buffer, (unsigned char *) "<i>");
|
||||
buffer += encode_line(ctx, buffer, (unsigned char *) "<i>");
|
||||
strcat(tagstack, "I");
|
||||
italics++;
|
||||
}
|
||||
if (has_ita == 0 && italics && !ccx_encoders_helpers_settings.no_type_setting) // Close italics
|
||||
if (has_ita == 0 && italics && !ctx->no_type_setting) // Close italics
|
||||
{
|
||||
buffer = close_tag(buffer, tagstack, 'I', &underlined, &italics, &changed_font);
|
||||
buffer = close_tag(ctx, buffer, tagstack, 'I', &underlined, &italics, &changed_font);
|
||||
}
|
||||
int bytes = 0;
|
||||
switch (ccx_encoders_helpers_settings.encoding)
|
||||
switch (ctx->encoding)
|
||||
{
|
||||
case CCX_ENC_UTF_8:
|
||||
bytes = get_char_in_utf_8(buffer, line[i]);
|
||||
@@ -247,7 +256,7 @@ unsigned get_decoder_line_encoded(unsigned char *buffer, int line_num, struct ei
|
||||
}
|
||||
buffer += bytes;
|
||||
}
|
||||
buffer = close_tag(buffer, tagstack, 'A', &underlined, &italics, &changed_font);
|
||||
buffer = close_tag(ctx, buffer, tagstack, 'A', &underlined, &italics, &changed_font);
|
||||
if (underlined || italics || changed_font)
|
||||
ccx_common_logging.fatal_ftn(CCX_COMMON_EXIT_BUG_BUG, "Not all tags closed in encoding, this is a bug, please report.\n");
|
||||
*buffer = 0;
|
||||
@@ -293,6 +302,11 @@ int add_word(const char *word)
|
||||
ptr_correct = (char **)realloc(spell_correct, sizeof (char *)*
|
||||
spell_capacity);
|
||||
}
|
||||
else
|
||||
{
|
||||
ptr_lower = spell_lower;
|
||||
ptr_correct = spell_correct;
|
||||
}
|
||||
size_t len = strlen(word);
|
||||
new_lower = (char *)malloc(len + 1);
|
||||
new_correct = (char *)malloc(len + 1);
|
||||
@@ -356,6 +370,9 @@ int add_built_in_words(void)
|
||||
* @param size size of each element
|
||||
* @param compar Comparison function, which is called with three argument
|
||||
* that point to the objects being compared and arg.
|
||||
* compare Funtion should return an integer less than, equal to,
|
||||
* or greater than zero if p1 is found, respectively, to be less than,
|
||||
* to match, or be greater than p2.
|
||||
* @param arg argument passed as it is, to compare function
|
||||
*/
|
||||
void shell_sort(void *base, int nb, size_t size, int(*compar)(const void*p1, const void *p2, void*arg), void *arg)
|
||||
@@ -384,9 +401,3 @@ void ccx_encoders_helpers_perform_shellsort_words(void)
|
||||
shell_sort(spell_correct, spell_words, sizeof(*spell_correct), string_cmp2, NULL);
|
||||
}
|
||||
|
||||
void ccx_encoders_helpers_setup(enum ccx_encoding_type encoding,int no_font_color,int no_type_setting,int trim_subs){
|
||||
ccx_encoders_helpers_settings.encoding = encoding;
|
||||
ccx_encoders_helpers_settings.no_font_color = no_font_color;
|
||||
ccx_encoders_helpers_settings.no_type_setting = no_type_setting;
|
||||
ccx_encoders_helpers_settings.trim_subs = trim_subs;
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "ccx_common_constants.h"
|
||||
#include "ccx_decoders_structs.h"
|
||||
#include "ccx_decoders_608.h"
|
||||
#include "ccx_encoders_common.h"
|
||||
|
||||
extern char **spell_lower;
|
||||
extern char **spell_correct;
|
||||
@@ -19,18 +20,18 @@ struct ccx_encoders_helpers_settings_t {
|
||||
int no_type_setting;
|
||||
enum ccx_encoding_type encoding;
|
||||
};
|
||||
extern struct ccx_encoders_helpers_settings_t ccx_encoders_helpers_settings;
|
||||
|
||||
// Helper functions
|
||||
void correct_case(int line_num, struct eia608_screen *data);
|
||||
void capitalize(int line_num, struct eia608_screen *data);
|
||||
void capitalize(struct encoder_ctx *context, int line_num, struct eia608_screen *data);
|
||||
unsigned get_decoder_line_encoded_for_gui(unsigned char *buffer, int line_num, struct eia608_screen *data);
|
||||
unsigned get_decoder_line_encoded(unsigned char *buffer, int line_num, struct eia608_screen *data);
|
||||
unsigned get_decoder_line_encoded(struct encoder_ctx *ctx, unsigned char *buffer, int line_num, struct eia608_screen *data);
|
||||
|
||||
int string_cmp(const void *p1, const void *p2);
|
||||
int string_cmp2(const void *p1, const void *p2, void *arg);
|
||||
int add_built_in_words(void);
|
||||
int add_word(const char *word);
|
||||
unsigned encode_line (struct encoder_ctx *ctx, unsigned char *buffer, unsigned char *text);
|
||||
|
||||
void shell_sort(void *base, int nb, size_t size, int(*compar)(const void*p1, const void *p2, void*arg), void *arg);
|
||||
|
||||
|
||||
270
src/lib_ccx/ccx_encoders_sami.c
Normal file
270
src/lib_ccx/ccx_encoders_sami.c
Normal file
@@ -0,0 +1,270 @@
|
||||
#include "lib_ccx.h"
|
||||
#include "ccx_common_option.h"
|
||||
#include "ccx_encoders_common.h"
|
||||
#include "png.h"
|
||||
#include "spupng_encoder.h"
|
||||
#include "ocr.h"
|
||||
#include "utility.h"
|
||||
#include "ccx_encoders_helpers.h"
|
||||
|
||||
int write_stringz_as_sami(char *string, struct encoder_ctx *context, LLONG ms_start, LLONG ms_end)
|
||||
{
|
||||
int used;
|
||||
int len = 0;
|
||||
int ret = 0;
|
||||
unsigned char *unescaped = NULL;
|
||||
unsigned char *el = NULL;
|
||||
char str[1024];
|
||||
|
||||
sprintf (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);
|
||||
}
|
||||
|
||||
used = encode_line(context, context->buffer, (unsigned char *) str);
|
||||
ret = write (context->out->fh, context->buffer, used);
|
||||
if(ret != used)
|
||||
{
|
||||
return ret;
|
||||
}
|
||||
|
||||
len = strlen (string);
|
||||
unescaped= (unsigned char *) malloc (len+1);
|
||||
if(!unescaped)
|
||||
{
|
||||
mprint ("In write_stringz_as_sami() - not enough memory for len %d.\n", len);
|
||||
ret = -1;
|
||||
goto end;
|
||||
}
|
||||
|
||||
el = (unsigned char *) malloc (len*3+1); // Be generous
|
||||
if (el == NULL)
|
||||
{
|
||||
mprint ("In write_stringz_as_sami() - not enough memory for len %d.\n", len);
|
||||
ret = -1;
|
||||
goto end;
|
||||
}
|
||||
|
||||
int pos_r=0;
|
||||
int pos_w=0;
|
||||
// Scan for \n in the string and replace it with a 0
|
||||
while (pos_r < len)
|
||||
{
|
||||
if (string[pos_r] == '\\' && string[pos_r+1]=='n')
|
||||
{
|
||||
unescaped[pos_w] = 0;
|
||||
pos_r += 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
unescaped[pos_w] = string[pos_r];
|
||||
pos_r++;
|
||||
}
|
||||
pos_w++;
|
||||
}
|
||||
unescaped[pos_w] = 0;
|
||||
// Now read the unescaped string (now several string'z and write them)
|
||||
unsigned char *begin = unescaped;
|
||||
while (begin < unescaped+len)
|
||||
{
|
||||
unsigned int u = encode_line (context, el, begin);
|
||||
if (context->encoding != CCX_ENC_UNICODE)
|
||||
{
|
||||
dbg_print(CCX_DMT_DECODER_608, "\r");
|
||||
dbg_print(CCX_DMT_DECODER_608, "%s\n",context->subline);
|
||||
}
|
||||
ret = write(context->out->fh, el, u);
|
||||
if(ret != u)
|
||||
goto end;
|
||||
|
||||
ret = write(context->out->fh, context->encoded_br, context->encoded_br_length);
|
||||
if(ret != context->encoded_br_length)
|
||||
goto end;
|
||||
|
||||
ret = write(context->out->fh, context->encoded_crlf, context->encoded_crlf_length);
|
||||
if(ret != context->encoded_crlf_length)
|
||||
goto end;
|
||||
|
||||
begin += strlen ((const char *) begin) + 1;
|
||||
}
|
||||
|
||||
sprintf ((char *) 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);
|
||||
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);
|
||||
if (context->encoding != CCX_ENC_UNICODE)
|
||||
{
|
||||
dbg_print(CCX_DMT_DECODER_608, "\r%s\n", str);
|
||||
}
|
||||
ret = write(context->out->fh, context->buffer, used);
|
||||
if(ret != used)
|
||||
goto end;
|
||||
|
||||
end:
|
||||
free(el);
|
||||
free(unescaped);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
int write_cc_bitmap_as_sami(struct cc_subtitle *sub, struct encoder_ctx *context)
|
||||
{
|
||||
int ret = 0;
|
||||
#ifdef ENABLE_OCR
|
||||
struct cc_bitmap* rect;
|
||||
LLONG ms_start, ms_end;
|
||||
|
||||
if (context->prev_start != -1 && (sub->flags & SUB_EOD_MARKER))
|
||||
{
|
||||
ms_start = context->prev_start + context->subs_delay;
|
||||
ms_end = sub->start_time - 1;
|
||||
}
|
||||
else if ( !(sub->flags & SUB_EOD_MARKER))
|
||||
{
|
||||
ms_start = sub->start_time + context->subs_delay;
|
||||
ms_end = sub->end_time - 1;
|
||||
}
|
||||
else if ( context->prev_start == -1 && (sub->flags & SUB_EOD_MARKER) )
|
||||
{
|
||||
ms_start = 1 + context->subs_delay;
|
||||
ms_end = sub->start_time - 1;
|
||||
}
|
||||
|
||||
if(sub->nb_data == 0 )
|
||||
return 0;
|
||||
rect = sub->data;
|
||||
|
||||
if ( sub->flags & SUB_EOD_MARKER )
|
||||
context->prev_start = sub->start_time;
|
||||
|
||||
if (rect[0].ocr_text && *(rect[0].ocr_text))
|
||||
{
|
||||
if (context->prev_start != -1 || !(sub->flags & SUB_EOD_MARKER))
|
||||
{
|
||||
char *token = NULL;
|
||||
char *buf = (char*)context->buffer;
|
||||
sprintf(buf,
|
||||
"<SYNC start=%llu><P class=\"UNKNOWNCC\">\r\n"
|
||||
,(unsigned long long)ms_start);
|
||||
write(context->out->fh, buf, strlen(buf));
|
||||
token = strtok(rect[0].ocr_text,"\r\n");
|
||||
while (token)
|
||||
{
|
||||
|
||||
sprintf(buf, "%s", token);
|
||||
token = strtok(NULL,"\r\n");
|
||||
if(token)
|
||||
strcat(buf, "<br>\n");
|
||||
else
|
||||
strcat(buf, "\n");
|
||||
write(context->out->fh, buf, strlen(buf));
|
||||
|
||||
}
|
||||
sprintf(buf,
|
||||
"<SYNC start=%llu><P class=\"UNKNOWNCC\"> </P></SYNC>\r\n\r\n"
|
||||
,(unsigned long long)ms_end);
|
||||
write(context->out->fh, buf, strlen(buf));
|
||||
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
sub->nb_data = 0;
|
||||
freep(&sub->data);
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
||||
int write_cc_subtitle_as_sami(struct cc_subtitle *sub, struct encoder_ctx *context)
|
||||
{
|
||||
int ret = 0;
|
||||
struct cc_subtitle *osub = sub;
|
||||
struct cc_subtitle *lsub = sub;
|
||||
while(sub)
|
||||
{
|
||||
if(sub->type == CC_TEXT)
|
||||
{
|
||||
ret = write_stringz_as_sami(sub->data, context, sub->start_time, sub->end_time);
|
||||
freep(&sub->data);
|
||||
sub->nb_data = 0;
|
||||
}
|
||||
lsub = sub;
|
||||
sub = sub->next;
|
||||
}
|
||||
while(lsub != osub)
|
||||
{
|
||||
sub = lsub->prev;
|
||||
freep(&lsub);
|
||||
lsub = sub;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int write_cc_buffer_as_sami(struct eia608_screen *data, struct encoder_ctx *context)
|
||||
{
|
||||
int used;
|
||||
LLONG startms, endms;
|
||||
int wrote_something=0;
|
||||
char str[1024];
|
||||
|
||||
startms = data->start_time;
|
||||
|
||||
startms+=context->subs_delay;
|
||||
if (startms<0) // Drop screens that because of subs_delay start too early
|
||||
return 0;
|
||||
|
||||
endms = data->end_time;
|
||||
endms--; // To prevent overlapping with next line.
|
||||
sprintf (str,"<SYNC start=%llu><P class=\"UNKNOWNCC\">\r\n",
|
||||
(unsigned long long)startms);
|
||||
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 (context->out->fh, context->buffer, used);
|
||||
for (int i=0;i<15;i++)
|
||||
{
|
||||
if (data->row_used[i])
|
||||
{
|
||||
int length = get_decoder_line_encoded (context, context->subline, i, data);
|
||||
if (context->encoding != CCX_ENC_UNICODE)
|
||||
{
|
||||
dbg_print(CCX_DMT_DECODER_608, "\r");
|
||||
dbg_print(CCX_DMT_DECODER_608, "%s\n",context->subline);
|
||||
}
|
||||
write (context->out->fh, context->subline, length);
|
||||
wrote_something = 1;
|
||||
if (i!=14)
|
||||
write (context->out->fh, context->encoded_br, context->encoded_br_length);
|
||||
write (context->out->fh, context->encoded_crlf, context->encoded_crlf_length);
|
||||
}
|
||||
}
|
||||
sprintf ((char *) 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 (context->out->fh, context->buffer, used);
|
||||
sprintf ((char *) str,
|
||||
"<SYNC start=%llu><P class=\"UNKNOWNCC\"> </P></SYNC>\r\n\r\n",
|
||||
(unsigned long long)endms);
|
||||
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 (context->out->fh, context->buffer, used);
|
||||
return wrote_something;
|
||||
}
|
||||
258
src/lib_ccx/ccx_encoders_smptett.c
Normal file
258
src/lib_ccx/ccx_encoders_smptett.c
Normal file
@@ -0,0 +1,258 @@
|
||||
/*
|
||||
Produces minimally-compliant SMPTE Timed Text (W3C TTML)
|
||||
format-compatible output
|
||||
|
||||
See http://www.w3.org/TR/ttaf1-dfxp/ and
|
||||
https://www.smpte.org/sites/default/files/st2052-1-2010.pdf
|
||||
|
||||
Copyright (C) 2012 John Kemp
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License
|
||||
as published by the Free Software Foundation; either version 2
|
||||
of the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
#include "lib_ccx.h"
|
||||
#include "ccx_common_option.h"
|
||||
#include "ccx_encoders_common.h"
|
||||
#include "png.h"
|
||||
#include "spupng_encoder.h"
|
||||
#include "ocr.h"
|
||||
#include "utility.h"
|
||||
#include "ccx_encoders_helpers.h"
|
||||
|
||||
|
||||
void write_stringz_as_smptett(char *string, struct encoder_ctx *context, LLONG ms_start, LLONG ms_end)
|
||||
{
|
||||
int used;
|
||||
unsigned h1, m1, s1, ms1;
|
||||
unsigned h2, m2, s2, ms2;
|
||||
int len = strlen (string);
|
||||
unsigned char *unescaped= (unsigned char *) malloc (len+1);
|
||||
unsigned char *el = (unsigned char *) malloc (len*3+1); // Be generous
|
||||
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_sami() - not enough memory.\n");
|
||||
|
||||
mstotime (ms_start, &h1, &m1, &s1, &ms1);
|
||||
mstotime (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);
|
||||
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 (context->out->fh, context->buffer, used);
|
||||
// Scan for \n in the string and replace it with a 0
|
||||
while (pos_r < len)
|
||||
{
|
||||
if (string[pos_r] == '\\' && string[pos_r+1] == 'n')
|
||||
{
|
||||
unescaped[pos_w] = 0;
|
||||
pos_r += 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
unescaped[pos_w] = string[pos_r];
|
||||
pos_r++;
|
||||
}
|
||||
pos_w++;
|
||||
}
|
||||
unescaped[pos_w] = 0;
|
||||
// Now read the unescaped string (now several string'z and write them)
|
||||
unsigned char *begin = unescaped;
|
||||
while (begin < unescaped+len)
|
||||
{
|
||||
unsigned int u = encode_line (context, el, begin);
|
||||
if (context->encoding != CCX_ENC_UNICODE)
|
||||
{
|
||||
dbg_print(CCX_DMT_DECODER_608, "\r");
|
||||
dbg_print(CCX_DMT_DECODER_608, "%s\n", context->subline);
|
||||
}
|
||||
write(context->out->fh, el, u);
|
||||
//write (wb->fh, encoded_br, encoded_br_length);
|
||||
|
||||
write(context->out->fh, context->encoded_crlf, context->encoded_crlf_length);
|
||||
begin += strlen ((const char *) begin)+1;
|
||||
}
|
||||
|
||||
sprintf ((char *) str, "</p>\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(context->out->fh, context->buffer, used);
|
||||
sprintf ((char *) str, "<p begin=\"%02u:%02u:%02u.%03u\">\n\n", h2, m2, s2, ms2);
|
||||
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 (context->out->fh, context->buffer, used);
|
||||
sprintf ((char *) str, "</p>\n");
|
||||
free(el);
|
||||
free(unescaped);
|
||||
}
|
||||
|
||||
|
||||
int write_cc_bitmap_as_smptett(struct cc_subtitle *sub, struct encoder_ctx *context)
|
||||
{
|
||||
int ret = 0;
|
||||
#ifdef ENABLE_OCR
|
||||
struct cc_bitmap* rect;
|
||||
LLONG ms_start, ms_end;
|
||||
//char timeline[128];
|
||||
int len = 0;
|
||||
|
||||
|
||||
if (context->prev_start != -1 && (sub->flags & SUB_EOD_MARKER))
|
||||
{
|
||||
ms_start = context->prev_start + context->subs_delay;
|
||||
ms_end = sub->start_time - 1;
|
||||
}
|
||||
else if ( !(sub->flags & SUB_EOD_MARKER))
|
||||
{
|
||||
ms_start = sub->start_time + context->subs_delay;
|
||||
ms_end = sub->end_time - 1;
|
||||
}
|
||||
else if (context->prev_start == -1 && (sub->flags & SUB_EOD_MARKER))
|
||||
{
|
||||
ms_start = 1 + context->subs_delay;
|
||||
ms_end = sub->start_time - 1;
|
||||
}
|
||||
|
||||
if(sub->nb_data == 0 )
|
||||
return 0;
|
||||
rect = sub->data;
|
||||
|
||||
if ( sub->flags & SUB_EOD_MARKER )
|
||||
context->prev_start = sub->start_time;
|
||||
|
||||
if (rect[0].ocr_text && *(rect[0].ocr_text))
|
||||
{
|
||||
if (context->prev_start != -1 || !(sub->flags & SUB_EOD_MARKER))
|
||||
{
|
||||
char *buf = (char *) context->buffer;
|
||||
unsigned h1, m1, s1, ms1;
|
||||
unsigned h2, m2, s2, ms2;
|
||||
mstotime (ms_start,&h1,&m1,&s1,&ms1);
|
||||
mstotime (ms_end-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 (context->out->fh, buf,strlen(buf) );
|
||||
len = strlen(rect[0].ocr_text);
|
||||
write (context->out->fh, rect[0].ocr_text, len);
|
||||
write (context->out->fh, context->encoded_crlf, context->encoded_crlf_length);
|
||||
sprintf ( buf,"</p>\n");
|
||||
write (context->out->fh, buf,strlen(buf) );
|
||||
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
sub->nb_data = 0;
|
||||
freep(&sub->data);
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
||||
int write_cc_subtitle_as_smptett(struct cc_subtitle *sub, struct encoder_ctx *context)
|
||||
{
|
||||
int ret = 0;
|
||||
struct cc_subtitle *osub = sub;
|
||||
struct cc_subtitle *lsub = sub;
|
||||
while(sub)
|
||||
{
|
||||
if(sub->type == CC_TEXT)
|
||||
{
|
||||
write_stringz_as_smptett(sub->data, context, sub->start_time, sub->end_time);
|
||||
freep(&sub->data);
|
||||
sub->nb_data = 0;
|
||||
}
|
||||
lsub = sub;
|
||||
sub = sub->next;
|
||||
}
|
||||
while(lsub != osub)
|
||||
{
|
||||
sub = lsub->prev;
|
||||
freep(&lsub);
|
||||
lsub = sub;
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
||||
int write_cc_buffer_as_smptett(struct eia608_screen *data, struct encoder_ctx *context)
|
||||
{
|
||||
int used;
|
||||
unsigned h1,m1,s1,ms1;
|
||||
unsigned h2,m2,s2,ms2;
|
||||
LLONG endms;
|
||||
int wrote_something=0;
|
||||
LLONG startms = data->start_time;
|
||||
char str[1024];
|
||||
|
||||
startms+=context->subs_delay;
|
||||
if (startms<0) // Drop screens that because of subs_delay start too early
|
||||
return 0;
|
||||
|
||||
endms = data->end_time;
|
||||
endms--; // To prevent overlapping with next line.
|
||||
mstotime (startms,&h1,&m1,&s1,&ms1);
|
||||
mstotime (endms-1,&h2,&m2,&s2,&ms2);
|
||||
|
||||
sprintf ((char *) str,"<p begin=\"%02u:%02u:%02u.%03u\" end=\"%02u:%02u:%02u.%03u\">\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);
|
||||
}
|
||||
used = encode_line(context, context->buffer,(unsigned char *) str);
|
||||
write (context->out->fh, context->buffer, used);
|
||||
for (int i=0; i < 15; i++)
|
||||
{
|
||||
if (data->row_used[i])
|
||||
{
|
||||
int length = get_decoder_line_encoded (context, context->subline, i, data);
|
||||
if (context->encoding!=CCX_ENC_UNICODE)
|
||||
{
|
||||
dbg_print(CCX_DMT_DECODER_608, "\r");
|
||||
dbg_print(CCX_DMT_DECODER_608, "%s\n",context->subline);
|
||||
}
|
||||
write(context->out->fh, context->subline, length);
|
||||
wrote_something=1;
|
||||
|
||||
write(context->out->fh, context->encoded_crlf, context->encoded_crlf_length);
|
||||
}
|
||||
}
|
||||
sprintf ((char *) str,"</p>\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 (context->out->fh, context->buffer, used);
|
||||
|
||||
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 (wb->fh, enc_buffer,enc_buffer_used);
|
||||
|
||||
return wrote_something;
|
||||
}
|
||||
414
src/lib_ccx/ccx_encoders_spupng.c
Normal file
414
src/lib_ccx/ccx_encoders_spupng.c
Normal file
@@ -0,0 +1,414 @@
|
||||
#include <assert.h>
|
||||
#include <sys/stat.h>
|
||||
#include "ccx_encoders_spupng.h"
|
||||
#include "ccx_encoders_helpers.h"
|
||||
|
||||
void draw_str(char *str, uint8_t * canvas, int rowstride)
|
||||
{
|
||||
char *ptr;
|
||||
uint8_t* cell;
|
||||
uint8_t pen[2];
|
||||
int i = 0;
|
||||
pen[0] = COL_BLACK;
|
||||
pen[1] = COL_WHITE;
|
||||
for(ptr = str; ptr != '\0';ptr++)
|
||||
{
|
||||
cell = canvas + ((i+1) * CCW);
|
||||
draw_char_indexed(cell, rowstride, pen, 0, 0, 0);
|
||||
i++;
|
||||
}
|
||||
|
||||
}
|
||||
void draw_row(struct eia608_screen* data, int row, uint8_t * canvas, int rowstride)
|
||||
{
|
||||
int column;
|
||||
int unicode = 0;
|
||||
uint8_t pen[2];
|
||||
uint8_t* cell;
|
||||
int first = -1;
|
||||
int last = 0;
|
||||
|
||||
pen[0] = COL_BLACK;
|
||||
|
||||
for (column = 0; column < COLUMNS ; column++) {
|
||||
|
||||
if (COL_TRANSPARENT != data->colors[row][column])
|
||||
{
|
||||
cell = canvas + ((column+1) * CCW);
|
||||
get_char_in_unicode((unsigned char*)&unicode, data->characters[row][column]);
|
||||
pen[1] = data->colors[row][column];
|
||||
|
||||
int attr = data->fonts[row][column];
|
||||
draw_char_indexed(cell, rowstride, pen, unicode, (attr & FONT_ITALICS) != 0, (attr & FONT_UNDERLINED) != 0);
|
||||
if (first < 0)
|
||||
{
|
||||
// draw a printable space before the first non-space char
|
||||
first = column;
|
||||
if (unicode != 0x20)
|
||||
{
|
||||
cell = canvas + ((first) * CCW);
|
||||
draw_char_indexed(cell, rowstride, pen, 0x20, 0, 0);
|
||||
}
|
||||
}
|
||||
last = column;
|
||||
}
|
||||
}
|
||||
// draw a printable space after the last non-space char
|
||||
// unicode should still contain the last character
|
||||
// check whether it is a space
|
||||
if (unicode != 0x20)
|
||||
{
|
||||
cell = canvas + ((last+2) * CCW);
|
||||
draw_char_indexed(cell, rowstride, pen, 0x20, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static png_color palette[10] =
|
||||
{
|
||||
{ 0xff, 0xff, 0xff }, // COL_WHITE = 0,
|
||||
{ 0x00, 0xff, 0x00 }, // COL_GREEN = 1,
|
||||
{ 0x00, 0x00, 0xff }, // COL_BLUE = 2,
|
||||
{ 0x00, 0xff, 0xff }, // COL_CYAN = 3,
|
||||
{ 0xff, 0x00, 0x00 }, // COL_RED = 4,
|
||||
{ 0xff, 0xff, 0x00 }, // COL_YELLOW = 5,
|
||||
{ 0xff, 0x00, 0xff }, // COL_MAGENTA = 6,
|
||||
{ 0xff, 0xff, 0xff }, // COL_USERDEFINED = 7,
|
||||
{ 0x00, 0x00, 0x00 }, // COL_BLACK = 8
|
||||
{ 0x00, 0x00, 0x00 } // COL_TRANSPARENT = 9
|
||||
};
|
||||
|
||||
static png_byte alpha[10] =
|
||||
{
|
||||
255,
|
||||
255,
|
||||
255,
|
||||
255,
|
||||
255,
|
||||
255,
|
||||
255,
|
||||
255,
|
||||
255,
|
||||
0
|
||||
};
|
||||
|
||||
int spupng_write_png(struct spupng_t *sp, struct eia608_screen* data,
|
||||
png_structp png_ptr, png_infop info_ptr,
|
||||
png_bytep image,
|
||||
png_bytep* row_pointer,
|
||||
unsigned int ww, unsigned int wh)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
if (setjmp(png_jmpbuf(png_ptr)))
|
||||
return 0;
|
||||
|
||||
png_init_io (png_ptr, sp->fppng);
|
||||
|
||||
png_set_IHDR (png_ptr,
|
||||
info_ptr,
|
||||
ww,
|
||||
wh,
|
||||
/* bit_depth */ 8,
|
||||
PNG_COLOR_TYPE_PALETTE,
|
||||
PNG_INTERLACE_NONE,
|
||||
PNG_COMPRESSION_TYPE_DEFAULT,
|
||||
PNG_FILTER_TYPE_DEFAULT);
|
||||
|
||||
png_set_PLTE (png_ptr, info_ptr, palette, sizeof(palette) / sizeof(palette[0]));
|
||||
png_set_tRNS (png_ptr, info_ptr, alpha, sizeof(alpha) / sizeof(alpha[0]), NULL);
|
||||
|
||||
png_set_gAMA (png_ptr, info_ptr, 1.0 / 2.2);
|
||||
|
||||
png_write_info (png_ptr, info_ptr);
|
||||
|
||||
for (i = 0; i < wh; i++)
|
||||
row_pointer[i] = image + i * ww;
|
||||
|
||||
png_write_image (png_ptr, row_pointer);
|
||||
|
||||
png_write_end (png_ptr, info_ptr);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int spupng_export_png(struct spupng_t *sp, struct eia608_screen* data)
|
||||
{
|
||||
png_structp png_ptr;
|
||||
png_infop info_ptr;
|
||||
png_bytep *row_pointer;
|
||||
png_bytep image;
|
||||
int ww, wh, rowstride, row_adv;
|
||||
int row;
|
||||
|
||||
assert ((sizeof(png_byte) == sizeof(uint8_t))
|
||||
&& (sizeof(*image) == sizeof(uint8_t)));
|
||||
|
||||
// Allow space at beginning and end of each row for a padding space
|
||||
ww = CCW * (COLUMNS+2);
|
||||
wh = CCH * ROWS;
|
||||
row_adv = (COLUMNS+2) * CCW * CCH;
|
||||
|
||||
rowstride = ww * sizeof(*image);
|
||||
|
||||
if (!(row_pointer = (png_bytep*)malloc(sizeof(*row_pointer) * wh))) {
|
||||
mprint("Unable to allocate %d byte buffer.\n",
|
||||
sizeof(*row_pointer) * wh);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!(image = (png_bytep)malloc(wh * ww * sizeof(*image)))) {
|
||||
mprint("Unable to allocate %d KB image buffer.",
|
||||
wh * ww * sizeof(*image) / 1024);
|
||||
free(row_pointer);
|
||||
return 0;
|
||||
}
|
||||
// Initialize image to transparent
|
||||
memset(image, COL_TRANSPARENT, wh * ww * sizeof(*image));
|
||||
|
||||
/* draw the image */
|
||||
|
||||
for (row = 0; row < ROWS; row++) {
|
||||
if (data->row_used[row])
|
||||
draw_row(data, row, image + row * row_adv, rowstride);
|
||||
}
|
||||
|
||||
/* Now save the image */
|
||||
|
||||
if (!(png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING,
|
||||
NULL, NULL, NULL)))
|
||||
goto unknown_error;
|
||||
|
||||
if (!(info_ptr = png_create_info_struct(png_ptr))) {
|
||||
png_destroy_write_struct(&png_ptr, (png_infopp) NULL);
|
||||
goto unknown_error;
|
||||
}
|
||||
|
||||
if (!spupng_write_png (sp, data, png_ptr, info_ptr, image, row_pointer, ww, wh)) {
|
||||
png_destroy_write_struct (&png_ptr, &info_ptr);
|
||||
goto write_error;
|
||||
}
|
||||
|
||||
png_destroy_write_struct (&png_ptr, &info_ptr);
|
||||
|
||||
free (row_pointer);
|
||||
|
||||
free (image);
|
||||
|
||||
return 1;
|
||||
|
||||
write_error:
|
||||
|
||||
unknown_error:
|
||||
free (row_pointer);
|
||||
|
||||
free (image);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int spupng_export_string2png(struct spupng_t *sp, char *str)
|
||||
{
|
||||
png_structp png_ptr;
|
||||
png_infop info_ptr;
|
||||
png_bytep *row_pointer;
|
||||
png_bytep image;
|
||||
int ww, wh, rowstride, row_adv;
|
||||
int row = 0;
|
||||
|
||||
assert ((sizeof(png_byte) == sizeof(uint8_t))
|
||||
&& (sizeof(*image) == sizeof(uint8_t)));
|
||||
|
||||
// Allow space at beginning and end of each row for a padding space
|
||||
ww = CCW * (COLUMNS+2);
|
||||
wh = CCH * ROWS;
|
||||
row_adv = (COLUMNS+2) * CCW * CCH;
|
||||
|
||||
rowstride = ww * sizeof(*image);
|
||||
|
||||
if (!(row_pointer = (png_bytep*)malloc(sizeof(*row_pointer) * wh))) {
|
||||
mprint("Unable to allocate %d byte buffer.\n",
|
||||
sizeof(*row_pointer) * wh);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!(image = (png_bytep)malloc(wh * ww * sizeof(*image)))) {
|
||||
mprint("Unable to allocate %d KB image buffer.",
|
||||
wh * ww * sizeof(*image) / 1024);
|
||||
free(row_pointer);
|
||||
return 0;
|
||||
}
|
||||
// Initialize image to transparent
|
||||
memset(image, COL_TRANSPARENT, wh * ww * sizeof(*image));
|
||||
|
||||
/* draw the image */
|
||||
draw_str(str, image + row * row_adv, rowstride);
|
||||
|
||||
/* Now save the image */
|
||||
|
||||
if (!(png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING,
|
||||
NULL, NULL, NULL)))
|
||||
goto unknown_error;
|
||||
|
||||
if (!(info_ptr = png_create_info_struct(png_ptr))) {
|
||||
png_destroy_write_struct(&png_ptr, (png_infopp) NULL);
|
||||
goto unknown_error;
|
||||
}
|
||||
#if 0
|
||||
if (!spupng_write_png (sp, data, png_ptr, info_ptr, image, row_pointer, ww, wh)) {
|
||||
png_destroy_write_struct (&png_ptr, &info_ptr);
|
||||
goto write_error;
|
||||
}
|
||||
#endif
|
||||
png_destroy_write_struct (&png_ptr, &info_ptr);
|
||||
|
||||
free (row_pointer);
|
||||
|
||||
free (image);
|
||||
|
||||
return 1;
|
||||
|
||||
|
||||
unknown_error:
|
||||
free (row_pointer);
|
||||
|
||||
free (image);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int spupng_write_ccbuffer(struct spupng_t *sp, struct eia608_screen* data,
|
||||
struct encoder_ctx *context)
|
||||
{
|
||||
|
||||
int row;
|
||||
int empty_buf = 1;
|
||||
char str[256] = "";
|
||||
int str_len = 0;
|
||||
LLONG ms_start = data->start_time + context->subs_delay;
|
||||
if (ms_start < 0)
|
||||
{
|
||||
dbg_print(CCX_DMT_VERBOSE, "Negative start\n");
|
||||
return 0;
|
||||
}
|
||||
for (row = 0; row < 15; row++)
|
||||
{
|
||||
if (data->row_used[row])
|
||||
{
|
||||
empty_buf = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (empty_buf)
|
||||
{
|
||||
dbg_print(CCX_DMT_VERBOSE, "Blank page\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
LLONG ms_end = data->end_time;
|
||||
|
||||
sprintf(sp->pngfile, "%s/sub%04d.png", sp->dirname, sp->fileIndex++);
|
||||
if ((sp->fppng = fopen(sp->pngfile, "wb")) == NULL)
|
||||
{
|
||||
fatal(CCX_COMMON_EXIT_FILE_CREATION_FAILED, "Cannot open %s: %s\n",
|
||||
sp->pngfile, strerror(errno));
|
||||
}
|
||||
if (!spupng_export_png(sp, data))
|
||||
{
|
||||
fatal(CCX_COMMON_EXIT_FILE_CREATION_FAILED, "Cannot write %s: %s\n",
|
||||
sp->pngfile, strerror(errno));
|
||||
}
|
||||
fclose(sp->fppng);
|
||||
write_sputag(sp,ms_start,ms_end);
|
||||
for (row = 0; row < ROWS; row++)
|
||||
{
|
||||
if (data->row_used[row])
|
||||
{
|
||||
int len = get_decoder_line_encoded(context, context->subline, row, data);
|
||||
// Check for characters that spumux won't parse
|
||||
// null chars will be changed to space
|
||||
// pairs of dashes will be changed to underscores
|
||||
for (unsigned char* ptr = context->subline; ptr < context->subline+len; ptr++)
|
||||
{
|
||||
switch (*ptr)
|
||||
{
|
||||
case 0:
|
||||
*ptr = ' ';
|
||||
break;
|
||||
case '-':
|
||||
if (*(ptr+1) == '-')
|
||||
{
|
||||
*ptr++ = '_';
|
||||
*ptr = '_';
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (str_len + len + 3 > 256 )
|
||||
{
|
||||
mprint("WARNING: Possible Loss of data\n");
|
||||
break;
|
||||
}
|
||||
strncat(str, (const char*)context->subline, len);
|
||||
strncat(str,"\n",3);
|
||||
str_len = str_len + len + 2;
|
||||
}
|
||||
}
|
||||
|
||||
write_spucomment(sp,str);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int spupng_write_string(struct spupng_t *sp, char *string, LLONG start_time, LLONG end_time,
|
||||
struct encoder_ctx *context)
|
||||
{
|
||||
|
||||
char str[256] = "";
|
||||
LLONG ms_start = start_time + context->subs_delay;
|
||||
if (ms_start < 0)
|
||||
{
|
||||
dbg_print(CCX_DMT_VERBOSE, "Negative start\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
LLONG ms_end = end_time;
|
||||
|
||||
sprintf(sp->pngfile, "%s/sub%04d.png", sp->dirname, sp->fileIndex++);
|
||||
if ((sp->fppng = fopen(sp->pngfile, "wb")) == NULL)
|
||||
{
|
||||
fatal(CCX_COMMON_EXIT_FILE_CREATION_FAILED, "Cannot open %s: %s\n",
|
||||
sp->pngfile, strerror(errno));
|
||||
}
|
||||
if (!spupng_export_string2png(sp, str))
|
||||
{
|
||||
fatal(CCX_COMMON_EXIT_FILE_CREATION_FAILED, "Cannot write %s: %s\n",
|
||||
sp->pngfile, strerror(errno));
|
||||
}
|
||||
fclose(sp->fppng);
|
||||
write_sputag(sp,ms_start,ms_end);
|
||||
write_spucomment(sp,str);
|
||||
return 1;
|
||||
}
|
||||
int write_cc_subtitle_as_spupng(struct cc_subtitle *sub, struct encoder_ctx *context)
|
||||
{
|
||||
struct spupng_t *sp = (struct spupng_t *) context->out->spupng_data;
|
||||
if (!sp)
|
||||
return -1;
|
||||
|
||||
if(sub->type == CC_TEXT)
|
||||
{
|
||||
spupng_write_string(sp, sub->data, sub->start_time, sub->end_time, context);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int write_cc_buffer_as_spupng(struct eia608_screen *data,struct encoder_ctx *context)
|
||||
{
|
||||
struct spupng_t *sp = (struct spupng_t *) context->out->spupng_data;
|
||||
if (NULL != sp)
|
||||
{
|
||||
return spupng_write_ccbuffer(sp, data, context);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
#ifndef __608_SPUPNG_H__
|
||||
#define __608_SPUPNG_H__
|
||||
|
||||
#include "lib_ccx.h"
|
||||
#include "spupng_encoder.h"
|
||||
@@ -2,25 +2,30 @@
|
||||
#include "ccx_common_option.h"
|
||||
#include "ccx_encoders_common.h"
|
||||
#include "utility.h"
|
||||
#include "ccx_encoders_helpers.h"
|
||||
#include "ocr.h"
|
||||
|
||||
/* The timing here is not PTS based, but output based, i.e. user delay must be accounted for
|
||||
if there is any */
|
||||
void write_stringz_as_srt(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 used;
|
||||
unsigned h1,m1,s1,ms1;
|
||||
unsigned h2,m2,s2,ms2;
|
||||
char timeline[128];
|
||||
|
||||
if(!string || !string[0])
|
||||
return 0;
|
||||
|
||||
mstotime (ms_start,&h1,&m1,&s1,&ms1);
|
||||
mstotime (ms_end-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, encoded_crlf);
|
||||
used = encode_line(context->buffer,(unsigned char *) timeline);
|
||||
sprintf(timeline, "%u%s", context->srt_counter, context->encoded_crlf);
|
||||
used = encode_line(context, context->buffer,(unsigned char *) timeline);
|
||||
write(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, encoded_crlf);
|
||||
used = encode_line(context->buffer,(unsigned char *) timeline);
|
||||
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);
|
||||
|
||||
@@ -52,36 +57,39 @@ void write_stringz_as_srt(char *string, struct encoder_ctx *context, LLONG ms_st
|
||||
unsigned char *begin=unescaped;
|
||||
while (begin<unescaped+len)
|
||||
{
|
||||
unsigned int u = encode_line (el, begin);
|
||||
if (ccx_options.encoding!=CCX_ENC_UNICODE)
|
||||
unsigned int u = encode_line (context, el, begin);
|
||||
if (context->encoding != CCX_ENC_UNICODE)
|
||||
{
|
||||
dbg_print(CCX_DMT_DECODER_608, "\r");
|
||||
dbg_print(CCX_DMT_DECODER_608, "%s\n",subline);
|
||||
dbg_print(CCX_DMT_DECODER_608, "%s\n",context->subline);
|
||||
}
|
||||
write(context->out->fh, el, u);
|
||||
write(context->out->fh, encoded_crlf, encoded_crlf_length);
|
||||
write(context->out->fh, context->encoded_crlf, context->encoded_crlf_length);
|
||||
begin+= strlen ((const char *) begin)+1;
|
||||
}
|
||||
|
||||
dbg_print(CCX_DMT_DECODER_608, "- - - - - - - - - - - -\r\n");
|
||||
|
||||
write(context->out->fh, encoded_crlf, encoded_crlf_length);
|
||||
write(context->out->fh, context->encoded_crlf, context->encoded_crlf_length);
|
||||
free(el);
|
||||
free(unescaped);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int write_cc_bitmap_as_srt(struct cc_subtitle *sub, struct encoder_ctx *context)
|
||||
{
|
||||
int ret = 0;
|
||||
#ifdef ENABLE_OCR
|
||||
struct cc_bitmap* rect;
|
||||
LLONG ms_start, ms_end;
|
||||
#ifdef ENABLE_OCR
|
||||
unsigned h1,m1,s1,ms1;
|
||||
unsigned h2,m2,s2,ms2;
|
||||
char timeline[128];
|
||||
int len = 0;
|
||||
int used;
|
||||
#endif
|
||||
int i = 0;
|
||||
char *str;
|
||||
|
||||
if (context->prev_start != -1 && (sub->flags & SUB_EOD_MARKER))
|
||||
{
|
||||
@@ -105,9 +113,8 @@ int write_cc_bitmap_as_srt(struct cc_subtitle *sub, struct encoder_ctx *context)
|
||||
if(sub->flags & SUB_EOD_MARKER)
|
||||
context->prev_start = sub->start_time;
|
||||
|
||||
rect = sub->data;
|
||||
#ifdef ENABLE_OCR
|
||||
if (rect[0].ocr_text && *(rect[0].ocr_text))
|
||||
str = paraof_ocrtext(sub);
|
||||
if (str)
|
||||
{
|
||||
if (context->prev_start != -1 || !(sub->flags & SUB_EOD_MARKER))
|
||||
{
|
||||
@@ -115,16 +122,22 @@ int write_cc_bitmap_as_srt(struct cc_subtitle *sub, struct encoder_ctx *context)
|
||||
mstotime (ms_end-1,&h2,&m2,&s2,&ms2); // -1 To prevent overlapping with next line.
|
||||
context->srt_counter++;
|
||||
sprintf(timeline, "%u\r\n", context->srt_counter);
|
||||
used = encode_line(context->buffer,(unsigned char *) timeline);
|
||||
used = encode_line(context, context->buffer,(unsigned char *) timeline);
|
||||
write(context->out->fh, context->buffer, used);
|
||||
sprintf (timeline, "%02u:%02u:%02u,%03u --> %02u:%02u:%02u,%03u\r\n",
|
||||
h1,m1,s1,ms1, h2,m2,s2,ms2);
|
||||
used = encode_line(context->buffer,(unsigned char *) timeline);
|
||||
used = encode_line(context, context->buffer,(unsigned char *) timeline);
|
||||
write (context->out->fh, context->buffer, used);
|
||||
len = strlen(rect[0].ocr_text);
|
||||
write (context->out->fh, rect[0].ocr_text, len);
|
||||
write (context->out->fh, encoded_crlf, encoded_crlf_length);
|
||||
len = strlen(str);
|
||||
write (context->out->fh, str, len);
|
||||
write (context->out->fh, context->encoded_crlf, context->encoded_crlf_length);
|
||||
}
|
||||
freep(&str);
|
||||
}
|
||||
for(i = 0, rect = sub->data; i < sub->nb_data; i++, rect++)
|
||||
{
|
||||
freep(rect->data);
|
||||
freep(rect->data+1);
|
||||
}
|
||||
#endif
|
||||
sub->nb_data = 0;
|
||||
@@ -132,6 +145,33 @@ int write_cc_bitmap_as_srt(struct cc_subtitle *sub, struct encoder_ctx *context)
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
||||
int write_cc_subtitle_as_srt(struct cc_subtitle *sub,struct encoder_ctx *context)
|
||||
{
|
||||
int ret = 0;
|
||||
struct cc_subtitle *osub = sub;
|
||||
struct cc_subtitle *lsub = sub;
|
||||
|
||||
while(sub)
|
||||
{
|
||||
if(sub->type == CC_TEXT)
|
||||
{
|
||||
ret = write_stringz_as_srt(sub->data, context, sub->start_time, sub->end_time);
|
||||
freep(&sub->data);
|
||||
sub->nb_data = 0;
|
||||
}
|
||||
lsub = sub;
|
||||
sub = sub->next;
|
||||
}
|
||||
while(lsub != osub)
|
||||
{
|
||||
sub = lsub->prev;
|
||||
freep(&lsub);
|
||||
lsub = sub;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
int write_cc_buffer_as_srt(struct eia608_screen *data, struct encoder_ctx *context)
|
||||
{
|
||||
int used;
|
||||
@@ -165,12 +205,12 @@ int write_cc_buffer_as_srt(struct eia608_screen *data, struct encoder_ctx *conte
|
||||
mstotime (ms_end-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, encoded_crlf);
|
||||
used = encode_line(context->buffer,(unsigned char *) timeline);
|
||||
sprintf(timeline, "%u%s", context->srt_counter, context->encoded_crlf);
|
||||
used = encode_line(context, context->buffer,(unsigned char *) timeline);
|
||||
write(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, encoded_crlf);
|
||||
used = encode_line(context->buffer,(unsigned char *) timeline);
|
||||
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 ( %d) - - -\n", context->srt_counter);
|
||||
dbg_print(CCX_DMT_DECODER_608, "%s",timeline);
|
||||
@@ -180,17 +220,17 @@ int write_cc_buffer_as_srt(struct eia608_screen *data, struct encoder_ctx *conte
|
||||
{
|
||||
if (data->row_used[i])
|
||||
{
|
||||
if (ccx_options.sentence_cap)
|
||||
if (context->sentence_cap)
|
||||
{
|
||||
capitalize (i,data);
|
||||
capitalize (context, i, data);
|
||||
correct_case(i,data);
|
||||
}
|
||||
if (ccx_options.autodash && ccx_options.trim_subs)
|
||||
if (context->autodash && context->trim_subs)
|
||||
{
|
||||
int first=0, last=31, center1=-1, center2=-1;
|
||||
unsigned char *line = data->characters[i];
|
||||
int do_dash=1, colon_pos=-1;
|
||||
find_limit_characters(line,&first,&last);
|
||||
find_limit_characters(line, &first, &last, CCX_DECODER_608_SCREEN_WIDTH);
|
||||
if (first==-1 || last==-1) // Probably a bug somewhere though
|
||||
break;
|
||||
// Is there a speaker named, for example: TOM: What are you doing?
|
||||
@@ -242,21 +282,21 @@ int write_cc_buffer_as_srt(struct eia608_screen *data, struct encoder_ctx *conte
|
||||
prev_line_center2=center2;
|
||||
|
||||
}
|
||||
int length = get_decoder_line_encoded (subline, i, data);
|
||||
if (ccx_options.encoding!=CCX_ENC_UNICODE)
|
||||
int length = get_decoder_line_encoded (context, context->subline, i, data);
|
||||
if (context->encoding!=CCX_ENC_UNICODE)
|
||||
{
|
||||
dbg_print(CCX_DMT_DECODER_608, "\r");
|
||||
dbg_print(CCX_DMT_DECODER_608, "%s\n",subline);
|
||||
dbg_print(CCX_DMT_DECODER_608, "%s\n",context->subline);
|
||||
}
|
||||
write(context->out->fh, subline, length);
|
||||
write(context->out->fh, encoded_crlf, encoded_crlf_length);
|
||||
write(context->out->fh, context->subline, length);
|
||||
write(context->out->fh, context->encoded_crlf, context->encoded_crlf_length);
|
||||
wrote_something=1;
|
||||
// fprintf (wb->fh,encoded_crlf);
|
||||
// fprintf (wb->fh,context->encoded_crlf);
|
||||
}
|
||||
}
|
||||
dbg_print(CCX_DMT_DECODER_608, "- - - - - - - - - - - -\r\n");
|
||||
|
||||
// fprintf (wb->fh, encoded_crlf);
|
||||
write (context->out->fh, encoded_crlf, encoded_crlf_length);
|
||||
// fprintf (wb->fh, context->encoded_crlf);
|
||||
write (context->out->fh, context->encoded_crlf, context->encoded_crlf_length);
|
||||
return wrote_something;
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
#ifndef CCX_ENCODERS_STRUCTS_H
|
||||
#define CCX_ENCODERS_STRUCTS_H
|
||||
|
||||
typedef struct ccx_encoders_transcript_format {
|
||||
// TODO: add more options, and (perhaps) reduce other ccextractor options?
|
||||
@@ -14,12 +15,9 @@ typedef struct ccx_encoders_transcript_format {
|
||||
|
||||
struct ccx_s_write
|
||||
{
|
||||
int multiple_files;
|
||||
char *first_input_file;
|
||||
int fh;
|
||||
char *filename;
|
||||
void* spupng_data;
|
||||
};
|
||||
|
||||
#define CCX_ENCODERS_STRUCTS_H
|
||||
#endif
|
||||
|
||||
272
src/lib_ccx/ccx_encoders_webvtt.c
Normal file
272
src/lib_ccx/ccx_encoders_webvtt.c
Normal file
@@ -0,0 +1,272 @@
|
||||
#include "lib_ccx.h"
|
||||
#include "ccx_common_option.h"
|
||||
#include "ccx_encoders_common.h"
|
||||
#include "ccx_encoders_helpers.h"
|
||||
#include "utility.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_webvtt(char *string, struct encoder_ctx *context, LLONG ms_start, LLONG ms_end)
|
||||
{
|
||||
int used;
|
||||
unsigned h1, m1, s1, ms1;
|
||||
unsigned h2, m2, s2, ms2;
|
||||
int written;
|
||||
char timeline[128];
|
||||
|
||||
mstotime(ms_start, &h1, &m1, &s1, &ms1);
|
||||
mstotime(ms_end - 1, &h2, &m2, &s2, &ms2); // -1 To prevent overlapping with next line.
|
||||
|
||||
|
||||
used = encode_line(context, context->buffer, (unsigned char *)timeline);
|
||||
written = write(context->out->fh, context->buffer, used);
|
||||
if (written != used)
|
||||
return -1;
|
||||
sprintf(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- - - WEBVTT caption - - -\n");
|
||||
dbg_print(CCX_DMT_DECODER_608, "%s", timeline);
|
||||
|
||||
written = write(context->out->fh, context->buffer, used);
|
||||
if (written != used)
|
||||
return -1;
|
||||
int len = strlen(string);
|
||||
unsigned char *unescaped = (unsigned char *)malloc(len + 1);
|
||||
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_webvtt() - not enough memory.\n");
|
||||
int pos_r = 0;
|
||||
int pos_w = 0;
|
||||
// Scan for \n in the string and replace it with a 0
|
||||
while (pos_r<len)
|
||||
{
|
||||
if (string[pos_r] == '\\' && string[pos_r + 1] == 'n')
|
||||
{
|
||||
unescaped[pos_w] = 0;
|
||||
pos_r += 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
unescaped[pos_w] = string[pos_r];
|
||||
pos_r++;
|
||||
}
|
||||
pos_w++;
|
||||
}
|
||||
unescaped[pos_w] = 0;
|
||||
// Now read the unescaped string (now several string'z and write them)
|
||||
unsigned char *begin = unescaped;
|
||||
while (begin<unescaped + len)
|
||||
{
|
||||
unsigned int u = encode_line(context, el, begin);
|
||||
if (context->encoding != CCX_ENC_UNICODE)
|
||||
{
|
||||
dbg_print(CCX_DMT_DECODER_608, "\r");
|
||||
dbg_print(CCX_DMT_DECODER_608, "%s\n", context->subline);
|
||||
}
|
||||
written = write(context->out->fh, el, u);
|
||||
if (written != u)
|
||||
{
|
||||
free(el);
|
||||
free(unescaped);
|
||||
return -1;
|
||||
}
|
||||
written = write(context->out->fh, context->encoded_crlf, context->encoded_crlf_length);
|
||||
if (written != context->encoded_crlf_length)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
begin += strlen((const char *)begin) + 1;
|
||||
}
|
||||
|
||||
dbg_print(CCX_DMT_DECODER_608, "- - - - - - - - - - - -\r\n");
|
||||
|
||||
written = write(context->out->fh, context->encoded_crlf, context->encoded_crlf_length);
|
||||
free(el);
|
||||
free(unescaped);
|
||||
if (written != context->encoded_crlf_length)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int write_cc_bitmap_as_webvtt(struct cc_subtitle *sub, struct encoder_ctx *context)
|
||||
{
|
||||
//TODO
|
||||
int ret = 0;
|
||||
sub->nb_data = 0;
|
||||
freep(&sub->data);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int write_cc_subtitle_as_webvtt(struct cc_subtitle *sub, struct encoder_ctx *context)
|
||||
{
|
||||
int ret = 0;
|
||||
struct cc_subtitle *osub = sub;
|
||||
struct cc_subtitle *lsub = sub;
|
||||
|
||||
while (sub)
|
||||
{
|
||||
if (sub->type == CC_TEXT)
|
||||
{
|
||||
ret = write_stringz_as_webvtt(sub->data, context, sub->start_time, sub->end_time);
|
||||
freep(&sub->data);
|
||||
sub->nb_data = 0;
|
||||
}
|
||||
lsub = sub;
|
||||
sub = sub->next;
|
||||
}
|
||||
while (lsub != osub)
|
||||
{
|
||||
sub = lsub->prev;
|
||||
freep(&lsub);
|
||||
lsub = sub;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
int write_cc_buffer_as_webvtt(struct eia608_screen *data, struct encoder_ctx *context)
|
||||
{
|
||||
int used;
|
||||
int written;
|
||||
unsigned h1, m1, s1, ms1;
|
||||
unsigned h2, m2, s2, ms2;
|
||||
LLONG ms_start, ms_end;
|
||||
int wrote_something = 0;
|
||||
ms_start = data->start_time;
|
||||
|
||||
int prev_line_start = -1, prev_line_end = -1; // Column in which the previous line started and ended, for autodash
|
||||
int prev_line_center1 = -1, prev_line_center2 = -1; // Center column of previous line text
|
||||
int empty_buf = 1;
|
||||
char timeline[128] = "";
|
||||
for (int i = 0; i<15; i++)
|
||||
{
|
||||
if (data->row_used[i])
|
||||
{
|
||||
empty_buf = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (empty_buf) // Prevent writing empty screens. Not needed in .vtt
|
||||
return 0;
|
||||
|
||||
ms_start += context->subs_delay;
|
||||
if (ms_start<0) // Drop screens that because of subs_delay start too early
|
||||
return 0;
|
||||
|
||||
ms_end = data->end_time;
|
||||
|
||||
mstotime(ms_start, &h1, &m1, &s1, &ms1);
|
||||
mstotime(ms_end - 1, &h2, &m2, &s2, &ms2); // -1 To prevent overlapping with next line.
|
||||
|
||||
sprintf(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- - - WEBVTT caption - - -\n");
|
||||
dbg_print(CCX_DMT_DECODER_608, "%s", timeline);
|
||||
|
||||
written = write(context->out->fh, context->buffer, used);
|
||||
if (written != used)
|
||||
return -1;
|
||||
|
||||
for (int i = 0; i<15; i++)
|
||||
{
|
||||
if (data->row_used[i])
|
||||
{
|
||||
if (context->sentence_cap)
|
||||
{
|
||||
capitalize(context, i, data);
|
||||
correct_case(i, data);
|
||||
}
|
||||
if (context->autodash && context->trim_subs)
|
||||
{
|
||||
int first = 0, last = 31, center1 = -1, center2 = -1;
|
||||
unsigned char *line = data->characters[i];
|
||||
int do_dash = 1, colon_pos = -1;
|
||||
find_limit_characters(line, &first, &last, CCX_DECODER_608_SCREEN_WIDTH);
|
||||
if (first == -1 || last == -1) // Probably a bug somewhere though
|
||||
break;
|
||||
// Is there a speaker named, for example: TOM: What are you doing?
|
||||
for (int j = first; j <= last; j++)
|
||||
{
|
||||
if (line[j] == ':')
|
||||
{
|
||||
colon_pos = j;
|
||||
break;
|
||||
}
|
||||
if (!isupper(line[j]))
|
||||
break;
|
||||
}
|
||||
if (prev_line_start == -1)
|
||||
do_dash = 0;
|
||||
if (first == prev_line_start) // Case of left alignment
|
||||
do_dash = 0;
|
||||
if (last == prev_line_end) // Right align
|
||||
do_dash = 0;
|
||||
if (first>prev_line_start && last<prev_line_end) // Fully contained
|
||||
do_dash = 0;
|
||||
if ((first>prev_line_start && first<prev_line_end) || // Overlap
|
||||
(last>prev_line_start && last<prev_line_end))
|
||||
do_dash = 0;
|
||||
|
||||
center1 = (first + last) / 2;
|
||||
if (colon_pos != -1)
|
||||
{
|
||||
while (colon_pos<CCX_DECODER_608_SCREEN_WIDTH &&
|
||||
(line[colon_pos] == ':' ||
|
||||
line[colon_pos] == ' ' ||
|
||||
line[colon_pos] == 0x89))
|
||||
colon_pos++; // Find actual text
|
||||
center2 = (colon_pos + last) / 2;
|
||||
}
|
||||
else
|
||||
center2 = center1;
|
||||
|
||||
if (center1 >= prev_line_center1 - 1 && center1 <= prev_line_center1 + 1 && center1 != -1) // Center align
|
||||
do_dash = 0;
|
||||
if (center2 >= prev_line_center2 - 2 && center1 <= prev_line_center2 + 2 && center1 != -1) // Center align
|
||||
do_dash = 0;
|
||||
|
||||
if (do_dash)
|
||||
{
|
||||
written = write(context->out->fh, "- ", 2);
|
||||
if (written != 2)
|
||||
return -1;
|
||||
}
|
||||
prev_line_start = first;
|
||||
prev_line_end = last;
|
||||
prev_line_center1 = center1;
|
||||
prev_line_center2 = center2;
|
||||
|
||||
}
|
||||
int length = get_decoder_line_encoded(context, context->subline, i, data);
|
||||
if (context->encoding != CCX_ENC_UNICODE)
|
||||
{
|
||||
dbg_print(CCX_DMT_DECODER_608, "\r");
|
||||
dbg_print(CCX_DMT_DECODER_608, "%s\n", context->subline);
|
||||
}
|
||||
written = write(context->out->fh, context->subline, length);
|
||||
if (written != length)
|
||||
return -1;
|
||||
written = write(context->out->fh,
|
||||
context->encoded_crlf, context->encoded_crlf_length);
|
||||
if (written != context->encoded_crlf_length)
|
||||
return -1;
|
||||
wrote_something = 1;
|
||||
// fprintf (wb->fh,encoded_crlf);
|
||||
}
|
||||
}
|
||||
dbg_print(CCX_DMT_DECODER_608, "- - - - - - - - - - - -\r\n");
|
||||
|
||||
// fprintf (wb->fh, encoded_crlf);
|
||||
written = write(context->out->fh, context->encoded_crlf, context->encoded_crlf_length);
|
||||
if (written != context->encoded_crlf_length)
|
||||
return -1;
|
||||
|
||||
return wrote_something;
|
||||
}
|
||||
95
src/lib_ccx/ccx_encoders_xds.c
Normal file
95
src/lib_ccx/ccx_encoders_xds.c
Normal file
@@ -0,0 +1,95 @@
|
||||
#include "ccx_common_platform.h"
|
||||
#include "ccx_common_common.h"
|
||||
#include "ccx_encoders_common.h"
|
||||
#include "ccx_decoders_common.h"
|
||||
|
||||
static const char *XDSclasses_short[]=
|
||||
{
|
||||
"CUR",
|
||||
"FUT",
|
||||
"CHN",
|
||||
"MIS",
|
||||
"PUB",
|
||||
"RES",
|
||||
"PRV",
|
||||
"END"
|
||||
};
|
||||
|
||||
void xds_write_transcript_line_prefix (struct encoder_ctx *context, struct ccx_s_write *wb, LLONG start_time, LLONG end_time, int cur_xds_packet_class)
|
||||
{
|
||||
unsigned h1,m1,s1,ms1;
|
||||
unsigned h2,m2,s2,ms2;
|
||||
if (!wb || wb->fh==-1)
|
||||
return;
|
||||
|
||||
if (start_time == -1)
|
||||
{
|
||||
// Means we entered XDS mode without making a note of the XDS start time. This is a bug.
|
||||
ccx_common_logging.fatal_ftn(CCX_COMMON_EXIT_BUG_BUG, "Bug in timedtranscript (XDS). Please report.");
|
||||
}
|
||||
|
||||
if (context->transcript_settings->showStartTime)
|
||||
{
|
||||
char buffer[80];
|
||||
if (context->transcript_settings->relativeTimestamp)
|
||||
{
|
||||
if (utc_refvalue == UINT64_MAX)
|
||||
{
|
||||
mstotime(start_time + context->subs_delay, &h1, &m1, &s1, &ms1);
|
||||
fdprintf(wb->fh, "%02u:%02u:%02u%c%03u|", h1, m1, s1, context->millis_separator, ms1);
|
||||
}
|
||||
else
|
||||
{
|
||||
fdprintf(wb->fh, "%lld%c%03d|", (start_time + context->subs_delay) / 1000,
|
||||
context->millis_separator, (start_time + context->subs_delay) % 1000);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
mstotime(start_time + context->subs_delay, &h1, &m1, &s1, &ms1);
|
||||
time_t start_time_int = (start_time + context->subs_delay) / 1000;
|
||||
int start_time_dec = (start_time + context->subs_delay) % 1000;
|
||||
struct tm *start_time_struct = gmtime(&start_time_int);
|
||||
strftime(buffer, sizeof(buffer), "%Y%m%d%H%M%S", start_time_struct);
|
||||
fdprintf(wb->fh, "%s%c%03d|", buffer, context->millis_separator, start_time_dec);
|
||||
}
|
||||
}
|
||||
|
||||
if (context->transcript_settings->showEndTime)
|
||||
{
|
||||
char buffer[80];
|
||||
if (context->transcript_settings->relativeTimestamp)
|
||||
{
|
||||
if (utc_refvalue == UINT64_MAX)
|
||||
{
|
||||
mstotime(end_time, &h2, &m2, &s2, &ms2);
|
||||
fdprintf(wb->fh, "%02u:%02u:%02u%c%03u|", h2, m2, s2, context->millis_separator, ms2);
|
||||
}
|
||||
else
|
||||
{
|
||||
fdprintf(wb->fh, "%lld%s%03d|", end_time / 1000, context->millis_separator, end_time% 1000);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
mstotime(end_time, &h2, &m2, &s2, &ms2);
|
||||
time_t end_time_int = end_time / 1000;
|
||||
int end_time_dec = end_time % 1000;
|
||||
struct tm *end_time_struct = gmtime(&end_time_int);
|
||||
strftime(buffer, sizeof(buffer), "%Y%m%d%H%M%S", end_time_struct);
|
||||
fdprintf(wb->fh, "%s%c%03d|", buffer, context->millis_separator, end_time_dec);
|
||||
}
|
||||
}
|
||||
|
||||
if (context->transcript_settings->showMode)
|
||||
{
|
||||
const char *mode = "XDS";
|
||||
fdprintf(wb->fh, "%s|", mode);
|
||||
}
|
||||
|
||||
if (context->transcript_settings->showCC)
|
||||
{
|
||||
fdprintf(wb->fh, "%s|", XDSclasses_short[cur_xds_packet_class]);
|
||||
}
|
||||
}
|
||||
|
||||
5
src/lib_ccx/ccx_encoders_xds.h
Normal file
5
src/lib_ccx/ccx_encoders_xds.h
Normal file
@@ -0,0 +1,5 @@
|
||||
#ifndef _CCX_ENCODER_XDS_H
|
||||
#define _CCX_ENCODER_XDS_H
|
||||
|
||||
void xds_write_transcript_line_prefix (struct encoder_ctx *context, struct ccx_s_write *wb, LLONG start_time, LLONG end_time, int cur_xds_packet_class);
|
||||
#endif
|
||||
7
src/lib_ccx/ccx_mp4.h
Normal file
7
src/lib_ccx/ccx_mp4.h
Normal file
@@ -0,0 +1,7 @@
|
||||
#ifndef CXX_MP4_H
|
||||
#define CXX_MP4_H
|
||||
|
||||
|
||||
|
||||
int processmp4 (struct lib_ccx_ctx *ctx,struct ccx_s_mp4Cfg *cfg, char *file);
|
||||
#endif
|
||||
@@ -47,28 +47,13 @@ struct conf_map configuration_map[] = {
|
||||
{"BUFFER_INPUT",offsetof(struct ccx_s_options,buffer_input),set_int},
|
||||
{"NOFONT_COLOR",offsetof(struct ccx_s_options,nofontcolor),set_int},
|
||||
{"NOTYPE_SETTING",offsetof(struct ccx_s_options,notypesetting),set_int},
|
||||
{"CODEC",offsetof(struct ccx_s_options,codec),set_int},
|
||||
{"NOCODEC",offsetof(struct ccx_s_options,nocodec),set_int},
|
||||
{"OUTPUT_FORMAT",offsetof(struct ccx_s_options,write_format),set_int},
|
||||
{"START_CREDIT_TEXT",offsetof(struct ccx_s_options,start_credits_text),set_string},
|
||||
{"START_CREDIT_NOT_BEFORE",offsetof(struct ccx_s_options,startcreditsnotbefore),set_time},
|
||||
{"START_CREDIT_NOT_AFTER",offsetof(struct ccx_s_options,startcreditsnotafter),set_time},
|
||||
{"START_CREDIT_FOR_ATLEAST",offsetof(struct ccx_s_options,startcreditsforatleast),set_time},
|
||||
{"START_CREDIT_FOR_ATMOST",offsetof(struct ccx_s_options,startcreditsforatmost),set_time},
|
||||
{"END_CREDITS_TEXT",offsetof(struct ccx_s_options,end_credits_text),set_string},
|
||||
{"END_CREDITS_FOR_ATLEAST",offsetof(struct ccx_s_options,endcreditsforatleast),set_time},
|
||||
{"END_CREDITS_FOR_ATMOST",offsetof(struct ccx_s_options,endcreditsforatmost),set_time},
|
||||
{"VIDEO_EDITED",offsetof(struct ccx_s_options,binary_concat),set_int},
|
||||
{"GOP_TIME",offsetof(struct ccx_s_options,use_gop_as_pts),set_int},
|
||||
{"FIX_PADDINDG",offsetof(struct ccx_s_options,fix_padding),set_int},
|
||||
{"TRIM",offsetof(struct ccx_s_options,trim_subs),set_int},
|
||||
{"GUI_MODE_REPORTS",offsetof(struct ccx_s_options,gui_mode_reports),set_int},
|
||||
{"NO_PROGRESS_BAR",offsetof(struct ccx_s_options,no_progress_bar),set_int},
|
||||
{"SENTENCE_CAP",offsetof(struct ccx_s_options,sentence_cap),set_int},
|
||||
{"CAP_FILE",offsetof(struct ccx_s_options,sentence_cap_file),set_string},
|
||||
{"PROGRAM_NUMBER",offsetof(struct ccx_s_options,ts_forced_program),set_int},
|
||||
{"AUTO_PROGRAM",offsetof(struct ccx_s_options,ts_autoprogram),set_int},
|
||||
{"STREAM",offsetof(struct ccx_s_options,live_stream),set_int},
|
||||
{"START_AT",offsetof(struct ccx_s_options,extraction_start),set_time},
|
||||
{"END_AT",offsetof(struct ccx_s_options,extraction_end),set_time},
|
||||
{"INVASTIGATE_PACKET",offsetof(struct ccx_s_options,investigate_packets),set_int},
|
||||
@@ -76,15 +61,9 @@ struct conf_map configuration_map[] = {
|
||||
{"NO_SYNC",offsetof(struct ccx_s_options,nosync),set_int},
|
||||
{"HAUPPAUGE_MODE",offsetof(struct ccx_s_options,hauppauge_mode),set_int},
|
||||
{"MP4_VIDEO_TRACK",offsetof(struct ccx_s_options,mp4vidtrack),set_int},
|
||||
{"ENCODING",offsetof(struct ccx_s_options,encoding),set_int},
|
||||
{"USE_PIC_ORDER",offsetof(struct ccx_s_options,usepicorder),set_int},
|
||||
{"AUTO_MYTH",offsetof(struct ccx_s_options,auto_myth),set_int},
|
||||
{"WTV_MPEG2",offsetof(struct ccx_s_options,wtvmpeg2),set_int},
|
||||
{"OUTPUT_FILENAME",offsetof(struct ccx_s_options,output_filename),set_string},
|
||||
{"OUT_ELEMENTRY_STREAM_FILENAME",offsetof(struct ccx_s_options,out_elementarystream_filename),set_string},
|
||||
{"DATA_PID",offsetof(struct ccx_s_options,ts_cappid),set_int},
|
||||
{"STREAM_TYPE",offsetof(struct ccx_s_options,ts_datastreamtype),set_int},
|
||||
{"TS_FORCED_STREAM_TYPE",offsetof(struct ccx_s_options,ts_forced_streamtype),set_int},
|
||||
{"DATE_FORMAT",offsetof(struct ccx_s_options,date_format),set_int},
|
||||
// Settings for 608 decoder
|
||||
{ "NO_ROLL_UP", offsetof(struct ccx_s_options, settings_608.no_rollup), set_int },
|
||||
|
||||
@@ -429,9 +429,18 @@ void* dvbsub_init_decoder(struct dvb_config* cfg)
|
||||
DVBSubContext *ctx = (DVBSubContext*) malloc(sizeof(DVBSubContext));
|
||||
memset(ctx, 0, sizeof(DVBSubContext));
|
||||
|
||||
ctx->composition_id = cfg->composition_id[0];
|
||||
ctx->ancillary_id = cfg->ancillary_id[0];
|
||||
ctx->lang_index = cfg->lang_index[0];
|
||||
if(cfg)
|
||||
{
|
||||
ctx->composition_id = cfg->composition_id[0];
|
||||
ctx->ancillary_id = cfg->ancillary_id[0];
|
||||
ctx->lang_index = cfg->lang_index[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
ctx->composition_id = 1;
|
||||
ctx->ancillary_id = 1;
|
||||
ctx->lang_index = 1;
|
||||
}
|
||||
|
||||
#ifdef ENABLE_OCR
|
||||
ctx->ocr_ctx = init_ocr(ctx->lang_index);
|
||||
@@ -522,9 +531,9 @@ void* dvbsub_init_decoder(struct dvb_config* cfg)
|
||||
|
||||
return (void*) ctx;
|
||||
}
|
||||
int dvbsub_close_decoder(void *dvb_ctx)
|
||||
int dvbsub_close_decoder(void **dvb_ctx)
|
||||
{
|
||||
DVBSubContext *ctx = (DVBSubContext *) dvb_ctx;
|
||||
DVBSubContext *ctx = (DVBSubContext *) *dvb_ctx;
|
||||
DVBSubRegionDisplay *display;
|
||||
|
||||
delete_regions(ctx);
|
||||
@@ -542,6 +551,10 @@ int dvbsub_close_decoder(void *dvb_ctx)
|
||||
free(display);
|
||||
}
|
||||
|
||||
#ifdef ENABLE_OCR
|
||||
delete_ocr(&ctx->ocr_ctx);
|
||||
#endif
|
||||
freep(dvb_ctx);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1427,20 +1440,29 @@ static void dvbsub_parse_display_definition_segment(void *dvb_ctx,
|
||||
}
|
||||
}
|
||||
|
||||
static int write_dvb_sub(void *dvb_ctx, struct cc_subtitle *sub)
|
||||
/**
|
||||
* Write Subtitle in cc_subtitle structure in CC_BITMAP format
|
||||
* when OCR subsystem is present then it also write recognised text in
|
||||
* cc_bitmap ocr_text variable.
|
||||
*/
|
||||
static int write_dvb_sub(struct lib_cc_decode *dec_ctx, struct cc_subtitle *sub)
|
||||
{
|
||||
DVBSubContext *ctx = (DVBSubContext *) dvb_ctx;
|
||||
DVBSubContext *ctx;
|
||||
DVBSubRegion *region;
|
||||
DVBSubRegionDisplay *display;
|
||||
DVBSubCLUT *clut;
|
||||
DVBSubDisplayDefinition *display_def = ctx->display_definition;
|
||||
DVBSubDisplayDefinition *display_def;
|
||||
struct cc_bitmap *rect = NULL;
|
||||
uint32_t *clut_table;
|
||||
int offset_x=0, offset_y=0;
|
||||
int ret = 0;
|
||||
|
||||
ctx = (DVBSubContext *) dec_ctx->private_data;
|
||||
|
||||
display_def = ctx->display_definition;
|
||||
sub->type = CC_BITMAP;
|
||||
sub->lang_index = ctx->lang_index;
|
||||
|
||||
if (display_def)
|
||||
{
|
||||
offset_x = display_def->x;
|
||||
@@ -1464,7 +1486,8 @@ static int write_dvb_sub(void *dvb_ctx, struct cc_subtitle *sub)
|
||||
return -1;
|
||||
}
|
||||
|
||||
sub->start_time = get_visible_start();
|
||||
/* USE PTS and convert here in required time */
|
||||
sub->start_time = get_visible_start(dec_ctx->timing, 1);
|
||||
sub->end_time = sub->start_time + ( ctx->time_out * 1000 );
|
||||
sub->flags |= SUB_EOD_MARKER;
|
||||
sub->got_output = 1;
|
||||
@@ -1521,12 +1544,6 @@ static int write_dvb_sub(void *dvb_ctx, struct cc_subtitle *sub)
|
||||
rect++;
|
||||
|
||||
}
|
||||
#ifdef DeBUG
|
||||
if (ctx->object_list)
|
||||
{
|
||||
//save_display_set(ctx);
|
||||
}
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1539,9 +1556,9 @@ static int write_dvb_sub(void *dvb_ctx, struct cc_subtitle *sub)
|
||||
*
|
||||
* @return -1 on error
|
||||
*/
|
||||
int dvbsub_decode(void *dvb_ctx, const unsigned char *buf, int buf_size, struct cc_subtitle *sub)
|
||||
int dvbsub_decode(struct lib_cc_decode *dec_ctx, const unsigned char *buf, int buf_size, struct cc_subtitle *sub)
|
||||
{
|
||||
DVBSubContext *ctx = (DVBSubContext *) dvb_ctx;
|
||||
DVBSubContext *ctx = (DVBSubContext *) dec_ctx->private_data;
|
||||
const uint8_t *p, *p_end;
|
||||
int segment_type;
|
||||
int page_id;
|
||||
@@ -1551,14 +1568,16 @@ int dvbsub_decode(void *dvb_ctx, const unsigned char *buf, int buf_size, struct
|
||||
|
||||
if (buf_size <= 6 || *buf != 0x0f)
|
||||
{
|
||||
mprint("incomplete or broken packet\n");
|
||||
mprint("dvbsub_decode: incomplete, broken or empty packet\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
p = buf;
|
||||
p_end = buf + buf_size;
|
||||
|
||||
set_fts();
|
||||
dec_ctx->timing->current_tref = 0;
|
||||
set_fts(dec_ctx->timing);
|
||||
|
||||
while (p_end - p >= 6 && *p == 0x0f)
|
||||
{
|
||||
p += 1;
|
||||
@@ -1570,7 +1589,7 @@ int dvbsub_decode(void *dvb_ctx, const unsigned char *buf, int buf_size, struct
|
||||
|
||||
if (p_end - p < segment_length)
|
||||
{
|
||||
mprint("incomplete or broken packet\n");
|
||||
mprint("dvbsub_decode: incomplete, broken or empty packet\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -1580,29 +1599,29 @@ int dvbsub_decode(void *dvb_ctx, const unsigned char *buf, int buf_size, struct
|
||||
switch (segment_type)
|
||||
{
|
||||
case DVBSUB_PAGE_SEGMENT:
|
||||
dvbsub_parse_page_segment(dvb_ctx, p, segment_length);
|
||||
dvbsub_parse_page_segment(ctx, p, segment_length);
|
||||
got_segment |= 1;
|
||||
break;
|
||||
case DVBSUB_REGION_SEGMENT:
|
||||
dvbsub_parse_region_segment(dvb_ctx, p, segment_length);
|
||||
dvbsub_parse_region_segment(ctx, p, segment_length);
|
||||
got_segment |= 2;
|
||||
break;
|
||||
case DVBSUB_CLUT_SEGMENT:
|
||||
ret = dvbsub_parse_clut_segment(dvb_ctx, p, segment_length);
|
||||
ret = dvbsub_parse_clut_segment(ctx, p, segment_length);
|
||||
if (ret < 0)
|
||||
goto end;
|
||||
got_segment |= 4;
|
||||
break;
|
||||
case DVBSUB_OBJECT_SEGMENT:
|
||||
dvbsub_parse_object_segment(dvb_ctx, p, segment_length);
|
||||
dvbsub_parse_object_segment(ctx, p, segment_length);
|
||||
got_segment |= 8;
|
||||
break;
|
||||
case DVBSUB_DISPLAYDEFINITION_SEGMENT:
|
||||
dvbsub_parse_display_definition_segment(dvb_ctx, p,
|
||||
dvbsub_parse_display_definition_segment(ctx, p,
|
||||
segment_length);
|
||||
break;
|
||||
case DVBSUB_DISPLAY_SEGMENT:
|
||||
write_dvb_sub(dvb_ctx,sub);
|
||||
write_dvb_sub(dec_ctx, sub);
|
||||
got_segment |= 16;
|
||||
break;
|
||||
default:
|
||||
@@ -1618,7 +1637,7 @@ int dvbsub_decode(void *dvb_ctx, const unsigned char *buf, int buf_size, struct
|
||||
// segments then we need no further data.
|
||||
if (got_segment == 15)
|
||||
{
|
||||
write_dvb_sub(dvb_ctx,sub);
|
||||
write_dvb_sub(dec_ctx, sub);
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -44,7 +44,7 @@ struct dvb_config
|
||||
*/
|
||||
void* dvbsub_init_decoder(struct dvb_config* cfg);
|
||||
|
||||
int dvbsub_close_decoder(void *dvb_ctx);
|
||||
int dvbsub_close_decoder(void **dvb_ctx);
|
||||
|
||||
/**
|
||||
* @param dvb_ctx PreInitialized DVB context using DVB
|
||||
@@ -55,7 +55,7 @@ int dvbsub_close_decoder(void *dvb_ctx);
|
||||
*
|
||||
* @return -1 on error
|
||||
*/
|
||||
int dvbsub_decode(void *dvb_ctx, const unsigned char *buf, int buf_size, struct cc_subtitle *sub);
|
||||
int dvbsub_decode(struct lib_cc_decode *dec_ctx, const unsigned char *buf, int buf_size, struct cc_subtitle *sub);
|
||||
|
||||
/**
|
||||
* @func parse_dvb_description
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -9,473 +9,473 @@
|
||||
// Return TRUE if the data parsing finished, FALSE otherwise.
|
||||
// estream->pos is advanced. Data is only processed if ustream->error
|
||||
// is FALSE, parsing can set ustream->error to TRUE.
|
||||
int user_data(struct lib_ccx_ctx *ctx, struct bitstream *ustream, int udtype, struct cc_subtitle *sub)
|
||||
int user_data(struct lib_cc_decode *ctx, struct bitstream *ustream, int udtype, struct cc_subtitle *sub)
|
||||
{
|
||||
struct lib_cc_decode *dec_ctx = NULL;
|
||||
dec_ctx = ctx->dec_ctx;
|
||||
dbg_print(CCX_DMT_VERBOSE, "user_data(%d)\n", udtype);
|
||||
dbg_print(CCX_DMT_VERBOSE, "user_data(%d)\n", udtype);
|
||||
|
||||
// Shall not happen
|
||||
if (ustream->error || ustream->bitsleft <= 0)
|
||||
// Shall not happen
|
||||
if (ustream->error || ustream->bitsleft <= 0)
|
||||
{
|
||||
// ustream->error=1;
|
||||
return 0; // Actually discarded on call.
|
||||
// CFS: Seen in a Wobble edited file.
|
||||
// fatal(CCX_COMMON_EXIT_BUG_BUG, "user_data: Impossible!");
|
||||
// fatal(CCX_COMMON_EXIT_BUG_BUG, "user_data: Impossible!");
|
||||
}
|
||||
|
||||
// Do something
|
||||
ctx->stat_numuserheaders++;
|
||||
//header+=4;
|
||||
// Do something
|
||||
//ctx->stat_numuserheaders++;
|
||||
//header+=4;
|
||||
|
||||
unsigned char *ud_header = next_bytes(ustream, 4);
|
||||
if (ustream->error || ustream->bitsleft <= 0)
|
||||
unsigned char *ud_header = next_bytes(ustream, 4);
|
||||
if (ustream->error || ustream->bitsleft <= 0)
|
||||
{
|
||||
return 0; // Actually discarded on call.
|
||||
// CFS: Seen in Stick_VHS.mpg.
|
||||
// fatal(CCX_COMMON_EXIT_BUG_BUG, "user_data: Impossible!");
|
||||
// fatal(CCX_COMMON_EXIT_BUG_BUG, "user_data: Impossible!");
|
||||
}
|
||||
|
||||
// DVD CC header, see
|
||||
// <http://www.geocities.com/mcpoodle43/SCC_TOOLS/DOCS/SCC_FORMAT.HTML>
|
||||
if ( !memcmp(ud_header,"\x43\x43", 2 ) )
|
||||
{
|
||||
ctx->stat_dvdccheaders++;
|
||||
// DVD CC header, see
|
||||
// <http://www.geocities.com/mcpoodle43/SCC_TOOLS/DOCS/SCC_FORMAT.HTML>
|
||||
if ( !memcmp(ud_header,"\x43\x43", 2 ) )
|
||||
{
|
||||
// ctx->stat_dvdccheaders++;
|
||||
|
||||
// Probably unneeded, but keep looking for extra caption blocks
|
||||
int maybeextracb = 1;
|
||||
// Probably unneeded, but keep looking for extra caption blocks
|
||||
int maybeextracb = 1;
|
||||
|
||||
read_bytes(ustream, 4); // "43 43 01 F8"
|
||||
read_bytes(ustream, 4); // "43 43 01 F8"
|
||||
|
||||
unsigned char pattern_flag = (unsigned char) read_bits(ustream,1);
|
||||
read_bits(ustream,1);
|
||||
int capcount=(int) read_bits(ustream,5);
|
||||
int truncate_flag = (int) read_bits(ustream,1); // truncate_flag - one CB extra
|
||||
unsigned char pattern_flag = (unsigned char) read_bits(ustream,1);
|
||||
read_bits(ustream,1);
|
||||
int capcount=(int) read_bits(ustream,5);
|
||||
int truncate_flag = (int) read_bits(ustream,1); // truncate_flag - one CB extra
|
||||
|
||||
int field1packet = 0; // expect Field 1 first
|
||||
if (pattern_flag == 0x00)
|
||||
field1packet=1; // expect Field 1 second
|
||||
int field1packet = 0; // expect Field 1 first
|
||||
if (pattern_flag == 0x00)
|
||||
field1packet=1; // expect Field 1 second
|
||||
|
||||
dbg_print(CCX_DMT_VERBOSE, "Reading %d%s DVD CC segments\n",
|
||||
capcount, (truncate_flag?"+1":""));
|
||||
dbg_print(CCX_DMT_VERBOSE, "Reading %d%s DVD CC segments\n",
|
||||
capcount, (truncate_flag?"+1":""));
|
||||
|
||||
capcount += truncate_flag;
|
||||
capcount += truncate_flag;
|
||||
|
||||
// This data comes before the first frame header, so
|
||||
// in order to get the correct timing we need to set the
|
||||
// current time to one frame after the maximum time of the
|
||||
// last GOP. Only usefull when there are frames before
|
||||
// the GOP.
|
||||
if (fts_max > 0)
|
||||
fts_now = fts_max + (LLONG) (1000.0/current_fps);
|
||||
// This data comes before the first frame header, so
|
||||
// in order to get the correct timing we need to set the
|
||||
// current time to one frame after the maximum time of the
|
||||
// last GOP. Only usefull when there are frames before
|
||||
// the GOP.
|
||||
if (ctx->timing->fts_max > 0)
|
||||
ctx->timing->fts_now = ctx->timing->fts_max + (LLONG) (1000.0/current_fps);
|
||||
|
||||
int rcbcount = 0;
|
||||
for (int i=0; i<capcount; i++)
|
||||
{
|
||||
for (int j=0;j<2;j++)
|
||||
{
|
||||
unsigned char data[3];
|
||||
data[0]=read_u8(ustream);
|
||||
data[1]=read_u8(ustream);
|
||||
data[2]=read_u8(ustream);
|
||||
int rcbcount = 0;
|
||||
for (int i=0; i<capcount; i++)
|
||||
{
|
||||
for (int j=0;j<2;j++)
|
||||
{
|
||||
unsigned char data[3];
|
||||
data[0]=read_u8(ustream);
|
||||
data[1]=read_u8(ustream);
|
||||
data[2]=read_u8(ustream);
|
||||
|
||||
// Obey the truncate flag.
|
||||
if ( truncate_flag && i == capcount-1 && j == 1 )
|
||||
{
|
||||
maybeextracb = 0;
|
||||
break;
|
||||
}
|
||||
/* Field 1 and 2 data can be in either order,
|
||||
with marker bytes of \xff and \xfe
|
||||
Since markers can be repeated, use pattern as well */
|
||||
if ((data[0]&0xFE) == 0xFE) // Check if valid
|
||||
{
|
||||
if (data[0]==0xff && j==field1packet)
|
||||
data[0]=0x04; // Field 1
|
||||
else
|
||||
data[0]=0x05; // Field 2
|
||||
do_cb(dec_ctx, data, sub);
|
||||
rcbcount++;
|
||||
}
|
||||
else
|
||||
{
|
||||
dbg_print(CCX_DMT_VERBOSE, "Illegal caption segment - stop here.\n");
|
||||
maybeextracb = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Theoretically this should not happen, oh well ...
|
||||
// Deal with extra closed captions some DVD have.
|
||||
int ecbcount = 0;
|
||||
while ( maybeextracb && (next_u8(ustream)&0xFE) == 0xFE )
|
||||
{
|
||||
for (int j=0;j<2;j++)
|
||||
{
|
||||
unsigned char data[3];
|
||||
data[0]=read_u8(ustream);
|
||||
data[1]=read_u8(ustream);
|
||||
data[2]=read_u8(ustream);
|
||||
/* Field 1 and 2 data can be in either order,
|
||||
with marker bytes of \xff and \xfe
|
||||
Since markers can be repeated, use pattern as well */
|
||||
if ((data[0]&0xFE) == 0xFE) // Check if valid
|
||||
{
|
||||
if (data[0]==0xff && j==field1packet)
|
||||
data[0]=0x04; // Field 1
|
||||
else
|
||||
data[0]=0x05; // Field 2
|
||||
do_cb(dec_ctx, data, sub);
|
||||
ecbcount++;
|
||||
}
|
||||
else
|
||||
{
|
||||
dbg_print(CCX_DMT_VERBOSE, "Illegal (extra) caption segment - stop here.\n");
|
||||
maybeextracb = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Obey the truncate flag.
|
||||
if ( truncate_flag && i == capcount-1 && j == 1 )
|
||||
{
|
||||
maybeextracb = 0;
|
||||
break;
|
||||
}
|
||||
/* Field 1 and 2 data can be in either order,
|
||||
with marker bytes of \xff and \xfe
|
||||
Since markers can be repeated, use pattern as well */
|
||||
if ((data[0]&0xFE) == 0xFE) // Check if valid
|
||||
{
|
||||
if (data[0]==0xff && j==field1packet)
|
||||
data[0]=0x04; // Field 1
|
||||
else
|
||||
data[0]=0x05; // Field 2
|
||||
do_cb(ctx, data, sub);
|
||||
rcbcount++;
|
||||
}
|
||||
else
|
||||
{
|
||||
dbg_print(CCX_DMT_VERBOSE, "Illegal caption segment - stop here.\n");
|
||||
maybeextracb = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Theoretically this should not happen, oh well ...
|
||||
// Deal with extra closed captions some DVD have.
|
||||
int ecbcount = 0;
|
||||
while ( maybeextracb && (next_u8(ustream)&0xFE) == 0xFE )
|
||||
{
|
||||
for (int j=0;j<2;j++)
|
||||
{
|
||||
unsigned char data[3];
|
||||
data[0]=read_u8(ustream);
|
||||
data[1]=read_u8(ustream);
|
||||
data[2]=read_u8(ustream);
|
||||
/* Field 1 and 2 data can be in either order,
|
||||
with marker bytes of \xff and \xfe
|
||||
Since markers can be repeated, use pattern as well */
|
||||
if ((data[0]&0xFE) == 0xFE) // Check if valid
|
||||
{
|
||||
if (data[0]==0xff && j==field1packet)
|
||||
data[0]=0x04; // Field 1
|
||||
else
|
||||
data[0]=0x05; // Field 2
|
||||
do_cb(ctx, data, sub);
|
||||
ecbcount++;
|
||||
}
|
||||
else
|
||||
{
|
||||
dbg_print(CCX_DMT_VERBOSE, "Illegal (extra) caption segment - stop here.\n");
|
||||
maybeextracb = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dbg_print(CCX_DMT_VERBOSE, "Read %d/%d DVD CC blocks\n", rcbcount, ecbcount);
|
||||
}
|
||||
// SCTE 20 user data
|
||||
else if (ud_header[0] == 0x03)
|
||||
{
|
||||
if ((ud_header[1]&0x7F) == 0x01)
|
||||
{
|
||||
unsigned char cc_data[3*31+1]; // Maximum cc_count is 31
|
||||
dbg_print(CCX_DMT_VERBOSE, "Read %d/%d DVD CC blocks\n", rcbcount, ecbcount);
|
||||
}
|
||||
// SCTE 20 user data
|
||||
else if (!ctx->noscte20 && ud_header[0] == 0x03)
|
||||
{
|
||||
if ((ud_header[1]&0x7F) == 0x01)
|
||||
{
|
||||
unsigned char cc_data[3*31+1]; // Maximum cc_count is 31
|
||||
|
||||
ctx->stat_scte20ccheaders++;
|
||||
read_bytes(ustream, 2); // "03 01"
|
||||
// ctx->stat_scte20ccheaders++;
|
||||
read_bytes(ustream, 2); // "03 01"
|
||||
|
||||
unsigned cc_count = (unsigned int) read_bits(ustream,5);
|
||||
dbg_print(CCX_DMT_VERBOSE, "Reading %d SCTE 20 CC blocks\n", cc_count);
|
||||
unsigned cc_count = (unsigned int) read_bits(ustream,5);
|
||||
dbg_print(CCX_DMT_VERBOSE, "Reading %d SCTE 20 CC blocks\n", cc_count);
|
||||
|
||||
unsigned field_number;
|
||||
unsigned cc_data1;
|
||||
unsigned cc_data2;
|
||||
/* unsigned marker; */
|
||||
for (unsigned j=0;j<cc_count;j++)
|
||||
{
|
||||
skip_bits(ustream,2); // priority - unused
|
||||
field_number = (unsigned int) read_bits(ustream,2);
|
||||
skip_bits(ustream,5); // line_offset - unused
|
||||
cc_data1 = (unsigned int) read_bits(ustream,8);
|
||||
cc_data2 = (unsigned int) read_bits(ustream,8);
|
||||
/* marker = (unsigned int)read_bits(ustream,1); // TODO: Add syntax check */
|
||||
unsigned field_number;
|
||||
unsigned cc_data1;
|
||||
unsigned cc_data2;
|
||||
/* unsigned marker; */
|
||||
for (unsigned j=0;j<cc_count;j++)
|
||||
{
|
||||
skip_bits(ustream,2); // priority - unused
|
||||
field_number = (unsigned int) read_bits(ustream,2);
|
||||
skip_bits(ustream,5); // line_offset - unused
|
||||
cc_data1 = (unsigned int) read_bits(ustream,8);
|
||||
cc_data2 = (unsigned int) read_bits(ustream,8);
|
||||
/* marker = (unsigned int)read_bits(ustream,1); // TODO: Add syntax check */
|
||||
|
||||
if (ustream->bitsleft < 0)
|
||||
fatal(CCX_COMMON_EXIT_BUG_BUG, "Oops!");
|
||||
if (ustream->bitsleft < 0)
|
||||
fatal(CCX_COMMON_EXIT_BUG_BUG, "Oops!");
|
||||
|
||||
// Field_number is either
|
||||
// 0 .. forbiden
|
||||
// 1 .. field 1 (odd)
|
||||
// 2 .. field 2 (even)
|
||||
// 3 .. repeated, from repeat_first_field, effectively field 1
|
||||
if (field_number < 1)
|
||||
{
|
||||
// 0 is invalid
|
||||
cc_data[j*3]=0x00; // Set to invalid
|
||||
cc_data[j*3+1]=0x00;
|
||||
cc_data[j*3+2]=0x00;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Treat field_number 3 as 1
|
||||
field_number = (field_number - 1) & 0x01;
|
||||
// top_field_first also affects to which field the caption
|
||||
// belongs.
|
||||
if(!top_field_first)
|
||||
field_number ^= 0x01;
|
||||
cc_data[j*3]=0x04|(field_number);
|
||||
cc_data[j*3+1]=reverse8(cc_data1);
|
||||
cc_data[j*3+2]=reverse8(cc_data2);
|
||||
}
|
||||
}
|
||||
cc_data[cc_count*3]=0xFF;
|
||||
store_hdcc(ctx, cc_data, cc_count, current_tref, fts_now, sub);
|
||||
// Field_number is either
|
||||
// 0 .. forbiden
|
||||
// 1 .. field 1 (odd)
|
||||
// 2 .. field 2 (even)
|
||||
// 3 .. repeated, from repeat_first_field, effectively field 1
|
||||
if (field_number < 1)
|
||||
{
|
||||
// 0 is invalid
|
||||
cc_data[j*3]=0x00; // Set to invalid
|
||||
cc_data[j*3+1]=0x00;
|
||||
cc_data[j*3+2]=0x00;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Treat field_number 3 as 1
|
||||
field_number = (field_number - 1) & 0x01;
|
||||
// top_field_first also affects to which field the caption
|
||||
// belongs.
|
||||
if(!ctx->top_field_first)
|
||||
field_number ^= 0x01;
|
||||
cc_data[j*3]=0x04|(field_number);
|
||||
cc_data[j*3+1]=reverse8(cc_data1);
|
||||
cc_data[j*3+2]=reverse8(cc_data2);
|
||||
}
|
||||
}
|
||||
cc_data[cc_count*3]=0xFF;
|
||||
store_hdcc(ctx, cc_data, cc_count, ctx->timing->current_tref, ctx->timing->fts_now, sub);
|
||||
|
||||
dbg_print(CCX_DMT_VERBOSE, "Reading SCTE 20 CC blocks - done\n");
|
||||
}
|
||||
// reserved - unspecified
|
||||
}
|
||||
// ReplayTV 4000/5000 caption header - parsing information
|
||||
// derived from CCExtract.bdl
|
||||
else if ( (ud_header[0] == 0xbb //ReplayTV 4000
|
||||
|| ud_header[0] == 0x99) //ReplayTV 5000
|
||||
&& ud_header[1] == 0x02 )
|
||||
{
|
||||
unsigned char data[3];
|
||||
if (ud_header[0]==0xbb)
|
||||
ctx->stat_replay4000headers++;
|
||||
else
|
||||
ctx->stat_replay5000headers++;
|
||||
dbg_print(CCX_DMT_VERBOSE, "Reading SCTE 20 CC blocks - done\n");
|
||||
}
|
||||
// reserved - unspecified
|
||||
}
|
||||
// ReplayTV 4000/5000 caption header - parsing information
|
||||
// derived from CCExtract.bdl
|
||||
else if ( (ud_header[0] == 0xbb //ReplayTV 4000
|
||||
|| ud_header[0] == 0x99) //ReplayTV 5000
|
||||
&& ud_header[1] == 0x02 )
|
||||
{
|
||||
unsigned char data[3];
|
||||
#if 0
|
||||
if (ud_header[0]==0xbb)
|
||||
ctx->stat_replay4000headers++;
|
||||
else
|
||||
ctx->stat_replay5000headers++;
|
||||
#endif
|
||||
|
||||
read_bytes(ustream, 2); // "BB 02" or "99 02"
|
||||
data[0]=0x05; // Field 2
|
||||
data[1]=read_u8(ustream);
|
||||
data[2]=read_u8(ustream);
|
||||
do_cb(dec_ctx, data, sub);
|
||||
read_bytes(ustream, 2); // Skip "CC 02" for R4000 or "AA 02" for R5000
|
||||
data[0]=0x04; // Field 1
|
||||
data[1]=read_u8(ustream);
|
||||
data[2]=read_u8(ustream);
|
||||
do_cb(dec_ctx, data, sub);
|
||||
}
|
||||
// HDTV - see A/53 Part 4 (Video)
|
||||
else if ( !memcmp(ud_header,"\x47\x41\x39\x34", 4 ) )
|
||||
{
|
||||
ctx->stat_hdtv++;
|
||||
read_bytes(ustream, 2); // "BB 02" or "99 02"
|
||||
data[0]=0x05; // Field 2
|
||||
data[1]=read_u8(ustream);
|
||||
data[2]=read_u8(ustream);
|
||||
do_cb(ctx, data, sub);
|
||||
read_bytes(ustream, 2); // Skip "CC 02" for R4000 or "AA 02" for R5000
|
||||
data[0]=0x04; // Field 1
|
||||
data[1]=read_u8(ustream);
|
||||
data[2]=read_u8(ustream);
|
||||
do_cb(ctx, data, sub);
|
||||
}
|
||||
// HDTV - see A/53 Part 4 (Video)
|
||||
else if ( !memcmp(ud_header,"\x47\x41\x39\x34", 4 ) )
|
||||
{
|
||||
// ctx->stat_hdtv++;
|
||||
|
||||
read_bytes(ustream, 4); // "47 41 39 34"
|
||||
read_bytes(ustream, 4); // "47 41 39 34"
|
||||
|
||||
unsigned char type_code = read_u8(ustream);
|
||||
if (type_code==0x03) // CC data.
|
||||
{
|
||||
skip_bits(ustream,1); // reserved
|
||||
unsigned char process_cc_data = (unsigned char) read_bits(ustream,1);
|
||||
skip_bits(ustream,1); // additional_data - unused
|
||||
unsigned char cc_count = (unsigned char) read_bits(ustream,5);
|
||||
read_bytes(ustream, 1); // "FF"
|
||||
if (process_cc_data)
|
||||
{
|
||||
dbg_print(CCX_DMT_VERBOSE, "Reading %d HDTV CC blocks\n", cc_count);
|
||||
unsigned char type_code = read_u8(ustream);
|
||||
if (type_code==0x03) // CC data.
|
||||
{
|
||||
skip_bits(ustream,1); // reserved
|
||||
unsigned char process_cc_data = (unsigned char) read_bits(ustream,1);
|
||||
skip_bits(ustream,1); // additional_data - unused
|
||||
unsigned char cc_count = (unsigned char) read_bits(ustream,5);
|
||||
read_bytes(ustream, 1); // "FF"
|
||||
if (process_cc_data)
|
||||
{
|
||||
dbg_print(CCX_DMT_VERBOSE, "Reading %d HDTV CC blocks\n", cc_count);
|
||||
|
||||
int proceed = 1;
|
||||
unsigned char *cc_data = read_bytes(ustream, cc_count*3);
|
||||
if (ustream->bitsleft < 0)
|
||||
fatal(CCX_COMMON_EXIT_BUG_BUG, "Not enough for CC captions!");
|
||||
int proceed = 1;
|
||||
unsigned char *cc_data = read_bytes(ustream, cc_count*3);
|
||||
if (ustream->bitsleft < 0)
|
||||
fatal(CCX_COMMON_EXIT_BUG_BUG, "Not enough for CC captions!");
|
||||
|
||||
// Check for proper marker - This read makes sure that
|
||||
// cc_count*3+1 bytes are read and available in cc_data.
|
||||
if (read_u8(ustream)!=0xFF)
|
||||
proceed=0;
|
||||
// Check for proper marker - This read makes sure that
|
||||
// cc_count*3+1 bytes are read and available in cc_data.
|
||||
if (read_u8(ustream)!=0xFF)
|
||||
proceed=0;
|
||||
|
||||
if (!proceed)
|
||||
{
|
||||
dbg_print(CCX_DMT_VERBOSE, "\rThe following payload is not properly terminated.\n");
|
||||
dump (CCX_DMT_VERBOSE, cc_data, cc_count*3+1, 0, 0);
|
||||
}
|
||||
dbg_print(CCX_DMT_VERBOSE, "Reading %d HD CC blocks\n", cc_count);
|
||||
if (!proceed)
|
||||
{
|
||||
dbg_print(CCX_DMT_VERBOSE, "\rThe following payload is not properly terminated.\n");
|
||||
dump (CCX_DMT_VERBOSE, cc_data, cc_count*3+1, 0, 0);
|
||||
}
|
||||
dbg_print(CCX_DMT_VERBOSE, "Reading %d HD CC blocks\n", cc_count);
|
||||
|
||||
// B-frames might be (temporal) before or after the anchor
|
||||
// frame they belong to. Store the buffer until the next anchor
|
||||
// frame occurs. The buffer will be flushed (sorted) in the
|
||||
// picture header (or GOP) section when the next anchor occurs.
|
||||
// Please note we store the current value of the global
|
||||
// fts_now variable (and not get_fts()) as we are going to
|
||||
// re-create the timeline in process_hdcc() (Slightly ugly).
|
||||
store_hdcc(ctx, cc_data, cc_count, current_tref, fts_now, sub);
|
||||
// B-frames might be (temporal) before or after the anchor
|
||||
// frame they belong to. Store the buffer until the next anchor
|
||||
// frame occurs. The buffer will be flushed (sorted) in the
|
||||
// picture header (or GOP) section when the next anchor occurs.
|
||||
// Please note we store the current value of the global
|
||||
// fts_now variable (and not get_fts()) as we are going to
|
||||
// re-create the timeline in process_hdcc() (Slightly ugly).
|
||||
store_hdcc(ctx, cc_data, cc_count, ctx->timing->current_tref, ctx->timing->fts_now, sub);
|
||||
|
||||
dbg_print(CCX_DMT_VERBOSE, "Reading HDTV blocks - done\n");
|
||||
}
|
||||
}
|
||||
// reserved - additional_cc_data
|
||||
}
|
||||
// DVB closed caption header for Dish Network (Field 1 only) */
|
||||
else if ( !memcmp(ud_header,"\x05\x02", 2 ) )
|
||||
{
|
||||
// Like HDTV (above) Dish Network captions can be stored at each
|
||||
// frame, but maximal two caption blocks per frame and only one
|
||||
// field is stored.
|
||||
// To process this with the HDTV framework we create a "HDTV" caption
|
||||
// format compatible array. Two times 3 bytes plus one for the 0xFF
|
||||
// marker at the end. Pre-init to field 1 and set the 0xFF marker.
|
||||
static unsigned char dishdata[7] = {0x04, 0, 0, 0x04, 0, 0, 0xFF};
|
||||
int cc_count;
|
||||
dbg_print(CCX_DMT_VERBOSE, "Reading HDTV blocks - done\n");
|
||||
}
|
||||
}
|
||||
// reserved - additional_cc_data
|
||||
}
|
||||
// DVB closed caption header for Dish Network (Field 1 only) */
|
||||
else if ( !memcmp(ud_header,"\x05\x02", 2 ) )
|
||||
{
|
||||
// Like HDTV (above) Dish Network captions can be stored at each
|
||||
// frame, but maximal two caption blocks per frame and only one
|
||||
// field is stored.
|
||||
// To process this with the HDTV framework we create a "HDTV" caption
|
||||
// format compatible array. Two times 3 bytes plus one for the 0xFF
|
||||
// marker at the end. Pre-init to field 1 and set the 0xFF marker.
|
||||
static unsigned char dishdata[7] = {0x04, 0, 0, 0x04, 0, 0, 0xFF};
|
||||
int cc_count;
|
||||
|
||||
dbg_print(CCX_DMT_VERBOSE, "Reading Dish Network user data\n");
|
||||
dbg_print(CCX_DMT_VERBOSE, "Reading Dish Network user data\n");
|
||||
|
||||
ctx->stat_dishheaders++;
|
||||
// ctx->stat_dishheaders++;
|
||||
|
||||
read_bytes(ustream, 2); // "05 02"
|
||||
read_bytes(ustream, 2); // "05 02"
|
||||
|
||||
// The next bytes are like this:
|
||||
// header[2] : ID: 0x04 (MPEG?), 0x03 (H264?)
|
||||
// header[3-4]: Two byte counter (counting (sub-)GOPs?)
|
||||
// header[5-6]: Two bytes, maybe checksum?
|
||||
// header[7]: Pattern type
|
||||
// on B-frame: 0x02, 0x04
|
||||
// on I-/P-frame: 0x05
|
||||
unsigned char id = read_u8(ustream);
|
||||
unsigned dishcount = read_u16(ustream);
|
||||
unsigned something = read_u16(ustream);
|
||||
unsigned char type = read_u8(ustream);
|
||||
dbg_print(CCX_DMT_PARSE, "DN ID: %02X Count: %5u Unknown: %04X Pattern: %X",
|
||||
id, dishcount, something, type);
|
||||
// The next bytes are like this:
|
||||
// header[2] : ID: 0x04 (MPEG?), 0x03 (H264?)
|
||||
// header[3-4]: Two byte counter (counting (sub-)GOPs?)
|
||||
// header[5-6]: Two bytes, maybe checksum?
|
||||
// header[7]: Pattern type
|
||||
// on B-frame: 0x02, 0x04
|
||||
// on I-/P-frame: 0x05
|
||||
unsigned char id = read_u8(ustream);
|
||||
unsigned dishcount = read_u16(ustream);
|
||||
unsigned something = read_u16(ustream);
|
||||
unsigned char type = read_u8(ustream);
|
||||
dbg_print(CCX_DMT_PARSE, "DN ID: %02X Count: %5u Unknown: %04X Pattern: %X",
|
||||
id, dishcount, something, type);
|
||||
|
||||
unsigned char hi;
|
||||
unsigned char hi;
|
||||
|
||||
// The following block needs 4 to 6 bytes starting from the
|
||||
// current position
|
||||
unsigned char *dcd = ustream->pos; // dish caption data
|
||||
switch (type)
|
||||
{
|
||||
case 0x02:
|
||||
// Two byte caption - always on B-frame
|
||||
// The following 4 bytes are:
|
||||
// 0 : 0x09
|
||||
// 1-2: caption block
|
||||
// 3 : REPEAT - 02: two bytes
|
||||
// - 04: four bytes (repeat first two)
|
||||
dbg_print(CCX_DMT_PARSE, "\n02 %02X %02X:%02X - R:%02X :",
|
||||
dcd[0], dcd[1], dcd[2], dcd[3]);
|
||||
// The following block needs 4 to 6 bytes starting from the
|
||||
// current position
|
||||
unsigned char *dcd = ustream->pos; // dish caption data
|
||||
switch (type)
|
||||
{
|
||||
case 0x02:
|
||||
// Two byte caption - always on B-frame
|
||||
// The following 4 bytes are:
|
||||
// 0 : 0x09
|
||||
// 1-2: caption block
|
||||
// 3 : REPEAT - 02: two bytes
|
||||
// - 04: four bytes (repeat first two)
|
||||
dbg_print(CCX_DMT_PARSE, "\n02 %02X %02X:%02X - R:%02X :",
|
||||
dcd[0], dcd[1], dcd[2], dcd[3]);
|
||||
|
||||
cc_count = 1;
|
||||
dishdata[1]=dcd[1];
|
||||
dishdata[2]=dcd[2];
|
||||
cc_count = 1;
|
||||
dishdata[1]=dcd[1];
|
||||
dishdata[2]=dcd[2];
|
||||
|
||||
dbg_print(CCX_DMT_PARSE, "%s", debug_608toASC( dishdata, 0) );
|
||||
dbg_print(CCX_DMT_PARSE, "%s", debug_608toASC( dishdata, 0) );
|
||||
|
||||
type=dcd[3]; // repeater (0x02 or 0x04)
|
||||
hi = dishdata[1] & 0x7f; // Get only the 7 low bits
|
||||
if (type==0x04 && hi<32) // repeat (only for non-character pairs)
|
||||
{
|
||||
cc_count = 2;
|
||||
dishdata[3]=0x04; // Field 1
|
||||
dishdata[4]=dishdata[1];
|
||||
dishdata[5]=dishdata[2];
|
||||
type=dcd[3]; // repeater (0x02 or 0x04)
|
||||
hi = dishdata[1] & 0x7f; // Get only the 7 low bits
|
||||
if (type==0x04 && hi<32) // repeat (only for non-character pairs)
|
||||
{
|
||||
cc_count = 2;
|
||||
dishdata[3]=0x04; // Field 1
|
||||
dishdata[4]=dishdata[1];
|
||||
dishdata[5]=dishdata[2];
|
||||
|
||||
dbg_print(CCX_DMT_PARSE, "%s:\n", debug_608toASC( dishdata+3, 0) );
|
||||
}
|
||||
else
|
||||
{
|
||||
dbg_print(CCX_DMT_PARSE, ":\n");
|
||||
}
|
||||
dbg_print(CCX_DMT_PARSE, "%s:\n", debug_608toASC( dishdata+3, 0) );
|
||||
}
|
||||
else
|
||||
{
|
||||
dbg_print(CCX_DMT_PARSE, ":\n");
|
||||
}
|
||||
|
||||
dishdata[cc_count*3] = 0xFF; // Set end marker
|
||||
dishdata[cc_count*3] = 0xFF; // Set end marker
|
||||
|
||||
store_hdcc(ctx, dishdata, cc_count, current_tref, fts_now, sub);
|
||||
store_hdcc(ctx, dishdata, cc_count, ctx->timing->current_tref, ctx->timing->fts_now, sub);
|
||||
|
||||
// Ignore 3 (0x0A, followed by two unknown) bytes.
|
||||
break;
|
||||
case 0x04:
|
||||
// Four byte caption - always on B-frame
|
||||
// The following 5 bytes are:
|
||||
// 0 : 0x09
|
||||
// 1-2: caption block
|
||||
// 3-4: caption block
|
||||
dbg_print(CCX_DMT_PARSE, "\n04 %02X %02X:%02X:%02X:%02X :",
|
||||
dcd[0], dcd[1], dcd[2], dcd[3], dcd[4]);
|
||||
// Ignore 3 (0x0A, followed by two unknown) bytes.
|
||||
break;
|
||||
case 0x04:
|
||||
// Four byte caption - always on B-frame
|
||||
// The following 5 bytes are:
|
||||
// 0 : 0x09
|
||||
// 1-2: caption block
|
||||
// 3-4: caption block
|
||||
dbg_print(CCX_DMT_PARSE, "\n04 %02X %02X:%02X:%02X:%02X :",
|
||||
dcd[0], dcd[1], dcd[2], dcd[3], dcd[4]);
|
||||
|
||||
cc_count = 2;
|
||||
dishdata[1]=dcd[1];
|
||||
dishdata[2]=dcd[2];
|
||||
cc_count = 2;
|
||||
dishdata[1]=dcd[1];
|
||||
dishdata[2]=dcd[2];
|
||||
|
||||
dishdata[3]=0x04; // Field 1
|
||||
dishdata[4]=dcd[3];
|
||||
dishdata[5]=dcd[4];
|
||||
dishdata[6] = 0xFF; // Set end marker
|
||||
dishdata[3]=0x04; // Field 1
|
||||
dishdata[4]=dcd[3];
|
||||
dishdata[5]=dcd[4];
|
||||
dishdata[6] = 0xFF; // Set end marker
|
||||
|
||||
dbg_print(CCX_DMT_PARSE, "%s", debug_608toASC( dishdata, 0) );
|
||||
dbg_print(CCX_DMT_PARSE, "%s:\n", debug_608toASC( dishdata+3, 0) );
|
||||
dbg_print(CCX_DMT_PARSE, "%s", debug_608toASC( dishdata, 0) );
|
||||
dbg_print(CCX_DMT_PARSE, "%s:\n", debug_608toASC( dishdata+3, 0) );
|
||||
|
||||
store_hdcc(ctx, dishdata, cc_count, current_tref, fts_now, sub);
|
||||
store_hdcc(ctx, dishdata, cc_count, ctx->timing->current_tref, ctx->timing->fts_now, sub);
|
||||
|
||||
// Ignore 4 (0x020A, followed by two unknown) bytes.
|
||||
break;
|
||||
case 0x05:
|
||||
// Buffered caption - always on I-/P-frame
|
||||
// The following six bytes are:
|
||||
// 0 : 0x04
|
||||
// - the following are from previous 0x05 caption header -
|
||||
// 1 : prev dcd[2]
|
||||
// 2-3: prev dcd[3-4]
|
||||
// 4-5: prev dcd[5-6]
|
||||
dbg_print(CCX_DMT_PARSE, " - %02X pch: %02X %5u %02X:%02X\n",
|
||||
dcd[0], dcd[1],
|
||||
(unsigned)dcd[2]*256+dcd[3],
|
||||
dcd[4], dcd[5]);
|
||||
dcd+=6; // Skip these 6 bytes
|
||||
// Ignore 4 (0x020A, followed by two unknown) bytes.
|
||||
break;
|
||||
case 0x05:
|
||||
// Buffered caption - always on I-/P-frame
|
||||
// The following six bytes are:
|
||||
// 0 : 0x04
|
||||
// - the following are from previous 0x05 caption header -
|
||||
// 1 : prev dcd[2]
|
||||
// 2-3: prev dcd[3-4]
|
||||
// 4-5: prev dcd[5-6]
|
||||
dbg_print(CCX_DMT_PARSE, " - %02X pch: %02X %5u %02X:%02X\n",
|
||||
dcd[0], dcd[1],
|
||||
(unsigned)dcd[2]*256+dcd[3],
|
||||
dcd[4], dcd[5]);
|
||||
dcd+=6; // Skip these 6 bytes
|
||||
|
||||
// Now one of the "regular" 0x02 or 0x04 captions follows
|
||||
dbg_print(CCX_DMT_PARSE, "%02X %02X %02X:%02X",
|
||||
dcd[0], dcd[1], dcd[2], dcd[3]);
|
||||
// Now one of the "regular" 0x02 or 0x04 captions follows
|
||||
dbg_print(CCX_DMT_PARSE, "%02X %02X %02X:%02X",
|
||||
dcd[0], dcd[1], dcd[2], dcd[3]);
|
||||
|
||||
type=dcd[0]; // Number of caption bytes (0x02 or 0x04)
|
||||
type=dcd[0]; // Number of caption bytes (0x02 or 0x04)
|
||||
|
||||
cc_count = 1;
|
||||
dishdata[1]=dcd[2];
|
||||
dishdata[2]=dcd[3];
|
||||
cc_count = 1;
|
||||
dishdata[1]=dcd[2];
|
||||
dishdata[2]=dcd[3];
|
||||
|
||||
dcd+=4; // Skip the first 4 bytes.
|
||||
if (type==0x02)
|
||||
{
|
||||
type=dcd[0]; // repeater (0x02 or 0x04)
|
||||
dcd++; // Skip the repeater byte.
|
||||
dcd+=4; // Skip the first 4 bytes.
|
||||
if (type==0x02)
|
||||
{
|
||||
type=dcd[0]; // repeater (0x02 or 0x04)
|
||||
dcd++; // Skip the repeater byte.
|
||||
|
||||
dbg_print(CCX_DMT_PARSE, " - R:%02X :%s", type, debug_608toASC( dishdata, 0) );
|
||||
dbg_print(CCX_DMT_PARSE, " - R:%02X :%s", type, debug_608toASC( dishdata, 0) );
|
||||
|
||||
hi = dishdata[1] & 0x7f; // Get only the 7 low bits
|
||||
if (type==0x04 && hi<32)
|
||||
{
|
||||
cc_count = 2;
|
||||
dishdata[3]=0x04; // Field 1
|
||||
dishdata[4]=dishdata[1];
|
||||
dishdata[5]=dishdata[2];
|
||||
dbg_print(CCX_DMT_PARSE, "%s:\n", debug_608toASC( dishdata+3, 0) );
|
||||
}
|
||||
else
|
||||
{
|
||||
dbg_print(CCX_DMT_PARSE, ":\n");
|
||||
}
|
||||
dishdata[cc_count*3] = 0xFF; // Set end marker
|
||||
}
|
||||
else
|
||||
{
|
||||
dbg_print(CCX_DMT_PARSE, ":%02X:%02X ",
|
||||
dcd[0], dcd[1]);
|
||||
cc_count = 2;
|
||||
dishdata[3]=0x04; // Field 1
|
||||
dishdata[4]=dcd[0];
|
||||
dishdata[5]=dcd[1];
|
||||
dishdata[6] = 0xFF; // Set end marker
|
||||
hi = dishdata[1] & 0x7f; // Get only the 7 low bits
|
||||
if (type==0x04 && hi<32)
|
||||
{
|
||||
cc_count = 2;
|
||||
dishdata[3]=0x04; // Field 1
|
||||
dishdata[4]=dishdata[1];
|
||||
dishdata[5]=dishdata[2];
|
||||
dbg_print(CCX_DMT_PARSE, "%s:\n", debug_608toASC( dishdata+3, 0) );
|
||||
}
|
||||
else
|
||||
{
|
||||
dbg_print(CCX_DMT_PARSE, ":\n");
|
||||
}
|
||||
dishdata[cc_count*3] = 0xFF; // Set end marker
|
||||
}
|
||||
else
|
||||
{
|
||||
dbg_print(CCX_DMT_PARSE, ":%02X:%02X ",
|
||||
dcd[0], dcd[1]);
|
||||
cc_count = 2;
|
||||
dishdata[3]=0x04; // Field 1
|
||||
dishdata[4]=dcd[0];
|
||||
dishdata[5]=dcd[1];
|
||||
dishdata[6] = 0xFF; // Set end marker
|
||||
|
||||
dbg_print(CCX_DMT_PARSE, ":%s", debug_608toASC( dishdata, 0) );
|
||||
dbg_print(CCX_DMT_PARSE, "%s:\n", debug_608toASC( dishdata+3, 0) );
|
||||
}
|
||||
dbg_print(CCX_DMT_PARSE, ":%s", debug_608toASC( dishdata, 0) );
|
||||
dbg_print(CCX_DMT_PARSE, "%s:\n", debug_608toASC( dishdata+3, 0) );
|
||||
}
|
||||
|
||||
store_hdcc(ctx, dishdata, cc_count, current_tref, fts_now, sub);
|
||||
store_hdcc(ctx, dishdata, cc_count, ctx->timing->current_tref, ctx->timing->fts_now, sub);
|
||||
|
||||
// Ignore 3 (0x0A, followed by 2 unknown) bytes.
|
||||
break;
|
||||
default:
|
||||
// printf ("Unknown?\n");
|
||||
break;
|
||||
} // switch
|
||||
// Ignore 3 (0x0A, followed by 2 unknown) bytes.
|
||||
break;
|
||||
default:
|
||||
// printf ("Unknown?\n");
|
||||
break;
|
||||
} // switch
|
||||
|
||||
dbg_print(CCX_DMT_VERBOSE, "Reading Dish Network user data - done\n");
|
||||
}
|
||||
// CEA 608 / aka "Divicom standard", see:
|
||||
// http://www.pixeltools.com/tech_tip_closed_captioning.html
|
||||
else if ( !memcmp(ud_header,"\x02\x09", 2 ) )
|
||||
{
|
||||
// Either a documentation or more examples are needed.
|
||||
ctx->stat_divicom++;
|
||||
dbg_print(CCX_DMT_VERBOSE, "Reading Dish Network user data - done\n");
|
||||
}
|
||||
// CEA 608 / aka "Divicom standard", see:
|
||||
// http://www.pixeltools.com/tech_tip_closed_captioning.html
|
||||
else if ( !memcmp(ud_header,"\x02\x09", 2 ) )
|
||||
{
|
||||
// Either a documentation or more examples are needed.
|
||||
// ctx->stat_divicom++;
|
||||
|
||||
unsigned char data[3];
|
||||
unsigned char data[3];
|
||||
|
||||
read_bytes(ustream, 2); // "02 09"
|
||||
read_bytes(ustream, 2); // "80 80" ???
|
||||
read_bytes(ustream, 2); // "02 0A" ???
|
||||
data[0]=0x04; // Field 1
|
||||
data[1]=read_u8(ustream);
|
||||
data[2]=read_u8(ustream);
|
||||
do_cb(dec_ctx, data, sub);
|
||||
// This is probably incomplete!
|
||||
}
|
||||
else
|
||||
{
|
||||
// Some other user data
|
||||
// 06 02 ... Seems to be DirectTV
|
||||
dbg_print(CCX_DMT_VERBOSE, "Unrecognized user data:\n");
|
||||
int udatalen = ustream->end - ustream->pos;
|
||||
dump (CCX_DMT_VERBOSE, ustream->pos, (udatalen > 128 ? 128 : udatalen),0 ,0);
|
||||
}
|
||||
read_bytes(ustream, 2); // "02 09"
|
||||
read_bytes(ustream, 2); // "80 80" ???
|
||||
read_bytes(ustream, 2); // "02 0A" ???
|
||||
data[0]=0x04; // Field 1
|
||||
data[1]=read_u8(ustream);
|
||||
data[2]=read_u8(ustream);
|
||||
do_cb(ctx, data, sub);
|
||||
// This is probably incomplete!
|
||||
}
|
||||
else
|
||||
{
|
||||
// Some other user data
|
||||
// 06 02 ... Seems to be DirectTV
|
||||
dbg_print(CCX_DMT_VERBOSE, "Unrecognized user data:\n");
|
||||
int udatalen = ustream->end - ustream->pos;
|
||||
dump (CCX_DMT_VERBOSE, ustream->pos, (udatalen > 128 ? 128 : udatalen),0 ,0);
|
||||
}
|
||||
|
||||
dbg_print(CCX_DMT_VERBOSE, "User data - processed\n");
|
||||
dbg_print(CCX_DMT_VERBOSE, "User data - processed\n");
|
||||
|
||||
// Read complete
|
||||
return 1;
|
||||
// Read complete
|
||||
return 1;
|
||||
}
|
||||
|
||||
92
src/lib_ccx/file_buffer.h
Normal file
92
src/lib_ccx/file_buffer.h
Normal file
@@ -0,0 +1,92 @@
|
||||
#ifndef FILE_BUFFER_H
|
||||
#define FILE_BUFFER_H
|
||||
|
||||
/**
|
||||
* Read from buffer if there is insufficient data then cache the buffer
|
||||
*
|
||||
* @param ctx ccx_demuxer context properly initilaized ccx_demuxer with some input
|
||||
* Not to be NULL, since ctx is derefrenced inside this function
|
||||
*
|
||||
* @param buffer if buffer then it must be allocated to at;east bytes len as
|
||||
* passed in third argument, If buffer is NULL then those number of bytes
|
||||
* are skipped from input.
|
||||
* @param bytes number of bytes to be read from file buffer.
|
||||
*
|
||||
* @return 0 or number of bytes, if returned 0 then op should check error number to know
|
||||
* details of error
|
||||
*/
|
||||
size_t buffered_read_opt (struct ccx_demuxer *ctx, unsigned char *buffer, size_t bytes);
|
||||
|
||||
|
||||
/**
|
||||
* Skip bytes from file buffer and if needed also seek file for number of bytes.
|
||||
*
|
||||
*/
|
||||
static size_t inline buffered_skip(struct ccx_demuxer *ctx, unsigned int bytes)
|
||||
{
|
||||
size_t result;
|
||||
if (bytes <= ctx->bytesinbuffer - ctx->filebuffer_pos)
|
||||
{
|
||||
ctx->filebuffer_pos += bytes;
|
||||
result = bytes;
|
||||
}
|
||||
else
|
||||
{
|
||||
result = buffered_read_opt (ctx, NULL, bytes);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read bytes from file buffer and if needed also read file for number of bytes.
|
||||
*
|
||||
*/
|
||||
static size_t inline buffered_read(struct ccx_demuxer *ctx, unsigned char *buffer, size_t bytes)
|
||||
{
|
||||
size_t result;
|
||||
if (bytes <= ctx->bytesinbuffer - ctx->filebuffer_pos)
|
||||
{
|
||||
if (buffer != NULL)
|
||||
memcpy (buffer, ctx->filebuffer + ctx->filebuffer_pos, bytes);
|
||||
ctx->filebuffer_pos+=bytes;
|
||||
result = bytes;
|
||||
}
|
||||
else
|
||||
{
|
||||
result = buffered_read_opt (ctx, buffer, bytes);
|
||||
if (ccx_options.gui_mode_reports && ccx_options.input_source == CCX_DS_NETWORK)
|
||||
{
|
||||
net_activity_gui++;
|
||||
if (!(net_activity_gui%1000))
|
||||
activity_report_data_read();
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read single byte from file buffer and if needed also read file for number of bytes.
|
||||
*
|
||||
*/
|
||||
static size_t inline buffered_read_byte(struct ccx_demuxer *ctx, unsigned char *buffer)
|
||||
{
|
||||
size_t result;
|
||||
if (ctx->bytesinbuffer - ctx->filebuffer_pos)
|
||||
{
|
||||
if (buffer)
|
||||
{
|
||||
*buffer=ctx->filebuffer[ctx->filebuffer_pos];
|
||||
ctx->filebuffer_pos++;
|
||||
result = 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
result = buffered_read_opt (ctx, buffer, 1);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
unsigned short buffered_get_be16(struct ccx_demuxer *ctx);
|
||||
unsigned char buffered_get_byte (struct ccx_demuxer *ctx);
|
||||
unsigned int buffered_get_be32(struct ccx_demuxer *ctx);
|
||||
#endif
|
||||
@@ -1,22 +1,23 @@
|
||||
#include "lib_ccx.h"
|
||||
#include "ccx_common_option.h"
|
||||
#include "activity.h"
|
||||
#include "file_buffer.h"
|
||||
long FILEBUFFERSIZE = 1024*1024*16; // 16 Mbytes no less. Minimize number of real read calls()
|
||||
LLONG buffered_read_opt_file (unsigned char *buffer, unsigned int bytes);
|
||||
|
||||
#ifdef _WIN32
|
||||
WSADATA wsaData = {0};
|
||||
int iResult = 0;
|
||||
WSADATA wsaData = {0};
|
||||
int iResult = 0;
|
||||
#endif
|
||||
|
||||
LLONG getfilesize (int in)
|
||||
{
|
||||
int ret = 0;
|
||||
LLONG current=LSEEK (in, 0, SEEK_CUR);
|
||||
LLONG length = LSEEK (in,0,SEEK_END);
|
||||
LLONG current = LSEEK (in, 0, SEEK_CUR);
|
||||
LLONG length = LSEEK (in, 0, SEEK_END);
|
||||
if(current < 0 ||length < 0)
|
||||
return -1;
|
||||
|
||||
ret = LSEEK (in,current,SEEK_SET);
|
||||
ret = LSEEK (in, current, SEEK_SET);
|
||||
if (ret < 0)
|
||||
return -1;
|
||||
|
||||
@@ -25,87 +26,69 @@ LLONG getfilesize (int in)
|
||||
|
||||
LLONG gettotalfilessize (struct lib_ccx_ctx *ctx) // -1 if one or more files failed to open
|
||||
{
|
||||
LLONG ts=0;
|
||||
int h;
|
||||
for (int i=0;i<ctx->num_input_files;i++)
|
||||
{
|
||||
if (0 == strcmp(ctx->inputfile[i],"-")) // Skip stdin
|
||||
LLONG ts=0;
|
||||
int h;
|
||||
for (int i = 0; i < ctx->num_input_files; i++)
|
||||
{
|
||||
if (0 == strcmp(ctx->inputfile[i], "-")) // Skip stdin
|
||||
continue;
|
||||
#ifdef _WIN32
|
||||
h=OPEN (ctx->inputfile[i],O_RDONLY | O_BINARY);
|
||||
h = OPEN (ctx->inputfile[i], O_RDONLY | O_BINARY);
|
||||
#else
|
||||
h=OPEN (ctx->inputfile[i],O_RDONLY);
|
||||
h = OPEN (ctx->inputfile[i], O_RDONLY);
|
||||
#endif
|
||||
if (h==-1)
|
||||
{
|
||||
mprint ("\rUnable to open %s\r\n",ctx->inputfile[i]);
|
||||
return -1;
|
||||
}
|
||||
if (!ccx_options.live_stream)
|
||||
ts+=getfilesize (h);
|
||||
close (h);
|
||||
}
|
||||
return ts;
|
||||
if (h == -1)
|
||||
{
|
||||
mprint ("\rUnable to open %s\r\n", ctx->inputfile[i]);
|
||||
return -1;
|
||||
}
|
||||
if (!ccx_options.live_stream)
|
||||
ts += getfilesize (h);
|
||||
close (h);
|
||||
}
|
||||
return ts;
|
||||
}
|
||||
|
||||
void prepare_for_new_file (struct lib_ccx_ctx *ctx)
|
||||
{
|
||||
struct lib_cc_decode *dec_ctx = NULL;
|
||||
dec_ctx = ctx->dec_ctx;
|
||||
// Init per file variables
|
||||
min_pts=0x01FFFFFFFFLL; // 33 bit
|
||||
sync_pts=0;
|
||||
pts_set = 0;
|
||||
// inputsize=0; Now responsibility of switch_to_next_file()
|
||||
ctx->last_reported_progress=-1;
|
||||
ctx->stat_numuserheaders = 0;
|
||||
ctx->stat_dvdccheaders = 0;
|
||||
ctx->stat_scte20ccheaders = 0;
|
||||
ctx->stat_replay5000headers = 0;
|
||||
ctx->stat_replay4000headers = 0;
|
||||
ctx->stat_dishheaders = 0;
|
||||
ctx->stat_hdtv = 0;
|
||||
ctx->stat_divicom = 0;
|
||||
total_frames_count = 0;
|
||||
ctx->total_pulldownfields = 0;
|
||||
ctx->total_pulldownframes = 0;
|
||||
dec_ctx->cc_stats[0]=0; dec_ctx->cc_stats[1]=0; dec_ctx->cc_stats[2]=0; dec_ctx->cc_stats[3]=0;
|
||||
ctx->false_pict_header=0;
|
||||
ctx->frames_since_last_gop=0;
|
||||
frames_since_ref_time=0;
|
||||
gop_time.inited=0;
|
||||
first_gop_time.inited=0;
|
||||
gop_rollover=0;
|
||||
printed_gop.inited=0;
|
||||
dec_ctx->saw_caption_block=0;
|
||||
ctx->past=0;
|
||||
pts_big_change=0;
|
||||
ctx->startbytes_pos=0;
|
||||
ctx->startbytes_avail=0;
|
||||
init_file_buffer();
|
||||
anchor_hdcc(-1);
|
||||
firstcall = 1;
|
||||
for(int x=0; x<0xfff; x++) {
|
||||
ctx->epg_buffers[x].buffer=NULL;
|
||||
ctx->epg_buffers[x].ccounter=0;
|
||||
ctx->last_reported_progress =-1;
|
||||
ctx->stat_numuserheaders = 0;
|
||||
ctx->stat_dvdccheaders = 0;
|
||||
ctx->stat_scte20ccheaders = 0;
|
||||
ctx->stat_replay5000headers = 0;
|
||||
ctx->stat_replay4000headers = 0;
|
||||
ctx->stat_dishheaders = 0;
|
||||
ctx->stat_hdtv = 0;
|
||||
ctx->stat_divicom = 0;
|
||||
total_frames_count = 0;
|
||||
ctx->false_pict_header = 0;
|
||||
frames_since_ref_time = 0;
|
||||
gop_time.inited = 0;
|
||||
first_gop_time.inited = 0;
|
||||
gop_rollover = 0;
|
||||
printed_gop.inited = 0;
|
||||
pts_big_change = 0;
|
||||
firstcall = 1;
|
||||
|
||||
for(int x = 0; x < 0xfff; x++)
|
||||
{
|
||||
ctx->epg_buffers[x].buffer = NULL;
|
||||
ctx->epg_buffers[x].ccounter = 0;
|
||||
}
|
||||
for (int i = 0; i < TS_PMT_MAP_SIZE; i++) {
|
||||
ctx->eit_programs[i].array_len=0;
|
||||
ctx->eit_current_events[i]=-1;
|
||||
for (int i = 0; i < TS_PMT_MAP_SIZE; i++)
|
||||
{
|
||||
ctx->eit_programs[i].array_len = 0;
|
||||
ctx->eit_current_events[i] = -1;
|
||||
}
|
||||
ctx->epg_last_output=-1;
|
||||
ctx->epg_last_live_output=-1;
|
||||
ctx->epg_last_output = -1;
|
||||
ctx->epg_last_live_output = -1;
|
||||
}
|
||||
|
||||
/* Close input file if there is one and let the GUI know */
|
||||
void close_input_file (struct lib_ccx_ctx *ctx)
|
||||
{
|
||||
if (ctx->infd!=-1 && ccx_options.input_source==CCX_DS_FILE)
|
||||
{
|
||||
close (ctx->infd);
|
||||
ctx->infd=-1;
|
||||
activity_input_file_closed();
|
||||
}
|
||||
ctx->demux_ctx->close(ctx->demux_ctx);
|
||||
}
|
||||
|
||||
/* Close current file and open next one in list -if any- */
|
||||
@@ -115,368 +98,389 @@ can be done */
|
||||
|
||||
int switch_to_next_file (struct lib_ccx_ctx *ctx, LLONG bytesinbuffer)
|
||||
{
|
||||
struct lib_cc_decode *dec_ctx = NULL;
|
||||
dec_ctx = ctx->dec_ctx;
|
||||
if (ctx->current_file==-1 || !ccx_options.binary_concat)
|
||||
int ret = 0;
|
||||
if (ctx->current_file == -1 || !ccx_options.binary_concat)
|
||||
{
|
||||
memset (ctx->PIDs_seen,0,65536*sizeof (int));
|
||||
memset (ctx->PIDs_programs,0,65536*sizeof (struct PMT_entry *));
|
||||
ctx->demux_ctx->reset(ctx->demux_ctx);
|
||||
}
|
||||
|
||||
if (ccx_options.input_source==CCX_DS_STDIN)
|
||||
switch(ccx_options.input_source)
|
||||
{
|
||||
if (ctx->infd!=-1) // Means we had already processed stdin. So we're done.
|
||||
{
|
||||
if (ccx_options.print_file_reports)
|
||||
print_file_report(ctx);
|
||||
return 0;
|
||||
}
|
||||
ctx->infd=0;
|
||||
mprint ("\n\r-----------------------------------------------------------------\n");
|
||||
mprint ("\rReading from standard input\n");
|
||||
return 1;
|
||||
case CCX_DS_STDIN:
|
||||
case CCX_DS_NETWORK:
|
||||
case CCX_DS_TCP:
|
||||
ret = ctx->demux_ctx->open(ctx->demux_ctx, NULL);
|
||||
if (ret < 0)
|
||||
return 0;
|
||||
else if (ret)
|
||||
return ret;
|
||||
else
|
||||
return 1;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (ccx_options.input_source==CCX_DS_NETWORK)
|
||||
/* Close current and make sure things are still sane */
|
||||
if (ctx->demux_ctx->is_open(ctx->demux_ctx))
|
||||
{
|
||||
if (ctx->infd!=-1) // Means we have already bound a socket.
|
||||
{
|
||||
if (ccx_options.print_file_reports)
|
||||
print_file_report(ctx);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
ctx->infd = start_upd_srv(ccx_options.udpaddr, ccx_options.udpport);
|
||||
return 1;
|
||||
if(ctx->infd < 0)
|
||||
fatal (CCX_COMMON_EXIT_BUG_BUG, "socket() failed.");
|
||||
|
||||
}
|
||||
|
||||
if (ccx_options.input_source==CCX_DS_TCP)
|
||||
{
|
||||
if (ctx->infd != -1)
|
||||
{
|
||||
if (ccx_options.print_file_reports)
|
||||
print_file_report(ctx);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
ctx->infd = start_tcp_srv(ccx_options.tcpport, ccx_options.tcp_password);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Close current and make sure things are still sane */
|
||||
if (ctx->infd!=-1)
|
||||
{
|
||||
if (ccx_options.print_file_reports)
|
||||
print_file_report(ctx);
|
||||
close_input_file (ctx);
|
||||
if (ctx->inputsize>0 && ((ctx->past+bytesinbuffer) < ctx->inputsize) && !dec_ctx->processed_enough)
|
||||
{
|
||||
mprint("\n\n\n\nATTENTION!!!!!!\n");
|
||||
mprint("In switch_to_next_file(): Processing of %s %d ended prematurely %lld < %lld, please send bug report.\n\n",
|
||||
ctx->inputfile[ctx->current_file], ctx->current_file, ctx->past, ctx->inputsize);
|
||||
}
|
||||
if (ccx_options.binary_concat)
|
||||
{
|
||||
ctx->total_past+=ctx->inputsize;
|
||||
ctx->past=0; // Reset always or at the end we'll have double the size
|
||||
}
|
||||
}
|
||||
for (;;)
|
||||
{
|
||||
ctx->current_file++;
|
||||
if (ctx->current_file>=ctx->num_input_files)
|
||||
break;
|
||||
|
||||
// The following \n keeps the progress percentage from being overwritten.
|
||||
mprint ("\n\r-----------------------------------------------------------------\n");
|
||||
if (ctx->inputsize > 0 && ((ctx->demux_ctx->past+bytesinbuffer) < ctx->inputsize) && is_decoder_processed_enough(ctx) == CCX_FALSE)
|
||||
{
|
||||
mprint("\n\n\n\nATTENTION!!!!!!\n");
|
||||
mprint("In switch_to_next_file(): Processing of %s %d ended prematurely %lld < %lld, please send bug report.\n\n",
|
||||
ctx->inputfile[ctx->current_file], ctx->current_file, ctx->demux_ctx->past, ctx->inputsize);
|
||||
}
|
||||
close_input_file (ctx);
|
||||
|
||||
if (ccx_options.binary_concat)
|
||||
{
|
||||
ctx->total_past += ctx->inputsize;
|
||||
ctx->demux_ctx->past = 0; // Reset always or at the end we'll have double the size
|
||||
}
|
||||
}
|
||||
for (;;)
|
||||
{
|
||||
ctx->current_file++;
|
||||
if (ctx->current_file >= ctx->num_input_files)
|
||||
break;
|
||||
|
||||
// The following \n keeps the progress percentage from being overwritten.
|
||||
mprint ("\n\r-----------------------------------------------------------------\n");
|
||||
mprint ("\rOpening file: %s\n", ctx->inputfile[ctx->current_file]);
|
||||
#ifdef _WIN32
|
||||
ctx->infd=OPEN (ctx->inputfile[ctx->current_file],O_RDONLY | O_BINARY);
|
||||
#else
|
||||
ctx->infd=OPEN (ctx->inputfile[ctx->current_file],O_RDONLY);
|
||||
#endif
|
||||
if (ctx->infd == -1)
|
||||
mprint ("\rWarning: Unable to open input file [%s]\n", ctx->inputfile[ctx->current_file]);
|
||||
else
|
||||
{
|
||||
activity_input_file_open (ctx->inputfile[ctx->current_file]);
|
||||
if (!ccx_options.live_stream)
|
||||
{
|
||||
ctx->inputsize = getfilesize (ctx->infd);
|
||||
if (!ccx_options.binary_concat)
|
||||
ctx->total_inputsize=ctx->inputsize;
|
||||
}
|
||||
return 1; // Succeeded
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
ret = ctx->demux_ctx->open(ctx->demux_ctx, ctx->inputfile[ctx->current_file]);
|
||||
if (ret < 0)
|
||||
mprint ("\rWarning: Unable to open input file [%s]\n", ctx->inputfile[ctx->current_file]);
|
||||
else
|
||||
{
|
||||
activity_input_file_open (ctx->inputfile[ctx->current_file]);
|
||||
if (!ccx_options.live_stream)
|
||||
{
|
||||
ctx->inputsize = ctx->demux_ctx->get_filesize (ctx->demux_ctx);
|
||||
if (!ccx_options.binary_concat)
|
||||
ctx->total_inputsize = ctx->inputsize;
|
||||
}
|
||||
return 1; // Succeeded
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void position_sanity_check (void)
|
||||
void position_sanity_check (int in)
|
||||
{
|
||||
#ifdef SANITY_CHECK
|
||||
if (in!=-1)
|
||||
{
|
||||
LLONG realpos=LSEEK (in,0,SEEK_CUR);
|
||||
if (realpos!=ctx->past-filebuffer_pos+bytesinbuffer)
|
||||
{
|
||||
fatal (CCX_COMMON_EXIT_BUG_BUG, "Position desync, THIS IS A BUG. Real pos =%lld, past=%lld.\n",realpos,ctx->past);
|
||||
}
|
||||
}
|
||||
if (in!=-1)
|
||||
{
|
||||
LLONG realpos = LSEEK (in,0,SEEK_CUR);
|
||||
if (realpos != ctx->demux_ctx->past - filebuffer_pos + bytesinbuffer)
|
||||
{
|
||||
fatal (CCX_COMMON_EXIT_BUG_BUG, "Position desync, THIS IS A BUG. Real pos =%lld, past=%lld.\n", realpos, ctx->demux_ctx->past);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
int init_file_buffer(void)
|
||||
int init_file_buffer(struct ccx_demuxer *ctx)
|
||||
{
|
||||
filebuffer_start=0;
|
||||
filebuffer_pos=0;
|
||||
if (filebuffer==NULL)
|
||||
{
|
||||
filebuffer=(unsigned char *) malloc (FILEBUFFERSIZE);
|
||||
bytesinbuffer=0;
|
||||
}
|
||||
if (filebuffer==NULL)
|
||||
{
|
||||
fatal (EXIT_NOT_ENOUGH_MEMORY, "Not enough memory\n");
|
||||
}
|
||||
return 0;
|
||||
ctx->filebuffer_start = 0;
|
||||
ctx->filebuffer_pos = 0;
|
||||
if (ctx->filebuffer == NULL)
|
||||
{
|
||||
ctx->filebuffer = (unsigned char *) malloc (FILEBUFFERSIZE);
|
||||
ctx->bytesinbuffer = 0;
|
||||
}
|
||||
if (ctx->filebuffer == NULL)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void buffered_seek (struct lib_ccx_ctx *ctx, int offset)
|
||||
void buffered_seek (struct ccx_demuxer *ctx, int offset)
|
||||
{
|
||||
position_sanity_check();
|
||||
if (offset<0)
|
||||
{
|
||||
filebuffer_pos+=offset;
|
||||
if (filebuffer_pos<0)
|
||||
{
|
||||
// We got into the start buffer (hopefully)
|
||||
if ((filebuffer_pos+ctx->startbytes_pos) < 0)
|
||||
{
|
||||
fatal (CCX_COMMON_EXIT_BUG_BUG, "PANIC: Attempt to seek before buffer start, this is a bug!");
|
||||
}
|
||||
ctx->startbytes_pos+=filebuffer_pos;
|
||||
filebuffer_pos=0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
buffered_read_opt (ctx, NULL, offset);
|
||||
position_sanity_check();
|
||||
}
|
||||
position_sanity_check(ctx->infd);
|
||||
if (offset < 0)
|
||||
{
|
||||
ctx->filebuffer_pos += offset;
|
||||
if (ctx->filebuffer_pos < 0)
|
||||
{
|
||||
// We got into the start buffer (hopefully)
|
||||
if ((ctx->filebuffer_pos + ctx->startbytes_pos) < 0)
|
||||
{
|
||||
fatal (CCX_COMMON_EXIT_BUG_BUG, "PANIC: Attempt to seek before buffer start, this is a bug!");
|
||||
}
|
||||
ctx->startbytes_pos += ctx->filebuffer_pos;
|
||||
ctx->filebuffer_pos = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
buffered_read_opt (ctx, NULL, offset);
|
||||
position_sanity_check(ctx->infd);
|
||||
}
|
||||
}
|
||||
|
||||
void sleepandchecktimeout (time_t start)
|
||||
{
|
||||
if (ccx_options.input_source==CCX_DS_STDIN)
|
||||
{
|
||||
if (ccx_options.input_source == CCX_DS_STDIN)
|
||||
{
|
||||
// CFS: Not 100% sure about this. Fine for files, not so sure what happens if stdin is
|
||||
// real time input from hardware.
|
||||
sleep_secs (1);
|
||||
ccx_options.live_stream=0;
|
||||
return;
|
||||
}
|
||||
sleep_secs (1);
|
||||
ccx_options.live_stream = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
if (ccx_options.live_stream==-1) // Just sleep, no timeout to check
|
||||
{
|
||||
sleep_secs (1);
|
||||
return;
|
||||
}
|
||||
if (time(NULL)>start+ccx_options.live_stream) // More than live_stream seconds elapsed. No more live
|
||||
ccx_options.live_stream=0;
|
||||
else
|
||||
sleep_secs(1);
|
||||
if (ccx_options.live_stream == -1) // Just sleep, no timeout to check
|
||||
{
|
||||
sleep_secs (1);
|
||||
return;
|
||||
}
|
||||
if (time(NULL) > start + ccx_options.live_stream) // More than live_stream seconds elapsed. No more live
|
||||
ccx_options.live_stream = 0;
|
||||
else
|
||||
sleep_secs(1);
|
||||
}
|
||||
|
||||
void return_to_buffer (unsigned char *buffer, unsigned int bytes)
|
||||
void return_to_buffer (struct ccx_demuxer *ctx, unsigned char *buffer, unsigned int bytes)
|
||||
{
|
||||
if (bytes == filebuffer_pos)
|
||||
if (bytes == ctx->filebuffer_pos)
|
||||
{
|
||||
// Usually we're just going back in the buffer and memcpy would be
|
||||
// unnecessary, but we do it in case we intentionally messed with the
|
||||
// buffer
|
||||
memcpy (filebuffer, buffer, bytes);
|
||||
filebuffer_pos=0;
|
||||
memcpy (ctx->filebuffer, buffer, bytes);
|
||||
ctx->filebuffer_pos = 0;
|
||||
return;
|
||||
}
|
||||
if (filebuffer_pos>0) // Discard old bytes, because we may need the space
|
||||
if (ctx->filebuffer_pos > 0) // Discard old bytes, because we may need the space
|
||||
{
|
||||
// Non optimal since data is moved later again but we don't care since
|
||||
// we're never here in ccextractor.
|
||||
memmove (filebuffer,filebuffer+filebuffer_pos,bytesinbuffer-filebuffer_pos);
|
||||
bytesinbuffer-=filebuffer_pos;
|
||||
bytesinbuffer=0;
|
||||
filebuffer_pos=0;
|
||||
memmove (ctx->filebuffer, ctx->filebuffer + ctx->filebuffer_pos, ctx->bytesinbuffer-ctx->filebuffer_pos);
|
||||
ctx->bytesinbuffer -= ctx->filebuffer_pos;
|
||||
ctx->bytesinbuffer = 0;
|
||||
ctx->filebuffer_pos = 0;
|
||||
}
|
||||
|
||||
if (bytesinbuffer + bytes > FILEBUFFERSIZE)
|
||||
if (ctx->bytesinbuffer + bytes > FILEBUFFERSIZE)
|
||||
fatal (CCX_COMMON_EXIT_BUG_BUG, "Invalid return_to_buffer() - please submit a bug report.");
|
||||
memmove (filebuffer+bytes,filebuffer,bytesinbuffer);
|
||||
memcpy (filebuffer,buffer,bytes);
|
||||
bytesinbuffer+=bytes;
|
||||
|
||||
memmove (ctx->filebuffer + bytes, ctx->filebuffer, ctx->bytesinbuffer);
|
||||
memcpy (ctx->filebuffer, buffer, bytes);
|
||||
ctx->bytesinbuffer += bytes;
|
||||
}
|
||||
|
||||
LLONG buffered_read_opt (struct lib_ccx_ctx *ctx, unsigned char *buffer, unsigned int bytes)
|
||||
/**
|
||||
* @param buffer can be NULL, in case when user want to just buffer it or skip some data.
|
||||
*
|
||||
* Global options that have efffect on this function are following
|
||||
* 1) ccx_options.live_stream
|
||||
* 2) ccx_options.buffer_input
|
||||
* 3) ccx_options.input_source
|
||||
* 4) ccx_options.binary_concat
|
||||
*
|
||||
* TODO instead of using global ccx_options move them to ccx_demuxer
|
||||
*/
|
||||
size_t buffered_read_opt (struct ccx_demuxer *ctx, unsigned char *buffer, size_t bytes)
|
||||
{
|
||||
LLONG copied=0;
|
||||
position_sanity_check();
|
||||
time_t seconds=0;
|
||||
if (ccx_options.live_stream>0)
|
||||
time (&seconds);
|
||||
if (ccx_options.buffer_input || filebuffer_pos<bytesinbuffer)
|
||||
{
|
||||
// Needs to return data from filebuffer_start+pos to filebuffer_start+pos+bytes-1;
|
||||
int eof = (ctx->infd==-1);
|
||||
size_t copied = 0;
|
||||
time_t seconds = 0;
|
||||
|
||||
while ((!eof || ccx_options.live_stream) && bytes)
|
||||
{
|
||||
if (eof)
|
||||
{
|
||||
// No more data available inmediately, we sleep a while to give time
|
||||
// for the data to come up
|
||||
sleepandchecktimeout (seconds);
|
||||
}
|
||||
size_t ready = bytesinbuffer-filebuffer_pos;
|
||||
if (ready==0) // We really need to read more
|
||||
{
|
||||
if (!ccx_options.buffer_input)
|
||||
{
|
||||
// We got in the buffering code because of the initial buffer for
|
||||
// detection stuff. However we don't want more buffering so
|
||||
// we do the rest directly on the final buffer.
|
||||
int i;
|
||||
do
|
||||
{
|
||||
position_sanity_check(ctx->infd);
|
||||
|
||||
if (ccx_options.live_stream > 0)
|
||||
time (&seconds);
|
||||
|
||||
if (ccx_options.buffer_input || ctx->filebuffer_pos < ctx->bytesinbuffer)
|
||||
{
|
||||
// Needs to return data from filebuffer_start+pos to filebuffer_start+pos+bytes-1;
|
||||
int eof = (ctx->infd == -1);
|
||||
|
||||
while ((!eof || ccx_options.live_stream) && bytes)
|
||||
{
|
||||
if (eof)
|
||||
{
|
||||
// No more data available inmediately, we sleep a while to give time
|
||||
// for the data to come up
|
||||
sleepandchecktimeout (seconds);
|
||||
}
|
||||
size_t ready = ctx->bytesinbuffer - ctx->filebuffer_pos;
|
||||
if (ready == 0) // We really need to read more
|
||||
{
|
||||
if (!ccx_options.buffer_input)
|
||||
{
|
||||
// We got in the buffering code because of the initial buffer for
|
||||
// detection stuff. However we don't want more buffering so
|
||||
// we do the rest directly on the final buffer.
|
||||
int i;
|
||||
do
|
||||
{
|
||||
// No code for network support here, because network is always
|
||||
// buffered - if here, then it must be files.
|
||||
if (buffer!=NULL) // Read
|
||||
{
|
||||
i=read (ctx->infd,buffer,bytes);
|
||||
if( i == -1)
|
||||
fatal (EXIT_READ_ERROR, "Error reading input file!\n");
|
||||
buffer+=i;
|
||||
}
|
||||
else // Seek
|
||||
{
|
||||
if (buffer != NULL) // Read
|
||||
{
|
||||
i = read (ctx->infd, buffer, bytes);
|
||||
if( i == -1)
|
||||
fatal (EXIT_READ_ERROR, "Error reading input file!\n");
|
||||
buffer += i;
|
||||
}
|
||||
else // Seek
|
||||
{
|
||||
LLONG op, np;
|
||||
op =LSEEK (ctx->infd,0,SEEK_CUR); // Get current pos
|
||||
if (op+bytes<0) // Would mean moving beyond start of file: Not supported
|
||||
return 0;
|
||||
np =LSEEK (ctx->infd,bytes,SEEK_CUR); // Pos after moving
|
||||
i=(int) (np-op);
|
||||
}
|
||||
if (i==0 && ccx_options.live_stream)
|
||||
{
|
||||
if (ccx_options.input_source==CCX_DS_STDIN)
|
||||
{
|
||||
ccx_options.live_stream = 0;
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
sleepandchecktimeout (seconds);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
copied+=i;
|
||||
bytes-=i;
|
||||
}
|
||||
op = LSEEK (ctx->infd, 0, SEEK_CUR); // Get current pos
|
||||
if (op + bytes < 0) // Would mean moving beyond start of file: Not supported
|
||||
return 0;
|
||||
np = LSEEK (ctx->infd, bytes, SEEK_CUR); // Pos after moving
|
||||
i = (int) (np - op);
|
||||
}
|
||||
// if both above lseek returned -1 (error); i would be 0 here and
|
||||
// in case when its not live stream copied would decrease and bytes would...
|
||||
if (i == 0 && ccx_options.live_stream)
|
||||
{
|
||||
if (ccx_options.input_source == CCX_DS_STDIN)
|
||||
{
|
||||
ccx_options.live_stream = 0;
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
sleepandchecktimeout (seconds);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
copied += i;
|
||||
bytes -= i;
|
||||
}
|
||||
|
||||
}
|
||||
while ((i || ccx_options.live_stream ||
|
||||
(ccx_options.binary_concat && switch_to_next_file(ctx, copied))) && bytes);
|
||||
return copied;
|
||||
}
|
||||
// Keep the last 8 bytes, so we have a guaranteed
|
||||
// working seek (-8) - needed by mythtv.
|
||||
int keep = bytesinbuffer > 8 ? 8 : bytesinbuffer;
|
||||
memmove (filebuffer,filebuffer+(FILEBUFFERSIZE-keep),keep);
|
||||
}
|
||||
while ((i || ccx_options.live_stream ||
|
||||
(ccx_options.binary_concat && switch_to_next_file(ctx->parent, copied))) && bytes);
|
||||
return copied;
|
||||
}
|
||||
// Keep the last 8 bytes, so we have a guaranteed
|
||||
// working seek (-8) - needed by mythtv.
|
||||
int keep = ctx->bytesinbuffer > 8 ? 8 : ctx->bytesinbuffer;
|
||||
memmove (ctx->filebuffer, ctx->filebuffer+(FILEBUFFERSIZE-keep),keep);
|
||||
int i;
|
||||
if (ccx_options.input_source==CCX_DS_FILE || ccx_options.input_source==CCX_DS_STDIN)
|
||||
i=read (ctx->infd, filebuffer+keep,FILEBUFFERSIZE-keep);
|
||||
if (ccx_options.input_source == CCX_DS_FILE || ccx_options.input_source == CCX_DS_STDIN)
|
||||
i = read (ctx->infd, ctx->filebuffer + keep, FILEBUFFERSIZE-keep);
|
||||
else if (ccx_options.input_source == CCX_DS_TCP)
|
||||
i = net_tcp_read(ctx->infd, (char *) ctx->filebuffer + keep, FILEBUFFERSIZE - keep);
|
||||
else
|
||||
i = recvfrom(ctx->infd,(char *) filebuffer+keep,FILEBUFFERSIZE-keep,0,NULL,NULL);
|
||||
if (i == -1)
|
||||
fatal (EXIT_READ_ERROR, "Error reading input stream!\n");
|
||||
if (i==0)
|
||||
{
|
||||
/* If live stream, don't try to switch - acknowledge eof here as it won't
|
||||
cause a loop end */
|
||||
if (ccx_options.live_stream || !(ccx_options.binary_concat && switch_to_next_file(ctx, copied)))
|
||||
eof=1;
|
||||
}
|
||||
filebuffer_pos=keep;
|
||||
bytesinbuffer=(int) i+keep;
|
||||
ready=i;
|
||||
}
|
||||
int copy = (int) (ready>=bytes ? bytes:ready);
|
||||
if (copy)
|
||||
{
|
||||
if (buffer!=NULL)
|
||||
{
|
||||
memcpy (buffer, filebuffer+filebuffer_pos, copy);
|
||||
buffer+=copy;
|
||||
}
|
||||
filebuffer_pos+=copy;
|
||||
bytes-=copy;
|
||||
copied+=copy;
|
||||
}
|
||||
}
|
||||
return copied;
|
||||
}
|
||||
else // Read without buffering
|
||||
{
|
||||
i = recvfrom(ctx->infd,(char *) ctx->filebuffer + keep, FILEBUFFERSIZE - keep, 0, NULL, NULL);
|
||||
if (i == -1)
|
||||
fatal (EXIT_READ_ERROR, "Error reading input stream!\n");
|
||||
if (i == 0)
|
||||
{
|
||||
/* If live stream, don't try to switch - acknowledge eof here as it won't
|
||||
cause a loop end */
|
||||
if (ccx_options.live_stream || !(ccx_options.binary_concat && switch_to_next_file(ctx->parent, copied)))
|
||||
eof = 1;
|
||||
}
|
||||
ctx->filebuffer_pos = keep;
|
||||
ctx->bytesinbuffer = (int) i + keep;
|
||||
ready = i;
|
||||
}
|
||||
int copy = (int) (ready>=bytes ? bytes:ready);
|
||||
if (copy)
|
||||
{
|
||||
if (buffer != NULL)
|
||||
{
|
||||
memcpy (buffer, ctx->filebuffer + ctx->filebuffer_pos, copy);
|
||||
buffer += copy;
|
||||
}
|
||||
ctx->filebuffer_pos += copy;
|
||||
bytes -= copy;
|
||||
copied += copy;
|
||||
}
|
||||
}
|
||||
}
|
||||
else // Read without buffering
|
||||
{
|
||||
|
||||
if (buffer!=NULL)
|
||||
{
|
||||
int i;
|
||||
while (bytes>0 && ctx->infd!=-1 &&
|
||||
((i=read(ctx->infd,buffer,bytes))!=0 || ccx_options.live_stream ||
|
||||
(ccx_options.binary_concat && switch_to_next_file(ctx, copied))))
|
||||
{
|
||||
if( i == -1)
|
||||
fatal (EXIT_READ_ERROR, "Error reading input file!\n");
|
||||
else if (i==0)
|
||||
sleepandchecktimeout (seconds);
|
||||
else
|
||||
{
|
||||
copied+=i;
|
||||
bytes-=i;
|
||||
buffer+=i;
|
||||
}
|
||||
}
|
||||
return copied;
|
||||
}
|
||||
// return fread(buffer,1,bytes,in);
|
||||
//return FSEEK (in,bytes,SEEK_CUR);
|
||||
while (bytes!=0 && ctx->infd!=-1)
|
||||
{
|
||||
if (buffer != NULL)
|
||||
{
|
||||
int i;
|
||||
while (bytes > 0 && ctx->infd != -1 &&
|
||||
((i = read(ctx->infd, buffer, bytes)) != 0 || ccx_options.live_stream ||
|
||||
(ccx_options.binary_concat && switch_to_next_file(ctx->parent, copied))))
|
||||
{
|
||||
if( i == -1)
|
||||
fatal (EXIT_READ_ERROR, "Error reading input file!\n");
|
||||
else if (i == 0)
|
||||
sleepandchecktimeout (seconds);
|
||||
else
|
||||
{
|
||||
copied += i;
|
||||
bytes -= i;
|
||||
buffer += i;
|
||||
}
|
||||
}
|
||||
return copied;
|
||||
}
|
||||
while (bytes != 0 && ctx->infd != -1)
|
||||
{
|
||||
LLONG op, np;
|
||||
op =LSEEK (ctx->infd,0,SEEK_CUR); // Get current pos
|
||||
if (op+bytes<0) // Would mean moving beyond start of file: Not supported
|
||||
return 0;
|
||||
np =LSEEK (ctx->infd,bytes,SEEK_CUR); // Pos after moving
|
||||
copied=copied+(np-op);
|
||||
bytes=bytes-(unsigned int) copied;
|
||||
if (copied==0)
|
||||
{
|
||||
if (ccx_options.live_stream)
|
||||
sleepandchecktimeout (seconds);
|
||||
else
|
||||
{
|
||||
if (ccx_options.binary_concat)
|
||||
switch_to_next_file(ctx, 0);
|
||||
else
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return copied;
|
||||
}
|
||||
op = LSEEK (ctx->infd, 0, SEEK_CUR); // Get current pos
|
||||
if (op + bytes < 0) // Would mean moving beyond start of file: Not supported
|
||||
return 0;
|
||||
|
||||
np = LSEEK (ctx->infd, bytes, SEEK_CUR); // Pos after moving
|
||||
copied = copied + (np - op);
|
||||
bytes = bytes- (unsigned int) copied;
|
||||
if (copied == 0)
|
||||
{
|
||||
if (ccx_options.live_stream)
|
||||
sleepandchecktimeout (seconds);
|
||||
else
|
||||
{
|
||||
if (ccx_options.binary_concat)
|
||||
switch_to_next_file(ctx->parent, 0);
|
||||
else
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return copied;
|
||||
}
|
||||
|
||||
unsigned short buffered_get_be16(struct ccx_demuxer *ctx)
|
||||
{
|
||||
unsigned char a,b;
|
||||
unsigned char *a_p = &a; // Just to suppress warnings
|
||||
unsigned char *b_p = &b;
|
||||
buffered_read_byte(ctx, a_p);
|
||||
ctx->past++;
|
||||
buffered_read_byte(ctx, b_p);
|
||||
ctx->past++;
|
||||
return ( (unsigned short) (a<<8) )| ( (unsigned short) b);
|
||||
}
|
||||
|
||||
unsigned char buffered_get_byte (struct ccx_demuxer *ctx)
|
||||
{
|
||||
unsigned char b;
|
||||
unsigned char *b_p = &b;
|
||||
size_t result;
|
||||
|
||||
result = buffered_read_byte(ctx, b_p);
|
||||
if (result == 1)
|
||||
{
|
||||
ctx->past++;
|
||||
return b;
|
||||
}
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
unsigned int buffered_get_be32(struct ccx_demuxer *ctx)
|
||||
{
|
||||
unsigned int val;
|
||||
val = buffered_get_be16(ctx) << 16;
|
||||
val |= buffered_get_be16(ctx);
|
||||
return val;
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,10 @@
|
||||
#include "lib_ccx.h"
|
||||
#include "ccx_common_option.h"
|
||||
#include "activity.h"
|
||||
#include "utility.h"
|
||||
#include "dvb_subtitle_decoder.h"
|
||||
#include "ccx_decoders_708.h"
|
||||
#include "ccx_decoders_isdb.h"
|
||||
|
||||
struct ccx_common_logging_t ccx_common_logging;
|
||||
static struct ccx_decoders_common_settings_t *init_decoder_setting(
|
||||
@@ -14,9 +19,20 @@ static struct ccx_decoders_common_settings_t *init_decoder_setting(
|
||||
setting->subs_delay = opt->subs_delay;
|
||||
setting->output_format = opt->write_format;
|
||||
setting->fix_padding = opt->fix_padding;
|
||||
setting->extract = opt->extract;
|
||||
setting->fullbin = opt->fullbin;
|
||||
setting->no_rollup = opt->no_rollup;
|
||||
setting->noscte20 = opt->noscte20;
|
||||
memcpy(&setting->extraction_start,&opt->extraction_start,sizeof(struct ccx_boundary_time));
|
||||
memcpy(&setting->extraction_end,&opt->extraction_end,sizeof(struct ccx_boundary_time));
|
||||
setting->cc_to_stdout = opt->cc_to_stdout;
|
||||
setting->settings_608 = &opt->settings_608;
|
||||
setting->settings_dtvcc = &opt->settings_dtvcc;
|
||||
setting->cc_channel = opt->cc_channel;
|
||||
setting->send_to_srv = opt->send_to_srv;
|
||||
setting->hauppauge_mode = opt->hauppauge_mode;
|
||||
/* if in transcript setting xds is not selected then set ignore xds flag */
|
||||
setting->ignore_xds = !opt->transcript_settings.xds;
|
||||
return setting;
|
||||
}
|
||||
static void dinit_decoder_setting (struct ccx_decoders_common_settings_t **setting)
|
||||
@@ -24,40 +40,78 @@ static void dinit_decoder_setting (struct ccx_decoders_common_settings_t **setti
|
||||
freep(setting);
|
||||
}
|
||||
|
||||
|
||||
static int init_ctx_outbase(struct ccx_s_options *opt, struct lib_ccx_ctx *ctx)
|
||||
{
|
||||
char *file;
|
||||
|
||||
if (opt->output_filename)
|
||||
{
|
||||
ctx->basefilename = get_basename(opt->output_filename);
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (opt->input_source)
|
||||
{
|
||||
case CCX_DS_FILE:
|
||||
if(!ctx->inputfile || !ctx->inputfile[0])
|
||||
{
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
file = ctx->inputfile[0];
|
||||
break;
|
||||
case CCX_DS_STDIN:
|
||||
file = "stdin";
|
||||
break;
|
||||
case CCX_DS_NETWORK:
|
||||
case CCX_DS_TCP:
|
||||
file = "network";
|
||||
break;
|
||||
default:
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
ctx->basefilename = get_basename(file);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct encoder_ctx *get_encoder_by_pn(struct lib_ccx_ctx *ctx, int pn)
|
||||
{
|
||||
struct encoder_ctx *enc_ctx;
|
||||
list_for_each_entry(enc_ctx, &ctx->enc_ctx_head, list, struct encoder_ctx)
|
||||
{
|
||||
if (enc_ctx->program_number == pn)
|
||||
return enc_ctx;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct lib_ccx_ctx* init_libraries(struct ccx_s_options *opt)
|
||||
{
|
||||
struct lib_ccx_ctx *ctx;
|
||||
struct ccx_decoder_608_report *report_608;
|
||||
struct ccx_decoders_common_settings_t *dec_setting;
|
||||
int ret = 0;
|
||||
|
||||
ctx = malloc(sizeof(struct lib_ccx_ctx));
|
||||
struct lib_ccx_ctx *ctx = malloc(sizeof(struct lib_ccx_ctx));
|
||||
if(!ctx)
|
||||
return NULL;
|
||||
memset(ctx,0,sizeof(struct lib_ccx_ctx));
|
||||
ccx_common_logging.fatal_ftn(EXIT_NOT_ENOUGH_MEMORY, "lib_ccx_ctx");
|
||||
memset(ctx, 0, sizeof(struct lib_ccx_ctx));
|
||||
|
||||
report_608 = malloc(sizeof(struct ccx_decoder_608_report));
|
||||
struct ccx_decoder_608_report *report_608 = malloc(sizeof(struct ccx_decoder_608_report));
|
||||
if (!report_608)
|
||||
return NULL;
|
||||
memset(report_608,0,sizeof(struct ccx_decoder_608_report));
|
||||
ccx_common_logging.fatal_ftn(EXIT_NOT_ENOUGH_MEMORY, "report_608");
|
||||
memset(report_608, 0, sizeof(struct ccx_decoder_608_report));
|
||||
|
||||
ctx->capbufsize = 20000;
|
||||
ctx->capbuf = NULL;
|
||||
ctx->capbuflen = 0; // Bytes read in capbuf
|
||||
ccx_decoder_dtvcc_report *report_dtvcc = (ccx_decoder_dtvcc_report *)
|
||||
malloc(sizeof(ccx_decoder_dtvcc_report));
|
||||
if (!report_dtvcc)
|
||||
ccx_common_logging.fatal_ftn(EXIT_NOT_ENOUGH_MEMORY, "report_dtvcc");
|
||||
memset(report_dtvcc, 0, sizeof(ccx_decoder_dtvcc_report));
|
||||
|
||||
// Initialize some constants
|
||||
init_ts(ctx);
|
||||
init_avc();
|
||||
|
||||
ctx->stream_mode = CCX_SM_ELEMENTARY_OR_NOT_FOUND;
|
||||
ctx->auto_stream = opt->auto_stream;
|
||||
ctx->m2ts = opt->m2ts;
|
||||
ctx->screens_to_process = -1;
|
||||
ctx->current_file = -1;
|
||||
ctx->infd = -1;//Set to -1 to indicate no file is open.
|
||||
// Default name for output files if input is stdin
|
||||
ctx->basefilename_for_stdin=(char *) "stdin";
|
||||
// Default name for output files if input is network
|
||||
ctx->basefilename_for_network=(char *) "network";
|
||||
|
||||
// Set logging functions for libraries
|
||||
ccx_common_logging.debug_ftn = &dbg_print;
|
||||
@@ -66,63 +120,271 @@ struct lib_ccx_ctx* init_libraries(struct ccx_s_options *opt)
|
||||
ccx_common_logging.log_ftn = &mprint;
|
||||
ccx_common_logging.gui_ftn = &activity_library_process;
|
||||
|
||||
// Init shared decoder settings
|
||||
ctx->dec_global_setting = init_decoder_setting(opt);
|
||||
if (!ctx->dec_global_setting)
|
||||
return NULL;
|
||||
|
||||
// Need to set the 608 data for the report to the correct variable.
|
||||
ctx->freport.data_from_608 = report_608;
|
||||
// Same applies for 708 data
|
||||
ctx->freport.data_from_708 = &ccx_decoder_708_report;
|
||||
|
||||
// Init shared decoder settings
|
||||
dec_setting = init_decoder_setting(opt);
|
||||
ctx->dec_ctx = init_cc_decode(dec_setting);
|
||||
dinit_decoder_setting(&dec_setting);
|
||||
|
||||
// Init encoder helper variables
|
||||
ccx_encoders_helpers_setup(opt->encoding, opt->nofontcolor, opt->notypesetting, opt->trim_subs);
|
||||
|
||||
// Init 708 decoder(s)
|
||||
ccx_decoders_708_init_library(ctx->basefilename,ctx->extension,opt->print_file_reports);
|
||||
|
||||
// Set output structures for the 608 decoder
|
||||
//ctx->dec_ctx->context_cc608_field_1->out = ctx->dec_ctx->wbout1;
|
||||
//ctx->dec_ctx->context_cc608_field_2->out = ctx->dec_ctx->wbout2;
|
||||
|
||||
// Init XDS buffers
|
||||
ccx_decoders_xds_init_library(&opt->transcript_settings, ctx->subs_delay, opt->millis_separator);
|
||||
//xds_cea608_test();
|
||||
|
||||
ctx->dec_global_setting->settings_608->report = report_608;
|
||||
ctx->freport.data_from_708 = report_dtvcc;
|
||||
ctx->dec_global_setting->settings_dtvcc->report = report_dtvcc;
|
||||
ctx->mp4_cfg.mp4vidtrack = opt->mp4vidtrack;
|
||||
//Initialize input files
|
||||
ctx->inputfile = opt->inputfile;
|
||||
ctx->num_input_files = opt->num_input_files;
|
||||
ctx->subs_delay = opt->subs_delay;
|
||||
ctx->wbout1.filename = opt->wbout2.filename;
|
||||
ctx->wbout2.filename = opt->wbout2.filename;
|
||||
ctx->buffer = (unsigned char *) malloc (BUFSIZE);
|
||||
ctx->pesheaderbuf = (unsigned char *) malloc (188); // Never larger anyway
|
||||
|
||||
// Init timing
|
||||
ccx_common_timing_init(&ctx->past,opt->nosync);
|
||||
ret = init_ctx_outbase(opt, ctx);
|
||||
if (ret < 0) {
|
||||
goto end;
|
||||
}
|
||||
|
||||
ctx->subs_delay = opt->subs_delay;
|
||||
|
||||
ctx->pesheaderbuf = (unsigned char *) malloc (188); // Never larger anyway
|
||||
|
||||
ctx->cc_to_stdout = opt->cc_to_stdout;
|
||||
|
||||
ctx->hauppauge_mode = opt->hauppauge_mode;
|
||||
ctx->live_stream = opt->live_stream;
|
||||
ctx->binary_concat = opt->binary_concat;
|
||||
build_parity_table();
|
||||
|
||||
ctx->demux_ctx = init_demuxer(ctx, &opt->demux_cfg);
|
||||
INIT_LIST_HEAD(&ctx->dec_ctx_head);
|
||||
INIT_LIST_HEAD(&ctx->enc_ctx_head);
|
||||
|
||||
// Init timing
|
||||
ccx_common_timing_init(&ctx->demux_ctx->past,opt->nosync);
|
||||
ctx->multiprogram = opt->multiprogram;
|
||||
ctx->write_format = opt->write_format;
|
||||
ctx->out_interval = opt->out_interval;
|
||||
ctx->segment_counter = 0;
|
||||
ctx->system_start_time = -1;
|
||||
|
||||
end:
|
||||
if (ret != EXIT_OK)
|
||||
{
|
||||
free(ctx);
|
||||
return NULL;
|
||||
}
|
||||
return ctx;
|
||||
}
|
||||
|
||||
void dinit_libraries( struct lib_ccx_ctx **ctx)
|
||||
{
|
||||
struct lib_ccx_ctx *lctx = *ctx;
|
||||
int i = 0;
|
||||
for (i = 0; i < MAX_PID; i++)
|
||||
struct encoder_ctx *enc_ctx;
|
||||
struct lib_cc_decode *dec_ctx;
|
||||
struct lib_cc_decode *dec_ctx1;
|
||||
int i;
|
||||
list_for_each_entry_safe(dec_ctx, dec_ctx1, &lctx->dec_ctx_head, list, struct lib_cc_decode)
|
||||
{
|
||||
if( lctx->PIDs_programs[i])
|
||||
freep(lctx->PIDs_programs + i);
|
||||
LLONG cfts;
|
||||
if (dec_ctx->codec == CCX_CODEC_DVB)
|
||||
dvbsub_close_decoder(&dec_ctx->private_data);
|
||||
//Test memory for teletext
|
||||
else if (dec_ctx->codec == CCX_CODEC_TELETEXT)
|
||||
telxcc_close(&dec_ctx->private_data, NULL);
|
||||
else if (dec_ctx->codec == CCX_CODEC_ISDB_CC)
|
||||
delete_isdb_decoder(&dec_ctx->private_data);
|
||||
|
||||
flush_cc_decode(dec_ctx, &dec_ctx->dec_sub);
|
||||
cfts = get_fts(dec_ctx->timing, dec_ctx->current_field);
|
||||
enc_ctx = get_encoder_by_pn(lctx, dec_ctx->program_number);
|
||||
if (enc_ctx && dec_ctx->dec_sub.got_output == CCX_TRUE)
|
||||
{
|
||||
encode_sub(enc_ctx, &dec_ctx->dec_sub);
|
||||
dec_ctx->dec_sub.got_output = CCX_FALSE;
|
||||
}
|
||||
list_del(&dec_ctx->list);
|
||||
dinit_cc_decode(&dec_ctx);
|
||||
if (enc_ctx)
|
||||
{
|
||||
list_del(&enc_ctx->list);
|
||||
dinit_encoder(&enc_ctx, cfts);
|
||||
}
|
||||
}
|
||||
|
||||
// free EPG memory
|
||||
EPG_free(lctx);
|
||||
dinit_ts(lctx);
|
||||
dinit_cc_decode(&lctx->dec_ctx);
|
||||
freep(&lctx->buffer);
|
||||
freep(&lctx->pesheaderbuf);
|
||||
freep(&lctx->freport.data_from_608);
|
||||
freep(&lctx->freport.data_from_708);
|
||||
ccx_demuxer_delete(&lctx->demux_ctx);
|
||||
dinit_decoder_setting(&lctx->dec_global_setting);
|
||||
freep(&ccx_options.enc_cfg.output_filename);
|
||||
freep(&lctx->basefilename);
|
||||
freep(&lctx->pesheaderbuf);
|
||||
for(i = 0;i < lctx->num_input_files;i++)
|
||||
freep(&lctx->inputfile[i]);
|
||||
freep(&lctx->inputfile);
|
||||
freep(ctx);
|
||||
}
|
||||
|
||||
int is_decoder_processed_enough(struct lib_ccx_ctx *ctx)
|
||||
{
|
||||
struct lib_cc_decode *dec_ctx;
|
||||
list_for_each_entry(dec_ctx, &ctx->dec_ctx_head, list, struct lib_cc_decode)
|
||||
{
|
||||
if (dec_ctx->processed_enough == CCX_TRUE && ctx->multiprogram == CCX_FALSE)
|
||||
return CCX_TRUE;
|
||||
}
|
||||
|
||||
return CCX_FALSE;
|
||||
}
|
||||
struct lib_cc_decode *update_decoder_list(struct lib_ccx_ctx *ctx)
|
||||
{
|
||||
struct lib_cc_decode *dec_ctx;
|
||||
list_for_each_entry(dec_ctx, &ctx->dec_ctx_head, list, struct lib_cc_decode)
|
||||
{
|
||||
return dec_ctx;
|
||||
}
|
||||
|
||||
if (list_empty(&ctx->dec_ctx_head))
|
||||
{
|
||||
ctx->dec_global_setting->codec = CCX_CODEC_ATSC_CC;
|
||||
ctx->dec_global_setting->program_number = 0;
|
||||
dec_ctx = init_cc_decode(ctx->dec_global_setting);
|
||||
if (!dec_ctx)
|
||||
fatal(EXIT_NOT_ENOUGH_MEMORY, "Not enough memory\n");
|
||||
list_add_tail( &(dec_ctx->list), &(ctx->dec_ctx_head) );
|
||||
}
|
||||
return dec_ctx;
|
||||
}
|
||||
|
||||
struct lib_cc_decode *update_decoder_list_cinfo(struct lib_ccx_ctx *ctx, struct cap_info* cinfo)
|
||||
{
|
||||
struct lib_cc_decode *dec_ctx = NULL;
|
||||
|
||||
list_for_each_entry(dec_ctx, &ctx->dec_ctx_head, list, struct lib_cc_decode)
|
||||
{
|
||||
if (!cinfo || ctx->multiprogram == CCX_FALSE)
|
||||
return dec_ctx;
|
||||
|
||||
if (dec_ctx->program_number == cinfo->program_number)
|
||||
return dec_ctx;
|
||||
}
|
||||
if(cinfo)
|
||||
{
|
||||
ctx->dec_global_setting->program_number = cinfo->program_number;
|
||||
ctx->dec_global_setting->codec = cinfo->codec;
|
||||
ctx->dec_global_setting->private_data = cinfo->codec_private_data;
|
||||
}
|
||||
else
|
||||
{
|
||||
ctx->dec_global_setting->program_number = 0;
|
||||
ctx->dec_global_setting->codec = CCX_CODEC_ATSC_CC;
|
||||
}
|
||||
if(ctx->multiprogram == CCX_FALSE)
|
||||
{
|
||||
if (list_empty(&ctx->dec_ctx_head))
|
||||
{
|
||||
dec_ctx = init_cc_decode(ctx->dec_global_setting);
|
||||
if (!dec_ctx)
|
||||
fatal(EXIT_NOT_ENOUGH_MEMORY, "Not enough memory\n");
|
||||
list_add_tail( &(dec_ctx->list), &(ctx->dec_ctx_head) );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
dec_ctx = init_cc_decode(ctx->dec_global_setting);
|
||||
if (!dec_ctx)
|
||||
fatal(EXIT_NOT_ENOUGH_MEMORY, "Not enough memory\n");
|
||||
list_add_tail( &(dec_ctx->list), &(ctx->dec_ctx_head) );
|
||||
}
|
||||
return dec_ctx;
|
||||
}
|
||||
|
||||
struct encoder_ctx *update_encoder_list_cinfo(struct lib_ccx_ctx *ctx, struct cap_info* cinfo)
|
||||
{
|
||||
struct encoder_ctx *enc_ctx;
|
||||
unsigned int pn = 0;
|
||||
unsigned char in_format = 1;
|
||||
char *extension;
|
||||
|
||||
|
||||
if (ctx->write_format == CCX_OF_NULL)
|
||||
return NULL;
|
||||
|
||||
if(cinfo)
|
||||
{
|
||||
pn = cinfo->program_number;
|
||||
if (cinfo->codec == CCX_CODEC_ISDB_CC)
|
||||
in_format = 3;
|
||||
else if (cinfo->codec == CCX_CODEC_TELETEXT)
|
||||
in_format = 2;
|
||||
else
|
||||
in_format = 1;
|
||||
}
|
||||
list_for_each_entry(enc_ctx, &ctx->enc_ctx_head, list, struct encoder_ctx)
|
||||
{
|
||||
if ( ctx->multiprogram == CCX_FALSE)
|
||||
return enc_ctx;
|
||||
|
||||
if (enc_ctx->program_number == pn)
|
||||
return enc_ctx;
|
||||
}
|
||||
|
||||
extension = get_file_extension(ccx_options.enc_cfg.write_format);
|
||||
if(!extension)
|
||||
return NULL;
|
||||
|
||||
if(ctx->multiprogram == CCX_FALSE)
|
||||
{
|
||||
if(ctx->out_interval != -1)
|
||||
{
|
||||
int len;
|
||||
|
||||
len = strlen(ctx->basefilename) + 10 + strlen(extension);
|
||||
|
||||
freep(&ccx_options.enc_cfg.output_filename);
|
||||
ccx_options.enc_cfg.output_filename = malloc(len);
|
||||
|
||||
sprintf(ccx_options.enc_cfg.output_filename, "%s_%06d%s", ctx->basefilename, ctx->segment_counter+1, extension);
|
||||
}
|
||||
if (list_empty(&ctx->enc_ctx_head))
|
||||
{
|
||||
ccx_options.enc_cfg.program_number = pn;
|
||||
ccx_options.enc_cfg.in_format = in_format;
|
||||
enc_ctx = init_encoder(&ccx_options.enc_cfg);
|
||||
if (!enc_ctx)
|
||||
return NULL;
|
||||
list_add_tail( &(enc_ctx->list), &(ctx->enc_ctx_head) );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
int len;
|
||||
|
||||
len = strlen(ctx->basefilename) + 10 + strlen(extension);
|
||||
|
||||
ccx_options.enc_cfg.program_number = pn;
|
||||
ccx_options.enc_cfg.output_filename = malloc(len);
|
||||
if (!ccx_options.enc_cfg.output_filename)
|
||||
{
|
||||
freep(&extension);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
sprintf(ccx_options.enc_cfg.output_filename, "%s_%d%s", ctx->basefilename, pn, extension);
|
||||
enc_ctx = init_encoder(&ccx_options.enc_cfg);
|
||||
if (!enc_ctx)
|
||||
{
|
||||
freep(&extension);
|
||||
freep(&ccx_options.enc_cfg.output_filename);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
list_add_tail( &(enc_ctx->list), &(ctx->enc_ctx_head) );
|
||||
freep(&extension);
|
||||
freep(&ccx_options.enc_cfg.output_filename);
|
||||
}
|
||||
freep(&extension);
|
||||
return enc_ctx;
|
||||
}
|
||||
|
||||
struct encoder_ctx *update_encoder_list(struct lib_ccx_ctx *ctx)
|
||||
{
|
||||
return update_encoder_list_cinfo(ctx, NULL);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#ifndef CCX_CCEXTRACTOR_H
|
||||
#define CCX_CCEXTRACTOR_H
|
||||
|
||||
#define VERSION "0.76"
|
||||
#define VERSION "0.79"
|
||||
|
||||
// Load common includes and constants for library usage
|
||||
#include "ccx_common_platform.h"
|
||||
@@ -12,111 +12,33 @@
|
||||
#include "ccx_common_timing.h"
|
||||
#include "ccx_common_option.h"
|
||||
|
||||
#include "ccx_demuxer.h"
|
||||
#include "ccx_encoders_common.h"
|
||||
#include "ccx_decoders_608.h"
|
||||
#include "ccx_decoders_xds.h"
|
||||
#include "ccx_decoders_708.h"
|
||||
#include "ccx_decoders_common.h"
|
||||
#include "bitstream.h"
|
||||
|
||||
#include "networking.h"
|
||||
|
||||
extern int cc_buffer_saved; // Do we have anything in the CC buffer already?
|
||||
extern int ccblocks_in_avc_total; // Total CC blocks found by the AVC code
|
||||
extern int ccblocks_in_avc_lost; // CC blocks found by the AVC code lost due to overwrites (should be 0)
|
||||
|
||||
#define TS_PMT_MAP_SIZE 128
|
||||
|
||||
struct ts_payload
|
||||
{
|
||||
unsigned char *start; // Payload start
|
||||
unsigned length; // Payload length
|
||||
unsigned pesstart; // PES or PSI start
|
||||
unsigned pid; // Stream PID
|
||||
int counter; // continuity counter
|
||||
int transport_error; // 0 = packet OK, non-zero damaged
|
||||
unsigned char section_buf[1024];
|
||||
int section_index;
|
||||
int section_size;
|
||||
};
|
||||
|
||||
struct PAT_entry
|
||||
{
|
||||
unsigned program_number;
|
||||
unsigned PMT_PID;
|
||||
unsigned char *last_pmt_payload;
|
||||
unsigned last_pmt_length;
|
||||
};
|
||||
|
||||
struct PMT_entry
|
||||
{
|
||||
unsigned program_number;
|
||||
unsigned PMT_PID;
|
||||
unsigned elementary_PID;
|
||||
unsigned ccx_stream_type;
|
||||
unsigned printable_stream_type;
|
||||
};
|
||||
|
||||
struct EIT_buffer
|
||||
{
|
||||
uint32_t prev_ccounter;
|
||||
uint8_t *buffer;
|
||||
uint32_t buffer_length;
|
||||
uint32_t ccounter;
|
||||
};
|
||||
|
||||
struct EPG_rating
|
||||
{
|
||||
char country_code[4];
|
||||
uint8_t age;
|
||||
};
|
||||
|
||||
struct EPG_event
|
||||
{
|
||||
uint32_t id;
|
||||
char start_time_string[21]; //"YYYYMMDDHHMMSS +0000" = 20 chars
|
||||
char end_time_string[21];
|
||||
uint8_t running_status;
|
||||
uint8_t free_ca_mode;
|
||||
char ISO_639_language_code[4];
|
||||
char *event_name;
|
||||
char *text;
|
||||
char extended_ISO_639_language_code[4];
|
||||
char *extended_text;
|
||||
uint8_t has_simple;
|
||||
struct EPG_rating *ratings;
|
||||
uint32_t num_ratings;
|
||||
uint8_t *categories;
|
||||
uint32_t num_categories;
|
||||
uint16_t service_id;
|
||||
long long int count; //incremented by one each time the event is updated
|
||||
uint8_t live_output; //boolean flag, true if this event has been output
|
||||
};
|
||||
|
||||
#define EPG_MAX_EVENTS 60*24*7
|
||||
struct EIT_program
|
||||
{
|
||||
uint32_t array_len;
|
||||
struct EPG_event epg_events[EPG_MAX_EVENTS];
|
||||
};
|
||||
#include "avc_functions.h"
|
||||
//#include "ccx_decoders_708.h"
|
||||
|
||||
/* Report information */
|
||||
#define SUB_STREAMS_CNT 10
|
||||
|
||||
#define TELETEXT_CHUNK_LEN 1 + 8 + 44
|
||||
struct file_report
|
||||
{
|
||||
unsigned program_cnt;
|
||||
unsigned width;
|
||||
unsigned height;
|
||||
unsigned aspect_ratio;
|
||||
unsigned frame_rate;
|
||||
struct ccx_decoder_608_report *data_from_608;
|
||||
struct ccx_decoder_708_report_t *data_from_708;
|
||||
unsigned dvb_sub_pid[SUB_STREAMS_CNT];
|
||||
unsigned tlt_sub_pid[SUB_STREAMS_CNT];
|
||||
struct ccx_decoder_dtvcc_report *data_from_708;
|
||||
unsigned mp4_cc_track_cnt;
|
||||
};
|
||||
|
||||
// Stuff for telcc.c
|
||||
struct ccx_s_teletext_config {
|
||||
struct ccx_s_teletext_config
|
||||
{
|
||||
uint8_t verbose : 1; // should telxcc be verbose?
|
||||
uint16_t page; // teletext page containing cc we want to filter
|
||||
uint16_t tid; // 13-bit packet ID for teletext stream
|
||||
@@ -126,19 +48,28 @@ struct ccx_s_teletext_config {
|
||||
// uint8_t se_mode : 1; // search engine compatible mode => Uses CCExtractor's write_format
|
||||
// uint64_t utc_refvalue; // UTC referential value => Moved to ccx_decoders_common, so can be used for other decoders (608/xds) too
|
||||
uint16_t user_page; // Page selected by user, which MIGHT be different to 'page' depending on autodetection stuff
|
||||
int levdistmincnt, levdistmaxpct; // Means 2 fails or less is "the same", 10% or less is also "the same"
|
||||
struct ccx_boundary_time extraction_start, extraction_end; // Segment we actually process
|
||||
enum ccx_output_format write_format; // 0=Raw, 1=srt, 2=SMI
|
||||
int gui_mode_reports; // If 1, output in stderr progress updates so the GUI can grab them
|
||||
enum ccx_output_date_format date_format;
|
||||
int noautotimeref; // Do NOT set time automatically?
|
||||
unsigned send_to_srv;
|
||||
enum ccx_encoding_type encoding;
|
||||
int nofontcolor;
|
||||
int nohtmlescape;
|
||||
char millis_separator;
|
||||
};
|
||||
#define MAX_PID 65536
|
||||
|
||||
struct ccx_s_mp4Cfg
|
||||
{
|
||||
unsigned int mp4vidtrack :1;
|
||||
};
|
||||
|
||||
struct lib_ccx_ctx
|
||||
{
|
||||
// TODO relates to fts_global
|
||||
uint32_t global_timestamp;
|
||||
uint32_t min_global_timestamp;
|
||||
int global_timestamp_inited;
|
||||
|
||||
|
||||
// Stuff common to both loops
|
||||
unsigned char *buffer;
|
||||
LLONG past; /* Position in file, if in sync same as ftell() */
|
||||
unsigned char *pesheaderbuf;
|
||||
LLONG inputsize;
|
||||
LLONG total_inputsize;
|
||||
@@ -146,10 +77,6 @@ struct lib_ccx_ctx
|
||||
|
||||
int last_reported_progress;
|
||||
|
||||
// Small buffer to help us with the initial sync
|
||||
unsigned char startbytes[STARTBYTESLENGTH];
|
||||
unsigned int startbytes_pos;
|
||||
int startbytes_avail;
|
||||
|
||||
/* Stats */
|
||||
int stat_numuserheaders;
|
||||
@@ -160,26 +87,11 @@ struct lib_ccx_ctx
|
||||
int stat_dishheaders;
|
||||
int stat_hdtv;
|
||||
int stat_divicom;
|
||||
unsigned total_pulldownfields;
|
||||
unsigned total_pulldownframes;
|
||||
int false_pict_header;
|
||||
|
||||
/* GOP-based timing */
|
||||
int saw_gop_header;
|
||||
int frames_since_last_gop;
|
||||
|
||||
|
||||
/* Time info for timed-transcript */
|
||||
int max_gop_length; // (Maximum) length of a group of pictures
|
||||
int last_gop_length; // Length of the previous group of pictures
|
||||
|
||||
// int hex_mode=HEX_NONE; // Are we processing an hex file?
|
||||
|
||||
struct lib_cc_decode *dec_ctx;
|
||||
enum ccx_stream_mode_enum stream_mode;
|
||||
enum ccx_stream_mode_enum auto_stream;
|
||||
int m2ts;
|
||||
|
||||
struct ccx_decoders_common_settings_t *dec_global_setting;
|
||||
struct list_head dec_ctx_head;
|
||||
|
||||
int rawmode; // Broadcast or DVD
|
||||
// See -d from
|
||||
@@ -201,24 +113,9 @@ struct lib_ccx_ctx
|
||||
char **inputfile; // List of files to process
|
||||
int num_input_files; // How many?
|
||||
|
||||
/* Hauppauge support */
|
||||
unsigned hauppauge_warning_shown; // Did we detect a possible Hauppauge capture and told the user already?
|
||||
unsigned teletext_warning_shown; // Did we detect a possible PAL (with teletext subs) and told the user already?
|
||||
|
||||
// Output structures
|
||||
struct ccx_s_write wbout1;
|
||||
struct ccx_s_write wbout2;
|
||||
|
||||
/* File handles */
|
||||
FILE *fh_out_elementarystream;
|
||||
int infd; // descriptor number to input.
|
||||
char *basefilename_for_stdin;
|
||||
char *basefilename_for_network;
|
||||
int PIDs_seen[MAX_PID];
|
||||
struct PMT_entry *PIDs_programs[MAX_PID];
|
||||
|
||||
//struct EIT_buffer eit_buffer;
|
||||
struct EIT_buffer epg_buffers[0xfff+1];
|
||||
struct PSI_buffer epg_buffers[0xfff+1];
|
||||
struct EIT_program eit_programs[TS_PMT_MAP_SIZE+1];
|
||||
int32_t eit_current_events[TS_PMT_MAP_SIZE+1];
|
||||
int16_t ATSC_source_pg_map[0xffff];
|
||||
@@ -226,97 +123,59 @@ struct lib_ccx_ctx
|
||||
int epg_last_live_output;
|
||||
struct file_report freport;
|
||||
|
||||
long capbufsize;
|
||||
unsigned char *capbuf;
|
||||
long capbuflen; // Bytes read in capbuf
|
||||
unsigned int hauppauge_mode; // If 1, use PID=1003, process specially and so on
|
||||
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 */
|
||||
int binary_concat; // Disabled by -ve or --videoedited
|
||||
int multiprogram;
|
||||
enum ccx_output_format write_format; // 0=Raw, 1=srt, 2=SMI
|
||||
|
||||
struct ccx_demuxer *demux_ctx;
|
||||
struct list_head enc_ctx_head;
|
||||
struct ccx_s_mp4Cfg mp4_cfg;
|
||||
int out_interval;
|
||||
int segment_counter;
|
||||
LLONG system_start_time;
|
||||
};
|
||||
#ifdef DEBUG_TELEXCC
|
||||
int main_telxcc (int argc, char *argv[]);
|
||||
#endif
|
||||
|
||||
#define buffered_skip(ctx, bytes) if (bytes<=bytesinbuffer-filebuffer_pos) { \
|
||||
filebuffer_pos+=bytes; \
|
||||
result=bytes; \
|
||||
} else result=buffered_read_opt (ctx, NULL,bytes);
|
||||
|
||||
#define buffered_read(ctx, buffer,bytes) if (bytes<=bytesinbuffer-filebuffer_pos) { \
|
||||
if (buffer!=NULL) memcpy (buffer,filebuffer+filebuffer_pos,bytes); \
|
||||
filebuffer_pos+=bytes; \
|
||||
result=bytes; \
|
||||
} else { result=buffered_read_opt (ctx, buffer,bytes); if (ccx_options.gui_mode_reports && ccx_options.input_source==CCX_DS_NETWORK) {net_activity_gui++; if (!(net_activity_gui%1000))activity_report_data_read();}}
|
||||
|
||||
#define buffered_read_4(buffer) if (4<=bytesinbuffer-filebuffer_pos) { \
|
||||
if (buffer) { buffer[0]=filebuffer[filebuffer_pos]; \
|
||||
buffer[1]=filebuffer[filebuffer_pos+1]; \
|
||||
buffer[2]=filebuffer[filebuffer_pos+2]; \
|
||||
buffer[3]=filebuffer[filebuffer_pos+3]; \
|
||||
filebuffer_pos+=4; \
|
||||
result=4; } \
|
||||
} else result=buffered_read_opt (buffer,4);
|
||||
|
||||
#define buffered_read_byte(ctx, buffer) if (bytesinbuffer-filebuffer_pos) { \
|
||||
if (buffer) { *buffer=filebuffer[filebuffer_pos]; \
|
||||
filebuffer_pos++; \
|
||||
result=1; } \
|
||||
} else result=buffered_read_opt (ctx, buffer,1);
|
||||
|
||||
LLONG buffered_read_opt (struct lib_ccx_ctx *ctx, unsigned char *buffer, unsigned int bytes);
|
||||
|
||||
struct lib_ccx_ctx* init_libraries(struct ccx_s_options *opt);
|
||||
void dinit_libraries( struct lib_ccx_ctx **ctx);
|
||||
|
||||
//params.c
|
||||
void parse_parameters (struct ccx_s_options *opt, int argc, char *argv[]);
|
||||
int parse_parameters (struct ccx_s_options *opt, int argc, char *argv[]);
|
||||
void usage (void);
|
||||
int detect_input_file_overwrite(struct lib_ccx_ctx *ctx, const char *output_filename);
|
||||
int atoi_hex (char *s);
|
||||
int stringztoms (const char *s, struct ccx_boundary_time *bt);
|
||||
|
||||
// general_loop.c
|
||||
void position_sanity_check (void);
|
||||
int init_file_buffer( void );
|
||||
LLONG ps_getmoredata(struct lib_ccx_ctx *ctx);
|
||||
LLONG general_getmoredata(struct lib_ccx_ctx *ctx);
|
||||
void raw_loop (struct lib_ccx_ctx *ctx, void *enc_ctx);
|
||||
LLONG process_raw (struct lib_ccx_ctx *ctx, struct cc_subtitle *sub);
|
||||
void general_loop(struct lib_ccx_ctx *ctx, void *enc_ctx);
|
||||
void position_sanity_check (int in);
|
||||
int init_file_buffer(struct ccx_demuxer *ctx);
|
||||
int ps_getmoredata(struct lib_ccx_ctx *ctx, struct demuxer_data **ppdata);
|
||||
int general_getmoredata(struct lib_ccx_ctx *ctx, struct demuxer_data **data);
|
||||
void raw_loop (struct lib_ccx_ctx *ctx);
|
||||
size_t process_raw(struct lib_cc_decode *ctx, struct cc_subtitle *sub, unsigned char *buffer, size_t len);
|
||||
void general_loop(struct lib_ccx_ctx *ctx);
|
||||
void processhex (char *filename);
|
||||
void rcwt_loop(struct lib_ccx_ctx *ctx, void *enc_ctx);
|
||||
void rcwt_loop(struct lib_ccx_ctx *ctx);
|
||||
|
||||
// activity.c
|
||||
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);
|
||||
|
||||
extern LLONG result;
|
||||
extern int end_of_file;
|
||||
extern LLONG inbuf;
|
||||
extern int ccx_bufferdatatype; // Can be RAW or PES
|
||||
|
||||
// asf_functions.c
|
||||
LLONG asf_getmoredata(struct lib_ccx_ctx *ctx);
|
||||
int asf_getmoredata(struct lib_ccx_ctx *ctx, struct demuxer_data **ppdata);
|
||||
|
||||
// wtv_functions.c
|
||||
LLONG wtv_getmoredata(struct lib_ccx_ctx *ctx);
|
||||
|
||||
// avc_functions.c
|
||||
LLONG process_avc (struct lib_ccx_ctx *ctx, unsigned char *avcbuf, LLONG avcbuflen ,struct cc_subtitle *sub);
|
||||
void init_avc(void);
|
||||
int wtv_getmoredata(struct lib_ccx_ctx *ctx, struct demuxer_data **ppdata);
|
||||
|
||||
// es_functions.c
|
||||
LLONG process_m2v (struct lib_ccx_ctx *ctx, unsigned char *data, LLONG length,struct cc_subtitle *sub);
|
||||
size_t process_m2v(struct lib_cc_decode *ctx, unsigned char *data, size_t length, struct cc_subtitle *sub);
|
||||
|
||||
extern unsigned top_field_first;
|
||||
|
||||
// es_userdata.c
|
||||
int user_data(struct lib_ccx_ctx *ctx, struct bitstream *ustream, int udtype, struct cc_subtitle *sub);
|
||||
int user_data(struct lib_cc_decode *ctx, struct bitstream *ustream, int udtype, struct cc_subtitle *sub);
|
||||
|
||||
// bitstream.c - see bitstream.h
|
||||
|
||||
@@ -326,52 +185,50 @@ LLONG gettotalfilessize (struct lib_ccx_ctx *ctx);
|
||||
void prepare_for_new_file (struct lib_ccx_ctx *ctx);
|
||||
void close_input_file (struct lib_ccx_ctx *ctx);
|
||||
int switch_to_next_file (struct lib_ccx_ctx *ctx, LLONG bytesinbuffer);
|
||||
void return_to_buffer (unsigned char *buffer, unsigned int bytes);
|
||||
void return_to_buffer (struct ccx_demuxer *ctx, unsigned char *buffer, unsigned int bytes);
|
||||
|
||||
// sequencing.c
|
||||
void init_hdcc (void);
|
||||
void store_hdcc(struct lib_ccx_ctx *ctx, unsigned char *cc_data, int cc_count, int sequence_number,
|
||||
LLONG current_fts_now,struct cc_subtitle *sub);
|
||||
void anchor_hdcc(int seq);
|
||||
void process_hdcc (struct lib_ccx_ctx *ctx, struct cc_subtitle *sub);
|
||||
// mp4.c
|
||||
int processmp4 (struct lib_ccx_ctx *ctx, char *file,void *enc_ctx);
|
||||
void init_hdcc (struct lib_cc_decode *ctx);
|
||||
void store_hdcc(struct lib_cc_decode *ctx, unsigned char *cc_data, int cc_count, int sequence_number, LLONG current_fts_now, struct cc_subtitle *sub);
|
||||
void anchor_hdcc(struct lib_cc_decode *ctx, int seq);
|
||||
void process_hdcc (struct lib_cc_decode *ctx, struct cc_subtitle *sub);
|
||||
|
||||
// params_dump.c
|
||||
void params_dump(struct lib_ccx_ctx *ctx);
|
||||
void print_file_report(struct lib_ccx_ctx *ctx);
|
||||
|
||||
// output.c
|
||||
void init_write(struct ccx_s_write *wb, char *filename);
|
||||
void writeraw (const unsigned char *data, int length, struct ccx_s_write *wb);
|
||||
void writedata(const unsigned char *data, int length, ccx_decoder_608_context *context, struct cc_subtitle *sub);
|
||||
void dinit_write(struct ccx_s_write *wb);
|
||||
int init_write (struct ccx_s_write *wb,char *filename);
|
||||
int writeraw (const unsigned char *data, int length, void *private_data, struct cc_subtitle *sub);
|
||||
void flushbuffer (struct lib_ccx_ctx *ctx, struct ccx_s_write *wb, int closefile);
|
||||
void writercwtdata (struct lib_cc_decode *ctx, const unsigned char *data);
|
||||
void writercwtdata (struct lib_cc_decode *ctx, const unsigned char *data, struct cc_subtitle *sub);
|
||||
|
||||
// stream_functions.c
|
||||
void detect_stream_type (struct lib_ccx_ctx *ctx);
|
||||
int detect_myth( struct lib_ccx_ctx *ctx );
|
||||
int read_video_pes_header (struct lib_ccx_ctx *ctx, unsigned char *nextheader, int *headerlength, int sbuflen);
|
||||
int read_pts_pes(unsigned char*header, int len);
|
||||
int isValidMP4Box(unsigned char *buffer, size_t position, size_t *nextBoxLocation, int *boxScore);
|
||||
void detect_stream_type (struct ccx_demuxer *ctx);
|
||||
int detect_myth( struct ccx_demuxer *ctx );
|
||||
int read_video_pes_header (struct ccx_demuxer *ctx, struct demuxer_data *data, unsigned char *nextheader, int *headerlength, int sbuflen);
|
||||
|
||||
// ts_functions.c
|
||||
void init_ts(struct lib_ccx_ctx *ctx);
|
||||
void dinit_ts (struct lib_ccx_ctx *ctx);
|
||||
int ts_readpacket(struct lib_ccx_ctx* ctx);
|
||||
long ts_readstream(struct lib_ccx_ctx *ctx);
|
||||
LLONG ts_getmoredata(struct lib_ccx_ctx *ctx);
|
||||
int write_section(struct lib_ccx_ctx *ctx, struct ts_payload *payload, unsigned char*buf, int size, int pos);
|
||||
int parse_PMT (struct lib_ccx_ctx *ctx, unsigned char *buf, int len, int pos);
|
||||
int parse_PAT (struct lib_ccx_ctx *ctx);
|
||||
void init_ts(struct ccx_demuxer *ctx);
|
||||
int ts_readpacket(struct ccx_demuxer* ctx, struct ts_payload *payload);
|
||||
long ts_readstream(struct ccx_demuxer *ctx, struct demuxer_data **data);
|
||||
int ts_getmoredata(struct ccx_demuxer *ctx, struct demuxer_data **data);
|
||||
int write_section(struct ccx_demuxer *ctx, struct ts_payload *payload, unsigned char*buf, int size, struct program_info *pinfo);
|
||||
void ts_buffer_psi_packet(struct ccx_demuxer *ctx);
|
||||
int parse_PMT (struct ccx_demuxer *ctx, unsigned char *buf, int len, struct program_info *pinfo);
|
||||
int parse_PAT (struct ccx_demuxer *ctx);
|
||||
void parse_EPG_packet (struct lib_ccx_ctx *ctx);
|
||||
void EPG_free();
|
||||
void EPG_free(struct lib_ccx_ctx *ctx);
|
||||
char* EPG_DVB_decode_string(uint8_t *in, size_t size);
|
||||
void parse_SDT(struct ccx_demuxer *ctx);
|
||||
|
||||
// myth.c
|
||||
void myth_loop(struct lib_ccx_ctx *ctx, void *enc_ctx);
|
||||
void myth_loop(struct lib_ccx_ctx *ctx);
|
||||
|
||||
// utility.c
|
||||
void fatal(int exit_code, const char *fmt, ...);
|
||||
void dvprint(const char *fmt, ...);
|
||||
void mprint (const char *fmt, ...);
|
||||
void sleep_secs (int secs);
|
||||
void dump (LLONG mask, unsigned char *start, int l, unsigned long abs_start, unsigned clear_high_bit);
|
||||
@@ -379,98 +236,54 @@ bool_t in_array(uint16_t *array, uint16_t length, uint16_t element) ;
|
||||
int hex2int (char high, char low);
|
||||
void timestamp_to_srttime(uint64_t timestamp, char *buffer);
|
||||
void timestamp_to_smptetttime(uint64_t timestamp, char *buffer);
|
||||
void millis_to_date (uint64_t timestamp, char *buffer) ;
|
||||
int levenshtein_dist (const uint64_t *s1, const uint64_t *s2, unsigned s1len, unsigned s2len);
|
||||
void millis_to_date (uint64_t timestamp, char *buffer, enum ccx_output_date_format date_format, char millis_separator);
|
||||
#ifndef _WIN32
|
||||
void m_signal(int sig, void (*func)(int));
|
||||
#endif
|
||||
|
||||
|
||||
unsigned encode_line (unsigned char *buffer, unsigned char *text);
|
||||
void buffered_seek (struct lib_ccx_ctx *ctx, int offset);
|
||||
void buffered_seek (struct ccx_demuxer *ctx, int offset);
|
||||
extern void build_parity_table(void);
|
||||
|
||||
void tlt_process_pes_packet(struct lib_ccx_ctx *ctx, uint8_t *buffer, uint16_t size);
|
||||
void telxcc_init(struct lib_ccx_ctx *ctx);
|
||||
void telxcc_close(struct lib_ccx_ctx *ctx);
|
||||
void tlt_read_rcwt(struct lib_ccx_ctx *ctx);
|
||||
int tlt_process_pes_packet(struct lib_cc_decode *dec_ctx, uint8_t *buffer, uint16_t size, struct cc_subtitle *sub);
|
||||
void* telxcc_init(void);
|
||||
void telxcc_close(void **ctx, struct cc_subtitle *sub);
|
||||
void tlt_read_rcwt(void *codec, unsigned char *buf, struct cc_subtitle *sub);
|
||||
void telxcc_configure (void *codec, struct ccx_s_teletext_config *cfg);
|
||||
void telxcc_update_gt(void *codec, uint32_t global_timestamp);
|
||||
|
||||
extern unsigned rollover_bits;
|
||||
extern int global_timestamp_inited;
|
||||
|
||||
extern int strangeheader;
|
||||
|
||||
extern unsigned char *filebuffer;
|
||||
extern LLONG filebuffer_start; // Position of buffer start relative to file
|
||||
extern int filebuffer_pos; // Position of pointer relative to buffer start
|
||||
extern int bytesinbuffer; // Number of bytes we actually have on buffer
|
||||
|
||||
extern const char *desc[256];
|
||||
|
||||
|
||||
extern long FILEBUFFERSIZE; // Uppercase because it used to be a define
|
||||
extern unsigned long net_activity_gui;
|
||||
|
||||
/* General (ES stream) video information */
|
||||
extern unsigned current_hor_size;
|
||||
extern unsigned current_vert_size;
|
||||
extern unsigned current_aspect_ratio;
|
||||
extern unsigned current_frame_rate;
|
||||
|
||||
extern enum ccx_bufferdata_type bufferdatatype; // Can be CCX_BUFFERDATA_TYPE_RAW or CCX_BUFFERDATA_TYPE_PES
|
||||
|
||||
extern int firstcall;
|
||||
|
||||
#define MAXBFRAMES 50
|
||||
#define SORTBUF (2*MAXBFRAMES+1)
|
||||
extern int cc_data_count[SORTBUF];
|
||||
extern unsigned char cc_data_pkts[SORTBUF][10*31*3+1];
|
||||
extern int has_ccdata_buffered;
|
||||
|
||||
extern unsigned char *subline;
|
||||
|
||||
|
||||
// From ts_functions
|
||||
extern unsigned cap_stream_type;
|
||||
extern struct ts_payload payload;
|
||||
//extern struct ts_payload payload;
|
||||
extern unsigned char tspacket[188];
|
||||
extern struct PAT_entry pmt_array[TS_PMT_MAP_SIZE];
|
||||
extern uint16_t pmt_array_length;
|
||||
extern unsigned pmtpid;
|
||||
extern unsigned TS_program_number;
|
||||
extern unsigned char *last_pat_payload;
|
||||
extern unsigned last_pat_length;
|
||||
extern long capbuflen;
|
||||
|
||||
|
||||
#define HAUPPAGE_CCPID 1003 // PID for CC's in some Hauppauge recordings
|
||||
|
||||
/* Exit codes. Take this seriously as the GUI depends on them.
|
||||
0 means OK as usual,
|
||||
<100 means display whatever was output to stderr as a warning
|
||||
>=100 means display whatever was output to stdout as an error
|
||||
*/
|
||||
// Some moved to ccx_common_common.h
|
||||
#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_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
|
||||
|
||||
extern unsigned teletext_mode;
|
||||
|
||||
#define MAX_TLT_PAGES 1000
|
||||
extern short int seen_sub_page[MAX_TLT_PAGES];
|
||||
|
||||
extern struct ccx_s_teletext_config tlt_config;
|
||||
extern uint32_t tlt_packet_counter;
|
||||
extern uint32_t tlt_frames_produced;
|
||||
|
||||
int is_decoder_processed_enough(struct lib_ccx_ctx *ctx);
|
||||
struct lib_cc_decode *update_decoder_list_cinfo(struct lib_ccx_ctx *ctx, struct cap_info* cinfo);
|
||||
struct lib_cc_decode *update_decoder_list(struct lib_ccx_ctx *ctx);
|
||||
|
||||
struct encoder_ctx *update_encoder_list_cinfo(struct lib_ccx_ctx *ctx, struct cap_info* cinfo);
|
||||
struct encoder_ctx * update_encoder_list(struct lib_ccx_ctx *ctx);
|
||||
struct encoder_ctx *get_encoder_by_pn(struct lib_ccx_ctx *ctx, int pn);
|
||||
#endif
|
||||
|
||||
511
src/lib_ccx/list.h
Normal file
511
src/lib_ccx/list.h
Normal file
@@ -0,0 +1,511 @@
|
||||
/**
|
||||
*
|
||||
* Grabed from linux kernel source code and fix it for user space
|
||||
* program. Of course, this is a GPL licensed header file.
|
||||
*
|
||||
*
|
||||
*/
|
||||
#ifndef _LINUX_LIST_H
|
||||
#define _LINUX_LIST_H
|
||||
#include "ccx_common_platform.h"
|
||||
/**
|
||||
* @name from other kernel headers
|
||||
*/
|
||||
/*@{*/
|
||||
|
||||
/**
|
||||
* Get offset of a member
|
||||
*/
|
||||
|
||||
#ifndef ccx_offsetof
|
||||
#define ccx_offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Casts a member of a structure out to the containing structure
|
||||
* @param ptr the pointer to the member.
|
||||
* @param type the type of the container struct this is embedded in.
|
||||
* @param member the name of the member within the struct.
|
||||
*
|
||||
*/
|
||||
#define container_of(ptr, type, member) ((type *)( (char *)ptr - ccx_offsetof(type,member) ))
|
||||
/*@}*/
|
||||
|
||||
|
||||
/*
|
||||
* These are non-NULL pointers that will result in page faults
|
||||
* under normal circumstances, used to verify that nobody uses
|
||||
* non-initialized list entries.
|
||||
*/
|
||||
#define LIST_POISON1 ((void *) 0x00100100)
|
||||
#define LIST_POISON2 ((void *) 0x00200200)
|
||||
|
||||
/**
|
||||
* Simple doubly linked list implementation.
|
||||
*
|
||||
* Some of the internal functions ("__xxx") are useful when
|
||||
* manipulating whole lists rather than single entries, as
|
||||
* sometimes we already know the next/prev entries and we can
|
||||
* generate better code by using them directly rather than
|
||||
* using the generic single-entry routines.
|
||||
*/
|
||||
struct list_head {
|
||||
struct list_head *next, *prev;
|
||||
};
|
||||
|
||||
#define LIST_HEAD_INIT(name) { &(name), &(name) }
|
||||
|
||||
#define LIST_HEAD(name) \
|
||||
struct list_head name = LIST_HEAD_INIT(name)
|
||||
|
||||
#define INIT_LIST_HEAD(ptr) do { \
|
||||
(ptr)->next = (ptr); (ptr)->prev = (ptr); \
|
||||
} while (0)
|
||||
|
||||
/*
|
||||
* Insert a new entry between two known consecutive entries.
|
||||
*
|
||||
* This is only for internal list manipulation where we know
|
||||
* the prev/next entries already!
|
||||
*/
|
||||
static inline void __list_add(struct list_head *new,
|
||||
struct list_head *prev,
|
||||
struct list_head *next)
|
||||
{
|
||||
next->prev = new;
|
||||
new->next = next;
|
||||
new->prev = prev;
|
||||
prev->next = new;
|
||||
}
|
||||
|
||||
/**
|
||||
* list_add - add a new entry
|
||||
* @new: new entry to be added
|
||||
* @head: list head to add it after
|
||||
*
|
||||
* Insert a new entry after the specified head.
|
||||
* This is good for implementing stacks.
|
||||
*/
|
||||
static inline void list_add(struct list_head *new, struct list_head *head)
|
||||
{
|
||||
__list_add(new, head, head->next);
|
||||
}
|
||||
|
||||
/**
|
||||
* list_add_tail - add a new entry
|
||||
* @new: new entry to be added
|
||||
* @head: list head to add it before
|
||||
*
|
||||
* Insert a new entry before the specified head.
|
||||
* This is useful for implementing queues.
|
||||
*/
|
||||
static inline void list_add_tail(struct list_head *new, struct list_head *head)
|
||||
{
|
||||
__list_add(new, head->prev, head);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Delete a list entry by making the prev/next entries
|
||||
* point to each other.
|
||||
*
|
||||
* This is only for internal list manipulation where we know
|
||||
* the prev/next entries already!
|
||||
*/
|
||||
static inline void __list_del(struct list_head * prev, struct list_head * next)
|
||||
{
|
||||
next->prev = prev;
|
||||
prev->next = next;
|
||||
}
|
||||
|
||||
/**
|
||||
* list_del - deletes entry from list.
|
||||
* @entry: the element to delete from the list.
|
||||
* Note: list_empty on entry does not return true after this, the entry is
|
||||
* in an undefined state.
|
||||
*/
|
||||
static inline void list_del(struct list_head *entry)
|
||||
{
|
||||
__list_del(entry->prev, entry->next);
|
||||
entry->next = LIST_POISON1;
|
||||
entry->prev = LIST_POISON2;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* list_del_init - deletes entry from list and reinitialize it.
|
||||
* @entry: the element to delete from the list.
|
||||
*/
|
||||
static inline void list_del_init(struct list_head *entry)
|
||||
{
|
||||
__list_del(entry->prev, entry->next);
|
||||
INIT_LIST_HEAD(entry);
|
||||
}
|
||||
|
||||
/**
|
||||
* list_move - delete from one list and add as another's head
|
||||
* @list: the entry to move
|
||||
* @head: the head that will precede our entry
|
||||
*/
|
||||
static inline void list_move(struct list_head *list, struct list_head *head)
|
||||
{
|
||||
__list_del(list->prev, list->next);
|
||||
list_add(list, head);
|
||||
}
|
||||
|
||||
/**
|
||||
* list_move_tail - delete from one list and add as another's tail
|
||||
* @list: the entry to move
|
||||
* @head: the head that will follow our entry
|
||||
*/
|
||||
static inline void list_move_tail(struct list_head *list,
|
||||
struct list_head *head)
|
||||
{
|
||||
__list_del(list->prev, list->next);
|
||||
list_add_tail(list, head);
|
||||
}
|
||||
|
||||
/**
|
||||
* list_empty - tests whether a list is empty
|
||||
* @head: the list to test.
|
||||
*/
|
||||
static inline int list_empty(const struct list_head *head)
|
||||
{
|
||||
return head->next == head;
|
||||
}
|
||||
|
||||
static inline void __list_splice(struct list_head *list,
|
||||
struct list_head *head)
|
||||
{
|
||||
struct list_head *first = list->next;
|
||||
struct list_head *last = list->prev;
|
||||
struct list_head *at = head->next;
|
||||
|
||||
first->prev = head;
|
||||
head->next = first;
|
||||
|
||||
last->next = at;
|
||||
at->prev = last;
|
||||
}
|
||||
|
||||
/**
|
||||
* list_splice - join two lists
|
||||
* @list: the new list to add.
|
||||
* @head: the place to add it in the first list.
|
||||
*/
|
||||
static inline void list_splice(struct list_head *list, struct list_head *head)
|
||||
{
|
||||
if (!list_empty(list))
|
||||
__list_splice(list, head);
|
||||
}
|
||||
|
||||
/**
|
||||
* list_splice_init - join two lists and reinitialise the emptied list.
|
||||
* @list: the new list to add.
|
||||
* @head: the place to add it in the first list.
|
||||
*
|
||||
* The list at @list is reinitialised
|
||||
*/
|
||||
static inline void list_splice_init(struct list_head *list,
|
||||
struct list_head *head)
|
||||
{
|
||||
if (!list_empty(list)) {
|
||||
__list_splice(list, head);
|
||||
INIT_LIST_HEAD(list);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* list_entry - get the struct for this entry
|
||||
* @ptr: the &struct list_head pointer.
|
||||
* @type: the type of the struct this is embedded in.
|
||||
* @member: the name of the list_struct within the struct.
|
||||
*/
|
||||
#define list_entry(ptr, type, member) \
|
||||
container_of(ptr, type, member)
|
||||
|
||||
/**
|
||||
* list_for_each - iterate over a list
|
||||
* @pos: the &struct list_head to use as a loop counter.
|
||||
* @head: the head for your list.
|
||||
*/
|
||||
|
||||
#define list_for_each(pos, head) \
|
||||
for (pos = (head)->next; pos != (head); \
|
||||
pos = pos->next)
|
||||
|
||||
/**
|
||||
* __list_for_each - iterate over a list
|
||||
* @pos: the &struct list_head to use as a loop counter.
|
||||
* @head: the head for your list.
|
||||
*
|
||||
* This variant differs from list_for_each() in that it's the
|
||||
* simplest possible list iteration code, no prefetching is done.
|
||||
* Use this for code that knows the list to be very short (empty
|
||||
* or 1 entry) most of the time.
|
||||
*/
|
||||
#define __list_for_each(pos, head) \
|
||||
for (pos = (head)->next; pos != (head); pos = pos->next)
|
||||
|
||||
/**
|
||||
* list_for_each_prev - iterate over a list backwards
|
||||
* @pos: the &struct list_head to use as a loop counter.
|
||||
* @head: the head for your list.
|
||||
*/
|
||||
#define list_for_each_prev(pos, head) \
|
||||
for (pos = (head)->prev; prefetch(pos->prev), pos != (head); \
|
||||
pos = pos->prev)
|
||||
|
||||
/**
|
||||
* list_for_each_safe - iterate over a list safe against removal of list entry
|
||||
* @pos: the &struct list_head to use as a loop counter.
|
||||
* @n: another &struct list_head to use as temporary storage
|
||||
* @head: the head for your list.
|
||||
*/
|
||||
#define list_for_each_safe(pos, n, head) \
|
||||
for (pos = (head)->next, n = pos->next; pos != (head); \
|
||||
pos = n, n = pos->next)
|
||||
|
||||
/**
|
||||
* list_for_each_entry - iterate over list of given type
|
||||
* @pos: the type * to use as a loop counter.
|
||||
* @head: the head for your list.
|
||||
* @member: the name of the list_struct within the struct.
|
||||
* @type iterator type
|
||||
*/
|
||||
#define list_for_each_entry(pos, head, member, type) \
|
||||
for (pos = list_entry((head)->next, type, member); \
|
||||
&pos->member != (head); \
|
||||
pos = list_entry(pos->member.next, type, member))
|
||||
|
||||
/**
|
||||
* list_for_each_entry_reverse - iterate backwards over list of given type.
|
||||
* @pos: the type * to use as a loop counter.
|
||||
* @head: the head for your list.
|
||||
* @member: the name of the list_struct within the struct.
|
||||
*/
|
||||
#define list_for_each_entry_reverse(pos, head, member) \
|
||||
for (pos = list_entry((head)->prev, typeof(*pos), member); \
|
||||
&pos->member != (head); \
|
||||
pos = list_entry(pos->member.prev, typeof(*pos), member))
|
||||
|
||||
/**
|
||||
* list_prepare_entry - prepare a pos entry for use as a start point in
|
||||
* list_for_each_entry_continue
|
||||
* @pos: the type * to use as a start point
|
||||
* @head: the head of the list
|
||||
* @member: the name of the list_struct within the struct.
|
||||
*/
|
||||
#define list_prepare_entry(pos, head, member) \
|
||||
((pos) ? : list_entry(head, typeof(*pos), member))
|
||||
|
||||
/**
|
||||
* list_for_each_entry_continue - iterate over list of given type
|
||||
* continuing after existing point
|
||||
* @pos: the type * to use as a loop counter.
|
||||
* @head: the head for your list.
|
||||
* @member: the name of the list_struct within the struct.
|
||||
*/
|
||||
#define list_for_each_entry_continue(pos, head, member) \
|
||||
for (pos = list_entry(pos->member.next, typeof(*pos), member); \
|
||||
&pos->member != (head); \
|
||||
pos = list_entry(pos->member.next, typeof(*pos), member))
|
||||
|
||||
/**
|
||||
* list_for_each_entry_safe - iterate over list of given type safe against removal of list entry
|
||||
* @pos: the type * to use as a loop counter.
|
||||
* @n: another type * to use as temporary storage
|
||||
* @head: the head for your list.
|
||||
* @member: the name of the list_struct within the struct.
|
||||
*/
|
||||
#define list_for_each_entry_safe(pos, n, head, member, type) \
|
||||
for (pos = list_entry((head)->next, type, member), \
|
||||
n = list_entry(pos->member.next, type, member); \
|
||||
&pos->member != (head); \
|
||||
pos = n, n = list_entry(n->member.next, type, member))
|
||||
|
||||
/**
|
||||
* list_for_each_entry_safe_continue - iterate over list of given type
|
||||
* continuing after existing point safe against removal of list entry
|
||||
* @pos: the type * to use as a loop counter.
|
||||
* @n: another type * to use as temporary storage
|
||||
* @head: the head for your list.
|
||||
* @member: the name of the list_struct within the struct.
|
||||
*/
|
||||
#define list_for_each_entry_safe_continue(pos, n, head, member) \
|
||||
for (pos = list_entry(pos->member.next, typeof(*pos), member), \
|
||||
n = list_entry(pos->member.next, typeof(*pos), member); \
|
||||
&pos->member != (head); \
|
||||
pos = n, n = list_entry(n->member.next, typeof(*n), member))
|
||||
|
||||
/**
|
||||
* list_for_each_entry_safe_reverse - iterate backwards over list of given type safe against
|
||||
* removal of list entry
|
||||
* @pos: the type * to use as a loop counter.
|
||||
* @n: another type * to use as temporary storage
|
||||
* @head: the head for your list.
|
||||
* @member: the name of the list_struct within the struct.
|
||||
*/
|
||||
#define list_for_each_entry_safe_reverse(pos, n, head, member) \
|
||||
for (pos = list_entry((head)->prev, typeof(*pos), member), \
|
||||
n = list_entry(pos->member.prev, typeof(*pos), member); \
|
||||
&pos->member != (head); \
|
||||
pos = n, n = list_entry(n->member.prev, typeof(*n), member))
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Double linked lists with a single pointer list head.
|
||||
* Mostly useful for hash tables where the two pointer list head is
|
||||
* too wasteful.
|
||||
* You lose the ability to access the tail in O(1).
|
||||
*/
|
||||
|
||||
struct hlist_head {
|
||||
struct hlist_node *first;
|
||||
};
|
||||
|
||||
struct hlist_node {
|
||||
struct hlist_node *next, **pprev;
|
||||
};
|
||||
|
||||
#define HLIST_HEAD_INIT { .first = NULL }
|
||||
#define HLIST_HEAD(name) struct hlist_head name = { .first = NULL }
|
||||
#define INIT_HLIST_HEAD(ptr) ((ptr)->first = NULL)
|
||||
#define INIT_HLIST_NODE(ptr) ((ptr)->next = NULL, (ptr)->pprev = NULL)
|
||||
|
||||
static inline int hlist_unhashed(const struct hlist_node *h)
|
||||
{
|
||||
return !h->pprev;
|
||||
}
|
||||
|
||||
static inline int hlist_empty(const struct hlist_head *h)
|
||||
{
|
||||
return !h->first;
|
||||
}
|
||||
|
||||
static inline void __hlist_del(struct hlist_node *n)
|
||||
{
|
||||
struct hlist_node *next = n->next;
|
||||
struct hlist_node **pprev = n->pprev;
|
||||
*pprev = next;
|
||||
if (next)
|
||||
next->pprev = pprev;
|
||||
}
|
||||
|
||||
static inline void hlist_del(struct hlist_node *n)
|
||||
{
|
||||
__hlist_del(n);
|
||||
n->next = LIST_POISON1;
|
||||
n->pprev = LIST_POISON2;
|
||||
}
|
||||
|
||||
|
||||
static inline void hlist_del_init(struct hlist_node *n)
|
||||
{
|
||||
if (n->pprev) {
|
||||
__hlist_del(n);
|
||||
INIT_HLIST_NODE(n);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void hlist_add_head(struct hlist_node *n, struct hlist_head *h)
|
||||
{
|
||||
struct hlist_node *first = h->first;
|
||||
n->next = first;
|
||||
if (first)
|
||||
first->pprev = &n->next;
|
||||
h->first = n;
|
||||
n->pprev = &h->first;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* next must be != NULL */
|
||||
static inline void hlist_add_before(struct hlist_node *n,
|
||||
struct hlist_node *next)
|
||||
{
|
||||
n->pprev = next->pprev;
|
||||
n->next = next;
|
||||
next->pprev = &n->next;
|
||||
*(n->pprev) = n;
|
||||
}
|
||||
|
||||
static inline void hlist_add_after(struct hlist_node *n,
|
||||
struct hlist_node *next)
|
||||
{
|
||||
next->next = n->next;
|
||||
n->next = next;
|
||||
next->pprev = &n->next;
|
||||
|
||||
if(next->next)
|
||||
next->next->pprev = &next->next;
|
||||
}
|
||||
|
||||
|
||||
|
||||
#define hlist_entry(ptr, type, member) container_of(ptr,type,member)
|
||||
|
||||
#define hlist_for_each(pos, head) \
|
||||
for (pos = (head)->first; pos && ({ prefetch(pos->next); 1; }); \
|
||||
pos = pos->next)
|
||||
|
||||
#define hlist_for_each_safe(pos, n, head) \
|
||||
for (pos = (head)->first; pos && ({ n = pos->next; 1; }); \
|
||||
pos = n)
|
||||
|
||||
/**
|
||||
* hlist_for_each_entry - iterate over list of given type
|
||||
* @tpos: the type * to use as a loop counter.
|
||||
* @pos: the &struct hlist_node to use as a loop counter.
|
||||
* @head: the head for your list.
|
||||
* @member: the name of the hlist_node within the struct.
|
||||
*/
|
||||
#define hlist_for_each_entry(tpos, pos, head, member) \
|
||||
for (pos = (head)->first; \
|
||||
pos && ({ prefetch(pos->next); 1;}) && \
|
||||
({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \
|
||||
pos = pos->next)
|
||||
|
||||
/**
|
||||
* hlist_for_each_entry_continue - iterate over a hlist continuing after existing point
|
||||
* @tpos: the type * to use as a loop counter.
|
||||
* @pos: the &struct hlist_node to use as a loop counter.
|
||||
* @member: the name of the hlist_node within the struct.
|
||||
*/
|
||||
#define hlist_for_each_entry_continue(tpos, pos, member) \
|
||||
for (pos = (pos)->next; \
|
||||
pos && ({ prefetch(pos->next); 1;}) && \
|
||||
({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \
|
||||
pos = pos->next)
|
||||
|
||||
/**
|
||||
* hlist_for_each_entry_from - iterate over a hlist continuing from existing point
|
||||
* @tpos: the type * to use as a loop counter.
|
||||
* @pos: the &struct hlist_node to use as a loop counter.
|
||||
* @member: the name of the hlist_node within the struct.
|
||||
*/
|
||||
#define hlist_for_each_entry_from(tpos, pos, member) \
|
||||
for (; pos && ({ prefetch(pos->next); 1;}) && \
|
||||
({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \
|
||||
pos = pos->next)
|
||||
|
||||
/**
|
||||
* hlist_for_each_entry_safe - iterate over list of given type safe against removal of list entry
|
||||
* @tpos: the type * to use as a loop counter.
|
||||
* @pos: the &struct hlist_node to use as a loop counter.
|
||||
* @n: another &struct hlist_node to use as temporary storage
|
||||
* @head: the head for your list.
|
||||
* @member: the name of the hlist_node within the struct.
|
||||
*/
|
||||
#define hlist_for_each_entry_safe(tpos, pos, n, head, member) \
|
||||
for (pos = (head)->first; \
|
||||
pos && ({ n = pos->next; 1; }) && \
|
||||
({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \
|
||||
pos = n)
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
@@ -14,10 +14,11 @@ For now, integration with ccextractor is a quick hack. It could get better with
|
||||
#include <stdio.h>
|
||||
#include <fcntl.h>
|
||||
#include "ccx_encoders_common.h"
|
||||
#include "activity.h"
|
||||
#include "file_buffer.h"
|
||||
|
||||
static unsigned int header_state;
|
||||
static unsigned char psm_es_type[256];
|
||||
int cc608_parity_table[256];
|
||||
|
||||
// LLONG processed_ccblocks = 0;
|
||||
|
||||
@@ -288,61 +289,29 @@ typedef struct AVPacket
|
||||
|
||||
static AVPacket av;
|
||||
|
||||
int get_be16(struct lib_ccx_ctx *ctx)
|
||||
{
|
||||
unsigned char a,b;
|
||||
unsigned char *a_p = &a; // Just to suppress warnings
|
||||
unsigned char *b_p = &b;
|
||||
buffered_read_byte (ctx, a_p);
|
||||
ctx->past++;
|
||||
buffered_read_byte (ctx, b_p);
|
||||
ctx->past++;
|
||||
return (a<<8) | b;
|
||||
}
|
||||
|
||||
int get_byte (struct lib_ccx_ctx *ctx)
|
||||
{
|
||||
unsigned char b;
|
||||
unsigned char *b_p = &b;
|
||||
buffered_read_byte(ctx, b_p);
|
||||
if (result==1)
|
||||
{
|
||||
ctx->past++;
|
||||
return b;
|
||||
}
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
unsigned int get_be32(struct lib_ccx_ctx *ctx)
|
||||
{
|
||||
unsigned int val;
|
||||
val = get_be16(ctx) << 16;
|
||||
val |= get_be16(ctx);
|
||||
return val;
|
||||
}
|
||||
|
||||
|
||||
static LLONG get_pts(struct lib_ccx_ctx *ctx, int c)
|
||||
static LLONG get_pts(struct ccx_demuxer *ctx, int c)
|
||||
{
|
||||
LLONG pts;
|
||||
int val;
|
||||
|
||||
if (c < 0)
|
||||
c = get_byte(ctx);
|
||||
c = buffered_get_byte(ctx);
|
||||
pts = (LLONG) ((c >> 1) & 0x07) << 30;
|
||||
val = get_be16(ctx);
|
||||
val = buffered_get_be16(ctx);
|
||||
pts |= (LLONG) (val >> 1) << 15;
|
||||
val = get_be16(ctx);
|
||||
val = buffered_get_be16(ctx);
|
||||
pts |= (LLONG) (val >> 1);
|
||||
return pts;
|
||||
}
|
||||
|
||||
static int find_next_start_code(struct lib_ccx_ctx *ctx, int *size_ptr,
|
||||
static int find_next_start_code(struct ccx_demuxer *ctx, int *size_ptr,
|
||||
unsigned int *header_state)
|
||||
{
|
||||
unsigned int state, v;
|
||||
int val, n;
|
||||
LLONG result;
|
||||
|
||||
state = *header_state;
|
||||
n = *size_ptr;
|
||||
@@ -350,8 +319,8 @@ static int find_next_start_code(struct lib_ccx_ctx *ctx, int *size_ptr,
|
||||
{
|
||||
unsigned char cx;
|
||||
unsigned char *cx_p = &cx;
|
||||
buffered_read_byte (ctx, cx_p);
|
||||
if (result!=1)
|
||||
result = buffered_read_byte(ctx, cx_p);
|
||||
if (result != 1)
|
||||
break;
|
||||
ctx->past++;
|
||||
v = cx;
|
||||
@@ -370,43 +339,43 @@ found:
|
||||
return val;
|
||||
}
|
||||
|
||||
void url_fskip (struct lib_ccx_ctx *ctx, int length)
|
||||
void url_fskip (struct ccx_demuxer *ctx, int length)
|
||||
{
|
||||
buffered_seek (ctx, length);
|
||||
ctx->past+=length;
|
||||
ctx->past += length;
|
||||
}
|
||||
|
||||
static long mpegps_psm_parse(struct lib_ccx_ctx *ctx)
|
||||
static long mpegps_psm_parse(struct ccx_demuxer *ctx)
|
||||
{
|
||||
int psm_length, ps_info_length, es_map_length;
|
||||
|
||||
psm_length = get_be16(ctx);
|
||||
get_byte(ctx);
|
||||
get_byte(ctx);
|
||||
ps_info_length = get_be16(ctx);
|
||||
psm_length = buffered_get_be16(ctx);
|
||||
buffered_get_byte(ctx);
|
||||
buffered_get_byte(ctx);
|
||||
ps_info_length = buffered_get_be16(ctx);
|
||||
|
||||
/* skip program_stream_info */
|
||||
url_fskip(ctx, ps_info_length);
|
||||
es_map_length = get_be16(ctx);
|
||||
es_map_length = buffered_get_be16(ctx);
|
||||
|
||||
/* at least one es available? */
|
||||
while (es_map_length >= 4)
|
||||
{
|
||||
unsigned char type = (unsigned char) get_byte(ctx);
|
||||
unsigned char es_id =(unsigned char) get_byte(ctx);
|
||||
unsigned int es_info_length = get_be16(ctx);
|
||||
unsigned char type = (unsigned char) buffered_get_byte(ctx);
|
||||
unsigned char es_id =(unsigned char) buffered_get_byte(ctx);
|
||||
unsigned int es_info_length = buffered_get_be16(ctx);
|
||||
/* remember mapping from stream id to stream type */
|
||||
psm_es_type[es_id] = type;
|
||||
/* skip program_stream_info */
|
||||
url_fskip(ctx, es_info_length);
|
||||
es_map_length -= 4 + es_info_length;
|
||||
}
|
||||
get_be32(ctx); /* crc32 */
|
||||
buffered_get_be32(ctx); /* crc32 */
|
||||
return 2 + psm_length;
|
||||
}
|
||||
|
||||
|
||||
static int mpegps_read_pes_header(struct lib_ccx_ctx *ctx, int *pstart_code,
|
||||
static int mpegps_read_pes_header(struct ccx_demuxer *ctx, int *pstart_code,
|
||||
LLONG *ppts, LLONG *pdts)
|
||||
{
|
||||
int len, size, startcode, c, flags, header_len;
|
||||
@@ -428,11 +397,11 @@ redo:
|
||||
startcode == PRIVATE_STREAM_2)
|
||||
{
|
||||
/* skip them */
|
||||
len = get_be16(ctx);
|
||||
len = buffered_get_be16(ctx);
|
||||
// url_fskip(ctx, len);
|
||||
goto redo;
|
||||
}
|
||||
position_sanity_check();
|
||||
position_sanity_check(ctx->infd);
|
||||
if (startcode == PROGRAM_STREAM_MAP)
|
||||
{
|
||||
mpegps_psm_parse(ctx);
|
||||
@@ -445,30 +414,30 @@ redo:
|
||||
(startcode == 0x1bd)))
|
||||
goto redo;
|
||||
|
||||
len = get_be16(ctx);
|
||||
len = buffered_get_be16(ctx);
|
||||
pts = AV_NOPTS_VALUE;
|
||||
dts = AV_NOPTS_VALUE;
|
||||
position_sanity_check();
|
||||
position_sanity_check(ctx->infd);
|
||||
/* stuffing */
|
||||
for(;;) {
|
||||
if (len < 1)
|
||||
goto redo;
|
||||
c = get_byte(ctx);
|
||||
c = buffered_get_byte(ctx);
|
||||
len--;
|
||||
/* XXX: for mpeg1, should test only bit 7 */
|
||||
if (c != 0xff)
|
||||
break;
|
||||
}
|
||||
position_sanity_check();
|
||||
position_sanity_check(ctx->infd);
|
||||
if ((c & 0xc0) == 0x40) {
|
||||
/* buffer scale & size */
|
||||
if (len < 2)
|
||||
goto redo;
|
||||
get_byte(ctx);
|
||||
c = get_byte(ctx);
|
||||
buffered_get_byte(ctx);
|
||||
c = buffered_get_byte(ctx);
|
||||
len -= 2;
|
||||
}
|
||||
position_sanity_check();
|
||||
position_sanity_check(ctx->infd);
|
||||
if ((c & 0xf0) == 0x20) {
|
||||
if (len < 4)
|
||||
goto redo;
|
||||
@@ -488,8 +457,8 @@ redo:
|
||||
goto redo;
|
||||
}
|
||||
#endif
|
||||
flags = get_byte(ctx);
|
||||
header_len = get_byte(ctx);
|
||||
flags = buffered_get_byte(ctx);
|
||||
header_len = buffered_get_byte(ctx);
|
||||
len -= 2;
|
||||
if (header_len > len)
|
||||
goto redo;
|
||||
@@ -509,26 +478,26 @@ redo:
|
||||
}
|
||||
len -= header_len;
|
||||
while (header_len > 0) {
|
||||
get_byte(ctx);
|
||||
buffered_get_byte(ctx);
|
||||
header_len--;
|
||||
}
|
||||
}
|
||||
else if( c!= 0xf )
|
||||
goto redo;
|
||||
position_sanity_check();
|
||||
position_sanity_check(ctx->infd);
|
||||
if (startcode == PRIVATE_STREAM_1 /* && psm_es_type[startcode & 0xff] */)
|
||||
{
|
||||
if (len < 1)
|
||||
goto redo;
|
||||
startcode = get_byte(ctx);
|
||||
startcode = buffered_get_byte(ctx);
|
||||
len--;
|
||||
if (startcode >= 0x80 && startcode <= 0xbf) {
|
||||
/* audio: skip header */
|
||||
if (len < 3)
|
||||
goto redo;
|
||||
get_byte(ctx);
|
||||
get_byte(ctx);
|
||||
get_byte(ctx);
|
||||
buffered_get_byte(ctx);
|
||||
buffered_get_byte(ctx);
|
||||
buffered_get_byte(ctx);
|
||||
len -= 3;
|
||||
}
|
||||
}
|
||||
@@ -563,7 +532,7 @@ void ProcessVBIDataPacket(struct lib_ccx_ctx *ctx, struct cc_subtitle *sub)
|
||||
}
|
||||
|
||||
LLONG linemask = 0;
|
||||
dec_ctx = ctx->dec_ctx;
|
||||
dec_ctx = update_decoder_list(ctx);
|
||||
// unsigned long long utc = lastccptsu;
|
||||
|
||||
// [i]tv0 means there is a linemask
|
||||
@@ -646,7 +615,7 @@ void ProcessVBIDataPacket(struct lib_ccx_ctx *ctx, struct cc_subtitle *sub)
|
||||
// lastccptsu = utc;
|
||||
}
|
||||
|
||||
static int mpegps_read_packet(struct lib_ccx_ctx *ctx)
|
||||
static int mpegps_read_packet(struct ccx_demuxer *ctx)
|
||||
{
|
||||
LLONG pts, dts;
|
||||
|
||||
@@ -655,7 +624,7 @@ redo:
|
||||
len = mpegps_read_pes_header(ctx, &startcode, &pts, &dts);
|
||||
if (len < 0)
|
||||
return len;
|
||||
position_sanity_check();
|
||||
position_sanity_check(ctx->infd);
|
||||
/* now find stream */
|
||||
/*
|
||||
for(i=0;i<s->nb_streams;i++) {
|
||||
@@ -697,7 +666,7 @@ redo:
|
||||
{
|
||||
static const unsigned char avs_seqh[4] = { 0, 0, 1, 0xb0 };
|
||||
unsigned char buf[8];
|
||||
buffered_read (ctx, buf,8);
|
||||
buffered_read(ctx, buf, 8);
|
||||
ctx->past+=8;
|
||||
// get_buffer(&s->pb, buf, 8);
|
||||
buffered_seek(ctx, -8);
|
||||
@@ -755,9 +724,9 @@ goto skip; */
|
||||
// audio data
|
||||
if (len <= 3)
|
||||
goto skip;
|
||||
get_byte(ctx); // emphasis (1), muse(1), reserved(1), frame number(5)
|
||||
get_byte(ctx); // quant (2), freq(2), reserved(1), channels(3)
|
||||
get_byte(ctx); // dynamic range control (0x80 = off)
|
||||
buffered_get_byte(ctx); // emphasis (1), muse(1), reserved(1), frame number(5)
|
||||
buffered_get_byte(ctx); // quant (2), freq(2), reserved(1), channels(3)
|
||||
buffered_get_byte(ctx); // dynamic range control (0x80 = off)
|
||||
len -= 3;
|
||||
//freq = (b1 >> 4) & 3;
|
||||
//st->codec->sample_rate = lpcm_freq_tab[freq];
|
||||
@@ -779,9 +748,9 @@ goto skip; */
|
||||
}
|
||||
av.codec_id=codec_id;
|
||||
av.type=type;
|
||||
buffered_read (ctx, av.data,av.size);
|
||||
ctx->past+=av.size;
|
||||
position_sanity_check();
|
||||
buffered_read(ctx, av.data, av.size);
|
||||
ctx->past += av.size;
|
||||
position_sanity_check(ctx->infd);
|
||||
// LSEEK (fh,pkt->size,SEEK_CUR);
|
||||
av.pts = pts;
|
||||
av.dts = dts;
|
||||
@@ -795,62 +764,30 @@ goto skip; */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cc608_parity(unsigned int byte)
|
||||
{
|
||||
int ones = 0;
|
||||
|
||||
for (int i = 0; i < 7; i++)
|
||||
{
|
||||
if (byte & (1 << i))
|
||||
ones++;
|
||||
}
|
||||
|
||||
return ones & 1;
|
||||
}
|
||||
|
||||
static void cc608_build_parity_table(int *parity_table)
|
||||
{
|
||||
unsigned int byte;
|
||||
int parity_v;
|
||||
for (byte = 0; byte <= 127; byte++)
|
||||
{
|
||||
parity_v = cc608_parity(byte);
|
||||
/* CC uses odd parity (i.e., # of 1's in byte is odd.) */
|
||||
parity_table[byte] = parity_v;
|
||||
parity_table[byte | 0x80] = !parity_v;
|
||||
}
|
||||
}
|
||||
|
||||
void build_parity_table (void)
|
||||
{
|
||||
cc608_build_parity_table(cc608_parity_table);
|
||||
}
|
||||
|
||||
void myth_loop(struct lib_ccx_ctx *ctx, void *enc_ctx)
|
||||
void myth_loop(struct lib_ccx_ctx *ctx)
|
||||
{
|
||||
int rc;
|
||||
int has_vbi=0;
|
||||
LLONG saved = 0;
|
||||
struct cc_subtitle dec_sub;
|
||||
struct lib_cc_decode *dec_ctx = NULL;
|
||||
struct encoder_ctx *enc_ctx = update_encoder_list(ctx);
|
||||
unsigned long desp_length=65536;
|
||||
unsigned char *desp=(unsigned char *) malloc (desp_length);
|
||||
|
||||
av.data=NULL;
|
||||
ccx_options.buffer_input = 1;
|
||||
dec_ctx = ctx->dec_ctx;
|
||||
if (init_file_buffer())
|
||||
{
|
||||
fatal (EXIT_NOT_ENOUGH_MEMORY, "Not enough memory.\n");
|
||||
}
|
||||
unsigned long desp_length=65536;
|
||||
unsigned char *desp=(unsigned char *) malloc (desp_length);
|
||||
dec_ctx = update_decoder_list(ctx);
|
||||
desp_length = 65536;
|
||||
desp = (unsigned char *) malloc (desp_length);
|
||||
if (!desp)
|
||||
fatal (EXIT_NOT_ENOUGH_MEMORY, "Not enough memory.\n");
|
||||
saved=0;
|
||||
|
||||
memset(&dec_sub, 0, sizeof(dec_sub));
|
||||
while (!dec_ctx->processed_enough && (rc=mpegps_read_packet(ctx))==0)
|
||||
while (is_decoder_processed_enough(ctx) == CCX_FALSE && (rc=mpegps_read_packet(ctx->demux_ctx))==0)
|
||||
{
|
||||
position_sanity_check();
|
||||
position_sanity_check(ctx->demux_ctx->infd);
|
||||
if (av.codec_id==CODEC_ID_MPEG2VBI && av.type==CODEC_TYPE_DATA)
|
||||
{
|
||||
if (!has_vbi)
|
||||
@@ -874,19 +811,17 @@ void myth_loop(struct lib_ccx_ctx *ctx, void *enc_ctx)
|
||||
}
|
||||
if (av.pts!=AV_NOPTS_VALUE)
|
||||
{
|
||||
current_pts=av.pts;
|
||||
if (pts_set==0)
|
||||
pts_set=1;
|
||||
set_current_pts(dec_ctx->timing, av.pts);
|
||||
}
|
||||
memcpy (desp+saved,av.data,av.size);
|
||||
LLONG used = process_m2v(ctx, desp, length, &dec_sub);
|
||||
LLONG used = process_m2v(dec_ctx, desp, length, &dec_sub);
|
||||
memmove (desp,desp+used,(unsigned int) (length-used));
|
||||
saved=length-used;
|
||||
}
|
||||
|
||||
if (ccx_options.live_stream)
|
||||
{
|
||||
int cur_sec = (int) (get_fts() / 1000);
|
||||
int cur_sec = (int) (get_fts(dec_ctx->timing, dec_ctx->current_field) / 1000);
|
||||
int th=cur_sec/10;
|
||||
if (ctx->last_reported_progress!=th)
|
||||
{
|
||||
@@ -898,10 +833,10 @@ void myth_loop(struct lib_ccx_ctx *ctx, void *enc_ctx)
|
||||
{
|
||||
if (ctx->total_inputsize > 0 )
|
||||
{
|
||||
int progress = (int) ((((ctx->total_past+ctx->past)>>8)*100)/(ctx->total_inputsize>>8));
|
||||
int progress = (int) ((((ctx->total_past+ctx->demux_ctx->past)>>8)*100)/(ctx->total_inputsize>>8));
|
||||
if (ctx->last_reported_progress != progress)
|
||||
{
|
||||
int cur_sec = (int) (get_fts() / 1000);
|
||||
int cur_sec = (int) (get_fts(dec_ctx->timing, dec_ctx->current_field) / 1000);
|
||||
activity_progress (progress, cur_sec/60, cur_sec%60);
|
||||
|
||||
fflush (stdout);
|
||||
|
||||
@@ -16,30 +16,39 @@
|
||||
#define PASSWORD 2
|
||||
#define BIN_MODE 3
|
||||
#define CC_DESC 4
|
||||
#define BIN_HEADER 5
|
||||
#define BIN_DATA 6
|
||||
#define EPG_DATA 7
|
||||
#pragma warning( suppress : 4005)
|
||||
#define ERROR 51
|
||||
#define UNKNOWN_COMMAND 52
|
||||
#define WRONG_PASSWORD 53
|
||||
#define CONN_LIMIT 54
|
||||
#define PING 55
|
||||
|
||||
/* #include <time.h> */
|
||||
|
||||
#define DFT_PORT "2048" /* Default port for server and client */
|
||||
#define WRONG_PASSWORD_DELAY 2 /* Seconds */
|
||||
#define BUFFER_SIZE 50
|
||||
#define NO_RESPONCE_INTERVAL 20
|
||||
#define PING_INTERVAL 3
|
||||
|
||||
int srv_sd = -1; /* Server socket descriptor */
|
||||
|
||||
const char *srv_addr;
|
||||
const char *srv_port;
|
||||
const char *srv_cc_desc;
|
||||
const char *srv_pwd;
|
||||
unsigned char *srv_header;
|
||||
size_t srv_header_len;
|
||||
|
||||
/*
|
||||
* Established connection to speciefied addres.
|
||||
* Returns socked id
|
||||
*/
|
||||
int tcp_connect(const char *addr, const char *port);
|
||||
|
||||
/*
|
||||
* Asks password from stdin, sends it to the server and waits for
|
||||
* it's response
|
||||
*/
|
||||
int ask_passwd(int sd);
|
||||
|
||||
int check_password(int fd, const char *pwd);
|
||||
|
||||
int tcp_bind(const char *port, int *family);
|
||||
@@ -68,7 +77,10 @@ void init_sockets (void);
|
||||
void pr_command(char c);
|
||||
#endif
|
||||
|
||||
void connect_to_srv(const char *addr, const char *port, const char *cc_desc)
|
||||
void handle_write_error();
|
||||
int set_nonblocking(int fd);
|
||||
|
||||
void connect_to_srv(const char *addr, const char *port, const char *cc_desc, const char *pwd)
|
||||
{
|
||||
if (NULL == addr)
|
||||
{
|
||||
@@ -85,14 +97,16 @@ void connect_to_srv(const char *addr, const char *port, const char *cc_desc)
|
||||
if ((srv_sd = tcp_connect(addr, port)) < 0)
|
||||
fatal(EXIT_FAILURE, "Unable to connect\n");
|
||||
|
||||
if (ask_passwd(srv_sd) < 0)
|
||||
if (write_block(srv_sd, PASSWORD, pwd, pwd ? strlen(pwd) : 0) < 0)
|
||||
fatal(EXIT_FAILURE, "Unable to connect\n");
|
||||
|
||||
if (cc_desc != NULL &&
|
||||
write_block(srv_sd, CC_DESC, cc_desc, strlen(cc_desc)) < 0)
|
||||
{
|
||||
if (write_block(srv_sd, CC_DESC, cc_desc, cc_desc ? strlen(cc_desc) : 0) < 0)
|
||||
fatal(EXIT_FAILURE, "Unable to connect\n");
|
||||
}
|
||||
|
||||
srv_addr = addr;
|
||||
srv_port = port;
|
||||
srv_cc_desc = cc_desc;
|
||||
srv_pwd = pwd;
|
||||
|
||||
mprint("Connected to %s:%s\n", addr, port);
|
||||
}
|
||||
@@ -106,38 +120,24 @@ void net_send_header(const unsigned char *data, size_t len)
|
||||
fprintf(stderr, "File created by %02X version %02X%02X\n", data[3], data[4], data[5]);
|
||||
fprintf(stderr, "File format revision: %02X%02X\n", data[6], data[7]);
|
||||
#endif
|
||||
if (write_block(srv_sd, BIN_MODE, NULL, 0) <= 0)
|
||||
|
||||
if (write_block(srv_sd, BIN_HEADER, data, len) <= 0)
|
||||
{
|
||||
printf("Can't send BIN header\n");
|
||||
return;
|
||||
}
|
||||
|
||||
char ok;
|
||||
if (read_byte(srv_sd, &ok) != 1)
|
||||
if (srv_header != NULL)
|
||||
return;
|
||||
|
||||
#if DEBUG_OUT
|
||||
fprintf(stderr, "[S] ");
|
||||
pr_command(ok);
|
||||
fprintf(stderr, "\n");
|
||||
#endif
|
||||
if ((srv_header = malloc(len)) == NULL)
|
||||
fatal(EXIT_FAILURE, "Not enought memory");
|
||||
|
||||
if (ERROR == ok)
|
||||
{
|
||||
printf("Internal server error\n");
|
||||
return;
|
||||
}
|
||||
|
||||
ssize_t rc;
|
||||
if ((rc = writen(srv_sd, data, len)) != (int) len)
|
||||
{
|
||||
if (rc < 0)
|
||||
mprint("write() error: %s", strerror(errno));
|
||||
return;
|
||||
}
|
||||
memcpy(srv_header, data, len);
|
||||
srv_header_len = len;
|
||||
}
|
||||
|
||||
void net_send_cc(const unsigned char *data, size_t len)
|
||||
int net_send_cc(const unsigned char *data, int len, void *private_data, struct cc_subtitle *sub)
|
||||
{
|
||||
assert(srv_sd > 0);
|
||||
|
||||
@@ -145,19 +145,188 @@ void net_send_cc(const unsigned char *data, size_t len)
|
||||
fprintf(stderr, "[C] Sending %u bytes\n", len);
|
||||
#endif
|
||||
|
||||
ssize_t rc;
|
||||
if ((rc = writen(srv_sd, data, len)) != (int) len)
|
||||
if (write_block(srv_sd, BIN_DATA, data, len) <= 0)
|
||||
{
|
||||
if (rc < 0)
|
||||
mprint("write() error: %s", strerror(errno));
|
||||
return;
|
||||
printf("Can't send BIN data\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* nanosleep((struct timespec[]){{0, 100000000}}, NULL); */
|
||||
/* nanosleep((struct timespec[]){{0, 4000000}}, NULL); */
|
||||
/* Sleep(100); */
|
||||
return 1;
|
||||
}
|
||||
|
||||
void net_check_conn()
|
||||
{
|
||||
time_t now;
|
||||
static time_t last_ping = 0;
|
||||
char c = 0;
|
||||
int rc;
|
||||
|
||||
if (srv_sd <= 0)
|
||||
return;
|
||||
|
||||
now = time(NULL);
|
||||
|
||||
if (last_ping == 0)
|
||||
last_ping = now;
|
||||
|
||||
do {
|
||||
c = 0;
|
||||
rc = read_byte(srv_sd, &c);
|
||||
if (c == PING) {
|
||||
#if DEBUG_OUT
|
||||
fprintf(stderr, "[S] Recieved PING\n");
|
||||
#endif
|
||||
last_ping = now;
|
||||
}
|
||||
} while (rc > 0 && c == PING);
|
||||
|
||||
if (now - last_ping > NO_RESPONCE_INTERVAL)
|
||||
{
|
||||
fprintf(stderr,
|
||||
"[S] No PING recieved from the server in %u sec, reconnecting\n",
|
||||
NO_RESPONCE_INTERVAL);
|
||||
close(srv_sd);
|
||||
srv_sd = -1;
|
||||
|
||||
connect_to_srv(srv_addr, srv_port, srv_cc_desc, srv_pwd);
|
||||
|
||||
net_send_header(srv_header, srv_header_len);
|
||||
last_ping = now;
|
||||
}
|
||||
|
||||
static time_t last_send_ping = 0;
|
||||
if (now - last_send_ping >= PING_INTERVAL)
|
||||
{
|
||||
if (write_block(srv_sd, PING, NULL, 0) < 0)
|
||||
{
|
||||
printf("Unable to send data\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
last_send_ping = now;
|
||||
}
|
||||
}
|
||||
|
||||
void net_send_epg(
|
||||
const char *start,
|
||||
const char *stop,
|
||||
const char *title,
|
||||
const char *desc,
|
||||
const char *lang,
|
||||
const char *category
|
||||
)
|
||||
{
|
||||
size_t st;
|
||||
size_t sp;
|
||||
size_t t;
|
||||
size_t d;
|
||||
size_t l;
|
||||
size_t c;
|
||||
size_t len;
|
||||
char *epg;
|
||||
char *end;
|
||||
|
||||
/* nanosleep((struct timespec[]){{0, 100000000}}, NULL); */
|
||||
assert(srv_sd > 0);
|
||||
if (NULL == start)
|
||||
return;
|
||||
if (NULL == stop)
|
||||
return;
|
||||
|
||||
st = strlen(start) + 1;
|
||||
sp = strlen(stop) + 1;
|
||||
|
||||
t = 1;
|
||||
if (title != NULL)
|
||||
t += strlen(title);
|
||||
|
||||
d = 1;
|
||||
if (desc != NULL)
|
||||
d += strlen(desc);
|
||||
|
||||
l = 1;
|
||||
if (lang != NULL)
|
||||
l += strlen(lang);
|
||||
|
||||
c = 1;
|
||||
if (category != NULL)
|
||||
c += strlen(category);
|
||||
|
||||
len = st + sp + t + d + l + c;
|
||||
|
||||
epg = (char *) calloc(len, sizeof(char));
|
||||
if (NULL == epg)
|
||||
return;
|
||||
|
||||
end = epg;
|
||||
|
||||
memcpy(end, start, st);
|
||||
end += st;
|
||||
|
||||
memcpy(end, stop, sp);
|
||||
end += sp;
|
||||
|
||||
if (title != NULL)
|
||||
memcpy(end, title, t);
|
||||
end += t;
|
||||
|
||||
if (desc != NULL)
|
||||
memcpy(end, desc, d);
|
||||
end += d;
|
||||
|
||||
if (lang != NULL)
|
||||
memcpy(end, lang, l);
|
||||
end += l;
|
||||
|
||||
if (category != NULL)
|
||||
memcpy(end, category, c);
|
||||
end += c;
|
||||
|
||||
#if DEBUG_OUT
|
||||
fprintf(stderr, "[C] Sending EPG: %u bytes\n", len);
|
||||
#endif
|
||||
|
||||
if (write_block(srv_sd, EPG_DATA, epg, len) <= 0)
|
||||
fprintf(stderr, "Can't send EPG data\n");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
int net_tcp_read(int socket, void *buffer, size_t length)
|
||||
{
|
||||
assert(buffer != NULL);
|
||||
assert(length > 0);
|
||||
|
||||
time_t now = time(NULL);
|
||||
static time_t last_ping = 0;
|
||||
if (last_ping == 0)
|
||||
last_ping = now;
|
||||
|
||||
if (now - last_ping > PING_INTERVAL)
|
||||
{
|
||||
last_ping = now;
|
||||
if (write_byte(socket, PING) <= 0)
|
||||
fatal(EXIT_FAILURE, "Unable to send keep-alive packet to client\n");
|
||||
}
|
||||
|
||||
int rc;
|
||||
char c;
|
||||
size_t l;
|
||||
|
||||
do
|
||||
{
|
||||
l = length;
|
||||
|
||||
if ((rc = read_block(socket, &c, buffer, &l)) <= 0)
|
||||
return rc;
|
||||
}
|
||||
while (c != BIN_DATA && c != BIN_HEADER);
|
||||
|
||||
return l;
|
||||
}
|
||||
|
||||
/*
|
||||
* command | lenght | data | \r\n
|
||||
* 1 byte | INT_LEN bytes | lenght bytes | 2 bytes
|
||||
@@ -206,7 +375,7 @@ ssize_t write_block(int fd, char command, const char *buf, size_t buf_len)
|
||||
}
|
||||
|
||||
#if DEBUG_OUT
|
||||
if (buf != NULL)
|
||||
if (buf != NULL && command != BIN_HEADER && command != BIN_DATA)
|
||||
{
|
||||
fwrite(buf, sizeof(char), buf_len, stderr);
|
||||
fprintf(stderr, " ");
|
||||
@@ -296,83 +465,12 @@ int tcp_connect(const char *host, const char *port)
|
||||
if (NULL == p)
|
||||
return -1;
|
||||
|
||||
if (set_nonblocking(sockfd) < 0)
|
||||
return -1;
|
||||
|
||||
return sockfd;
|
||||
}
|
||||
|
||||
int ask_passwd(int sd)
|
||||
{
|
||||
assert(sd >= 0);
|
||||
|
||||
size_t len;
|
||||
char pw[BUFFER_SIZE] = { 0 };
|
||||
|
||||
char ok;
|
||||
|
||||
do {
|
||||
do {
|
||||
if (read_byte(sd, &ok) != 1)
|
||||
{
|
||||
fatal(EXIT_FAILURE, "read() error: %s", strerror(errno));
|
||||
}
|
||||
|
||||
#if DEBUG_OUT
|
||||
fprintf(stderr, "[S] ");
|
||||
pr_command(ok);
|
||||
fprintf(stderr, "\n");
|
||||
#endif
|
||||
|
||||
if (OK == ok)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
else if (CONN_LIMIT == ok)
|
||||
{
|
||||
mprint("Too many connections to the server, try later\n");
|
||||
return -1;
|
||||
}
|
||||
else if (ERROR == ok)
|
||||
{
|
||||
mprint("Internal server error\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
} while(ok != PASSWORD);
|
||||
|
||||
printf("Enter password: ");
|
||||
fflush(stdout);
|
||||
|
||||
char *p = pw;
|
||||
while ((unsigned)(p - pw) < sizeof(pw) && ((*p = fgetc(stdin)) != '\n'))
|
||||
p++;
|
||||
len = p - pw; /* without \n */
|
||||
|
||||
if (write_block(sd, PASSWORD, pw, len) < 0)
|
||||
return -1;
|
||||
|
||||
if (read_byte(sd, &ok) != 1)
|
||||
return -1;
|
||||
|
||||
#if DEBUG_OUT
|
||||
fprintf(stderr, "[S] ");
|
||||
pr_command(ok);
|
||||
fprintf(stderr, "\n");
|
||||
#endif
|
||||
|
||||
if (UNKNOWN_COMMAND == ok)
|
||||
{
|
||||
printf("Wrong password\n");
|
||||
fflush(stdout);
|
||||
}
|
||||
else if (ERROR == ok)
|
||||
{
|
||||
mprint("Internal server error\n");
|
||||
return -1;
|
||||
}
|
||||
} while(OK != ok);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int start_tcp_srv(const char *port, const char *pwd)
|
||||
{
|
||||
if (NULL == port)
|
||||
@@ -436,33 +534,9 @@ int start_tcp_srv(const char *port, const char *pwd)
|
||||
|
||||
free(cliaddr);
|
||||
|
||||
if (pwd != NULL && (rc = check_password(sockfd, pwd)) <= 0)
|
||||
goto close_conn;
|
||||
if (check_password(sockfd, pwd) > 0)
|
||||
break;
|
||||
|
||||
#if DEBUG_OUT
|
||||
fprintf(stderr, "[S] OK\n");
|
||||
#endif
|
||||
if (write_byte(sockfd, OK) != 1)
|
||||
goto close_conn;
|
||||
|
||||
char c;
|
||||
size_t len = BUFFER_SIZE;
|
||||
char buf[BUFFER_SIZE];
|
||||
|
||||
do {
|
||||
if (read_block(sockfd, &c, buf, &len) <= 0)
|
||||
goto close_conn;
|
||||
} while (c != BIN_MODE);
|
||||
|
||||
#if DEBUG_OUT
|
||||
fprintf(stderr, "[S] OK\n");
|
||||
#endif
|
||||
if (write_byte(sockfd, OK) != 1)
|
||||
goto close_conn;
|
||||
|
||||
break;
|
||||
|
||||
close_conn:
|
||||
mprint("Connection closed\n");
|
||||
#if _WIN32
|
||||
closesocket(sockfd);
|
||||
@@ -482,43 +556,33 @@ close_conn:
|
||||
|
||||
int check_password(int fd, const char *pwd)
|
||||
{
|
||||
assert(pwd != NULL);
|
||||
|
||||
char c;
|
||||
int rc;
|
||||
size_t len;
|
||||
char buf[BUFFER_SIZE];
|
||||
size_t len = BUFFER_SIZE;
|
||||
static char buf[BUFFER_SIZE + 1];
|
||||
|
||||
while(1)
|
||||
{
|
||||
len = BUFFER_SIZE;
|
||||
#if DEBUG_OUT
|
||||
fprintf(stderr, "[S] PASSWORD\n");
|
||||
#endif
|
||||
if ((rc = write_byte(fd, PASSWORD)) <= 0)
|
||||
return rc;
|
||||
if ((rc = read_block(fd, &c, buf, &len)) <= 0)
|
||||
return rc;
|
||||
|
||||
if ((rc = read_block(fd, &c, buf, &len)) <= 0)
|
||||
return rc;
|
||||
buf[len] = '\0';
|
||||
|
||||
if (c != PASSWORD)
|
||||
return -1;
|
||||
|
||||
if (strlen(pwd) != len || strncmp(pwd, buf, len) != 0)
|
||||
{
|
||||
sleep(WRONG_PASSWORD_DELAY);
|
||||
|
||||
#if DEBUG_OUT
|
||||
fprintf(stderr, "[S] WRONG_PASSWORD\n");
|
||||
#endif
|
||||
if ((rc = write_byte(fd, WRONG_PASSWORD)) <= 0)
|
||||
return rc;
|
||||
|
||||
continue;
|
||||
}
|
||||
if (pwd == NULL)
|
||||
return 1;
|
||||
|
||||
if (c == PASSWORD && strcmp(pwd, buf) == 0) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
#if DEBUG_OUT
|
||||
fprintf(stderr, "[C] Wrong passsword\n");
|
||||
#endif
|
||||
|
||||
#if DEBUG_OUT
|
||||
fprintf(stderr, "[S] PASSWORD\n");
|
||||
#endif
|
||||
if (write_byte(fd, PASSWORD) < 0)
|
||||
return -1;
|
||||
return -1;
|
||||
}
|
||||
|
||||
int tcp_bind(const char *port, int *family)
|
||||
@@ -652,7 +716,7 @@ ssize_t read_block(int fd, char *command, char *buf, size_t *buf_len)
|
||||
fprintf(stderr, " ");
|
||||
#endif
|
||||
|
||||
size_t len = atoi(len_str);
|
||||
size_t len = atoi(len_str);
|
||||
|
||||
if (len > 0)
|
||||
{
|
||||
@@ -679,8 +743,11 @@ ssize_t read_block(int fd, char *command, char *buf, size_t *buf_len)
|
||||
nread += rc;
|
||||
|
||||
#if DEBUG_OUT
|
||||
fwrite(buf, sizeof(char), len, stderr);
|
||||
fprintf(stderr, " ");
|
||||
if (*command != BIN_DATA && *command != BIN_HEADER)
|
||||
{
|
||||
fwrite(buf, sizeof(char), len, stderr);
|
||||
fprintf(stderr, " ");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -734,6 +801,18 @@ void pr_command(char c)
|
||||
case PASSWORD:
|
||||
fprintf(stderr, "PASSWORD");
|
||||
break;
|
||||
case BIN_HEADER:
|
||||
fprintf(stderr, "BIN_HEADER");
|
||||
break;
|
||||
case BIN_DATA:
|
||||
fprintf(stderr, "BIN_DATA");
|
||||
break;
|
||||
case EPG_DATA:
|
||||
fprintf(stderr, "EPG_DATA");
|
||||
break;
|
||||
case PING:
|
||||
fprintf(stderr, "PING");
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "UNKNOWN (%d)", (int) c);
|
||||
break;
|
||||
@@ -767,6 +846,10 @@ ssize_t readn(int fd, void *vptr, size_t n)
|
||||
{
|
||||
nread = 0;
|
||||
}
|
||||
else if (errno == EAGAIN || errno == EWOULDBLOCK)
|
||||
{
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
#if _WIN32
|
||||
@@ -810,11 +893,7 @@ ssize_t writen(int fd, const void *vptr, size_t n)
|
||||
}
|
||||
else
|
||||
{
|
||||
#if _WIN32
|
||||
wprintf(L"send() eror: %ld\n", WSAGetLastError());
|
||||
#else
|
||||
mprint("send() error: %s\n", strerror(errno));
|
||||
#endif
|
||||
handle_write_error();
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
@@ -897,9 +976,11 @@ int start_upd_srv(const char *addr_str, unsigned port)
|
||||
struct sockaddr_in servaddr;
|
||||
servaddr.sin_family = AF_INET;
|
||||
servaddr.sin_port = htons(port);
|
||||
#ifndef _WIN32
|
||||
if (IN_MULTICAST(addr))
|
||||
servaddr.sin_addr.s_addr = htonl(addr);
|
||||
else
|
||||
#endif
|
||||
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
|
||||
|
||||
if (bind(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) != 0)
|
||||
@@ -961,3 +1042,71 @@ void init_sockets (void)
|
||||
socket_inited = 1;
|
||||
}
|
||||
}
|
||||
|
||||
void handle_write_error()
|
||||
{
|
||||
#if _WIN32
|
||||
long err = WSAGetLastError();
|
||||
#else
|
||||
char *err = strerror(errno);
|
||||
#endif
|
||||
|
||||
if (srv_sd < 0)
|
||||
return;
|
||||
|
||||
char c = 0;
|
||||
int rc;
|
||||
do {
|
||||
c = 0;
|
||||
rc = read_byte(srv_sd, &c);
|
||||
if (rc < 0)
|
||||
{
|
||||
#if _WIN32
|
||||
wprintf(L"send() eror: %ld\n", err);
|
||||
#else
|
||||
mprint("send() error: %s\n", err);
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
} while (rc > 0 && c == PING);
|
||||
|
||||
switch (c)
|
||||
{
|
||||
case PASSWORD:
|
||||
mprint("Wrong password (use -tcppassword)\n");
|
||||
break;
|
||||
case CONN_LIMIT:
|
||||
mprint("Too many connections to the server, please wait\n");
|
||||
break;
|
||||
case ERROR:
|
||||
mprint("Internal server error");
|
||||
break;
|
||||
default:
|
||||
#if _WIN32
|
||||
wprintf(L"send() eror: %ld\n", err);
|
||||
#else
|
||||
mprint("send() error: %s\n", err);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
int set_nonblocking(int fd)
|
||||
{
|
||||
int f;
|
||||
#ifdef O_NONBLOCK
|
||||
if ((f = fcntl(fd, F_GETFL, 0)) < 0)
|
||||
f = 0;
|
||||
|
||||
return fcntl(fd, F_SETFL, f | O_NONBLOCK);
|
||||
#else
|
||||
f = 1;
|
||||
#if _WIN32
|
||||
return ioctlsocket(fd, FIONBIO, &f);
|
||||
#else
|
||||
return ioctl(fd, FIONBIO, &f);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -3,10 +3,23 @@
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
void connect_to_srv(const char *addr, const char *port, const char *cc_desc);
|
||||
void connect_to_srv(const char *addr, const char *port, const char *cc_desc, const char *pwd);
|
||||
|
||||
void net_send_header(const unsigned char *data, size_t len);
|
||||
void net_send_cc(const unsigned char *data, size_t len);
|
||||
int net_send_cc(const unsigned char *data, int length, void *private_data, struct cc_subtitle *sub);
|
||||
|
||||
void net_check_conn();
|
||||
|
||||
void net_send_epg(
|
||||
const char *start,
|
||||
const char *stop,
|
||||
const char *title,
|
||||
const char *desc,
|
||||
const char *lang,
|
||||
const char *category
|
||||
);
|
||||
|
||||
int net_tcp_read(int socket, void *buffer, size_t length);
|
||||
|
||||
int start_tcp_srv(const char *port, const char *pwd);
|
||||
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
#include "png.h"
|
||||
#include "lib_ccx.h"
|
||||
#ifdef ENABLE_OCR
|
||||
#include "platform.h"
|
||||
#include "capi.h"
|
||||
#include "ccx_common_constants.h"
|
||||
#include "allheaders.h"
|
||||
#include <dirent.h>
|
||||
#include "spupng_encoder.h"
|
||||
|
||||
#include "ccx_encoders_helpers.h"
|
||||
#undef OCR_DEBUG
|
||||
struct ocrCtx
|
||||
{
|
||||
TessBaseAPI* api;
|
||||
@@ -63,11 +63,12 @@ static int search_language_pack(const char *dirname,const char *lang)
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void delete_ocr (struct ocrCtx* ctx)
|
||||
void delete_ocr (void** arg)
|
||||
{
|
||||
struct ocrCtx* ctx = *arg;
|
||||
TessBaseAPIEnd(ctx->api);
|
||||
TessBaseAPIDelete(ctx->api);
|
||||
freep(&ctx);
|
||||
freep(arg);
|
||||
}
|
||||
void* init_ocr(int lang_index)
|
||||
{
|
||||
@@ -93,24 +94,58 @@ void* init_ocr(int lang_index)
|
||||
/* select english */
|
||||
lang_index = 1;
|
||||
}
|
||||
ret = TessBaseAPIInit3(ctx->api,"", language[lang_index]);
|
||||
|
||||
ret = TessBaseAPIInit3(ctx->api, NULL, language[lang_index]);
|
||||
if(ret < 0)
|
||||
{
|
||||
goto fail;
|
||||
}
|
||||
return ctx;
|
||||
fail:
|
||||
delete_ocr(ctx);
|
||||
delete_ocr((void**)&ctx);
|
||||
return NULL;
|
||||
|
||||
}
|
||||
|
||||
int ignore_alpha_at_edge(png_byte *alpha, unsigned char* indata, int w, int h, PIX *in, PIX **out)
|
||||
{
|
||||
int i, j, index, start_y, end_y;
|
||||
int find_end_x = CCX_FALSE;
|
||||
BOX* cropWindow;
|
||||
for (j = 1; j < w-1; j++)
|
||||
{
|
||||
for (i = 0; i < h; i++)
|
||||
{
|
||||
index = indata[i * w + (j)];
|
||||
if(alpha[index] != 0)
|
||||
{
|
||||
if(find_end_x == CCX_FALSE)
|
||||
{
|
||||
start_y = j;
|
||||
find_end_x = CCX_TRUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
end_y = j;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
cropWindow = boxCreate(start_y, 0, (w - (start_y + ( w - end_y) )), h - 1);
|
||||
*out = pixClipRectangle(in, cropWindow, NULL);
|
||||
boxDestroy(&cropWindow);
|
||||
|
||||
return 0;
|
||||
}
|
||||
char* ocr_bitmap(void* arg, png_color *palette,png_byte *alpha, unsigned char* indata,int w, int h)
|
||||
{
|
||||
PIX *pix;
|
||||
PIX *pix = NULL;
|
||||
PIX *cpix = NULL;
|
||||
char*text_out= NULL;
|
||||
int i,j,index;
|
||||
unsigned int wpl;
|
||||
unsigned int *data,*ppixel;
|
||||
BOOL tess_ret = FALSE;
|
||||
struct ocrCtx* ctx = arg;
|
||||
pix = pixCreate(w, h, 32);
|
||||
if(pix == NULL)
|
||||
@@ -133,13 +168,24 @@ char* ocr_bitmap(void* arg, png_color *palette,png_byte *alpha, unsigned char* i
|
||||
ppixel++;
|
||||
}
|
||||
}
|
||||
|
||||
text_out = TessBaseAPIProcessPage(ctx->api, pix, 0, NULL, NULL, 0);
|
||||
if(!text_out)
|
||||
ignore_alpha_at_edge(alpha, indata, w, h, pix, &cpix);
|
||||
#ifdef OCR_DEBUG
|
||||
{
|
||||
char str[128] = "";
|
||||
static int i = 0;
|
||||
sprintf(str,"temp/file_c_%d.png",i);
|
||||
pixWrite(str, cpix, IFF_PNG);
|
||||
i++;
|
||||
}
|
||||
#endif
|
||||
TessBaseAPISetImage2(ctx->api, cpix);
|
||||
tess_ret = TessBaseAPIRecognize(ctx->api, NULL);
|
||||
if( tess_ret != 0)
|
||||
printf("\nsomething messy\n");
|
||||
|
||||
//TessDeleteText(text_out);
|
||||
pixDestroy(&pix);
|
||||
text_out = TessBaseAPIGetUTF8Text(ctx->api);
|
||||
pixDestroy(&pix);
|
||||
pixDestroy(&cpix);
|
||||
|
||||
return text_out;
|
||||
}
|
||||
@@ -207,7 +253,7 @@ static int quantize_map(png_byte *alpha, png_color *palette,
|
||||
/* sorted in increasing order of intensity */
|
||||
shell_sort((void*)iot, nb_color, sizeof(*iot), check_trans_tn_intensity, (void*)&ti);
|
||||
|
||||
#if OCR_DEBUG
|
||||
#ifdef OCR_DEBUG
|
||||
ccx_common_logging.log_ftn("Intensity ordered table\n");
|
||||
for (int i = 0; i < nb_color; i++)
|
||||
{
|
||||
@@ -240,7 +286,7 @@ static int quantize_map(png_byte *alpha, png_color *palette,
|
||||
histogram[iot[max_ind]] = 0;
|
||||
}
|
||||
|
||||
#if OCR_DEBUG
|
||||
#ifdef OCR_DEBUG
|
||||
ccx_common_logging.log_ftn("max redundant intensities table\n");
|
||||
for (int i = 0; i < max_color; i++)
|
||||
{
|
||||
@@ -276,7 +322,7 @@ static int quantize_map(png_byte *alpha, png_color *palette,
|
||||
}
|
||||
|
||||
}
|
||||
#if OCR_DEBUG
|
||||
#ifdef OCR_DEBUG
|
||||
ccx_common_logging.log_ftn("Colors present in quantized Image\n");
|
||||
for (int i = 0; i < nb_color; i++)
|
||||
{
|
||||
@@ -296,30 +342,88 @@ int ocr_rect(void* arg, struct cc_bitmap *rect, char **str)
|
||||
png_color *palette = NULL;
|
||||
png_byte *alpha = NULL;
|
||||
|
||||
palette = (png_color*) malloc(rect[0].nb_colors * sizeof(png_color));
|
||||
palette = (png_color*) malloc(rect->nb_colors * sizeof(png_color));
|
||||
if(!palette)
|
||||
{
|
||||
ret = -1;
|
||||
goto end;
|
||||
}
|
||||
alpha = (png_byte*) malloc(rect[0].nb_colors * sizeof(png_byte));
|
||||
alpha = (png_byte*) malloc(rect->nb_colors * sizeof(png_byte));
|
||||
if(!alpha)
|
||||
{
|
||||
ret = -1;
|
||||
goto end;
|
||||
}
|
||||
|
||||
/* TODO do rectangle wise, one color table should not be used for all rectangles */
|
||||
mapclut_paletee(palette, alpha, (uint32_t *)rect->data[1],rect->nb_colors);
|
||||
|
||||
quantize_map(alpha, palette, rect->data[0], rect->w * rect->h, 3, rect->nb_colors);
|
||||
*str = ocr_bitmap(arg, palette, alpha, rect->data[0], rect->w, rect->h);
|
||||
|
||||
end:
|
||||
freep(&palette);
|
||||
freep(&alpha);
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Call back function used while sorting rectangle by y position
|
||||
* if both rectangle have same y position then x position is considered
|
||||
*/
|
||||
int compare_rect_by_ypos(const void*p1, const void *p2, void*arg)
|
||||
{
|
||||
const struct cc_bitmap* r1 = p1;
|
||||
const struct cc_bitmap* r2 = p2;
|
||||
if(r1->y > r2->y)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
else if (r1->y == r2->y)
|
||||
{
|
||||
if(r1->x > r2->x)
|
||||
return 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check multiple rectangles and combine them to give one paragraph
|
||||
* for all text detected from rectangles
|
||||
*/
|
||||
char *paraof_ocrtext(struct cc_subtitle *sub)
|
||||
{
|
||||
int i;
|
||||
int len = 0;
|
||||
char *str;
|
||||
struct cc_bitmap* rect;
|
||||
|
||||
shell_sort(sub->data, sub->nb_data, sizeof(struct cc_bitmap), compare_rect_by_ypos, NULL);
|
||||
for(i = 0, rect = sub->data; i < sub->nb_data; i++, rect++)
|
||||
{
|
||||
if(rect->ocr_text)
|
||||
len += strlen(rect->ocr_text);
|
||||
}
|
||||
if(len <= 0)
|
||||
return NULL;
|
||||
else
|
||||
{
|
||||
str = malloc(len+1);
|
||||
if(!str)
|
||||
return NULL;
|
||||
*str = '\0';
|
||||
}
|
||||
|
||||
for(i = 0, rect = sub->data; i < sub->nb_data; i++, rect++)
|
||||
{
|
||||
strcat(str, rect->ocr_text);
|
||||
freep(&rect->ocr_text);
|
||||
}
|
||||
return str;
|
||||
}
|
||||
#else
|
||||
char* ocr_bitmap(png_color *palette,png_byte *alpha, unsigned char* indata,unsigned char d,int w, int h)
|
||||
{
|
||||
|
||||
@@ -2,8 +2,10 @@
|
||||
#define OCR_H
|
||||
#include <png.h>
|
||||
|
||||
void delete_ocr (void** arg);
|
||||
void* init_ocr(int lang_index);
|
||||
char* ocr_bitmap(void* arg, png_color *palette,png_byte *alpha, unsigned char* indata,int w, int h);
|
||||
int ocr_rect(void* arg, struct cc_bitmap *rect, char **str);
|
||||
char *paraof_ocrtext(struct cc_subtitle *sub);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -6,256 +6,235 @@
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
/* TODO remove dependency of encoder by removing writeDVDraw from this file */
|
||||
#include "ccx_encoders_structs.h"
|
||||
|
||||
void init_write (struct ccx_s_write *wb,char *filename)
|
||||
void dinit_write(struct ccx_s_write *wb)
|
||||
{
|
||||
if (wb->fh > 0)
|
||||
close(wb->fh);
|
||||
freep(&wb->filename);
|
||||
}
|
||||
int init_write (struct ccx_s_write *wb, char *filename)
|
||||
{
|
||||
memset(wb, 0, sizeof(struct ccx_s_write));
|
||||
wb->fh=-1;
|
||||
wb->filename=filename;
|
||||
}
|
||||
|
||||
void writeraw (const unsigned char *data, int length, struct ccx_s_write *wb)
|
||||
{
|
||||
write (wb->fh,data,length);
|
||||
}
|
||||
|
||||
void writedata(const unsigned char *data, int length, ccx_decoder_608_context *context, struct cc_subtitle *sub)
|
||||
{
|
||||
// Don't do anything for empty data
|
||||
if (data==NULL)
|
||||
return;
|
||||
|
||||
if (ccx_options.write_format==CCX_OF_RAW || ccx_options.write_format==CCX_OF_DVDRAW)
|
||||
wb->fh=-1;
|
||||
wb->filename = filename;
|
||||
mprint ("Creating %s\n", filename);
|
||||
wb->fh = open (filename, O_RDWR | O_CREAT | O_TRUNC | O_BINARY, S_IREAD | S_IWRITE);
|
||||
if (wb->fh == -1)
|
||||
{
|
||||
if (context && context->out)
|
||||
writeraw (data,length,context->out);
|
||||
return CCX_COMMON_EXIT_FILE_CREATION_FAILED;
|
||||
}
|
||||
else if (ccx_options.write_format==CCX_OF_SMPTETT ||
|
||||
ccx_options.write_format==CCX_OF_SAMI ||
|
||||
ccx_options.write_format==CCX_OF_SRT ||
|
||||
ccx_options.write_format==CCX_OF_TRANSCRIPT ||
|
||||
ccx_options.write_format==CCX_OF_SPUPNG ||
|
||||
ccx_options.write_format==CCX_OF_NULL)
|
||||
process608 (data,length,context, sub);
|
||||
else
|
||||
fatal(CCX_COMMON_EXIT_BUG_BUG, "Should not be reached!");
|
||||
return EXIT_OK;
|
||||
}
|
||||
|
||||
void flushbuffer (struct lib_ccx_ctx *ctx, struct ccx_s_write *wb, int closefile)
|
||||
int writeraw (const unsigned char *data, int length, void *private_data, struct cc_subtitle *sub)
|
||||
{
|
||||
if (closefile && wb!=NULL && wb->fh!=-1 && !ctx->cc_to_stdout)
|
||||
close (wb->fh);
|
||||
unsigned char* sub_data = NULL;
|
||||
// Don't do anything for empty data
|
||||
if (data==NULL)
|
||||
return -1;
|
||||
|
||||
sub->data = realloc(sub->data, length + sub->nb_data);
|
||||
if (!sub->data)
|
||||
return EXIT_NOT_ENOUGH_MEMORY;
|
||||
|
||||
sub_data = sub->data;
|
||||
memcpy(sub_data + sub->nb_data, data, length);
|
||||
sub->got_output = 1;
|
||||
sub->nb_data += length;
|
||||
sub->type = CC_RAW;
|
||||
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
void writeDVDraw (const unsigned char *data1, int length1,
|
||||
const unsigned char *data2, int length2,
|
||||
struct ccx_s_write *wb)
|
||||
struct cc_subtitle *sub)
|
||||
{
|
||||
/* these are only used by DVD raw mode: */
|
||||
static int loopcount = 1; /* loop 1: 5 elements, loop 2: 8 elements,
|
||||
loop 3: 11 elements, rest: 15 elements */
|
||||
static int datacount = 0; /* counts within loop */
|
||||
/* these are only used by DVD raw mode: */
|
||||
static int loopcount = 1; /* loop 1: 5 elements, loop 2: 8 elements,
|
||||
loop 3: 11 elements, rest: 15 elements */
|
||||
static int datacount = 0; /* counts within loop */
|
||||
|
||||
if (datacount==0)
|
||||
{
|
||||
write (wb->fh,DVD_HEADER,sizeof (DVD_HEADER));
|
||||
if (loopcount==1)
|
||||
write (wb->fh,lc1,sizeof (lc1));
|
||||
if (loopcount==2)
|
||||
write (wb->fh,lc2,sizeof (lc2));
|
||||
if (loopcount==3)
|
||||
{
|
||||
write (wb->fh,lc3,sizeof (lc3));
|
||||
if (data2 && length2)
|
||||
write (wb->fh,data2,length2);
|
||||
}
|
||||
if (loopcount>3)
|
||||
{
|
||||
write (wb->fh,lc4,sizeof (lc4));
|
||||
if (datacount==0)
|
||||
{
|
||||
writeraw (DVD_HEADER, sizeof (DVD_HEADER), NULL, sub);
|
||||
if (loopcount==1)
|
||||
writeraw (lc1, sizeof (lc1), NULL, sub);
|
||||
if (loopcount==2)
|
||||
writeraw (lc2, sizeof (lc2), NULL, sub);
|
||||
if (loopcount==3)
|
||||
{
|
||||
writeraw (lc3, sizeof (lc3), NULL, sub);
|
||||
if (data2 && length2)
|
||||
write (wb->fh,data2,length2);
|
||||
}
|
||||
}
|
||||
datacount++;
|
||||
write (wb->fh,lc5,sizeof (lc5));
|
||||
writeraw (data2, length2, NULL, sub);
|
||||
}
|
||||
if (loopcount>3)
|
||||
{
|
||||
writeraw (lc4, sizeof (lc4), NULL, sub);
|
||||
if (data2 && length2)
|
||||
writeraw (data2, length2, NULL, sub);
|
||||
}
|
||||
}
|
||||
datacount++;
|
||||
writeraw (lc5, sizeof (lc5), NULL, sub);
|
||||
if (data1 && length1)
|
||||
write (wb->fh,data1,length1);
|
||||
if (((loopcount == 1) && (datacount < 5)) || ((loopcount == 2) &&
|
||||
(datacount < 8)) || (( loopcount == 3) && (datacount < 11)) ||
|
||||
((loopcount > 3) && (datacount < 15)))
|
||||
{
|
||||
write (wb->fh,lc6,sizeof (lc6));
|
||||
writeraw (data1, length1, NULL, sub);
|
||||
if (((loopcount == 1) && (datacount < 5)) || ((loopcount == 2) &&
|
||||
(datacount < 8)) || (( loopcount == 3) && (datacount < 11)) ||
|
||||
((loopcount > 3) && (datacount < 15)))
|
||||
{
|
||||
writeraw (lc6, sizeof(lc6), NULL, sub);
|
||||
if (data2 && length2)
|
||||
write (wb->fh,data2,length2);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (loopcount==1)
|
||||
{
|
||||
write (wb->fh,lc6,sizeof (lc6));
|
||||
writeraw (data2, length2, NULL, sub);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (loopcount==1)
|
||||
{
|
||||
writeraw (lc6, sizeof(lc6), NULL, sub);
|
||||
if (data2 && length2)
|
||||
write (wb->fh,data2,length2);
|
||||
}
|
||||
loopcount++;
|
||||
datacount=0;
|
||||
}
|
||||
writeraw (data2, length2, NULL, sub);
|
||||
}
|
||||
loopcount++;
|
||||
datacount=0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void printdata (struct lib_cc_decode *ctx, const unsigned char *data1, int length1,
|
||||
const unsigned char *data2, int length2, struct cc_subtitle *sub)
|
||||
{
|
||||
struct ccx_decoder_608_context *field_1 = ctx->context_cc608_field_1;
|
||||
struct ccx_decoder_608_context *field_2 = ctx->context_cc608_field_2;
|
||||
struct ccx_s_write *wbout1 = ctx->wbout1;
|
||||
field_1->out = ctx->wbout1 ;
|
||||
field_2->out = ctx->wbout2 ;
|
||||
if (ctx->write_format==CCX_OF_DVDRAW)
|
||||
writeDVDraw (data1, length1, data2, length2, wbout1);
|
||||
writeDVDraw (data1, length1, data2, length2, sub);
|
||||
else /* Broadcast raw or any non-raw */
|
||||
{
|
||||
if (length1 && ccx_options.extract != 2)
|
||||
if (length1 && ctx->extract != 2)
|
||||
{
|
||||
writedata(data1, length1, field_1, sub);
|
||||
ctx->current_field = 1;
|
||||
ctx->writedata(data1, length1, ctx, sub);
|
||||
}
|
||||
if (length2)
|
||||
{
|
||||
if (ccx_options.extract != 1)
|
||||
writedata(data2, length2, field_2, sub);
|
||||
ctx->current_field = 2;
|
||||
if (ctx->extract != 1)
|
||||
ctx->writedata(data2, length2, ctx, sub);
|
||||
else // User doesn't want field 2 data, but we want XDS.
|
||||
writedata (data2,length2,NULL, sub);
|
||||
{
|
||||
ctx->writedata (data2, length2, ctx, sub);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Buffer data with the same FTS and write when a new FTS or data==NULL
|
||||
* is encountered */
|
||||
void writercwtdata (struct lib_cc_decode *ctx, const unsigned char *data)
|
||||
void writercwtdata (struct lib_cc_decode *ctx, const unsigned char *data, struct cc_subtitle *sub)
|
||||
{
|
||||
static LLONG prevfts = -1;
|
||||
LLONG currfts = fts_now + fts_global;
|
||||
LLONG currfts = ctx->timing->fts_now + ctx->timing->fts_global;
|
||||
static uint16_t cbcount = 0;
|
||||
static int cbempty=0;
|
||||
static unsigned char cbbuffer[0xFFFF*3]; // TODO: use malloc
|
||||
static unsigned char cbheader[8+2];
|
||||
struct ccx_s_write *wbout1 = ctx->wbout1;
|
||||
|
||||
if ( (prevfts != currfts && prevfts != -1)
|
||||
|| data == NULL
|
||||
|| cbcount == 0xFFFF)
|
||||
{
|
||||
// Remove trailing empty or 608 padding caption blocks
|
||||
if ( cbcount != 0xFFFF)
|
||||
{
|
||||
unsigned char cc_valid;
|
||||
unsigned char cc_type;
|
||||
int storecbcount=cbcount;
|
||||
|
||||
for( int cb = cbcount-1; cb >= 0 ; cb-- )
|
||||
{
|
||||
cc_valid = (*(cbbuffer+3*cb) & 4) >>2;
|
||||
cc_type = *(cbbuffer+3*cb) & 3;
|
||||
|
||||
// The -fullbin option disables pruning of 608 padding blocks
|
||||
if ( (cc_valid && cc_type <= 1 // Only skip NTSC padding packets
|
||||
&& !ccx_options.fullbin // Unless we want to keep them
|
||||
&& *(cbbuffer+3*cb+1)==0x80
|
||||
&& *(cbbuffer+3*cb+2)==0x80)
|
||||
|| !(cc_valid || cc_type==3) ) // or unused packets
|
||||
{
|
||||
cbcount--;
|
||||
}
|
||||
else
|
||||
{
|
||||
cb = -1;
|
||||
}
|
||||
}
|
||||
dbg_print(CCX_DMT_CBRAW, "%s Write %d RCWT blocks - skipped %d padding / %d unused blocks.\n",
|
||||
print_mstime(prevfts), cbcount, storecbcount - cbcount, cbempty);
|
||||
}
|
||||
|
||||
// New FTS, write data header
|
||||
// RCWT data header (10 bytes):
|
||||
//byte(s) value description
|
||||
//0-7 FTS int64_t number with current FTS
|
||||
//8-9 blocks Number of 3 byte data blocks with the same FTS that are
|
||||
// following this header
|
||||
memcpy(cbheader,&prevfts,8);
|
||||
memcpy(cbheader+8,&cbcount,2);
|
||||
|
||||
if (cbcount > 0)
|
||||
{
|
||||
if (ccx_options.send_to_srv)
|
||||
{
|
||||
net_send_cc(cbheader, 10);
|
||||
net_send_cc(cbbuffer, 3*cbcount);
|
||||
}
|
||||
else
|
||||
{
|
||||
writeraw(cbheader,10,wbout1);
|
||||
writeraw(cbbuffer,3*cbcount, wbout1);
|
||||
}
|
||||
}
|
||||
cbcount = 0;
|
||||
cbempty = 0;
|
||||
}
|
||||
|
||||
if ( data )
|
||||
{
|
||||
// Store the data while the FTS is unchanged
|
||||
|
||||
unsigned char cc_valid = (*data & 4) >> 2;
|
||||
unsigned char cc_type = *data & 3;
|
||||
// Store only non-empty packets
|
||||
if (cc_valid || cc_type==3)
|
||||
{
|
||||
// Store in buffer until we know how many blocks come with
|
||||
// this FTS.
|
||||
memcpy(cbbuffer+cbcount*3, data, 3);
|
||||
cbcount++;
|
||||
}
|
||||
else
|
||||
{
|
||||
cbempty++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Write a padding block for field 1 and 2 data if this is the final
|
||||
// call to this function. This forces the RCWT file to have the
|
||||
// same length as the source video file.
|
||||
|
||||
// currfts currently holds the end time, subtract one block length
|
||||
// so that the FTS corresponds to the time before the last block is
|
||||
// written
|
||||
currfts -= 1001/30;
|
||||
|
||||
memcpy(cbheader,&currfts,8);
|
||||
cbcount = 2;
|
||||
memcpy(cbheader+8,&cbcount,2);
|
||||
|
||||
memcpy(cbbuffer, "\x04\x80\x80", 3); // Field 1 padding
|
||||
memcpy(cbbuffer+3, "\x05\x80\x80", 3); // Field 2 padding
|
||||
|
||||
if (ccx_options.send_to_srv)
|
||||
if ( (prevfts != currfts && prevfts != -1)
|
||||
|| data == NULL
|
||||
|| cbcount == 0xFFFF)
|
||||
{
|
||||
// Remove trailing empty or 608 padding caption blocks
|
||||
if ( cbcount != 0xFFFF)
|
||||
{
|
||||
net_send_cc(cbheader, 10);
|
||||
net_send_cc(cbbuffer, 3*cbcount);
|
||||
unsigned char cc_valid;
|
||||
unsigned char cc_type;
|
||||
int storecbcount=cbcount;
|
||||
|
||||
for( int cb = cbcount-1; cb >= 0 ; cb-- )
|
||||
{
|
||||
cc_valid = (*(cbbuffer+3*cb) & 4) >>2;
|
||||
cc_type = *(cbbuffer+3*cb) & 3;
|
||||
|
||||
// The -fullbin option disables pruning of 608 padding blocks
|
||||
if ( (cc_valid && cc_type <= 1 // Only skip NTSC padding packets
|
||||
&& !ctx->fullbin // Unless we want to keep them
|
||||
&& *(cbbuffer+3*cb+1)==0x80
|
||||
&& *(cbbuffer+3*cb+2)==0x80)
|
||||
|| !(cc_valid || cc_type==3) ) // or unused packets
|
||||
{
|
||||
cbcount--;
|
||||
}
|
||||
else
|
||||
{
|
||||
cb = -1;
|
||||
}
|
||||
}
|
||||
dbg_print(CCX_DMT_CBRAW, "%s Write %d RCWT blocks - skipped %d padding / %d unused blocks.\n",
|
||||
print_mstime(prevfts), cbcount, storecbcount - cbcount, cbempty);
|
||||
}
|
||||
|
||||
// New FTS, write data header
|
||||
// RCWT data header (10 bytes):
|
||||
//byte(s) value description
|
||||
//0-7 FTS int64_t number with current FTS
|
||||
//8-9 blocks Number of 3 byte data blocks with the same FTS that are
|
||||
// following this header
|
||||
memcpy(cbheader,&prevfts,8);
|
||||
memcpy(cbheader+8,&cbcount,2);
|
||||
|
||||
if (cbcount > 0)
|
||||
{
|
||||
ctx->writedata(cbheader, 10, ctx->context_cc608_field_1, sub);
|
||||
ctx->writedata(cbbuffer, 3 * cbcount, ctx->context_cc608_field_1, sub);
|
||||
}
|
||||
cbcount = 0;
|
||||
cbempty = 0;
|
||||
}
|
||||
|
||||
if ( data )
|
||||
{
|
||||
// Store the data while the FTS is unchanged
|
||||
|
||||
unsigned char cc_valid = (*data & 4) >> 2;
|
||||
unsigned char cc_type = *data & 3;
|
||||
// Store only non-empty packets
|
||||
if (cc_valid || cc_type==3)
|
||||
{
|
||||
// Store in buffer until we know how many blocks come with
|
||||
// this FTS.
|
||||
memcpy(cbbuffer+cbcount*3, data, 3);
|
||||
cbcount++;
|
||||
}
|
||||
else
|
||||
{
|
||||
writeraw(cbheader,10, wbout1);
|
||||
writeraw(cbbuffer,3*cbcount, wbout1);
|
||||
cbempty++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Write a padding block for field 1 and 2 data if this is the final
|
||||
// call to this function. This forces the RCWT file to have the
|
||||
// same length as the source video file.
|
||||
|
||||
cbcount = 0;
|
||||
cbempty = 0;
|
||||
// currfts currently holds the end time, subtract one block length
|
||||
// so that the FTS corresponds to the time before the last block is
|
||||
// written
|
||||
currfts -= 1001/30;
|
||||
|
||||
dbg_print(CCX_DMT_CBRAW, "%s Write final padding RCWT blocks.\n",
|
||||
print_mstime(currfts));
|
||||
}
|
||||
memcpy(cbheader,&currfts,8);
|
||||
cbcount = 2;
|
||||
memcpy(cbheader+8,&cbcount,2);
|
||||
|
||||
prevfts = currfts;
|
||||
memcpy(cbbuffer, "\x04\x80\x80", 3); // Field 1 padding
|
||||
memcpy(cbbuffer+3, "\x05\x80\x80", 3); // Field 2 padding
|
||||
ctx->writedata(cbheader, 10, ctx->context_cc608_field_1, sub);
|
||||
ctx->writedata(cbbuffer, 3 * cbcount, ctx->context_cc608_field_1, sub);
|
||||
|
||||
cbcount = 0;
|
||||
cbempty = 0;
|
||||
|
||||
dbg_print(CCX_DMT_CBRAW, "%s Write final padding RCWT blocks.\n",
|
||||
print_mstime(currfts));
|
||||
}
|
||||
|
||||
prevfts = currfts;
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,21 +1,24 @@
|
||||
#include "lib_ccx.h"
|
||||
#include "ccx_common_option.h"
|
||||
#include "teletext.h"
|
||||
|
||||
#include "ccx_decoders_708.h"
|
||||
|
||||
void params_dump(struct lib_ccx_ctx *ctx)
|
||||
{
|
||||
// Display parsed parameters
|
||||
mprint ("Input: ");
|
||||
// Display parsed parameters
|
||||
mprint ("Input: ");
|
||||
switch (ccx_options.input_source)
|
||||
{
|
||||
case CCX_DS_FILE:
|
||||
for (int i=0;i<ctx->num_input_files;i++)
|
||||
mprint ("%s%s",ctx->inputfile[i],i==(ctx->num_input_files-1)?"":",");
|
||||
mprint ("%s%s",ctx->inputfile[i],i==(ctx->num_input_files-1)?"":",");
|
||||
break;
|
||||
case CCX_DS_STDIN:
|
||||
mprint ("stdin");
|
||||
break;
|
||||
case CCX_DS_NETWORK:
|
||||
if (ccx_options.udpaddr == NULL)
|
||||
if (ccx_options.udpaddr == NULL)
|
||||
mprint ("Network, UDP/%u",ccx_options.udpport);
|
||||
else
|
||||
{
|
||||
@@ -26,82 +29,63 @@ void params_dump(struct lib_ccx_ctx *ctx)
|
||||
mprint("Network, TCP/%s", ccx_options.tcpport);
|
||||
break;
|
||||
}
|
||||
mprint ("\n");
|
||||
mprint ("[Extract: %d] ", ccx_options.extract);
|
||||
mprint ("[Stream mode: ");
|
||||
switch (ctx->auto_stream)
|
||||
{
|
||||
case CCX_SM_ELEMENTARY_OR_NOT_FOUND:
|
||||
mprint ("Elementary");
|
||||
break;
|
||||
case CCX_SM_TRANSPORT:
|
||||
mprint ("Transport");
|
||||
break;
|
||||
case CCX_SM_PROGRAM:
|
||||
mprint ("Program");
|
||||
break;
|
||||
case CCX_SM_ASF:
|
||||
mprint ("DVR-MS");
|
||||
break;
|
||||
case CCX_SM_WTV:
|
||||
mprint ("Windows Television (WTV)");
|
||||
break;
|
||||
case CCX_SM_MCPOODLESRAW:
|
||||
mprint ("McPoodle's raw");
|
||||
break;
|
||||
case CCX_SM_AUTODETECT:
|
||||
mprint ("Autodetect");
|
||||
break;
|
||||
case CCX_SM_RCWT:
|
||||
mprint ("BIN");
|
||||
break;
|
||||
case CCX_SM_MP4:
|
||||
mprint ("MP4");
|
||||
break;
|
||||
#ifdef WTV_DEBUG
|
||||
case CCX_SM_HEX_DUMP:
|
||||
mprint ("Hex");
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
fatal(CCX_COMMON_EXIT_BUG_BUG, "BUG: Unknown stream mode.\n");
|
||||
break;
|
||||
}
|
||||
mprint ("]\n");
|
||||
mprint ("\n");
|
||||
mprint ("[Extract: %d] ", ccx_options.extract);
|
||||
mprint ("[Stream mode: ");
|
||||
|
||||
ctx->demux_ctx->print_cfg(ctx->demux_ctx);
|
||||
mprint ("]\n");
|
||||
mprint ("[Program : ");
|
||||
if (ccx_options.ts_forced_program_selected != 0)
|
||||
mprint ("%u ]",ccx_options.ts_forced_program);
|
||||
if (ccx_options.demux_cfg.ts_forced_program_selected != 0)
|
||||
mprint ("%u ]",ccx_options.demux_cfg.ts_forced_program);
|
||||
else
|
||||
mprint ("Auto ]");
|
||||
mprint (" [Hauppage mode: %s]",ccx_options.hauppauge_mode?"Yes":"No");
|
||||
|
||||
mprint (" [Use MythTV code: ");
|
||||
switch (ccx_options.auto_myth)
|
||||
{
|
||||
case 0:
|
||||
mprint ("Disabled");
|
||||
break;
|
||||
case 1:
|
||||
mprint ("Forced - Overrides stream mode setting");
|
||||
break;
|
||||
case 2:
|
||||
mprint ("Auto");
|
||||
break;
|
||||
}
|
||||
mprint ("]");
|
||||
if (ccx_options.wtvconvertfix)
|
||||
{
|
||||
mprint (" [Windows 7 wtv to dvr-ms conversion fix: Enabled]");
|
||||
}
|
||||
mprint ("\n");
|
||||
mprint (" [Use MythTV code: ");
|
||||
switch (ccx_options.auto_myth)
|
||||
{
|
||||
case 0:
|
||||
mprint ("Disabled");
|
||||
break;
|
||||
case 1:
|
||||
mprint ("Forced - Overrides stream mode setting");
|
||||
break;
|
||||
case 2:
|
||||
mprint ("Auto");
|
||||
break;
|
||||
}
|
||||
mprint ("]");
|
||||
mprint ("\n");
|
||||
|
||||
if (ccx_options.wtvmpeg2)
|
||||
{
|
||||
mprint (" [WTV use MPEG2 stream: Enabled]");
|
||||
}
|
||||
mprint ("\n");
|
||||
if (ccx_options.settings_dtvcc.enabled)
|
||||
{
|
||||
mprint ("[CEA-708: %d decoders active]\n", ccx_options.settings_dtvcc.active_services_count);
|
||||
if (ccx_options.settings_dtvcc.active_services_count == CCX_DTVCC_MAX_SERVICES)
|
||||
{
|
||||
char *charset = ccx_options.enc_cfg.all_services_charset;
|
||||
mprint ("[CEA-708: using charset \"%s\" for all services]\n", charset ? charset : "none");
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < CCX_DTVCC_MAX_SERVICES; i++)
|
||||
{
|
||||
if (ccx_options.settings_dtvcc.services_enabled[i])
|
||||
mprint("[CEA-708: using charset \"%s\" for service %d]\n",
|
||||
ccx_options.enc_cfg.services_charsets[i] ?
|
||||
ccx_options.enc_cfg.services_charsets[i] : "none",
|
||||
i + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mprint ("[Timing mode: ");
|
||||
if (ccx_options.wtvconvertfix)
|
||||
mprint (" [Windows 7 wtv to dvr-ms conversion fix: Enabled]\n");
|
||||
|
||||
if (ccx_options.wtvmpeg2)
|
||||
mprint (" [WTV use MPEG2 stream: Enabled]\n");
|
||||
|
||||
mprint ("[Timing mode: ");
|
||||
switch (ccx_options.use_gop_as_pts)
|
||||
{
|
||||
case 1:
|
||||
@@ -115,101 +99,135 @@ void params_dump(struct lib_ccx_ctx *ctx)
|
||||
break;
|
||||
}
|
||||
mprint ("] ");
|
||||
mprint ("[Debug: %s] ", (ccx_options.debug_mask & CCX_DMT_VERBOSE) ? "Yes": "No");
|
||||
mprint ("[Buffer input: %s]\n", ccx_options.buffer_input ? "Yes": "No");
|
||||
mprint ("[Use pic_order_cnt_lsb for H.264: %s] ", ccx_options.usepicorder ? "Yes": "No");
|
||||
mprint ("[Debug: %s] ", (ccx_options.debug_mask & CCX_DMT_VERBOSE) ? "Yes": "No");
|
||||
mprint ("[Buffer input: %s]\n", ccx_options.buffer_input ? "Yes": "No");
|
||||
mprint ("[Use pic_order_cnt_lsb for H.264: %s] ", ccx_options.usepicorder ? "Yes": "No");
|
||||
mprint("[Print CC decoder traces: %s]\n", (ccx_options.debug_mask & CCX_DMT_DECODER_608) ? "Yes" : "No");
|
||||
mprint ("[Target format: %s] ",ctx->extension);
|
||||
mprint ("[Encoding: ");
|
||||
switch (ccx_options.encoding)
|
||||
{
|
||||
case CCX_ENC_UNICODE:
|
||||
mprint ("Unicode");
|
||||
break;
|
||||
case CCX_ENC_UTF_8:
|
||||
mprint ("UTF-8");
|
||||
break;
|
||||
case CCX_ENC_LATIN_1:
|
||||
mprint ("Latin-1");
|
||||
break;
|
||||
}
|
||||
mprint ("] ");
|
||||
mprint ("[Delay: %lld] ",ctx->subs_delay);
|
||||
mprint ("[Target format: %s] ",ctx->extension);
|
||||
mprint ("[Encoding: ");
|
||||
switch (ccx_options.enc_cfg.encoding)
|
||||
{
|
||||
case CCX_ENC_UNICODE:
|
||||
mprint ("Unicode");
|
||||
break;
|
||||
case CCX_ENC_UTF_8:
|
||||
mprint ("UTF-8");
|
||||
break;
|
||||
case CCX_ENC_LATIN_1:
|
||||
mprint ("Latin-1");
|
||||
break;
|
||||
}
|
||||
mprint ("] ");
|
||||
mprint ("[Delay: %lld] ",ctx->subs_delay);
|
||||
|
||||
mprint ("[Trim lines: %s]\n",ccx_options.trim_subs?"Yes":"No");
|
||||
mprint ("[Add font color data: %s] ", ccx_options.nofontcolor? "No" : "Yes");
|
||||
mprint ("[Trim lines: %s]\n",ccx_options.enc_cfg.trim_subs?"Yes":"No");
|
||||
mprint ("[Add font color data: %s] ", ccx_options.nofontcolor? "No" : "Yes");
|
||||
mprint ("[Add font typesetting: %s]\n", ccx_options.notypesetting? "No" : "Yes");
|
||||
mprint ("[Convert case: ");
|
||||
if (ccx_options.sentence_cap_file!=NULL)
|
||||
mprint ("Yes, using %s", ccx_options.sentence_cap_file);
|
||||
else
|
||||
{
|
||||
mprint ("%s",ccx_options.sentence_cap?"Yes, but only built-in words":"No");
|
||||
}
|
||||
mprint ("]");
|
||||
mprint (" [Video-edit join: %s]", ccx_options.binary_concat?"No":"Yes");
|
||||
mprint ("\n[Extraction start time: ");
|
||||
if (ccx_options.extraction_start.set==0)
|
||||
mprint ("not set (from start)");
|
||||
else
|
||||
mprint ("%02d:%02d:%02d", ccx_options.extraction_start.hh,
|
||||
ccx_options.extraction_start.mm,
|
||||
ccx_options.extraction_start.ss);
|
||||
mprint ("]\n");
|
||||
mprint ("[Extraction end time: ");
|
||||
if (ccx_options.extraction_end.set==0)
|
||||
mprint ("not set (to end)");
|
||||
else
|
||||
mprint ("%02d:%02d:%02d", ccx_options.extraction_end.hh,
|
||||
ccx_options.extraction_end.mm,
|
||||
ccx_options.extraction_end.ss);
|
||||
mprint ("]\n");
|
||||
mprint ("[Live stream: ");
|
||||
if (ccx_options.live_stream==0)
|
||||
mprint ("No");
|
||||
else
|
||||
{
|
||||
if (ccx_options.live_stream<1)
|
||||
mprint ("Yes, no timeout");
|
||||
else
|
||||
mprint ("Yes, timeout: %d seconds",ccx_options.live_stream);
|
||||
}
|
||||
mprint ("] [Clock frequency: %d]\n",MPEG_CLOCK_FREQ);
|
||||
mprint ("Teletext page: [");
|
||||
mprint ("[Convert case: ");
|
||||
if (ccx_options.sentence_cap_file!=NULL)
|
||||
mprint ("Yes, using %s", ccx_options.sentence_cap_file);
|
||||
else
|
||||
{
|
||||
mprint ("%s",ccx_options.enc_cfg.sentence_cap?"Yes, but only built-in words":"No");
|
||||
}
|
||||
mprint ("]");
|
||||
mprint (" [Video-edit join: %s]", ccx_options.binary_concat?"No":"Yes");
|
||||
mprint ("\n[Extraction start time: ");
|
||||
if (ccx_options.extraction_start.set==0)
|
||||
mprint ("not set (from start)");
|
||||
else
|
||||
mprint ("%02d:%02d:%02d", ccx_options.extraction_start.hh,
|
||||
ccx_options.extraction_start.mm,
|
||||
ccx_options.extraction_start.ss);
|
||||
mprint ("]\n");
|
||||
mprint ("[Extraction end time: ");
|
||||
if (ccx_options.extraction_end.set==0)
|
||||
mprint ("not set (to end)");
|
||||
else
|
||||
mprint ("%02d:%02d:%02d", ccx_options.extraction_end.hh,
|
||||
ccx_options.extraction_end.mm,
|
||||
ccx_options.extraction_end.ss);
|
||||
mprint ("]\n");
|
||||
mprint ("[Live stream: ");
|
||||
if (ccx_options.live_stream==0)
|
||||
mprint ("No");
|
||||
else
|
||||
{
|
||||
if (ccx_options.live_stream<1)
|
||||
mprint ("Yes, no timeout");
|
||||
else
|
||||
mprint ("Yes, timeout: %d seconds",ccx_options.live_stream);
|
||||
}
|
||||
mprint ("] [Clock frequency: %d]\n",MPEG_CLOCK_FREQ);
|
||||
mprint ("[Teletext page: ");
|
||||
if (tlt_config.page)
|
||||
mprint ("%d]\n",tlt_config.page);
|
||||
else
|
||||
mprint ("Autodetect]\n");
|
||||
mprint ("Start credits text: [%s]\n",
|
||||
ccx_options.start_credits_text?ccx_options.start_credits_text:"None");
|
||||
if (ccx_options.start_credits_text)
|
||||
{
|
||||
mprint ("Start credits time: Insert between [%ld] and [%ld] seconds\n",
|
||||
(long) (ccx_options.startcreditsnotbefore.time_in_ms/1000),
|
||||
(long) (ccx_options.startcreditsnotafter.time_in_ms/1000)
|
||||
);
|
||||
mprint (" Display for at least [%ld] and at most [%ld] seconds\n",
|
||||
(long) (ccx_options.startcreditsforatleast.time_in_ms/1000),
|
||||
(long) (ccx_options.startcreditsforatmost.time_in_ms/1000)
|
||||
);
|
||||
}
|
||||
if (ccx_options.end_credits_text)
|
||||
{
|
||||
mprint ("End credits text: [%s]\n",
|
||||
ccx_options.end_credits_text?ccx_options.end_credits_text:"None");
|
||||
mprint (" Display for at least [%ld] and at most [%ld] seconds\n",
|
||||
(long) (ccx_options.endcreditsforatleast.time_in_ms/1000),
|
||||
(long) (ccx_options.endcreditsforatmost.time_in_ms/1000)
|
||||
);
|
||||
}
|
||||
|
||||
mprint ("[Start credits text: %s]\n",
|
||||
ccx_options.enc_cfg.start_credits_text?ccx_options.enc_cfg.start_credits_text:"None");
|
||||
if (ccx_options.enc_cfg.start_credits_text)
|
||||
{
|
||||
mprint ("Start credits time: Insert between [%ld] and [%ld] seconds\n",
|
||||
(long) (ccx_options.enc_cfg.startcreditsnotbefore.time_in_ms/1000),
|
||||
(long) (ccx_options.enc_cfg.startcreditsnotafter.time_in_ms/1000)
|
||||
);
|
||||
mprint (" Display for at least [%ld] and at most [%ld] seconds\n",
|
||||
(long) (ccx_options.enc_cfg.startcreditsforatleast.time_in_ms/1000),
|
||||
(long) (ccx_options.enc_cfg.startcreditsforatmost.time_in_ms/1000)
|
||||
);
|
||||
}
|
||||
if (ccx_options.enc_cfg.end_credits_text)
|
||||
{
|
||||
mprint ("End credits text: [%s]\n",
|
||||
ccx_options.enc_cfg.end_credits_text?ccx_options.enc_cfg.end_credits_text:"None");
|
||||
mprint (" Display for at least [%ld] and at most [%ld] seconds\n",
|
||||
(long) (ccx_options.enc_cfg.endcreditsforatleast.time_in_ms/1000),
|
||||
(long) (ccx_options.enc_cfg.endcreditsforatmost.time_in_ms/1000)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#define Y_N(cond) ((cond) ? "Yes" : "No")
|
||||
|
||||
void print_cc_report(struct lib_ccx_ctx *ctx, struct cap_info* info)
|
||||
{
|
||||
struct lib_cc_decode *dec_ctx = NULL;
|
||||
dec_ctx = update_decoder_list_cinfo(ctx, info);
|
||||
printf("EIA-608: %s\n", Y_N(dec_ctx->cc_stats[0] > 0 || dec_ctx->cc_stats[1] > 0));
|
||||
|
||||
if (dec_ctx->cc_stats[0] > 0 || dec_ctx->cc_stats[1] > 0)
|
||||
{
|
||||
printf("XDS: %s\n", Y_N(ctx->freport.data_from_608->xds));
|
||||
|
||||
printf("CC1: %s\n", Y_N(ctx->freport.data_from_608->cc_channels[0]));
|
||||
printf("CC2: %s\n", Y_N(ctx->freport.data_from_608->cc_channels[1]));
|
||||
printf("CC3: %s\n", Y_N(ctx->freport.data_from_608->cc_channels[2]));
|
||||
printf("CC4: %s\n", Y_N(ctx->freport.data_from_608->cc_channels[3]));
|
||||
}
|
||||
printf("CEA-708: %s\n", Y_N(dec_ctx->cc_stats[2] > 0 || dec_ctx->cc_stats[3] > 0));
|
||||
|
||||
if (dec_ctx->cc_stats[2] > 0 || dec_ctx->cc_stats[3] > 0)
|
||||
{
|
||||
printf("Services: ");
|
||||
for (int i = 0; i < CCX_DTVCC_MAX_SERVICES; i++)
|
||||
{
|
||||
if (ctx->freport.data_from_708->services[i] == 0)
|
||||
continue;
|
||||
printf("%d ", i);
|
||||
}
|
||||
printf("\n");
|
||||
|
||||
printf("Primary Language Present: %s\n", Y_N(ctx->freport.data_from_708->services[1]));
|
||||
|
||||
printf("Secondary Language Present: %s\n", Y_N(ctx->freport.data_from_708->services[2]));
|
||||
}
|
||||
}
|
||||
void print_file_report(struct lib_ccx_ctx *ctx)
|
||||
{
|
||||
struct lib_cc_decode *dec_ctx = NULL;
|
||||
dec_ctx = ctx->dec_ctx;
|
||||
#define Y_N(cond) ((cond) ? "Yes" : "No")
|
||||
enum ccx_stream_mode_enum stream_mode;
|
||||
struct ccx_demuxer *demux_ctx = ctx->demux_ctx;
|
||||
|
||||
printf("File: ");
|
||||
switch (ccx_options.input_source)
|
||||
@@ -232,46 +250,44 @@ void print_file_report(struct lib_ccx_ctx *ctx)
|
||||
break;
|
||||
}
|
||||
|
||||
struct cap_info* program;
|
||||
printf("Stream Mode: ");
|
||||
switch (ctx->stream_mode)
|
||||
switch (demux_ctx->stream_mode)
|
||||
{
|
||||
case CCX_SM_TRANSPORT:
|
||||
printf("Transport Stream\n");
|
||||
|
||||
printf("Program Count: %d\n", ctx->freport.program_cnt);
|
||||
printf("Program Count: %d\n", demux_ctx->freport.program_cnt);
|
||||
|
||||
printf("Program Numbers: ");
|
||||
for (int i = 0; i < pmt_array_length; i++)
|
||||
{
|
||||
if (pmt_array[i].program_number == 0)
|
||||
continue;
|
||||
|
||||
printf("%u ", pmt_array[i].program_number);
|
||||
}
|
||||
for (int i = 0; i < demux_ctx->nb_program; i++)
|
||||
printf("%u ", demux_ctx->pinfo[i].program_number);
|
||||
|
||||
printf("\n");
|
||||
|
||||
for (int i = 0; i < 65536; i++)
|
||||
{
|
||||
if (ctx->PIDs_programs[i] == 0)
|
||||
if (demux_ctx->PIDs_programs[i] == 0)
|
||||
continue;
|
||||
|
||||
printf("PID: %u, Program: %u, ", i, ctx->PIDs_programs[i]->program_number);
|
||||
printf("PID: %u, Program: %u, ", i, demux_ctx->PIDs_programs[i]->program_number);
|
||||
int j;
|
||||
for (j = 0; j < SUB_STREAMS_CNT; j++)
|
||||
{
|
||||
if (ctx->freport.dvb_sub_pid[j] == i)
|
||||
if (demux_ctx->freport.dvb_sub_pid[j] == i)
|
||||
{
|
||||
printf("DVB Subtitles\n");
|
||||
break;
|
||||
}
|
||||
if (ctx->freport.tlt_sub_pid[j] == i)
|
||||
if (demux_ctx->freport.tlt_sub_pid[j] == i)
|
||||
{
|
||||
printf("Teletext Subtitles\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (j == SUB_STREAMS_CNT)
|
||||
printf("%s\n", desc[ctx->PIDs_programs[i]->printable_stream_type]);
|
||||
printf("%s\n", desc[demux_ctx->PIDs_programs[i]->printable_stream_type]);
|
||||
}
|
||||
|
||||
break;
|
||||
@@ -304,91 +320,65 @@ void print_file_report(struct lib_ccx_ctx *ctx)
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (ccx_bufferdatatype == CCX_PES &&
|
||||
(ctx->stream_mode == CCX_SM_TRANSPORT ||
|
||||
ctx->stream_mode == CCX_SM_PROGRAM ||
|
||||
ctx->stream_mode == CCX_SM_ASF ||
|
||||
ctx->stream_mode == CCX_SM_WTV))
|
||||
if(list_empty(&demux_ctx->cinfo_tree.all_stream))
|
||||
{
|
||||
printf("Width: %u\n", ctx->freport.width);
|
||||
printf("Height: %u\n", ctx->freport.height);
|
||||
printf("Aspect Ratio: %s\n", aspect_ratio_types[ctx->freport.aspect_ratio]);
|
||||
printf("Frame Rate: %s\n", framerates_types[ctx->freport.frame_rate]);
|
||||
print_cc_report(ctx, NULL);
|
||||
}
|
||||
|
||||
if (ctx->freport.program_cnt > 1)
|
||||
printf("//////// Program #%u: ////////\n", TS_program_number);
|
||||
|
||||
printf("DVB Subtitles: ");
|
||||
int j;
|
||||
for (j = 0; j < SUB_STREAMS_CNT; j++)
|
||||
list_for_each_entry(program, &demux_ctx->cinfo_tree.pg_stream, pg_stream, struct cap_info)
|
||||
{
|
||||
unsigned pid = ctx->freport.dvb_sub_pid[j];
|
||||
if (pid == 0)
|
||||
continue;
|
||||
if (ctx->PIDs_programs[pid]->program_number == TS_program_number)
|
||||
struct cap_info* info = NULL;
|
||||
printf("//////// Program #%u: ////////\n", program->program_number);
|
||||
|
||||
printf("DVB Subtitles: ");
|
||||
info = get_sib_stream_by_type(program, CCX_CODEC_DVB);
|
||||
if(info)
|
||||
printf("Yes\n");
|
||||
else
|
||||
printf("No\n");
|
||||
|
||||
printf("Teletext: ");
|
||||
info = get_sib_stream_by_type(program, CCX_CODEC_TELETEXT);
|
||||
if(info)
|
||||
{
|
||||
printf("Yes\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (j == SUB_STREAMS_CNT)
|
||||
printf("No\n");
|
||||
|
||||
printf("Teletext: ");
|
||||
for (j = 0; j < SUB_STREAMS_CNT; j++)
|
||||
{
|
||||
unsigned pid = ctx->freport.tlt_sub_pid[j];
|
||||
if (pid == 0)
|
||||
continue;
|
||||
if (ctx->PIDs_programs[pid]->program_number == TS_program_number)
|
||||
{
|
||||
printf("Yes\n");
|
||||
|
||||
dec_ctx = update_decoder_list_cinfo(ctx, info);
|
||||
printf("Pages With Subtitles: ");
|
||||
for (int i = 0; i < MAX_TLT_PAGES; i++)
|
||||
{
|
||||
if (seen_sub_page[i] == 0)
|
||||
continue;
|
||||
tlt_print_seen_pages(dec_ctx);
|
||||
|
||||
printf("%d ", i);
|
||||
}
|
||||
printf("\n");
|
||||
break;
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
if (j == SUB_STREAMS_CNT)
|
||||
printf("No\n");
|
||||
else
|
||||
printf("No\n");
|
||||
|
||||
printf("EIA-608: %s\n", Y_N(dec_ctx->cc_stats[0] > 0 || dec_ctx->cc_stats[1] > 0));
|
||||
|
||||
if (dec_ctx->cc_stats[0] > 0 || dec_ctx->cc_stats[1] > 0)
|
||||
{
|
||||
printf("XDS: %s\n", Y_N(ctx->freport.data_from_608->xds));
|
||||
|
||||
printf("CC1: %s\n", Y_N(ctx->freport.data_from_608->cc_channels[0]));
|
||||
printf("CC2: %s\n", Y_N(ctx->freport.data_from_608->cc_channels[1]));
|
||||
printf("CC3: %s\n", Y_N(ctx->freport.data_from_608->cc_channels[2]));
|
||||
printf("CC4: %s\n", Y_N(ctx->freport.data_from_608->cc_channels[3]));
|
||||
}
|
||||
|
||||
printf("CEA-708: %s\n", Y_N(dec_ctx->cc_stats[2] > 0 || dec_ctx->cc_stats[3] > 0));
|
||||
|
||||
if (dec_ctx->cc_stats[2] > 0 || dec_ctx->cc_stats[3] > 0)
|
||||
{
|
||||
printf("Services: ");
|
||||
for (int i = 0; i < CCX_DECODERS_708_MAX_SERVICES; i++)
|
||||
printf("ATSC Closed Caption: ");
|
||||
info = get_sib_stream_by_type(program, CCX_CODEC_ATSC_CC);
|
||||
if(info)
|
||||
{
|
||||
if (ctx->freport.data_from_708->services[i] == 0)
|
||||
continue;
|
||||
printf("%d ", i);
|
||||
printf("Yes\n");
|
||||
print_cc_report(ctx, info);
|
||||
}
|
||||
else
|
||||
printf("No\n");
|
||||
|
||||
|
||||
info = get_best_sib_stream(program);
|
||||
if(!info)
|
||||
continue;
|
||||
|
||||
dec_ctx = update_decoder_list_cinfo(ctx, info);
|
||||
if (dec_ctx->in_bufferdatatype == CCX_PES &&
|
||||
(info->stream == CCX_SM_TRANSPORT ||
|
||||
info->stream == CCX_SM_PROGRAM ||
|
||||
info->stream == CCX_SM_ASF ||
|
||||
info->stream == CCX_SM_WTV))
|
||||
{
|
||||
printf("Width: %u\n", dec_ctx->current_hor_size);
|
||||
printf("Height: %u\n", dec_ctx->current_vert_size);
|
||||
printf("Aspect Ratio: %s\n", aspect_ratio_types[dec_ctx->current_aspect_ratio]);
|
||||
printf("Frame Rate: %s\n", framerates_types[dec_ctx->current_frame_rate]);
|
||||
}
|
||||
printf("\n");
|
||||
|
||||
printf("Primary Language Present: %s\n", Y_N(ctx->freport.data_from_708->services[1]));
|
||||
|
||||
printf("Secondary Language Present: %s\n", Y_N(ctx->freport.data_from_708->services[2]));
|
||||
}
|
||||
|
||||
printf("MPEG-4 Timed Text: %s\n", Y_N(ctx->freport.mp4_cc_track_cnt));
|
||||
@@ -396,7 +386,7 @@ void print_file_report(struct lib_ccx_ctx *ctx)
|
||||
printf("MPEG-4 Timed Text tracks count: %d\n", ctx->freport.mp4_cc_track_cnt);
|
||||
}
|
||||
|
||||
freep(&ctx->freport.data_from_608);
|
||||
memset(&ctx->freport, 0, sizeof (struct file_report));
|
||||
|
||||
#undef Y_N
|
||||
#undef Y_N
|
||||
}
|
||||
|
||||
@@ -7,58 +7,50 @@
|
||||
// enough space.
|
||||
//#define SORTBUF (2*MAXBFRAMES+1) - from lib_ccx.h
|
||||
// B-Frames can be (temporally) before or after the anchor
|
||||
int cc_data_count[SORTBUF];
|
||||
// Store fts;
|
||||
static 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
|
||||
|
||||
// Set to true if data is buffered
|
||||
int has_ccdata_buffered = 0;
|
||||
// The sequence number of the current anchor frame. All currently read
|
||||
// B-Frames belong to this I- or P-frame.
|
||||
static int anchor_seq_number = -1;
|
||||
|
||||
void init_hdcc (void)
|
||||
void init_hdcc (struct lib_cc_decode *ctx)
|
||||
{
|
||||
for (int j=0; j<SORTBUF; j++)
|
||||
{
|
||||
cc_data_count[j] = 0;
|
||||
cc_fts[j] = 0;
|
||||
}
|
||||
memset(cc_data_pkts, 0, SORTBUF*(31*3+1));
|
||||
has_ccdata_buffered = 0;
|
||||
for (int j=0; j<SORTBUF; j++)
|
||||
{
|
||||
ctx->cc_data_count[j] = 0;
|
||||
ctx->cc_fts[j] = 0;
|
||||
}
|
||||
memset(ctx->cc_data_pkts, 0, SORTBUF*(31*3+1));
|
||||
ctx->has_ccdata_buffered = 0;
|
||||
}
|
||||
|
||||
// Buffer caption blocks for later sorting/flushing.
|
||||
void store_hdcc(struct lib_ccx_ctx *ctx, unsigned char *cc_data, int cc_count, int sequence_number, LLONG current_fts_now,struct cc_subtitle *sub)
|
||||
void store_hdcc(struct lib_cc_decode *ctx, unsigned char *cc_data, int cc_count, int sequence_number, LLONG current_fts_now, struct cc_subtitle *sub)
|
||||
{
|
||||
// Uninitialized?
|
||||
if (anchor_seq_number < 0)
|
||||
{
|
||||
anchor_hdcc( sequence_number);
|
||||
}
|
||||
enum ccx_stream_mode_enum stream_mode;
|
||||
|
||||
int seq_index = sequence_number - anchor_seq_number + MAXBFRAMES;
|
||||
//stream_mode = ctx->demux_ctx->get_stream_mode(ctx->demux_ctx);
|
||||
// Uninitialized?
|
||||
if (ctx->anchor_seq_number < 0)
|
||||
{
|
||||
anchor_hdcc( ctx, sequence_number);
|
||||
}
|
||||
|
||||
if (seq_index < 0 || seq_index > 2*MAXBFRAMES)
|
||||
{
|
||||
// Maybe missing an anchor frame - try to recover
|
||||
dbg_print(CCX_DMT_VERBOSE, "Too many B-frames, or missing anchor frame. Trying to recover ..\n");
|
||||
int seq_index = sequence_number - ctx->anchor_seq_number + MAXBFRAMES;
|
||||
|
||||
process_hdcc(ctx, sub);
|
||||
anchor_hdcc( sequence_number);
|
||||
seq_index = sequence_number - anchor_seq_number + MAXBFRAMES;
|
||||
}
|
||||
if (seq_index < 0 || seq_index > 2*MAXBFRAMES)
|
||||
{
|
||||
// Maybe missing an anchor frame - try to recover
|
||||
dbg_print(CCX_DMT_VERBOSE, "Too many B-frames, or missing anchor frame. Trying to recover ..\n");
|
||||
|
||||
has_ccdata_buffered = 1;
|
||||
process_hdcc(ctx, sub);
|
||||
anchor_hdcc( ctx, sequence_number);
|
||||
seq_index = sequence_number - ctx->anchor_seq_number + MAXBFRAMES;
|
||||
}
|
||||
|
||||
ctx->has_ccdata_buffered = 1;
|
||||
|
||||
// In GOP mode the fts is set only once for the whole GOP. Recreate
|
||||
// the right time according to the sequence number.
|
||||
if (ccx_options.use_gop_as_pts==1)
|
||||
{
|
||||
current_fts_now += (LLONG) (sequence_number*1000.0/current_fps);
|
||||
}
|
||||
// the right time according to the sequence number.
|
||||
if (ccx_options.use_gop_as_pts==1)
|
||||
{
|
||||
current_fts_now += (LLONG) (sequence_number*1000.0/current_fps);
|
||||
}
|
||||
|
||||
if (cc_count)
|
||||
{
|
||||
@@ -66,47 +58,46 @@ void store_hdcc(struct lib_ccx_ctx *ctx, unsigned char *cc_data, int cc_count, i
|
||||
{
|
||||
// Changed by CFS to concat, i.e. don't assume there's no data already for this seq_index.
|
||||
// Needed at least for MP4 samples. // TODO: make sure we don't overflow
|
||||
cc_fts[seq_index] = current_fts_now; // CFS: Maybe do even if there's no data?
|
||||
if (ctx->stream_mode!=CCX_SM_MP4) // CFS: Very ugly hack, but looks like overwriting is needed for at least some ES
|
||||
cc_data_count[seq_index] = 0;
|
||||
memcpy(cc_data_pkts[seq_index]+cc_data_count[seq_index]*3, cc_data, cc_count*3+1);
|
||||
ctx->cc_fts[seq_index] = current_fts_now; // CFS: Maybe do even if there's no data?
|
||||
//if (stream_mode!=CCX_SM_MP4) // CFS: Very ugly hack, but looks like overwriting is needed for at least some ES
|
||||
ctx->cc_data_count[seq_index] = 0;
|
||||
memcpy(ctx->cc_data_pkts[seq_index] + ctx->cc_data_count[seq_index] * 3, cc_data, cc_count * 3 + 1);
|
||||
}
|
||||
cc_data_count[seq_index] += cc_count;
|
||||
ctx->cc_data_count[seq_index] += cc_count;
|
||||
}
|
||||
// DEBUG STUFF
|
||||
/*
|
||||
printf("\nCC blocks, channel 0:\n");
|
||||
for ( int i=0; i < cc_count*3; i+=3)
|
||||
{
|
||||
printf("%s", debug_608toASC( cc_data+i, 0) );
|
||||
}
|
||||
printf("\n");
|
||||
*/
|
||||
// DEBUG STUFF
|
||||
/*
|
||||
printf("\nCC blocks, channel 0:\n");
|
||||
for ( int i=0; i < cc_count*3; i+=3)
|
||||
{
|
||||
printf("%s", debug_608toASC( cc_data+i, 0) );
|
||||
}
|
||||
printf("\n");
|
||||
*/
|
||||
}
|
||||
|
||||
// Set a new anchor frame that new B-frames refer to.
|
||||
void anchor_hdcc(int seq)
|
||||
void anchor_hdcc(struct lib_cc_decode *ctx, int seq)
|
||||
{
|
||||
// Re-init the index
|
||||
anchor_seq_number = seq;
|
||||
// Re-init the index
|
||||
ctx->anchor_seq_number = seq;
|
||||
}
|
||||
|
||||
// Sort/flash caption block buffer
|
||||
void process_hdcc (struct lib_ccx_ctx *ctx, struct cc_subtitle *sub)
|
||||
void process_hdcc (struct lib_cc_decode *ctx, struct cc_subtitle *sub)
|
||||
{
|
||||
// Remember the current value
|
||||
LLONG store_fts_now = fts_now;
|
||||
struct lib_cc_decode *dec_ctx;
|
||||
LLONG store_fts_now = ctx->timing->fts_now;
|
||||
int reset_cb = -1;
|
||||
|
||||
dbg_print(CCX_DMT_VERBOSE, "Flush HD caption blocks\n");
|
||||
dec_ctx = ctx->dec_ctx;
|
||||
|
||||
for (int seq=0; seq<SORTBUF; seq++)
|
||||
{
|
||||
|
||||
// We rely on this.
|
||||
if (ccx_bufferdatatype == CCX_H264)
|
||||
reset_cb = 1;
|
||||
if (ctx->in_bufferdatatype == CCX_H264)
|
||||
reset_cb = 1;
|
||||
|
||||
// If fts_now is unchanged we rely on cc block counting,
|
||||
// otherwise reset counters as they get changed by do_cb()
|
||||
@@ -114,9 +105,9 @@ void process_hdcc (struct lib_ccx_ctx *ctx, struct cc_subtitle *sub)
|
||||
// updated, like it used do happen for elementary streams.
|
||||
// Since use_gop_as_pts this is not needed anymore, but left
|
||||
// here for posterity.
|
||||
if (reset_cb < 0 && cc_fts[seq] && seq<SORTBUF-1 && cc_fts[seq+1])
|
||||
if (reset_cb < 0 && ctx->cc_fts[seq] && seq<SORTBUF-1 && ctx->cc_fts[seq+1])
|
||||
{
|
||||
if (cc_fts[seq] != cc_fts[seq+1])
|
||||
if (ctx->cc_fts[seq] != ctx->cc_fts[seq+1])
|
||||
reset_cb = 1;
|
||||
else
|
||||
reset_cb = 0;
|
||||
@@ -129,10 +120,10 @@ void process_hdcc (struct lib_ccx_ctx *ctx, struct cc_subtitle *sub)
|
||||
}
|
||||
|
||||
// Skip sequence numbers without data
|
||||
if (cc_data_count[seq] == 0)
|
||||
if (ctx->cc_data_count[seq] == 0)
|
||||
continue;
|
||||
|
||||
if (cc_data_pkts[seq][cc_data_count[seq]*3]!=0xFF)
|
||||
if (ctx->cc_data_pkts[seq][ctx->cc_data_count[seq]*3]!=0xFF)
|
||||
{
|
||||
// This is not optional. Something is wrong.
|
||||
dbg_print(CCX_DMT_VERBOSE, "Missing 0xFF marker at end\n");
|
||||
@@ -141,14 +132,14 @@ void process_hdcc (struct lib_ccx_ctx *ctx, struct cc_subtitle *sub)
|
||||
}
|
||||
|
||||
// Re-create original time
|
||||
fts_now = cc_fts[seq];
|
||||
process_cc_data( dec_ctx, cc_data_pkts[seq], cc_data_count[seq], sub);
|
||||
ctx->timing->fts_now = ctx->cc_fts[seq];
|
||||
process_cc_data( ctx, ctx->cc_data_pkts[seq], ctx->cc_data_count[seq], sub);
|
||||
|
||||
}
|
||||
|
||||
// Restore the value
|
||||
fts_now = store_fts_now;
|
||||
ctx->timing->fts_now = store_fts_now;
|
||||
|
||||
// Now that we are done, clean up.
|
||||
init_hdcc();
|
||||
init_hdcc(ctx);
|
||||
}
|
||||
|
||||
@@ -12,127 +12,123 @@
|
||||
|
||||
static int initialized = 0;
|
||||
|
||||
void
|
||||
spupng_init_font()
|
||||
void spupng_init_font()
|
||||
{
|
||||
uint8_t *t, *p;
|
||||
int i, j;
|
||||
uint8_t *t, *p;
|
||||
int i, j;
|
||||
|
||||
/* de-interleave font image (puts all chars in row 0) */
|
||||
if (!(t = (uint8_t*)malloc(ccfont2_width * ccfont2_height / 8)))
|
||||
ccx_common_logging.fatal_ftn(EXIT_NOT_ENOUGH_MEMORY, "Memory allocation failed");
|
||||
/* de-interleave font image (puts all chars in row 0) */
|
||||
if (!(t = (uint8_t*)malloc(ccfont2_width * ccfont2_height / 8)))
|
||||
ccx_common_logging.fatal_ftn(EXIT_NOT_ENOUGH_MEMORY, "Memory allocation failed");
|
||||
|
||||
for (p = t, i = 0; i < CCH; i++)
|
||||
for (j = 0; j < ccfont2_height; p += ccfont2_width / 8, j += CCH)
|
||||
memcpy(p, ccfont2_bits + (j + i) * ccfont2_width / 8,
|
||||
ccfont2_width / 8);
|
||||
for (p = t, i = 0; i < CCH; i++)
|
||||
for (j = 0; j < ccfont2_height; p += ccfont2_width / 8, j += CCH)
|
||||
memcpy(p, ccfont2_bits + (j + i) * ccfont2_width / 8,
|
||||
ccfont2_width / 8);
|
||||
|
||||
memcpy(ccfont2_bits, t, ccfont2_width * ccfont2_height / 8);
|
||||
free(t);
|
||||
memcpy(ccfont2_bits, t, ccfont2_width * ccfont2_height / 8);
|
||||
free(t);
|
||||
}
|
||||
|
||||
struct spupng_t *spunpg_init(struct ccx_s_write *out)
|
||||
{
|
||||
struct spupng_t *sp = (struct spupng_t *) malloc(sizeof(struct spupng_t));
|
||||
if (NULL == sp)
|
||||
ccx_common_logging.fatal_ftn(EXIT_NOT_ENOUGH_MEMORY, "Memory allocation failed");
|
||||
struct spupng_t *sp = (struct spupng_t *) malloc(sizeof(struct spupng_t));
|
||||
if (NULL == sp)
|
||||
ccx_common_logging.fatal_ftn(EXIT_NOT_ENOUGH_MEMORY, "Memory allocation failed");
|
||||
|
||||
if (!initialized)
|
||||
{
|
||||
initialized = 1;
|
||||
spupng_init_font();
|
||||
}
|
||||
if (!initialized)
|
||||
{
|
||||
initialized = 1;
|
||||
spupng_init_font();
|
||||
}
|
||||
|
||||
if ((sp->fpxml = fdopen(out->fh, "w")) == NULL)
|
||||
{
|
||||
{
|
||||
ccx_common_logging.fatal_ftn(CCX_COMMON_EXIT_FILE_CREATION_FAILED, "Cannot open %s: %s\n",
|
||||
out->filename, strerror(errno));
|
||||
}
|
||||
sp->dirname = (char *) malloc(
|
||||
sizeof(char) * (strlen(out->filename) + 3));
|
||||
if (NULL == sp->dirname)
|
||||
ccx_common_logging.fatal_ftn(EXIT_NOT_ENOUGH_MEMORY, "Memory allocation failed");
|
||||
out->filename, strerror(errno));
|
||||
}
|
||||
sp->dirname = (char *) malloc(
|
||||
sizeof(char) * (strlen(out->filename) + 3));
|
||||
if (NULL == sp->dirname)
|
||||
ccx_common_logging.fatal_ftn(EXIT_NOT_ENOUGH_MEMORY, "Memory allocation failed");
|
||||
|
||||
strcpy(sp->dirname, out->filename);
|
||||
char* p = strrchr(sp->dirname, '.');
|
||||
if (NULL == p)
|
||||
p = sp->dirname + strlen(sp->dirname);
|
||||
*p = '\0';
|
||||
strcat(sp->dirname, ".d");
|
||||
if (0 != mkdir(sp->dirname, 0777))
|
||||
{
|
||||
if (errno != EEXIST)
|
||||
{
|
||||
char* p = strrchr(sp->dirname, '.');
|
||||
if (NULL == p)
|
||||
p = sp->dirname + strlen(sp->dirname);
|
||||
*p = '\0';
|
||||
strcat(sp->dirname, ".d");
|
||||
if (0 != mkdir(sp->dirname, 0777))
|
||||
{
|
||||
if (errno != EEXIST)
|
||||
{
|
||||
ccx_common_logging.fatal_ftn(CCX_COMMON_EXIT_FILE_CREATION_FAILED, "Cannot create %s: %s\n",
|
||||
sp->dirname, strerror(errno));
|
||||
}
|
||||
// If dirname isn't a directory or if we don't have write permission,
|
||||
// the first attempt to create a .png file will fail and we'll XXxit.
|
||||
}
|
||||
sp->dirname, strerror(errno));
|
||||
}
|
||||
// If dirname isn't a directory or if we don't have write permission,
|
||||
// the first attempt to create a .png file will fail and we'll XXxit.
|
||||
}
|
||||
|
||||
// enough to append /subNNNN.png
|
||||
sp->pngfile = (char *) malloc(sizeof(char) * (strlen(sp->dirname) + 13));
|
||||
if (NULL == sp->pngfile)
|
||||
ccx_common_logging.fatal_ftn(EXIT_NOT_ENOUGH_MEMORY, "Memory allocation failed");
|
||||
sp->fileIndex = 0;
|
||||
sprintf(sp->pngfile, "%s/sub%04d.png", sp->dirname, sp->fileIndex);
|
||||
// enough to append /subNNNN.png
|
||||
sp->pngfile = (char *) malloc(sizeof(char) * (strlen(sp->dirname) + 13));
|
||||
if (NULL == sp->pngfile)
|
||||
ccx_common_logging.fatal_ftn(EXIT_NOT_ENOUGH_MEMORY, "Memory allocation failed");
|
||||
sp->fileIndex = 0;
|
||||
sprintf(sp->pngfile, "%s/sub%04d.png", sp->dirname, sp->fileIndex);
|
||||
|
||||
// For NTSC closed captions and 720x480 DVD subtitle resolution:
|
||||
// Each character is 16x26.
|
||||
// 15 rows by 32 columns, plus 2 columns for left & right padding
|
||||
// So each .png image will be 16*34 wide and 26*15 high, or 544x390
|
||||
// To center image in 720x480 DVD screen, offset image by 88 and 45
|
||||
// Need to keep yOffset even to prevent flicker on interlaced displays
|
||||
// Would need to do something different for PAL format and teletext.
|
||||
sp->xOffset = 88;
|
||||
sp->yOffset = 46;
|
||||
// For NTSC closed captions and 720x480 DVD subtitle resolution:
|
||||
// Each character is 16x26.
|
||||
// 15 rows by 32 columns, plus 2 columns for left & right padding
|
||||
// So each .png image will be 16*34 wide and 26*15 high, or 544x390
|
||||
// To center image in 720x480 DVD screen, offset image by 88 and 45
|
||||
// Need to keep yOffset even to prevent flicker on interlaced displays
|
||||
// Would need to do something different for PAL format and teletext.
|
||||
sp->xOffset = 88;
|
||||
sp->yOffset = 46;
|
||||
|
||||
return sp;
|
||||
return sp;
|
||||
}
|
||||
void
|
||||
spunpg_free(struct spupng_t *sp)
|
||||
void spunpg_free(struct spupng_t *sp)
|
||||
{
|
||||
free(sp->dirname);
|
||||
free(sp->pngfile);
|
||||
free(sp);
|
||||
free(sp->dirname);
|
||||
free(sp->pngfile);
|
||||
free(sp);
|
||||
}
|
||||
|
||||
void
|
||||
spupng_write_header(struct spupng_t *sp,int multiple_files,char *first_input_file)
|
||||
void spupng_write_header(struct spupng_t *sp,int multiple_files,char *first_input_file)
|
||||
{
|
||||
fprintf(sp->fpxml, "<subpictures>\n<stream>\n");
|
||||
if (multiple_files)
|
||||
fprintf(sp->fpxml, "<!-- %s -->\n", first_input_file);
|
||||
fprintf(sp->fpxml, "<subpictures>\n<stream>\n");
|
||||
if (multiple_files)
|
||||
fprintf(sp->fpxml, "<!-- %s -->\n", first_input_file);
|
||||
}
|
||||
|
||||
void
|
||||
spupng_write_footer(struct spupng_t *sp)
|
||||
void spupng_write_footer(struct spupng_t *sp)
|
||||
{
|
||||
fprintf(sp->fpxml, "</stream>\n</subpictures>\n");
|
||||
fflush(sp->fpxml);
|
||||
fclose(sp->fpxml);
|
||||
fprintf(sp->fpxml, "</stream>\n</subpictures>\n");
|
||||
fflush(sp->fpxml);
|
||||
fclose(sp->fpxml);
|
||||
}
|
||||
|
||||
void write_spumux_header(struct ccx_s_write *out)
|
||||
void write_spumux_header(struct encoder_ctx *ctx, struct ccx_s_write *out)
|
||||
{
|
||||
if (0 == out->spupng_data)
|
||||
out->spupng_data = spunpg_init(out);
|
||||
if (0 == out->spupng_data)
|
||||
out->spupng_data = spunpg_init(out);
|
||||
|
||||
spupng_write_header((struct spupng_t*)out->spupng_data,out->multiple_files,out->first_input_file);
|
||||
spupng_write_header((struct spupng_t*)out->spupng_data, ctx->multiple_files, ctx->first_input_file);
|
||||
}
|
||||
|
||||
void write_spumux_footer(struct ccx_s_write *out)
|
||||
{
|
||||
if (0 != out->spupng_data)
|
||||
{
|
||||
struct spupng_t *sp = (struct spupng_t *) out->spupng_data;
|
||||
if (0 != out->spupng_data)
|
||||
{
|
||||
struct spupng_t *sp = (struct spupng_t *) out->spupng_data;
|
||||
|
||||
spupng_write_footer(sp);
|
||||
spunpg_free(sp);
|
||||
spupng_write_footer(sp);
|
||||
spunpg_free(sp);
|
||||
|
||||
out->spupng_data = 0;
|
||||
out->fh = -1;
|
||||
}
|
||||
out->spupng_data = 0;
|
||||
out->fh = -1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -171,35 +167,37 @@ void write_spumux_footer(struct ccx_s_write *out)
|
||||
* @return
|
||||
* Glyph number.
|
||||
*/
|
||||
static unsigned int
|
||||
unicode_ccfont2(unsigned int c, int italic)
|
||||
static unsigned int unicode_ccfont2(unsigned int c, int italic)
|
||||
{
|
||||
static const unsigned short specials[] = {
|
||||
0x00E1, 0x00E9,
|
||||
0x00ED, 0x00F3, 0x00FA, 0x00E7, 0x00F7, 0x00D1, 0x00F1, 0x25A0,
|
||||
0x00AE, 0x00B0, 0x00BD, 0x00BF, 0x2122, 0x00A2, 0x00A3, 0x266A,
|
||||
0x00E0, 0x0020, 0x00E8, 0x00E2, 0x00EA, 0x00EE, 0x00F4, 0x00FB };
|
||||
unsigned int i;
|
||||
static const unsigned short specials[] = {
|
||||
0x00E1, 0x00E9,
|
||||
0x00ED, 0x00F3, 0x00FA, 0x00E7, 0x00F7, 0x00D1, 0x00F1, 0x25A0,
|
||||
0x00AE, 0x00B0, 0x00BD, 0x00BF, 0x2122, 0x00A2, 0x00A3, 0x266A,
|
||||
0x00E0, 0x0020, 0x00E8, 0x00E2, 0x00EA, 0x00EE, 0x00F4, 0x00FB };
|
||||
unsigned int i;
|
||||
|
||||
if (c < 0x0020)
|
||||
c = 15; /* invalid */
|
||||
else if (c < 0x0080)
|
||||
/*c = c */;
|
||||
else {
|
||||
for (i = 0; i < sizeof(specials) / sizeof(specials[0]); i++)
|
||||
if (specials[i] == c) {
|
||||
c = i + 6;
|
||||
goto slant;
|
||||
}
|
||||
|
||||
c = 15; /* invalid */
|
||||
}
|
||||
if (c < 0x0020)
|
||||
c = 15; /* invalid */
|
||||
else if (c < 0x0080)
|
||||
/*c = c */;
|
||||
else
|
||||
{
|
||||
for (i = 0; i < sizeof(specials) / sizeof(specials[0]); i++)
|
||||
{
|
||||
if (specials[i] == c)
|
||||
{
|
||||
c = i + 6;
|
||||
goto slant;
|
||||
}
|
||||
}
|
||||
c = 15; /* invalid */
|
||||
}
|
||||
|
||||
slant:
|
||||
if (italic)
|
||||
c += 4 * 32;
|
||||
if (italic)
|
||||
c += 4 * 32;
|
||||
|
||||
return c;
|
||||
return c;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -251,40 +249,41 @@ draw_blank(int canvas_type, uint8_t *canvas, unsigned int rowstride,
|
||||
* Draw one character (function template - define a static version with
|
||||
* constant @a canvas_type, @a font, @a cpl, @a cw, @a ch).
|
||||
*/
|
||||
static void
|
||||
draw_char(int canvas_type, uint8_t *canvas, int rowstride,
|
||||
static void draw_char(int canvas_type, uint8_t *canvas, int rowstride,
|
||||
uint8_t *pen, uint8_t *font, int cpl, int cw, int ch,
|
||||
int glyph, unsigned int underline)
|
||||
{
|
||||
uint8_t *src;
|
||||
int shift, x, y;
|
||||
uint8_t *src;
|
||||
int shift, x, y;
|
||||
|
||||
assert(cw >= 8 && cw <= 16);
|
||||
assert(ch >= 1 && ch <= 31);
|
||||
assert(cw >= 8 && cw <= 16);
|
||||
assert(ch >= 1 && ch <= 31);
|
||||
|
||||
x = glyph * cw;
|
||||
shift = x & 7;
|
||||
src = font + (x >> 3);
|
||||
x = glyph * cw;
|
||||
shift = x & 7;
|
||||
src = font + (x >> 3);
|
||||
|
||||
for (y = 0; y < ch; underline >>= 1, y++) {
|
||||
int bits = ~0;
|
||||
for (y = 0; y < ch; underline >>= 1, y++)
|
||||
{
|
||||
int bits = ~0;
|
||||
|
||||
if (!(underline & 1)) {
|
||||
if (!(underline & 1))
|
||||
{
|
||||
#ifdef __i386__
|
||||
bits = (*((uint16_t *) src) >> shift);
|
||||
bits = (*((uint16_t *) src) >> shift);
|
||||
#else
|
||||
/* unaligned/little endian */
|
||||
bits = ((src[1] * 256 + src[0]) >> shift);
|
||||
/* unaligned/little endian */
|
||||
bits = ((src[1] * 256 + src[0]) >> shift);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
for (x = 0; x < cw; bits >>= 1, x++)
|
||||
poke(canvas, x, peek(pen, bits & 1));
|
||||
for (x = 0; x < cw; bits >>= 1, x++)
|
||||
poke(canvas, x, peek(pen, bits & 1));
|
||||
|
||||
canvas += rowstride;
|
||||
canvas += rowstride;
|
||||
|
||||
src += cpl * cw / 8;
|
||||
}
|
||||
src += cpl * cw / 8;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -293,10 +292,10 @@ draw_char(int canvas_type, uint8_t *canvas, int rowstride,
|
||||
void draw_char_indexed(uint8_t * canvas, int rowstride, uint8_t * pen,
|
||||
int unicode, int italic, int underline)
|
||||
{
|
||||
draw_char(sizeof(*canvas), canvas, rowstride,
|
||||
pen, (uint8_t *) ccfont2_bits, CCPL, CCW, CCH,
|
||||
unicode_ccfont2(unicode, italic),
|
||||
underline * (3 << 24) /* cell row 24, 25 */);
|
||||
draw_char(sizeof(*canvas), canvas, rowstride,
|
||||
pen, (uint8_t *) ccfont2_bits, CCPL, CCW, CCH,
|
||||
unicode_ccfont2(unicode, italic),
|
||||
underline * (3 << 24) /* cell row 24, 25 */);
|
||||
}
|
||||
|
||||
void write_sputag(struct spupng_t *sp,LLONG ms_start,LLONG ms_end)
|
||||
@@ -319,8 +318,8 @@ void write_spucomment(struct spupng_t *sp,const char *str)
|
||||
|
||||
char* get_spupng_filename(void *ctx)
|
||||
{
|
||||
struct spupng_t *sp = (struct spupng_t *)ctx;
|
||||
return sp->pngfile;
|
||||
struct spupng_t *sp = (struct spupng_t *)ctx;
|
||||
return sp->pngfile;
|
||||
}
|
||||
void inc_spupng_fileindex(void *ctx)
|
||||
{
|
||||
@@ -330,9 +329,9 @@ void inc_spupng_fileindex(void *ctx)
|
||||
}
|
||||
void set_spupng_offset(void *ctx,int x,int y)
|
||||
{
|
||||
struct spupng_t *sp = (struct spupng_t *)ctx;
|
||||
sp->xOffset = x;
|
||||
sp->yOffset = y;
|
||||
struct spupng_t *sp = (struct spupng_t *)ctx;
|
||||
sp->xOffset = x;
|
||||
sp->yOffset = y;
|
||||
}
|
||||
int save_spupng(const char *filename, uint8_t *bitmap, int w, int h,
|
||||
png_color *palette, png_byte *alpha, int nb_color)
|
||||
@@ -554,7 +553,7 @@ 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].data[1],rect[0].nb_colors);
|
||||
#if ENABLE_OCR
|
||||
#ifdef ENABLE_OCR
|
||||
if (rect[0].ocr_text && *(rect[0].ocr_text))
|
||||
{
|
||||
write_spucomment(sp, rect[0].ocr_text);
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include "ccx_common_platform.h"
|
||||
#include "ccx_encoders_structs.h"
|
||||
#include "png.h"
|
||||
#include "ccx_encoders_common.h"
|
||||
|
||||
// CC page dimensions
|
||||
#define ROWS 15
|
||||
@@ -14,16 +15,16 @@
|
||||
|
||||
struct spupng_t
|
||||
{
|
||||
FILE* fpxml;
|
||||
FILE* fppng;
|
||||
char* dirname;
|
||||
char* pngfile;
|
||||
int fileIndex;
|
||||
int xOffset;
|
||||
int yOffset;
|
||||
FILE* fpxml;
|
||||
FILE* fppng;
|
||||
char* dirname;
|
||||
char* pngfile;
|
||||
int fileIndex;
|
||||
int xOffset;
|
||||
int yOffset;
|
||||
};
|
||||
|
||||
void write_spumux_header(struct ccx_s_write *out);
|
||||
void write_spumux_header(struct encoder_ctx *ctx, struct ccx_s_write *out);
|
||||
void write_spumux_footer(struct ccx_s_write *out);
|
||||
void draw_char_indexed(uint8_t * canvas, int rowstride, uint8_t * pen,
|
||||
int unicode, int italic, int underline);
|
||||
|
||||
@@ -5,86 +5,127 @@
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
#include "activity.h"
|
||||
#include "utility.h"
|
||||
#include "ccx_common_timing.h"
|
||||
#include "file_buffer.h"
|
||||
|
||||
void detect_stream_type (struct lib_ccx_ctx *ctx)
|
||||
void detect_stream_type (struct ccx_demuxer *ctx)
|
||||
{
|
||||
ctx->stream_mode=CCX_SM_ELEMENTARY_OR_NOT_FOUND; // Not found
|
||||
ctx->startbytes_avail = (int) buffered_read_opt(ctx, ctx->startbytes, STARTBYTESLENGTH);
|
||||
|
||||
if( ctx->startbytes_avail == -1)
|
||||
fatal (EXIT_READ_ERROR, "Error reading input file!\n");
|
||||
if( ctx->startbytes_avail == -1)
|
||||
fatal (EXIT_READ_ERROR, "Error reading input file!\n");
|
||||
|
||||
if (ctx->startbytes_avail>=4)
|
||||
{
|
||||
// Check for ASF magic bytes
|
||||
if (ctx->startbytes[0]==0x30 &&
|
||||
ctx->startbytes[1]==0x26 &&
|
||||
ctx->startbytes[2]==0xb2 &&
|
||||
ctx->startbytes[3]==0x75)
|
||||
ctx->stream_mode=CCX_SM_ASF;
|
||||
}
|
||||
if (ctx->startbytes_avail>=4)
|
||||
{
|
||||
// Check for ASF magic bytes
|
||||
if (ctx->startbytes[0]==0x30 &&
|
||||
ctx->startbytes[1]==0x26 &&
|
||||
ctx->startbytes[2]==0xb2 &&
|
||||
ctx->startbytes[3]==0x75)
|
||||
ctx->stream_mode=CCX_SM_ASF;
|
||||
}
|
||||
if (ctx->stream_mode == CCX_SM_ELEMENTARY_OR_NOT_FOUND && ctx->startbytes_avail >= 4)
|
||||
{
|
||||
if(ctx->startbytes[0]==0xb7 &&
|
||||
ctx->startbytes[1]==0xd8 &&
|
||||
ctx->startbytes[2]==0x00 &&
|
||||
ctx->startbytes[3]==0x20)
|
||||
ctx->stream_mode = CCX_SM_WTV;
|
||||
}
|
||||
{
|
||||
if(ctx->startbytes[0]==0xb7 &&
|
||||
ctx->startbytes[1]==0xd8 &&
|
||||
ctx->startbytes[2]==0x00 &&
|
||||
ctx->startbytes[3]==0x20)
|
||||
ctx->stream_mode = CCX_SM_WTV;
|
||||
}
|
||||
#ifdef WTV_DEBUG
|
||||
if (ctx->stream_mode==CCX_SM_ELEMENTARY_OR_NOT_FOUND && ctx->startbytes_avail>=6)
|
||||
{
|
||||
// Check for hexadecimal dump generated by wtvccdump
|
||||
// ; CCHD
|
||||
if (ctx->startbytes[0]==';' &&
|
||||
ctx->startbytes[1]==' ' &&
|
||||
ctx->startbytes[2]=='C' &&
|
||||
ctx->startbytes[3]=='C' &&
|
||||
ctx->startbytes[4]=='H' &&
|
||||
ctx->startbytes[5]=='D')
|
||||
ctx->stream_mode= CCX_SM_HEX_DUMP;
|
||||
if (ctx->startbytes[0]==';' &&
|
||||
ctx->startbytes[1]==' ' &&
|
||||
ctx->startbytes[2]=='C' &&
|
||||
ctx->startbytes[3]=='C' &&
|
||||
ctx->startbytes[4]=='H' &&
|
||||
ctx->startbytes[5]=='D')
|
||||
ctx->stream_mode= CCX_SM_HEX_DUMP;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (ctx->stream_mode==CCX_SM_ELEMENTARY_OR_NOT_FOUND && ctx->startbytes_avail>=11)
|
||||
{
|
||||
// Check for CCExtractor magic bytes
|
||||
if (ctx->startbytes[0]==0xCC &&
|
||||
ctx->startbytes[1]==0xCC &&
|
||||
ctx->startbytes[2]==0xED &&
|
||||
ctx->startbytes[8]==0 &&
|
||||
ctx->startbytes[9]==0 &&
|
||||
ctx->startbytes[10]==0)
|
||||
ctx->stream_mode=CCX_SM_RCWT;
|
||||
}
|
||||
if (ctx->stream_mode==CCX_SM_ELEMENTARY_OR_NOT_FOUND) // Still not found
|
||||
{
|
||||
if (ctx->startbytes_avail > 188*8) // Otherwise, assume no TS
|
||||
{
|
||||
// First check for TS
|
||||
for (unsigned i=0; i<188;i++)
|
||||
{
|
||||
if (ctx->startbytes[i]==0x47 && ctx->startbytes[i+188]==0x47 &&
|
||||
ctx->startbytes[i+188*2]==0x47 && ctx->startbytes[i+188*3]==0x47 &&
|
||||
ctx->startbytes[i+188*4]==0x47 && ctx->startbytes[i+188*5]==0x47 &&
|
||||
ctx->startbytes[i+188*6]==0x47 && ctx->startbytes[i+188*7]==0x47
|
||||
)
|
||||
{
|
||||
// Eight sync bytes, that's good enough
|
||||
ctx->startbytes_pos=i;
|
||||
ctx->stream_mode=CCX_SM_TRANSPORT;
|
||||
if (ctx->stream_mode==CCX_SM_ELEMENTARY_OR_NOT_FOUND && ctx->startbytes_avail>=11)
|
||||
{
|
||||
// Check for CCExtractor magic bytes
|
||||
if (ctx->startbytes[0]==0xCC &&
|
||||
ctx->startbytes[1]==0xCC &&
|
||||
ctx->startbytes[2]==0xED &&
|
||||
ctx->startbytes[8]==0 &&
|
||||
ctx->startbytes[9]==0 &&
|
||||
ctx->startbytes[10]==0)
|
||||
ctx->stream_mode=CCX_SM_RCWT;
|
||||
}
|
||||
if ((ctx->stream_mode == CCX_SM_ELEMENTARY_OR_NOT_FOUND || ccx_options.print_file_reports)
|
||||
&& ctx->startbytes_avail >= 4) // Still not found
|
||||
{
|
||||
size_t idx = 0, nextBoxLocation = 0;
|
||||
int boxScore = 0;
|
||||
// Scan the buffer for valid succeeding MP4 boxes.
|
||||
while (idx < ctx->startbytes_avail - 8){
|
||||
// Check if we have a valid box
|
||||
if (isValidMP4Box(ctx->startbytes, idx, &nextBoxLocation, &boxScore))
|
||||
{
|
||||
idx = nextBoxLocation; // If the box is valid, a new box should be found on the next location... Not somewhere in between.
|
||||
if (boxScore > 7)
|
||||
{
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Not a valid box, reset score. We need a couple of successive boxes to identify a MP4 file.
|
||||
boxScore = 0;
|
||||
idx++;
|
||||
}
|
||||
}
|
||||
if (boxScore > 1)
|
||||
{
|
||||
// We had at least one box (or multiple) at the end to "claim" this is MP4. A single valid box at the end is doubtful...
|
||||
ctx->stream_mode = CCX_SM_MP4;
|
||||
}
|
||||
}
|
||||
if (ctx->stream_mode==CCX_SM_ELEMENTARY_OR_NOT_FOUND) // Still not found
|
||||
{
|
||||
if (ctx->startbytes_avail > 188*8) // Otherwise, assume no TS
|
||||
{
|
||||
// First check for TS
|
||||
for (unsigned i=0; i<188;i++)
|
||||
{
|
||||
if (ctx->startbytes[i]==0x47 && ctx->startbytes[i+188]==0x47 &&
|
||||
ctx->startbytes[i+188*2]==0x47 && ctx->startbytes[i+188*3]==0x47 &&
|
||||
ctx->startbytes[i+188*4]==0x47 && ctx->startbytes[i+188*5]==0x47 &&
|
||||
ctx->startbytes[i+188*6]==0x47 && ctx->startbytes[i+188*7]==0x47
|
||||
)
|
||||
{
|
||||
// Eight sync bytes, that's good enough
|
||||
ctx->startbytes_pos=i;
|
||||
ctx->stream_mode=CCX_SM_TRANSPORT;
|
||||
ctx->m2ts = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (ctx->stream_mode == CCX_SM_TRANSPORT)
|
||||
{
|
||||
dbg_print(CCX_DMT_PARSE, "detect_stream_type: detected as TS\n");
|
||||
return_to_buffer (ctx, ctx->startbytes, (unsigned int)ctx->startbytes_avail);
|
||||
return;
|
||||
}
|
||||
|
||||
// Check for M2TS
|
||||
for (unsigned i = 0; i<192; i++)
|
||||
{
|
||||
if (ctx->startbytes[i+4] == 0x47 && ctx->startbytes[i + 4 + 192] == 0x47 &&
|
||||
ctx->startbytes[i + 192 * 2+4] == 0x47 && ctx->startbytes[i + 192 * 3+4] == 0x47 &&
|
||||
ctx->startbytes[i + 192 * 4+4] == 0x47 && ctx->startbytes[i + 192 * 5+4] == 0x47 &&
|
||||
ctx->startbytes[i + 192 * 6+4] == 0x47 && ctx->startbytes[i + 192 * 7+4] == 0x47
|
||||
)
|
||||
ctx->startbytes[i + 192 * 2+4] == 0x47 && ctx->startbytes[i + 192 * 3+4] == 0x47 &&
|
||||
ctx->startbytes[i + 192 * 4+4] == 0x47 && ctx->startbytes[i + 192 * 5+4] == 0x47 &&
|
||||
ctx->startbytes[i + 192 * 6+4] == 0x47 && ctx->startbytes[i + 192 * 7+4] == 0x47
|
||||
)
|
||||
{
|
||||
// Eight sync bytes, that's good enough
|
||||
ctx->startbytes_pos = i;
|
||||
@@ -93,138 +134,78 @@ void detect_stream_type (struct lib_ccx_ctx *ctx)
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Now check for PS (Needs PACK header)
|
||||
for (unsigned i=0;
|
||||
i < (unsigned) (ctx->startbytes_avail<50000?ctx->startbytes_avail-3:49997);
|
||||
i++)
|
||||
{
|
||||
if (ctx->startbytes[i]==0x00 && ctx->startbytes[i+1]==0x00 &&
|
||||
ctx->startbytes[i+2]==0x01 && ctx->startbytes[i+3]==0xBA)
|
||||
{
|
||||
// If we find a PACK header it is not an ES
|
||||
ctx->startbytes_pos=i;
|
||||
ctx->stream_mode=CCX_SM_PROGRAM;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// TiVo is also a PS
|
||||
if (ctx->startbytes[0]=='T' && ctx->startbytes[1]=='i' &&
|
||||
ctx->startbytes[2]=='V' && ctx->startbytes[3]=='o')
|
||||
{
|
||||
// The TiVo header is longer, but the PS loop will find the beginning
|
||||
ctx->startbytes_pos=187;
|
||||
ctx->stream_mode=CCX_SM_PROGRAM;
|
||||
strangeheader=1; // Avoid message about unrecognized header
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ctx->startbytes_pos=0;
|
||||
ctx->stream_mode=CCX_SM_ELEMENTARY_OR_NOT_FOUND;
|
||||
}
|
||||
}
|
||||
if ((ctx->stream_mode==CCX_SM_ELEMENTARY_OR_NOT_FOUND || ccx_options.print_file_reports)
|
||||
&& ctx->startbytes_avail>=4) // Still not found
|
||||
{
|
||||
// Try for MP4 by looking for box signatures - this should happen very
|
||||
// early in the file according to specs
|
||||
for (int i=0;i<ctx->startbytes_avail-3;i++)
|
||||
{
|
||||
// Look for the a box of type 'file'
|
||||
if (
|
||||
(ctx->startbytes[i]=='f' && ctx->startbytes[i+1]=='t' &&
|
||||
ctx->startbytes[i+2]=='y' && ctx->startbytes[i+3]=='p')
|
||||
||
|
||||
(ctx->startbytes[i]=='m' && ctx->startbytes[i+1]=='o' &&
|
||||
ctx->startbytes[i+2]=='o' && ctx->startbytes[i+3]=='v')
|
||||
||
|
||||
(ctx->startbytes[i]=='m' && ctx->startbytes[i+1]=='d' &&
|
||||
ctx->startbytes[i+2]=='a' && ctx->startbytes[i+3]=='t')
|
||||
||
|
||||
(ctx->startbytes[i]=='f' && ctx->startbytes[i+1]=='e' &&
|
||||
ctx->startbytes[i+2]=='e' && ctx->startbytes[i+3]=='e')
|
||||
||
|
||||
(ctx->startbytes[i]=='s' && ctx->startbytes[i+1]=='k' &&
|
||||
ctx->startbytes[i+2]=='i' && ctx->startbytes[i+3]=='p')
|
||||
||
|
||||
(ctx->startbytes[i]=='u' && ctx->startbytes[i+1]=='d' &&
|
||||
ctx->startbytes[i+2]=='t' && ctx->startbytes[i+3]=='a')
|
||||
||
|
||||
(ctx->startbytes[i]=='m' && ctx->startbytes[i+1]=='e' &&
|
||||
ctx->startbytes[i+2]=='t' && ctx->startbytes[i+3]=='a')
|
||||
||
|
||||
(ctx->startbytes[i]=='v' && ctx->startbytes[i+1]=='o' &&
|
||||
ctx->startbytes[i+2]=='i' && ctx->startbytes[i+3]=='d')
|
||||
)
|
||||
if (ctx->stream_mode == CCX_SM_TRANSPORT)
|
||||
{
|
||||
ctx->stream_mode=CCX_SM_MP4;
|
||||
break;
|
||||
dbg_print(CCX_DMT_PARSE, "detect_stream_type: detected as M2TS\n");
|
||||
return_to_buffer (ctx, ctx->startbytes, (unsigned int)ctx->startbytes_avail);
|
||||
return;
|
||||
}
|
||||
|
||||
// Now check for PS (Needs PACK header)
|
||||
for (unsigned i=0;
|
||||
i < (unsigned) (ctx->startbytes_avail<50000?ctx->startbytes_avail-3:49997);
|
||||
i++)
|
||||
{
|
||||
if (ctx->startbytes[i]==0x00 && ctx->startbytes[i+1]==0x00 &&
|
||||
ctx->startbytes[i+2]==0x01 && ctx->startbytes[i+3]==0xBA)
|
||||
{
|
||||
// If we find a PACK header it is not an ES
|
||||
ctx->startbytes_pos=i;
|
||||
ctx->stream_mode=CCX_SM_PROGRAM;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (ctx->stream_mode == CCX_SM_PROGRAM)
|
||||
{
|
||||
dbg_print(CCX_DMT_PARSE, "detect_stream_type: detected as PS\n");
|
||||
}
|
||||
|
||||
// TiVo is also a PS
|
||||
if (ctx->startbytes[0]=='T' && ctx->startbytes[1]=='i' &&
|
||||
ctx->startbytes[2]=='V' && ctx->startbytes[3]=='o')
|
||||
{
|
||||
// The TiVo header is longer, but the PS loop will find the beginning
|
||||
dbg_print(CCX_DMT_PARSE, "detect_stream_type: detected as Tivo PS\n");
|
||||
ctx->startbytes_pos = 187;
|
||||
ctx->stream_mode = CCX_SM_PROGRAM;
|
||||
ctx->strangeheader = 1; // Avoid message about unrecognized header
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ctx->startbytes_pos=0;
|
||||
ctx->stream_mode=CCX_SM_ELEMENTARY_OR_NOT_FOUND;
|
||||
}
|
||||
}
|
||||
// Don't use STARTBYTESLENGTH. It might be longer than the file length!
|
||||
return_to_buffer (ctx->startbytes, ctx->startbytes_avail);
|
||||
// Don't use STARTBYTESLENGTH. It might be longer than the file length!
|
||||
return_to_buffer (ctx, ctx->startbytes, ctx->startbytes_avail);
|
||||
}
|
||||
|
||||
|
||||
int detect_myth( struct lib_ccx_ctx *ctx )
|
||||
int detect_myth( struct ccx_demuxer *ctx )
|
||||
{
|
||||
int vbi_blocks=0;
|
||||
// VBI data? if yes, use myth loop
|
||||
// STARTBTYTESLENGTH is 1MB, if the file is shorter we will never detect
|
||||
// it as a mythTV file
|
||||
if (ctx->startbytes_avail==STARTBYTESLENGTH)
|
||||
{
|
||||
unsigned char uc[3];
|
||||
memcpy (uc,ctx->startbytes,3);
|
||||
for (int i=3;i<ctx->startbytes_avail;i++)
|
||||
{
|
||||
if (((uc[0]=='t') && (uc[1]=='v') && (uc[2] == '0')) ||
|
||||
((uc[0]=='T') && (uc[1]=='V') && (uc[2] == '0')))
|
||||
vbi_blocks++;
|
||||
uc[0]=uc[1];
|
||||
uc[1]=uc[2];
|
||||
uc[2]=ctx->startbytes[i];
|
||||
}
|
||||
}
|
||||
if (vbi_blocks>10) // Too much coincidence
|
||||
return 1;
|
||||
int vbi_blocks=0;
|
||||
// VBI data? if yes, use myth loop
|
||||
// STARTBTYTESLENGTH is 1MB, if the file is shorter we will never detect
|
||||
// it as a mythTV file
|
||||
if (ctx->startbytes_avail==STARTBYTESLENGTH)
|
||||
{
|
||||
unsigned char uc[3];
|
||||
memcpy (uc,ctx->startbytes,3);
|
||||
for (int i=3;i<ctx->startbytes_avail;i++)
|
||||
{
|
||||
if (((uc[0]=='t') && (uc[1]=='v') && (uc[2] == '0')) ||
|
||||
((uc[0]=='T') && (uc[1]=='V') && (uc[2] == '0')))
|
||||
vbi_blocks++;
|
||||
uc[0]=uc[1];
|
||||
uc[1]=uc[2];
|
||||
uc[2]=ctx->startbytes[i];
|
||||
}
|
||||
}
|
||||
if (vbi_blocks>10) // Too much coincidence
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
int read_pts_pes(unsigned char*header, int len)
|
||||
{
|
||||
/* unsigned int peslen = 0; */
|
||||
LLONG bits_9;
|
||||
unsigned int bits_10;
|
||||
unsigned int bits_11;
|
||||
unsigned int bits_12;
|
||||
unsigned int bits_13;
|
||||
|
||||
//function used only to set start time
|
||||
if(pts_set)
|
||||
return -1;
|
||||
//it might not be pes packet
|
||||
if (!(header[0] == 0 && header[1] == 0 && header[2] == 1))
|
||||
return -1;
|
||||
|
||||
|
||||
/* peslen = header[4] << 8 | header[5]; */
|
||||
|
||||
if (header[7] & 0x80)
|
||||
{
|
||||
bits_9 = ((LLONG) header[9] & 0x0E) << 29; // PTS 32..30 - Must be LLONG to prevent overflow
|
||||
bits_10 = header[10] << 22; // PTS 29..22
|
||||
bits_11 = (header[11] & 0xFE) << 14; // PTS 21..15
|
||||
bits_12 = header[12] << 7; // PTS 14-7
|
||||
bits_13 = header[13] >> 1; // PTS 6-0
|
||||
}
|
||||
else
|
||||
return -1;
|
||||
current_pts = bits_9 | bits_10 | bits_11 | bits_12 | bits_13;
|
||||
pts_set = 1;
|
||||
|
||||
return 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Read and evaluate the current video PES header. The function returns
|
||||
@@ -234,119 +215,144 @@ int read_pts_pes(unsigned char*header, int len)
|
||||
* 0 .. Read from file into nextheader
|
||||
* >0 .. Use data in nextheader with the length of sbuflen
|
||||
*/
|
||||
int read_video_pes_header (struct lib_ccx_ctx *ctx, unsigned char *nextheader, int *headerlength, int sbuflen)
|
||||
int read_video_pes_header (struct ccx_demuxer *ctx, struct demuxer_data *data, unsigned char *nextheader, int *headerlength, int sbuflen)
|
||||
{
|
||||
// Read the next video PES
|
||||
// ((nextheader[3]&0xf0)==0xe0)
|
||||
unsigned peslen=nextheader[4]<<8 | nextheader[5];
|
||||
unsigned payloadlength = 0; // Length of packet data bytes
|
||||
// Read the next video PES
|
||||
// ((nextheader[3]&0xf0)==0xe0)
|
||||
long long result;
|
||||
unsigned peslen=nextheader[4]<<8 | nextheader[5];
|
||||
unsigned payloadlength = 0; // Length of packet data bytes
|
||||
static LLONG current_pts_33=0; // Last PTS from the header, without rollover bits
|
||||
|
||||
if ( !sbuflen )
|
||||
{
|
||||
// Extension present, get it
|
||||
buffered_read (ctx, nextheader+6,3);
|
||||
ctx->past=ctx->past+result;
|
||||
if (result!=3) {
|
||||
// Consider this the end of the show.
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// We need at least 9 bytes to continue
|
||||
if( sbuflen < 9 )
|
||||
return -1;
|
||||
}
|
||||
*headerlength = 6+3;
|
||||
if ( !sbuflen )
|
||||
{
|
||||
// Extension present, get it
|
||||
result = buffered_read (ctx, nextheader+6,3);
|
||||
ctx->past=ctx->past+result;
|
||||
if (result!=3) {
|
||||
// Consider this the end of the show.
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (data->bufferdatatype == CCX_DVB_SUBTITLE
|
||||
&& peslen == 1 && nextheader[6] == 0xFF)
|
||||
{
|
||||
*headerlength = sbuflen;
|
||||
return 0;
|
||||
}
|
||||
if (sbuflen < 9) // We need at least 9 bytes to continue
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
*headerlength = 6+3;
|
||||
|
||||
unsigned hskip=0;
|
||||
unsigned hskip=0;
|
||||
|
||||
// Assume header[8] is right, but double check
|
||||
if ( !sbuflen )
|
||||
{
|
||||
if (nextheader[8] > 0) {
|
||||
buffered_read (ctx, nextheader+9,nextheader[8]);
|
||||
ctx->past=ctx->past+result;
|
||||
if (result!=nextheader[8]) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// See if the buffer is big enough
|
||||
if( sbuflen < *headerlength + (int)nextheader[8] )
|
||||
return -1;
|
||||
}
|
||||
*headerlength += (int) nextheader[8];
|
||||
int falsepes = 0;
|
||||
/* int pesext = 0; */
|
||||
// Assume header[8] is right, but double check
|
||||
if ( !sbuflen )
|
||||
{
|
||||
if (nextheader[8] > 0)
|
||||
{
|
||||
result = buffered_read (ctx, nextheader+9,nextheader[8]);
|
||||
ctx->past = ctx->past+result;
|
||||
if (result!=nextheader[8])
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// See if the buffer is big enough
|
||||
if( sbuflen < *headerlength + (int)nextheader[8] )
|
||||
return -1;
|
||||
}
|
||||
*headerlength += (int) nextheader[8];
|
||||
int falsepes = 0;
|
||||
/* int pesext = 0; */
|
||||
|
||||
// Avoid false positives, check --- not really needed
|
||||
if ( (nextheader[7]&0xC0) == 0x80 ) {
|
||||
// PTS only
|
||||
hskip += 5;
|
||||
if( (nextheader[9]&0xF1) != 0x21 || (nextheader[11]&0x01) != 0x01
|
||||
|| (nextheader[13]&0x01) != 0x01 ) {
|
||||
falsepes = 1;
|
||||
mprint("False PTS\n");
|
||||
}
|
||||
} else if ( (nextheader[7]&0xC0) == 0xC0 ) {
|
||||
// PTS and DTS
|
||||
hskip += 10;
|
||||
if( (nextheader[9]&0xF1) != 0x31 || (nextheader[11]&0x01) != 0x01
|
||||
|| (nextheader[13]&0x01) != 0x01
|
||||
|| (nextheader[14]&0xF1) != 0x11 || (nextheader[16]&0x01) != 0x01
|
||||
|| (nextheader[18]&0x01) != 0x01 ) {
|
||||
falsepes = 1;
|
||||
mprint("False PTS/DTS\n");
|
||||
}
|
||||
} else if ( (nextheader[7]&0xC0) == 0x40 ) {
|
||||
// Forbidden
|
||||
falsepes = 1;
|
||||
mprint("False PTS/DTS flag\n");
|
||||
}
|
||||
if ( !falsepes && nextheader[7]&0x20 ) { // ESCR
|
||||
if ((nextheader[9+hskip]&0xC4) != 0x04 || !(nextheader[11+hskip]&0x04)
|
||||
|| !(nextheader[13+hskip]&0x04) || !(nextheader[14+hskip]&0x01) ) {
|
||||
falsepes = 1;
|
||||
mprint("False ESCR\n");
|
||||
}
|
||||
hskip += 6;
|
||||
}
|
||||
if ( !falsepes && nextheader[7]&0x10 ) { // ES
|
||||
if ( !(nextheader[9+hskip]&0x80) || !(nextheader[11+hskip]&0x01) ) {
|
||||
mprint("False ES\n");
|
||||
falsepes = 1;
|
||||
}
|
||||
hskip += 3;
|
||||
}
|
||||
if ( !falsepes && nextheader[7]&0x04) { // add copy info
|
||||
if ( !(nextheader[9+hskip]&0x80) ) {
|
||||
mprint("False add copy info\n");
|
||||
falsepes = 1;
|
||||
}
|
||||
hskip += 1;
|
||||
}
|
||||
if ( !falsepes && nextheader[7]&0x02) { // PES CRC
|
||||
hskip += 2;
|
||||
}
|
||||
if ( !falsepes && nextheader[7]&0x01) { // PES extension
|
||||
if ( (nextheader[9+hskip]&0x0E)!=0x0E ) {
|
||||
mprint("False PES ext\n");
|
||||
falsepes = 1;
|
||||
}
|
||||
hskip += 1;
|
||||
/* pesext = 1; */
|
||||
}
|
||||
// Avoid false positives, check --- not really needed
|
||||
if ( (nextheader[7]&0xC0) == 0x80 )
|
||||
{
|
||||
// PTS only
|
||||
hskip += 5;
|
||||
if( (nextheader[9]&0xF1) != 0x21 || (nextheader[11]&0x01) != 0x01
|
||||
|| (nextheader[13]&0x01) != 0x01 )
|
||||
{
|
||||
falsepes = 1;
|
||||
mprint("False PTS\n");
|
||||
}
|
||||
}
|
||||
else if ( (nextheader[7]&0xC0) == 0xC0 )
|
||||
{
|
||||
// PTS and DTS
|
||||
hskip += 10;
|
||||
if( (nextheader[9]&0xF1) != 0x31 || (nextheader[11]&0x01) != 0x01
|
||||
|| (nextheader[13]&0x01) != 0x01
|
||||
|| (nextheader[14]&0xF1) != 0x11 || (nextheader[16]&0x01) != 0x01
|
||||
|| (nextheader[18]&0x01) != 0x01 ) {
|
||||
falsepes = 1;
|
||||
mprint("False PTS/DTS\n");
|
||||
}
|
||||
}
|
||||
else if ( (nextheader[7]&0xC0) == 0x40 )
|
||||
{
|
||||
// Forbidden
|
||||
falsepes = 1;
|
||||
mprint("False PTS/DTS flag\n");
|
||||
}
|
||||
if ( !falsepes && nextheader[7]&0x20 )
|
||||
{ // ESCR
|
||||
if ((nextheader[9+hskip]&0xC4) != 0x04 || !(nextheader[11+hskip]&0x04)
|
||||
|| !(nextheader[13+hskip]&0x04) || !(nextheader[14+hskip]&0x01) )
|
||||
{
|
||||
falsepes = 1;
|
||||
mprint("False ESCR\n");
|
||||
}
|
||||
hskip += 6;
|
||||
}
|
||||
if ( !falsepes && nextheader[7]&0x10 )
|
||||
{ // ES
|
||||
if ( !(nextheader[9+hskip]&0x80) || !(nextheader[11+hskip]&0x01) ) {
|
||||
mprint("False ES\n");
|
||||
falsepes = 1;
|
||||
}
|
||||
hskip += 3;
|
||||
}
|
||||
if ( !falsepes && nextheader[7]&0x04)
|
||||
{ // add copy info
|
||||
if ( !(nextheader[9+hskip]&0x80) ) {
|
||||
mprint("False add copy info\n");
|
||||
falsepes = 1;
|
||||
}
|
||||
hskip += 1;
|
||||
}
|
||||
if ( !falsepes && nextheader[7]&0x02)
|
||||
{ // PES CRC
|
||||
hskip += 2;
|
||||
}
|
||||
if ( !falsepes && nextheader[7]&0x01)
|
||||
{ // PES extension
|
||||
if ( (nextheader[9+hskip]&0x0E)!=0x0E )
|
||||
{
|
||||
mprint("False PES ext\n");
|
||||
falsepes = 1;
|
||||
}
|
||||
hskip += 1;
|
||||
/* pesext = 1; */
|
||||
}
|
||||
|
||||
if ( !falsepes ) {
|
||||
hskip = nextheader[8];
|
||||
}
|
||||
if ( !falsepes )
|
||||
{
|
||||
hskip = nextheader[8];
|
||||
}
|
||||
|
||||
if ( !falsepes && nextheader[7]&0x80 ) {
|
||||
// Read PTS from byte 9,10,11,12,13
|
||||
if ( !falsepes && nextheader[7]&0x80 )
|
||||
{
|
||||
// Read PTS from byte 9,10,11,12,13
|
||||
|
||||
LLONG bits_9 = ((LLONG) nextheader[9] & 0x0E)<<29; // PTS 32..30 - Must be LLONG to prevent overflow
|
||||
unsigned bits_10 = nextheader[10] << 22; // PTS 29..22
|
||||
@@ -354,7 +360,7 @@ int read_video_pes_header (struct lib_ccx_ctx *ctx, unsigned char *nextheader, i
|
||||
unsigned bits_12 = nextheader[12] << 7; // PTS 14-7
|
||||
unsigned bits_13 = nextheader[13] >> 1; // PTS 6-0
|
||||
|
||||
if (pts_set) // Otherwise can't check for rollovers yet
|
||||
if (data->pts != CCX_NOPTS) // Otherwise can't check for rollovers yet
|
||||
{
|
||||
if (!bits_9 && ((current_pts_33>>30)&7)==7) // PTS about to rollover
|
||||
rollover_bits++;
|
||||
@@ -364,33 +370,94 @@ int read_video_pes_header (struct lib_ccx_ctx *ctx, unsigned char *nextheader, i
|
||||
|
||||
|
||||
current_pts_33 = bits_9 | bits_10 | bits_11 | bits_12 | bits_13;
|
||||
current_pts = (LLONG) rollover_bits<<33 | current_pts_33;
|
||||
data->pts = (LLONG) rollover_bits<<33 | current_pts_33;
|
||||
|
||||
if (pts_set==0)
|
||||
pts_set=1;
|
||||
/* The user data holding the captions seems to come between GOP and
|
||||
* the first frame. The sync PTS (sync_pts) (set at picture 0)
|
||||
* corresponds to the first frame of the current GOP. */
|
||||
}
|
||||
|
||||
dbg_print(CCX_DMT_VERBOSE, "Set PTS: %s (%u)\n",
|
||||
print_mstime((current_pts)/(MPEG_CLOCK_FREQ/1000)),
|
||||
(unsigned) (current_pts) );
|
||||
/* The user data holding the captions seems to come between GOP and
|
||||
* the first frame. The sync PTS (sync_pts) (set at picture 0)
|
||||
* corresponds to the first frame of the current GOP. */
|
||||
}
|
||||
// This might happen in PES packets in TS stream. It seems that when the
|
||||
// packet length is unkown it is set to 0.
|
||||
if (peslen+6 >= hskip+9)
|
||||
{
|
||||
payloadlength = peslen - (hskip + 3); // for [6], [7] and [8]
|
||||
}
|
||||
|
||||
// This might happen in PES packets in TS stream. It seems that when the
|
||||
// packet length is unkown it is set to 0.
|
||||
if (peslen+6 >= hskip+9)
|
||||
{
|
||||
payloadlength = peslen - (hskip + 3); // for [6], [7] and [8]
|
||||
}
|
||||
// Use a save default if this is not a real PES header
|
||||
if (falsepes)
|
||||
{
|
||||
mprint("False PES header\n");
|
||||
}
|
||||
|
||||
// Use a save default if this is not a real PES header
|
||||
if (falsepes) {
|
||||
mprint("False PES header\n");
|
||||
}
|
||||
dbg_print(CCX_DMT_VERBOSE, "PES header length: %d (%d verified) Data length: %d\n",
|
||||
*headerlength, hskip+9, payloadlength);
|
||||
|
||||
dbg_print(CCX_DMT_VERBOSE, "PES header length: %d (%d verified) Data length: %d\n",
|
||||
*headerlength, hskip+9, payloadlength);
|
||||
|
||||
return payloadlength;
|
||||
return payloadlength;
|
||||
}
|
||||
|
||||
/*
|
||||
* Structure for a MP4 box type. Contains the name of the box type and the score we want to assign when a given box type is found.
|
||||
*/
|
||||
typedef struct ccx_stream_mp4_box
|
||||
{
|
||||
char boxType[5]; // Name of the box structure
|
||||
int score; // The weight of how heavy a box structure should be counted in order to "recognize" an MP4 file.
|
||||
} ccx_stream_mp4_box;
|
||||
|
||||
/* The box types below are taken from table 1 from ISO/IEC 14496-12_2012 (page 12 and further).
|
||||
* An asterisk (*) marks a mandatory box for a regular file.
|
||||
* Box types that are on the second level or deeper are omitted.
|
||||
*/
|
||||
ccx_stream_mp4_box ccx_stream_mp4_boxes[11] = {
|
||||
{ "ftyp", 6 }, // File type and compatibility*
|
||||
{ "pdin", 1 }, // Progressive download information
|
||||
{ "moov", 5 }, // Container for all metadata*
|
||||
{ "moof", 4 }, // Movie fragment
|
||||
{ "mfra", 3 }, // Movie fragment random access
|
||||
{ "mdat", 2 }, // Media data container
|
||||
{ "free", 1 }, // Free space
|
||||
{ "skip", 1 }, // Free space
|
||||
{ "meta", 1 }, // Metadata
|
||||
{ "wide", 1 }, // For boxes that are > 2^32 bytes (https://developer.apple.com/library/mac/documentation/QuickTime/QTFF/QTFFChap1/qtff1.html)
|
||||
{ "void", 1 } // Unknown where this is from/for, assume free space.
|
||||
};
|
||||
|
||||
/*
|
||||
* Checks if the buffer, beginning at the given position, contains a valid MP4 box (defined in the ccx_stream_mp4_boxes array).
|
||||
* If it does contain one, it will set the nextBoxLocation to position + current box size (this should be the place in the buffer
|
||||
* where the next box should be in case of an mp4) and increment the box score with the defined score for the found box.
|
||||
*
|
||||
* Returns 1 when a box is found, 0 when none is found.
|
||||
*/
|
||||
int isValidMP4Box(unsigned char *buffer, size_t position, size_t *nextBoxLocation, int *boxScore)
|
||||
{
|
||||
for (int idx = 0; idx < 11; idx++)
|
||||
{
|
||||
if (buffer[position + 4] == ccx_stream_mp4_boxes[idx].boxType[0] && buffer[position + 5] == ccx_stream_mp4_boxes[idx].boxType[1] &&
|
||||
buffer[position + 6] == ccx_stream_mp4_boxes[idx].boxType[2] && buffer[position + 7] == ccx_stream_mp4_boxes[idx].boxType[3]){
|
||||
mprint("Detected MP4 box with name: %s\n", ccx_stream_mp4_boxes[idx].boxType);
|
||||
// Box name matches. Do crude validation of possible box size, and if valid, add points for "valid" box
|
||||
size_t boxSize = buffer[position] << 24;
|
||||
boxSize |= buffer[position + 1] << 16;
|
||||
boxSize |= buffer[position + 2] << 8;
|
||||
boxSize |= buffer[position + 3];
|
||||
*boxScore += ccx_stream_mp4_boxes[idx].score;
|
||||
if (boxSize == 0 || boxSize == 1)
|
||||
{
|
||||
// If 0, runs to end of file. If 1, next field contains int_64 containing size, which is definitely bigger than the cache. Both times we can skip further checks by setting
|
||||
// nextBoxLocation to the max possible.
|
||||
*nextBoxLocation = UINT32_MAX;
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
// Set nextBoxLocation to current position + boxSize, as that will be the location of the next box.
|
||||
*nextBoxLocation = position + boxSize;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -7,247 +7,6 @@
|
||||
|
||||
//#include <inttypes.h>
|
||||
|
||||
typedef enum {
|
||||
LATIN = 0,
|
||||
CYRILLIC1,
|
||||
CYRILLIC2,
|
||||
CYRILLIC3,
|
||||
GREEK,
|
||||
ARABIC,
|
||||
HEBREW
|
||||
} g0_charsets_t;
|
||||
|
||||
// Note: All characters are encoded in UCS-2
|
||||
|
||||
// --- G0 ----------------------------------------------------------------------
|
||||
|
||||
// G0 charsets
|
||||
uint16_t G0[5][96] = {
|
||||
{ // Latin G0 Primary Set
|
||||
0x0020, 0x0021, 0x0022, 0x00a3, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f,
|
||||
0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f,
|
||||
0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f,
|
||||
0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005a, 0x00ab, 0x00bd, 0x00bb, 0x005e, 0x0023,
|
||||
0x002d, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f,
|
||||
0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007a, 0x00bc, 0x00a6, 0x00be, 0x00f7, 0x007f
|
||||
},
|
||||
{ // Cyrillic G0 Primary Set - Option 1 - Serbian/Croatian
|
||||
0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x044b, 0x0027, 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f,
|
||||
0x0030, 0x0031, 0x3200, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f,
|
||||
0x0427, 0x0410, 0x0411, 0x0426, 0x0414, 0x0415, 0x0424, 0x0413, 0x0425, 0x0418, 0x0408, 0x041a, 0x041b, 0x041c, 0x041d, 0x041e,
|
||||
0x041f, 0x040c, 0x0420, 0x0421, 0x0422, 0x0423, 0x0412, 0x0403, 0x0409, 0x040a, 0x0417, 0x040b, 0x0416, 0x0402, 0x0428, 0x040f,
|
||||
0x0447, 0x0430, 0x0431, 0x0446, 0x0434, 0x0435, 0x0444, 0x0433, 0x0445, 0x0438, 0x0428, 0x043a, 0x043b, 0x043c, 0x043d, 0x043e,
|
||||
0x043f, 0x042c, 0x0440, 0x0441, 0x0442, 0x0443, 0x0432, 0x0423, 0x0429, 0x042a, 0x0437, 0x042b, 0x0436, 0x0422, 0x0448, 0x042f
|
||||
},
|
||||
{ // Cyrillic G0 Primary Set - Option 2 - Russian/Bulgarian
|
||||
0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x044b, 0x0027, 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f,
|
||||
0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f,
|
||||
0x042e, 0x0410, 0x0411, 0x0426, 0x0414, 0x0415, 0x0424, 0x0413, 0x0425, 0x0418, 0x0419, 0x041a, 0x041b, 0x041c, 0x041d, 0x041e,
|
||||
0x041f, 0x042f, 0x0420, 0x0421, 0x0422, 0x0423, 0x0416, 0x0412, 0x042c, 0x042a, 0x0417, 0x0428, 0x042d, 0x0429, 0x0427, 0x042b,
|
||||
0x044e, 0x0430, 0x0431, 0x0446, 0x0434, 0x0435, 0x0444, 0x0433, 0x0445, 0x0438, 0x0439, 0x043a, 0x043b, 0x043c, 0x043d, 0x043e,
|
||||
0x043f, 0x044f, 0x0440, 0x0441, 0x0442, 0x0443, 0x0436, 0x0432, 0x044c, 0x044a, 0x0437, 0x0448, 0x044d, 0x0449, 0x0447, 0x044b
|
||||
},
|
||||
{ // Cyrillic G0 Primary Set - Option 3 - Ukrainian
|
||||
0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x00ef, 0x0027, 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f,
|
||||
0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f,
|
||||
0x042e, 0x0410, 0x0411, 0x0426, 0x0414, 0x0415, 0x0424, 0x0413, 0x0425, 0x0418, 0x0419, 0x041a, 0x041b, 0x041c, 0x041d, 0x041e,
|
||||
0x041f, 0x042f, 0x0420, 0x0421, 0x0422, 0x0423, 0x0416, 0x0412, 0x042c, 0x0049, 0x0417, 0x0428, 0x042d, 0x0429, 0x0427, 0x00cf,
|
||||
0x044e, 0x0430, 0x0431, 0x0446, 0x0434, 0x0435, 0x0444, 0x0433, 0x0445, 0x0438, 0x0439, 0x043a, 0x043b, 0x043c, 0x043d, 0x043e,
|
||||
0x043f, 0x044f, 0x0440, 0x0441, 0x0442, 0x0443, 0x0436, 0x0432, 0x044c, 0x0069, 0x0437, 0x0448, 0x044d, 0x0449, 0x0447, 0x00ff
|
||||
},
|
||||
{ // Greek G0 Primary Set
|
||||
0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f,
|
||||
0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f,
|
||||
0x0390, 0x0391, 0x0392, 0x0393, 0x0394, 0x0395, 0x0396, 0x0397, 0x0398, 0x0399, 0x039a, 0x039b, 0x039c, 0x039d, 0x039e, 0x039f,
|
||||
0x03a0, 0x03a1, 0x03a2, 0x03a3, 0x03a4, 0x03a5, 0x03a6, 0x03a7, 0x03a8, 0x03a9, 0x03aa, 0x03ab, 0x03ac, 0x03ad, 0x03ae, 0x03af,
|
||||
0x03b0, 0x03b1, 0x03b2, 0x03b3, 0x03b4, 0x03b5, 0x03b6, 0x03b7, 0x03b8, 0x03b9, 0x03ba, 0x03bb, 0x03bc, 0x03bd, 0x03be, 0x03bf,
|
||||
0x03c0, 0x03c1, 0x03c2, 0x03c3, 0x03c4, 0x03c5, 0x03c6, 0x03c7, 0x03c8, 0x03c9, 0x03ca, 0x03cb, 0x03cc, 0x03cd, 0x03ce, 0x03cf
|
||||
}
|
||||
//{ // Arabic G0 Primary Set
|
||||
//},
|
||||
//{ // Hebrew G0 Primary Set
|
||||
//}
|
||||
};
|
||||
|
||||
// array positions where chars from G0_LATIN_NATIONAL_SUBSETS are injected into G0[LATIN]
|
||||
const uint8_t G0_LATIN_NATIONAL_SUBSETS_POSITIONS[13] = {
|
||||
0x03, 0x04, 0x20, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x5b, 0x5c, 0x5d, 0x5e
|
||||
};
|
||||
|
||||
// ETS 300 706, chapter 15.2, table 32: Function of Default G0 and G2 Character Set Designation
|
||||
// and National Option Selection bits in packets X/28/0 Format 1, X/28/4, M/29/0 and M/29/4
|
||||
|
||||
// Latin National Option Sub-sets
|
||||
struct {
|
||||
const char *language;
|
||||
uint16_t characters[13];
|
||||
} const G0_LATIN_NATIONAL_SUBSETS[14] = {
|
||||
{ // 0
|
||||
"English",
|
||||
{ 0x00a3, 0x0024, 0x0040, 0x00ab, 0x00bd, 0x00bb, 0x005e, 0x0023, 0x002d, 0x00bc, 0x00a6, 0x00be, 0x00f7 }
|
||||
},
|
||||
{ // 1
|
||||
"French",
|
||||
{ 0x00e9, 0x00ef, 0x00e0, 0x00eb, 0x00ea, 0x00f9, 0x00ee, 0x0023, 0x00e8, 0x00e2, 0x00f4, 0x00fb, 0x00e7 }
|
||||
},
|
||||
{ // 2
|
||||
"Swedish, Finnish, Hungarian",
|
||||
{ 0x0023, 0x00a4, 0x00c9, 0x00c4, 0x00d6, 0x00c5, 0x00dc, 0x005f, 0x00e9, 0x00e4, 0x00f6, 0x00e5, 0x00fc }
|
||||
},
|
||||
{ // 3
|
||||
"Czech, Slovak",
|
||||
{ 0x0023, 0x016f, 0x010d, 0x0165, 0x017e, 0x00fd, 0x00ed, 0x0159, 0x00e9, 0x00e1, 0x011b, 0x00fa, 0x0161 }
|
||||
},
|
||||
{ // 4
|
||||
"German",
|
||||
{ 0x0023, 0x0024, 0x00a7, 0x00c4, 0x00d6, 0x00dc, 0x005e, 0x005f, 0x00b0, 0x00e4, 0x00f6, 0x00fc, 0x00df }
|
||||
},
|
||||
{ // 5
|
||||
"Portuguese, Spanish",
|
||||
{ 0x00e7, 0x0024, 0x00a1, 0x00e1, 0x00e9, 0x00ed, 0x00f3, 0x00fa, 0x00bf, 0x00fc, 0x00f1, 0x00e8, 0x00e0 }
|
||||
},
|
||||
{ // 6
|
||||
"Italian",
|
||||
{ 0x00a3, 0x0024, 0x00e9, 0x00b0, 0x00e7, 0x00bb, 0x005e, 0x0023, 0x00f9, 0x00e0, 0x00f2, 0x00e8, 0x00ec }
|
||||
},
|
||||
{ // 7
|
||||
"Rumanian",
|
||||
{ 0x0023, 0x00a4, 0x0162, 0x00c2, 0x015e, 0x0102, 0x00ce, 0x0131, 0x0163, 0x00e2, 0x015f, 0x0103, 0x00ee }
|
||||
},
|
||||
{ // 8
|
||||
"Polish",
|
||||
{ 0x0023, 0x0144, 0x0105, 0x017b, 0x015a, 0x0141, 0x0107, 0x00f3, 0x0119, 0x017c, 0x015b, 0x0142, 0x017a }
|
||||
},
|
||||
{ // 9
|
||||
"Turkish",
|
||||
{ 0x0054, 0x011f, 0x0130, 0x015e, 0x00d6, 0x00c7, 0x00dc, 0x011e, 0x0131, 0x015f, 0x00f6, 0x00e7, 0x00fc }
|
||||
},
|
||||
{ // a
|
||||
"Serbian, Croatian, Slovenian",
|
||||
{ 0x0023, 0x00cb, 0x010c, 0x0106, 0x017d, 0x0110, 0x0160, 0x00eb, 0x010d, 0x0107, 0x017e, 0x0111, 0x0161 }
|
||||
},
|
||||
{ // b
|
||||
"Estonian",
|
||||
{ 0x0023, 0x00f5, 0x0160, 0x00c4, 0x00d6, 0x017e, 0x00dc, 0x00d5, 0x0161, 0x00e4, 0x00f6, 0x017e, 0x00fc }
|
||||
},
|
||||
{ // c
|
||||
"Lettish, Lithuanian",
|
||||
{ 0x0023, 0x0024, 0x0160, 0x0117, 0x0119, 0x017d, 0x010d, 0x016b, 0x0161, 0x0105, 0x0173, 0x017e, 0x012f }
|
||||
}
|
||||
};
|
||||
|
||||
// References to the G0_LATIN_NATIONAL_SUBSETS array
|
||||
const uint8_t G0_LATIN_NATIONAL_SUBSETS_MAP[56] = {
|
||||
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
|
||||
0x08, 0x01, 0x02, 0x03, 0x04, 0xff, 0x06, 0xff,
|
||||
0x00, 0x01, 0x02, 0x09, 0x04, 0x05, 0x06, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0x0a, 0xff, 0x07,
|
||||
0xff, 0xff, 0x0b, 0x03, 0x04, 0xff, 0x0c, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0x09, 0xff, 0xff, 0xff, 0xff
|
||||
};
|
||||
|
||||
// --- G2 ----------------------------------------------------------------------
|
||||
|
||||
const uint16_t G2[1][96] = {
|
||||
{ // Latin G2 Supplementary Set
|
||||
0x0020, 0x00a1, 0x00a2, 0x00a3, 0x0024, 0x00a5, 0x0023, 0x00a7, 0x00a4, 0x2018, 0x201c, 0x00ab, 0x2190, 0x2191, 0x2192, 0x2193,
|
||||
0x00b0, 0x00b1, 0x00b2, 0x00b3, 0x00d7, 0x00b5, 0x00b6, 0x00b7, 0x00f7, 0x2019, 0x201d, 0x00bb, 0x00bc, 0x00bd, 0x00be, 0x00bf,
|
||||
0x0020, 0x0300, 0x0301, 0x0302, 0x0303, 0x0304, 0x0306, 0x0307, 0x0308, 0x0000, 0x030a, 0x0327, 0x005f, 0x030b, 0x0328, 0x030c,
|
||||
0x2015, 0x00b9, 0x00ae, 0x00a9, 0x2122, 0x266a, 0x20ac, 0x2030, 0x03B1, 0x0000, 0x0000, 0x0000, 0x215b, 0x215c, 0x215d, 0x215e,
|
||||
0x03a9, 0x00c6, 0x0110, 0x00aa, 0x0126, 0x0000, 0x0132, 0x013f, 0x0141, 0x00d8, 0x0152, 0x00ba, 0x00de, 0x0166, 0x014a, 0x0149,
|
||||
0x0138, 0x00e6, 0x0111, 0x00f0, 0x0127, 0x0131, 0x0133, 0x0140, 0x0142, 0x00f8, 0x0153, 0x00df, 0x00fe, 0x0167, 0x014b, 0x0020
|
||||
}
|
||||
// { // Cyrillic G2 Supplementary Set
|
||||
// },
|
||||
// { // Greek G2 Supplementary Set
|
||||
// },
|
||||
// { // Arabic G2 Supplementary Set
|
||||
// }
|
||||
};
|
||||
|
||||
const uint16_t G2_ACCENTS[15][52] = {
|
||||
// A B C D E F G H I J K L M N O P Q R S T U V W X Y Z a b c d e f g h i j k l m n o p q r s t u v w x y z
|
||||
{ // grave
|
||||
0x00c0, 0x0000, 0x0000, 0x0000, 0x00c8, 0x0000, 0x0000, 0x0000, 0x00cc, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x00d2, 0x0000,
|
||||
0x0000, 0x0000, 0x0000, 0x0000, 0x00d9, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x00e0, 0x0000, 0x0000, 0x0000, 0x00e8, 0x0000,
|
||||
0x0000, 0x0000, 0x00ec, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x00f2, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x00f9, 0x0000,
|
||||
0x0000, 0x0000, 0x0000, 0x0000
|
||||
},
|
||||
{ // acute
|
||||
0x00c1, 0x0000, 0x0106, 0x0000, 0x00c9, 0x0000, 0x0000, 0x0000, 0x00cd, 0x0000, 0x0000, 0x0139, 0x0000, 0x0143, 0x00d3, 0x0000,
|
||||
0x0000, 0x0154, 0x015a, 0x0000, 0x00da, 0x0000, 0x0000, 0x0000, 0x00dd, 0x0179, 0x00e1, 0x0000, 0x0107, 0x0000, 0x00e9, 0x0000,
|
||||
0x0123, 0x0000, 0x00ed, 0x0000, 0x0000, 0x013a, 0x0000, 0x0144, 0x00f3, 0x0000, 0x0000, 0x0155, 0x015b, 0x0000, 0x00fa, 0x0000,
|
||||
0x0000, 0x0000, 0x00fd, 0x017a
|
||||
},
|
||||
{ // circumflex
|
||||
0x00c2, 0x0000, 0x0108, 0x0000, 0x00ca, 0x0000, 0x011c, 0x0124, 0x00ce, 0x0134, 0x0000, 0x0000, 0x0000, 0x0000, 0x00d4, 0x0000,
|
||||
0x0000, 0x0000, 0x015c, 0x0000, 0x00db, 0x0000, 0x0174, 0x0000, 0x0176, 0x0000, 0x00e2, 0x0000, 0x0109, 0x0000, 0x00ea, 0x0000,
|
||||
0x011d, 0x0125, 0x00ee, 0x0135, 0x0000, 0x0000, 0x0000, 0x0000, 0x00f4, 0x0000, 0x0000, 0x0000, 0x015d, 0x0000, 0x00fb, 0x0000,
|
||||
0x0175, 0x0000, 0x0177, 0x0000
|
||||
},
|
||||
{ // tilde
|
||||
0x00c3, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0128, 0x0000, 0x0000, 0x0000, 0x0000, 0x00d1, 0x00d5, 0x0000,
|
||||
0x0000, 0x0000, 0x0000, 0x0000, 0x0168, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x00e3, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||
0x0000, 0x0000, 0x0129, 0x0000, 0x0000, 0x0000, 0x0000, 0x00f1, 0x00f5, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0169, 0x0000,
|
||||
0x0000, 0x0000, 0x0000, 0x0000
|
||||
},
|
||||
{ // macron
|
||||
0x0100, 0x0000, 0x0000, 0x0000, 0x0112, 0x0000, 0x0000, 0x0000, 0x012a, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x014c, 0x0000,
|
||||
0x0000, 0x0000, 0x0000, 0x0000, 0x016a, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0101, 0x0000, 0x0000, 0x0000, 0x0113, 0x0000,
|
||||
0x0000, 0x0000, 0x012b, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x014d, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x016b, 0x0000,
|
||||
0x0000, 0x0000, 0x0000, 0x0000
|
||||
},
|
||||
{ // breve
|
||||
0x0102, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x011e, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||
0x0000, 0x0000, 0x0000, 0x0000, 0x016c, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0103, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||
0x011f, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x016d, 0x0000,
|
||||
0x0000, 0x0000, 0x0000, 0x0000
|
||||
},
|
||||
{ // dot
|
||||
0x0000, 0x0000, 0x010a, 0x0000, 0x0116, 0x0000, 0x0120, 0x0000, 0x0130, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x017b, 0x0000, 0x0000, 0x010b, 0x0000, 0x0117, 0x0000,
|
||||
0x0121, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||
0x0000, 0x0000, 0x0000, 0x017c
|
||||
},
|
||||
{ // umlaut
|
||||
0x00c4, 0x0000, 0x0000, 0x0000, 0x00cb, 0x0000, 0x0000, 0x0000, 0x00cf, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x00d6, 0x0000,
|
||||
0x0000, 0x0000, 0x0000, 0x0000, 0x00dc, 0x0000, 0x0000, 0x0000, 0x0178, 0x0000, 0x00e4, 0x0000, 0x0000, 0x0000, 0x00eb, 0x0000,
|
||||
0x0000, 0x0000, 0x00ef, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x00f6, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x00fc, 0x0000,
|
||||
0x0000, 0x0000, 0x00ff, 0x0000
|
||||
},
|
||||
{ 0 },
|
||||
{ // ring
|
||||
0x00c5, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||
0x0000, 0x0000, 0x0000, 0x0000, 0x016e, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x00e5, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x016f, 0x0000,
|
||||
0x0000, 0x0000, 0x0000, 0x0000
|
||||
},
|
||||
{ // cedilla
|
||||
0x0000, 0x0000, 0x00c7, 0x0000, 0x0000, 0x0000, 0x0122, 0x0000, 0x0000, 0x0000, 0x0136, 0x013b, 0x0000, 0x0145, 0x0000, 0x0000,
|
||||
0x0000, 0x0156, 0x015e, 0x0162, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x00e7, 0x0000, 0x0000, 0x0000,
|
||||
0x0000, 0x0000, 0x0000, 0x0000, 0x0137, 0x013c, 0x0000, 0x0146, 0x0000, 0x0000, 0x0000, 0x0157, 0x015f, 0x0163, 0x0000, 0x0000,
|
||||
0x0000, 0x0000, 0x0000, 0x0000
|
||||
},
|
||||
{ 0 },
|
||||
{ // double acute
|
||||
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0150, 0x0000,
|
||||
0x0000, 0x0000, 0x0000, 0x0000, 0x0170, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0151, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0171, 0x0000,
|
||||
0x0000, 0x0000, 0x0000, 0x0000
|
||||
},
|
||||
{ // ogonek
|
||||
0x0104, 0x0000, 0x0000, 0x0000, 0x0118, 0x0000, 0x0000, 0x0000, 0x012e, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||
0x0000, 0x0000, 0x0000, 0x0000, 0x0172, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0105, 0x0000, 0x0000, 0x0000, 0x0119, 0x0000,
|
||||
0x0000, 0x0000, 0x012f, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0173, 0x0000,
|
||||
0x0000, 0x0000, 0x0000, 0x0000
|
||||
},
|
||||
{ // caron
|
||||
0x0000, 0x0000, 0x010c, 0x010e, 0x011a, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x013d, 0x0000, 0x0147, 0x0000, 0x0000,
|
||||
0x0000, 0x0158, 0x0160, 0x0164, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x017d, 0x0000, 0x0000, 0x010d, 0x010f, 0x011b, 0x0000,
|
||||
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x013e, 0x0000, 0x0148, 0x0000, 0x0000, 0x0000, 0x0159, 0x0161, 0x0165, 0x0000, 0x0000,
|
||||
0x0000, 0x0000, 0x0000, 0x017e
|
||||
}
|
||||
};
|
||||
|
||||
int tlt_print_seen_pages(struct lib_cc_decode *dec_ctx);
|
||||
#endif
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user