mirror of
https://github.com/CCExtractor/ccextractor.git
synced 2026-02-10 05:31:32 +00:00
Compare commits
474 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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,25 @@
|
||||
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
|
||||
|
||||
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.78
|
||||
-----------------
|
||||
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,45 @@ 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_OK)
|
||||
{
|
||||
fatal(EXIT_TOO_MANY_INPUT_FILES, "Live stream mode accepts only one input file.\n");
|
||||
}
|
||||
if (ctx->num_input_files && ccx_options.input_source==CCX_DS_NETWORK)
|
||||
{
|
||||
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 +68,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 +77,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 +93,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 +123,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 +137,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 +172,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 +205,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 +225,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 +265,68 @@ 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)));
|
||||
|
||||
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 +335,17 @@ 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);
|
||||
#endif
|
||||
dbg_print(CCX_DMT_708, "[CEA-708] The 708 decoder was reset [%d] times.\n", ctx->freport.data_from_708->reset_count);
|
||||
/*
|
||||
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);
|
||||
|
||||
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,40 @@
|
||||
#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_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 +43,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,17 @@ 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,
|
||||
};
|
||||
|
||||
enum ccx_output_date_format
|
||||
@@ -174,47 +178,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 +235,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 +257,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 +278,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,20 @@
|
||||
#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_bom = 0; // Use BOM by default.
|
||||
|
||||
options->settings_608.direct_rollup = 0;
|
||||
options->settings_608.no_rollup = 0;
|
||||
@@ -24,22 +22,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 +49,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 +71,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,9 +4,72 @@
|
||||
#include "ccx_common_timing.h"
|
||||
#include "ccx_decoders_608.h"
|
||||
#include "ccx_encoders_structs.h"
|
||||
#include "list.h"
|
||||
|
||||
struct demuxer_cfg
|
||||
{
|
||||
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 cc_channel; // Channel we want to dump in srt mode
|
||||
int buffer_input;
|
||||
int nofontcolor;
|
||||
@@ -14,26 +77,16 @@ struct ccx_s_options // Options from user parameters
|
||||
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.
|
||||
ccx_decoder_dtvcc_settings settings_dtvcc; //Same for 708 decoder
|
||||
|
||||
/* subtitle codec type */
|
||||
enum ccx_code_type codec;
|
||||
enum ccx_code_type nocodec;
|
||||
/* 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;
|
||||
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 +97,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 +105,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,162 @@ 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->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_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,21 @@ 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;
|
||||
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 +112,75 @@ 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 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
|
||||
@@ -67,17 +129,28 @@ void dinit_encoder(struct encoder_ctx *ctx);
|
||||
int encode_sub(struct encoder_ctx *ctx,struct cc_subtitle *sub);
|
||||
|
||||
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 +158,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
|
||||
|
||||
@@ -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 (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,19 @@ 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;
|
||||
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 +39,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 +119,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.78"
|
||||
|
||||
// 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,27 @@ 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;
|
||||
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 +76,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 +86,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 +112,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 +122,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 +184,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, long position, long *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 +235,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,128 @@
|
||||
#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
|
||||
{
|
||||
long idx = 0, nextBoxLocation = 0, lastBoxLocation = 0;
|
||||
int boxScore = 0;
|
||||
// Scan the buffer for valid succeeding MP4 boxes.
|
||||
while (idx < ctx->startbytes_avail - 7){
|
||||
lastBoxLocation = idx;
|
||||
// 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 +135,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 +216,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 +361,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 +371,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, long position, long *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
|
||||
long 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
|
||||
|
||||
1541
src/lib_ccx/telxcc.c
1541
src/lib_ccx/telxcc.c
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user