mirror of
https://github.com/CCExtractor/ccextractor.git
synced 2026-02-12 21:22:41 +00:00
Compare commits
329 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4f3e5fe677 | ||
|
|
ad99ccdfec | ||
|
|
1bc174efa2 | ||
|
|
983db8c92d | ||
|
|
b4ce115f7c | ||
|
|
cb7f31ef8c | ||
|
|
1a5d78b1b4 | ||
|
|
c873ff6a06 | ||
|
|
454024808d | ||
|
|
0a22e1e429 | ||
|
|
3fb288357d | ||
|
|
966d88fc10 | ||
|
|
c91aa79852 | ||
|
|
04ccb477f1 | ||
|
|
10ea570229 | ||
|
|
551c3d0e2f | ||
|
|
49530b079b | ||
|
|
02986fe7a8 | ||
|
|
a230ec5f6f | ||
|
|
7902b85d1b | ||
|
|
77d275d6a5 | ||
|
|
4c2a4a59a2 | ||
|
|
d80455a408 | ||
|
|
27e2bb9017 | ||
|
|
9358978a61 | ||
|
|
cffd02c106 | ||
|
|
b45370cbb4 | ||
|
|
56e15a3be0 | ||
|
|
7b4d93988f | ||
|
|
0e803eba95 | ||
|
|
8c9bfa17dd | ||
|
|
413173f5e5 | ||
|
|
1a050c76a5 | ||
|
|
c3d00d80f5 | ||
|
|
c989c941df | ||
|
|
dc9f6d250a | ||
|
|
9f00cec9c0 | ||
|
|
b7d2754518 | ||
|
|
94675b2dac | ||
|
|
2c9faa70ea | ||
|
|
b7616fcb17 | ||
|
|
eb93345544 | ||
|
|
e6024c1cb1 | ||
|
|
ecc4c2520e | ||
|
|
a83c686f6a | ||
|
|
c4a8135b4d | ||
|
|
004f9d512a | ||
|
|
53df68b4c3 | ||
|
|
74bb91aa5b | ||
|
|
453bb56520 | ||
|
|
db50730a00 | ||
|
|
36a266e43c | ||
|
|
354c52ec61 | ||
|
|
2b65419bfb | ||
|
|
740365db7e | ||
|
|
03dc0498ea | ||
|
|
e9a7474f3d | ||
|
|
1ecb2abd62 | ||
|
|
d3b4b8ff03 | ||
|
|
29aa63c23c | ||
|
|
7083a36ecb | ||
|
|
38421e886e | ||
|
|
b0de53c351 | ||
|
|
09dcb8b7c0 | ||
|
|
13ab9e6a4e | ||
|
|
08ae34bc24 | ||
|
|
f5f1a70eb8 | ||
|
|
471c130928 | ||
|
|
4db37e7ac3 | ||
|
|
3953f806b0 | ||
|
|
cf0ebd27f7 | ||
|
|
559d05c478 | ||
|
|
4d7e629ba5 | ||
|
|
866c4ea159 | ||
|
|
cf508653f2 | ||
|
|
b3b683aa83 | ||
|
|
9f6309ef14 | ||
|
|
b95e06c21c | ||
|
|
7ed99097ba | ||
|
|
3f4bbbde25 | ||
|
|
5828f50210 | ||
|
|
b5931e8749 | ||
|
|
c588b42e0a | ||
|
|
f9ee9570a4 | ||
|
|
55e408bbb7 | ||
|
|
93a5ae9728 | ||
|
|
5c7430cff5 | ||
|
|
fecf14bc15 | ||
|
|
a4275eba62 | ||
|
|
3873e8fd30 | ||
|
|
5e22be2576 | ||
|
|
0b568cb168 | ||
|
|
017d539710 | ||
|
|
06c8f69056 | ||
|
|
b06654b760 | ||
|
|
4ff49c2010 | ||
|
|
0c650711cf | ||
|
|
3128f10fb5 | ||
|
|
63b209929e | ||
|
|
5f68a9166f | ||
|
|
15616edea8 | ||
|
|
cce4ec5e1a | ||
|
|
72ecd279cf | ||
|
|
87f1a3845c | ||
|
|
4ae5f3483c | ||
|
|
38876690f8 | ||
|
|
7bb4e842dd | ||
|
|
26d9584a93 | ||
|
|
9d5ce9aaae | ||
|
|
47264425df | ||
|
|
e8f8d04369 | ||
|
|
78cb26c9cb | ||
|
|
2f5d45df01 | ||
|
|
53be44dfdb | ||
|
|
48b5a0b384 | ||
|
|
a08c7b1871 | ||
|
|
0a2a00f883 | ||
|
|
2125e58e1f | ||
|
|
5bdd6971f7 | ||
|
|
051bc7138d | ||
|
|
e6dca329ee | ||
|
|
6cfddb12a6 | ||
|
|
c9c063b8d8 | ||
|
|
e0cd8b2e56 | ||
|
|
38d2088db5 | ||
|
|
8e940b050a | ||
|
|
5733b40ca6 | ||
|
|
22c40675a6 | ||
|
|
a3ef46c21d | ||
|
|
93a546bab4 | ||
|
|
ec427fd82c | ||
|
|
70cc3c2046 | ||
|
|
5634960813 | ||
|
|
c7a49e80e3 | ||
|
|
617d2d30dc | ||
|
|
18f781d099 | ||
|
|
84db812769 | ||
|
|
7763f8aeab | ||
|
|
faa879801e | ||
|
|
4635329a5b | ||
|
|
b89cc3b6df | ||
|
|
e9f8313f7c | ||
|
|
324cd84ffe | ||
|
|
9d7518c9ec | ||
|
|
32e0d6023d | ||
|
|
c9465e476b | ||
|
|
45d237da40 | ||
|
|
631ae2e02f | ||
|
|
695aa14cd7 | ||
|
|
b638bb1d3a | ||
|
|
28950bf90e | ||
|
|
77f8289ca6 | ||
|
|
cece92a828 | ||
|
|
bd08454d66 | ||
|
|
c6e5dd5cf7 | ||
|
|
bd8e3ad137 | ||
|
|
8a2e71fcfb | ||
|
|
301c2a7138 | ||
|
|
5368d7292f | ||
|
|
29689b10aa | ||
|
|
4d8883d0f1 | ||
|
|
efe6fceb3a | ||
|
|
d9a998b95f | ||
|
|
bf94c2997d | ||
|
|
6405a3973d | ||
|
|
9270e22a65 | ||
|
|
02e19cf617 | ||
|
|
1493b6c4f5 | ||
|
|
74ab732999 | ||
|
|
5961d1b56e | ||
|
|
87d6a6bf91 | ||
|
|
456d23f547 | ||
|
|
02da93d0e4 | ||
|
|
597304e932 | ||
|
|
918889c890 | ||
|
|
47bfe6bffc | ||
|
|
5789a0a224 | ||
|
|
b15f54c7f9 | ||
|
|
bc3794eb14 | ||
|
|
4e66d290a6 | ||
|
|
7b48b01fd1 | ||
|
|
883dfaae4a | ||
|
|
884a25a7cd | ||
|
|
8cc8767634 | ||
|
|
90a6902ad4 | ||
|
|
b334acb426 | ||
|
|
9c847aa4f1 | ||
|
|
c3397d1949 | ||
|
|
41ad0b4006 | ||
|
|
7fa59a14f7 | ||
|
|
170a57b9c6 | ||
|
|
0a15c2d16e | ||
|
|
c52283676a | ||
|
|
ce3c2b5ae2 | ||
|
|
48e030d94c | ||
|
|
afaa7c8b76 | ||
|
|
4e6c0352b0 | ||
|
|
f84b69aca7 | ||
|
|
cc7ebbc4d7 | ||
|
|
7a74b815fa | ||
|
|
331038634d | ||
|
|
aa2c316093 | ||
|
|
3347f5d345 | ||
|
|
141792a299 | ||
|
|
183e613981 | ||
|
|
19e2912a71 | ||
|
|
dc82154a03 | ||
|
|
2029dab3bf | ||
|
|
405d808d0b | ||
|
|
783e889634 | ||
|
|
a32c8a3b7f | ||
|
|
5618df35c6 | ||
|
|
c054594383 | ||
|
|
c58f99378f | ||
|
|
45d1dfe425 | ||
|
|
a5dcf9242d | ||
|
|
ecc0714f48 | ||
|
|
26410b991d | ||
|
|
19b1bff7f4 | ||
|
|
bc1aff78d7 | ||
|
|
2df8931d00 | ||
|
|
b765198d53 | ||
|
|
22f620400a | ||
|
|
a68add78d0 | ||
|
|
14d866e8a2 | ||
|
|
335bca4507 | ||
|
|
f31425e9f0 | ||
|
|
3f54fab5f4 | ||
|
|
59c62d48b3 | ||
|
|
a926766478 | ||
|
|
cc233c61f2 | ||
|
|
e9fcdf392f | ||
|
|
c42f039297 | ||
|
|
8cbaf09b4f | ||
|
|
ee8d5dff66 | ||
|
|
ec0873f5a2 | ||
|
|
29515bfcd5 | ||
|
|
c85ebb0c1e | ||
|
|
d6e66d5c7f | ||
|
|
f65f2b229a | ||
|
|
5094351469 | ||
|
|
5d6a770ce8 | ||
|
|
010d7f9ce4 | ||
|
|
fb49e680a6 | ||
|
|
6b98856892 | ||
|
|
1a479a7199 | ||
|
|
2894bcd7ff | ||
|
|
1ae8040bd7 | ||
|
|
57a80084ce | ||
|
|
e35b4ea62c | ||
|
|
3546526177 | ||
|
|
12efb6166a | ||
|
|
9beee068ce | ||
|
|
9efcba5c02 | ||
|
|
947333ea64 | ||
|
|
7e527fc62a | ||
|
|
9f78c595b3 | ||
|
|
fa03442c85 | ||
|
|
e664c035a1 | ||
|
|
c522e3b054 | ||
|
|
b3c037ab21 | ||
|
|
097fdb643b | ||
|
|
43094a9897 | ||
|
|
aefa623cd4 | ||
|
|
1be11738eb | ||
|
|
5a09c116f6 | ||
|
|
bb700db08e | ||
|
|
2e8582b14c | ||
|
|
8d47dc2f82 | ||
|
|
6a3c736195 | ||
|
|
9463999325 | ||
|
|
e50c30eaaa | ||
|
|
45a3e21897 | ||
|
|
47dbcdff9c | ||
|
|
dc164f81e5 | ||
|
|
bab0ec8b60 | ||
|
|
f543e3f4e2 | ||
|
|
43d4b9abaa | ||
|
|
f5bbb6aef5 | ||
|
|
25b666c2ae | ||
|
|
78087073ac | ||
|
|
12c2ced497 | ||
|
|
87954419ba | ||
|
|
0add0bc2e9 | ||
|
|
7858afb837 | ||
|
|
69c0b2e223 | ||
|
|
855ca48220 | ||
|
|
02bee86397 | ||
|
|
315d466da8 | ||
|
|
d4597e0094 | ||
|
|
b8ff3414c0 | ||
|
|
2206cc1f78 | ||
|
|
00433467be | ||
|
|
1000e33e7f | ||
|
|
a64245fb5b | ||
|
|
2f784f00ff | ||
|
|
136012c558 | ||
|
|
874abf6442 | ||
|
|
20975957ee | ||
|
|
1fabae2870 | ||
|
|
abb3523ee3 | ||
|
|
ec63a06e25 | ||
|
|
8b4353f08c | ||
|
|
8bfb3b87c3 | ||
|
|
f1a493b999 | ||
|
|
3afcfa79e1 | ||
|
|
adaa436f6b | ||
|
|
37135762c5 | ||
|
|
e6aa7128df | ||
|
|
da8d4a290c | ||
|
|
4d11a727d9 | ||
|
|
3c232bfca0 | ||
|
|
b3fe96477a | ||
|
|
5f87544a0d | ||
|
|
f0c0ca4566 | ||
|
|
23719526cd | ||
|
|
67ffe22460 | ||
|
|
08996a3253 | ||
|
|
95e2f7665b | ||
|
|
a5ef2d1574 | ||
|
|
cc0a786004 | ||
|
|
4d6693d948 | ||
|
|
9c0792c5a0 | ||
|
|
0f06f47ab4 | ||
|
|
5371f854fc | ||
|
|
bd49f0f959 | ||
|
|
63d3071233 | ||
|
|
015a50bafd | ||
|
|
1ee4b9bd01 |
@@ -4,7 +4,7 @@ MAINTAINER = Marc Espie <espie@openbsd.org>
|
||||
CATEGORIES = multimedia
|
||||
COMMENT = closed caption subtitles extractor
|
||||
HOMEPAGE = http://ccextractor.sourceforge.net/
|
||||
V = 0.70
|
||||
V = 0.75
|
||||
DISTFILES = ccextractor.${V:S/.//}-src.zip
|
||||
MASTER_SITES = ${MASTER_SITE_SOURCEFORGE:=ccextractor/}
|
||||
DISTNAME = ccextractor-$V
|
||||
|
||||
23
README.md
23
README.md
@@ -1,21 +1,12 @@
|
||||
ccextractor
|
||||
===========
|
||||
|
||||
CCExtractor - Carlos' version (mainstream).
|
||||
Carlos' version (mainstream) is the most stable branch.
|
||||
|
||||
0.70 - GSOC
|
||||
-----------
|
||||
This is the first release that is part of Google's Summer of Code.
|
||||
Anshul, Ruslan and Willem joined CCExtractor to work on a number of things
|
||||
over the summer, and their work is already reaching the mainstream
|
||||
version of CCExtractor.
|
||||
Extracting subtitles has never been so easy. Just type the following command:
|
||||
ccextrator "name of input"
|
||||
|
||||
- Added a huge dictionary submitted by Matt Stockard.
|
||||
- Added DVB subtitles decoder, spupng in output
|
||||
- Added support for cdt2 media atoms in QT video files. Now multiple atoms in
|
||||
a single sample sequence are supported.
|
||||
- Changed Makefile.
|
||||
- Fixed some bugs.
|
||||
- Added feature to print info about file's subtitles and streams.
|
||||
- Support Long PMT.
|
||||
- Support Configuration file.
|
||||
Gui lovers should download the Sorceforge version of CCExtractor, the Git Version is not your cup of tea.
|
||||
http://ccextractor.sourceforge.net/download-ccextractor.html
|
||||
|
||||
For News about release, please find CHANGES.TXT
|
||||
|
||||
241
docs/CHANGES.TXT
241
docs/CHANGES.TXT
@@ -1,5 +1,52 @@
|
||||
0.70 - GSOC
|
||||
-----------
|
||||
0.76 (2015-03-28)
|
||||
-----------------
|
||||
- Added basic M2TS support
|
||||
- Added EPG support - you can now export the Program Guide to XML
|
||||
- Some bugfixes
|
||||
|
||||
0.75 (2015-01-15)
|
||||
-----------------
|
||||
- Fixed issue with teletext to other then srt.
|
||||
- CCExtractor can be used as library if compiled using cmake
|
||||
- By default the Windows version adds BOM to generated UTF files (this is
|
||||
because it's needed to open the files correctly) while all other
|
||||
builds don't add it (because it messes with text processing tools).
|
||||
You can use -bom and -nobom to change the behaviour.
|
||||
|
||||
0.74 (2014-09-24)
|
||||
-----------------
|
||||
- Fixed issue with -o1 -o2 and -12 parameters (where it would write output only in the o2 file)
|
||||
- Fixed UCLA parameter issue. Now the UCLA parameter settings can't be overwritten anymore by later parameters that affect the custom transcript
|
||||
- Switched order around for TLT and TT page number in custom transcript to match UCLA settings
|
||||
- Added nobom parameter, for when files are processed by tools that can't handle the BOM. If using this, files might be not readable under windows.
|
||||
- Segfault fix when no input files were given
|
||||
- No more bin output when sending to server + possibility to send TT to server for processing
|
||||
- Windows: Added the Microsoft redistributable MSVCR120.DLL to both the installation package and the application zip.
|
||||
|
||||
0.73 - GSOC (2014-08-19)
|
||||
------------------------
|
||||
- Added support of BIN format for Teletext
|
||||
- Added start of librarisation. This will allow in the future for other programs to use encoder/decoder functions and more.
|
||||
|
||||
0.72 - GSOC (2014-08-12)
|
||||
------------------------
|
||||
- Fix for WTV files with incorrect timing
|
||||
- Added support for fps change using data from AVC video track in a H264 TS file.
|
||||
- Added FFMpeg Support to enable all encapsulator and decoder provided by ffmpeg
|
||||
|
||||
0.71 - GSOC (2014-07-31)
|
||||
------------------------
|
||||
- Added feature to receive captions in BIN format according to CCExtractor's own
|
||||
protocol over TCP (-tcp port [-tcppassword password])
|
||||
- Added ability to send captions to the server described above or to the
|
||||
online repository (-sendto host[:port])
|
||||
- Added -stdin parameter for reading input stream from standard input
|
||||
- Compilation in Cygwin using linux/Makefile
|
||||
- Fix for .bin files when not using latin1 charset
|
||||
- Correction of mp4 timing, when one timestamp points timing of two atom
|
||||
|
||||
0.70 - GSOC (2014-07-06)
|
||||
------------------------
|
||||
This is the first release that is part of Google's Summer of Code.
|
||||
Anshul, Ruslan and Willem joined CCExtractor to work on a number of things
|
||||
over the summer, and their work is already reaching the mainstream
|
||||
@@ -37,9 +84,10 @@ version of CCExtractor.
|
||||
1111001 is the default setting for -ucla
|
||||
Make sure you use this parameter after others that might affect these
|
||||
settings (-out, -ucla, -xds, -txt, -ttxt, ...)
|
||||
- Fixed Negative timing Bug
|
||||
|
||||
0.69
|
||||
----
|
||||
0.69 (2014-04-05)
|
||||
-----------------
|
||||
- A few patches from Christopher Small, including proper support
|
||||
for multiple multicast clients listening on the same port.
|
||||
- GUI: Fixed teletext preview.
|
||||
@@ -58,8 +106,8 @@ version of CCExtractor.
|
||||
- Windows GUI: Some code refactoring, since the HDHomeRun support makes
|
||||
the code larger enough to require more than one source file :-)
|
||||
|
||||
0.68
|
||||
----
|
||||
0.68 (2013-12-24)
|
||||
-----------------
|
||||
- A couple of shared variables between 608 decoders were causing
|
||||
problems when both fields were processed at the same time with
|
||||
-12, fixed.
|
||||
@@ -73,8 +121,8 @@ version of CCExtractor.
|
||||
(Heleen Buus).
|
||||
- Some fixes (Chris Small).
|
||||
|
||||
0.67
|
||||
----
|
||||
0.67 (2013-10-09)
|
||||
-----------------
|
||||
- Padding bytes were being discarded early in the process in 0.66,
|
||||
which is convenient for debugging, but it messes with timing in
|
||||
.raw, which depends on padding. Fixed.
|
||||
@@ -94,8 +142,8 @@ version of CCExtractor.
|
||||
roll-up mode.
|
||||
|
||||
|
||||
0.66
|
||||
----
|
||||
0.66 (2013-07-01)
|
||||
-----------------
|
||||
- Fixed bug in auto detection code that triggered a message
|
||||
about file being auto of sync.
|
||||
- Added -investigate_packets
|
||||
@@ -173,8 +221,8 @@ version of CCExtractor.
|
||||
- Added -noautotimeref: Prevent UTC reference from being auto set from
|
||||
the stream data.
|
||||
|
||||
0.65
|
||||
----
|
||||
0.65 (2013-03-14)
|
||||
-----------------
|
||||
- Minor GUI changes for teletext
|
||||
- Added end timestamps in timed transcripts
|
||||
- Added support for SMPTE (patch by John Kemp)
|
||||
@@ -192,8 +240,8 @@ version of CCExtractor.
|
||||
display their contents (-parsePAT and -parsePMT) which makes
|
||||
troubleshooting easier.
|
||||
|
||||
0.64
|
||||
----
|
||||
0.64 (2012-10-29)
|
||||
-----------------
|
||||
- Changed Window GUI size (larger).
|
||||
- Added Teletext options to GUI.
|
||||
- Added -teletext to force teletext mode even if not detected
|
||||
@@ -213,8 +261,8 @@ version of CCExtractor.
|
||||
- Added --nogoptime to force PTS timing even when CCExtractor would
|
||||
use GOP timing otherwise.
|
||||
|
||||
0.63
|
||||
----
|
||||
0.63 (2012-08-17)
|
||||
-----------------
|
||||
- Telext support added, by integrating Petr Kutalek's telxcc. Integration is
|
||||
still quite basic (there's equivalent code from both CCExtractor and
|
||||
telxcc) and some clean up is needed, but it works. Petr has announced that
|
||||
@@ -222,8 +270,8 @@ version of CCExtractor.
|
||||
CCExtractor.
|
||||
- Some bug fixes, as usual.
|
||||
|
||||
0.62
|
||||
----
|
||||
0.62 (2012-05-23)
|
||||
-----------------
|
||||
- Corrected Mac build "script" (needed to add GPAC includes). Thanks to the
|
||||
Mac users that sent this.
|
||||
- Hauppauge mode now uses PES timing, needed for files that don't have
|
||||
@@ -245,8 +293,8 @@ version of CCExtractor.
|
||||
or certain samples (we had none like this in our test collection). Thanks,
|
||||
Rajesh.
|
||||
|
||||
0.61
|
||||
----
|
||||
0.61 (2012-03-08)
|
||||
-----------------
|
||||
- Fix: GCC 3.4.4 can now build CCExtractor.
|
||||
- Fix: Damaged TS packets (those that come with 'error in transport' bit
|
||||
on) are now skipped.
|
||||
@@ -256,8 +304,8 @@ version of CCExtractor.
|
||||
anything but please report).
|
||||
- Some non-interesting cleanup.
|
||||
|
||||
0.60
|
||||
----
|
||||
0.60 (unreleased)
|
||||
-----------------
|
||||
- Add: MP4 support, using GPAC (a media library). Integration is currently
|
||||
"enough so it works", but needs some more work. There's some duplicate
|
||||
code, the stream must be a file (no streaming), etc.
|
||||
@@ -267,8 +315,8 @@ version of CCExtractor.
|
||||
roll-up) was broken, with complete lines being missing.
|
||||
- Fix: bin format not working as input.
|
||||
|
||||
0.59
|
||||
----
|
||||
0.59 (2011-10-07)
|
||||
-----------------
|
||||
- More AVC/H.264 work. pic_order_cnt_type != 0 will be processed now.
|
||||
- Fix: Roll-up captions with interruptions for Text (with ResumeTextDisplay
|
||||
in the middle of the caption data) were missing complete lines.
|
||||
@@ -296,8 +344,8 @@ version of CCExtractor.
|
||||
- Some code clean up, minor refactoring.
|
||||
- Teletext detection (not yet processing).
|
||||
|
||||
0.58
|
||||
----
|
||||
0.58 (2011-08-21)
|
||||
-----------------
|
||||
- Implemented new PTS based mode to order the caption information
|
||||
of AVC/H.264 data streams. The old pic_order_cnt_lsb based method
|
||||
is still available via the -poc or --usepicorder command switches.
|
||||
@@ -321,18 +369,18 @@ version of CCExtractor.
|
||||
output are processed OK.
|
||||
- Updated Windows GUI.
|
||||
|
||||
0.57
|
||||
----
|
||||
0.57 (2010-12-16)
|
||||
-----------------
|
||||
- Bugfixes in the Windows version. Some debug code was unintentionally
|
||||
left in the released version.
|
||||
|
||||
0.56
|
||||
----
|
||||
0.56 (2010-12-09)
|
||||
-----------------
|
||||
- H264 support
|
||||
- Other minor changes a lot less important
|
||||
|
||||
0.55
|
||||
----
|
||||
0.55 (2009-08-09)
|
||||
-----------------
|
||||
- Replace pattern matching code with improved parser for MPEG-2 elementary
|
||||
streams.
|
||||
- Fix parsing of ReplayTV 5000 captions.
|
||||
@@ -347,8 +395,8 @@ version of CCExtractor.
|
||||
because of the odd number of fields. I used top_field_first to tell when the channels
|
||||
are reversed. See Table 6-1 of the SCTE 20 [Paul Fernquist]
|
||||
|
||||
0.54
|
||||
----
|
||||
0.54 (2009-04-16)
|
||||
-----------------
|
||||
- Add -nosync and -fullbin switches for debugging purposes.
|
||||
- Remove -lg (--largegops) switch.
|
||||
- Improve syncronization of captions for source files with
|
||||
@@ -361,8 +409,8 @@ version of CCExtractor.
|
||||
- Added a feature to add start and end messages (for credits).
|
||||
See help screen for details.
|
||||
|
||||
0.53
|
||||
----
|
||||
0.53 (2009-02-24)
|
||||
-----------------
|
||||
- Force generated RCWT files to have the same length as source file.
|
||||
- Fix documentation for -startat / -endat switches.
|
||||
- Make -startat / -endat work with all output formats.
|
||||
@@ -388,8 +436,8 @@ version of CCExtractor.
|
||||
it (there's not .NET 3.5 for Windows 2000), as
|
||||
requested by a couple of key users.
|
||||
|
||||
0.51
|
||||
----
|
||||
0.51 (unreleased)
|
||||
-----------------
|
||||
- Removed -autopad and -goppad, no longer needed.
|
||||
- In preparation to a new binary format we have
|
||||
renamed the current .bin to .raw. Raw files
|
||||
@@ -414,14 +462,14 @@ version of CCExtractor.
|
||||
too.
|
||||
- [Volker] Dish Network clean-up
|
||||
|
||||
0.50
|
||||
----
|
||||
0.50 (2008-12-12)
|
||||
-----------------
|
||||
- [Volker] Fix in DVR-MS NTSC timing
|
||||
- [Volker] More clean-up
|
||||
- Minor fixes
|
||||
|
||||
0.49
|
||||
----
|
||||
0.49 (2008-12-10)
|
||||
-----------------
|
||||
- [Volker] Major MPEG parser rework. Code much
|
||||
cleaner now.
|
||||
- Some stations transmit broken roll-up captions,
|
||||
@@ -438,8 +486,8 @@ version of CCExtractor.
|
||||
- [Volker] Added support for DVR-MS NTSC files.
|
||||
- Other minor bugfixes and changes.
|
||||
|
||||
0.46
|
||||
----
|
||||
0.46 (2008-11-24)
|
||||
-----------------
|
||||
- Added support for live streaming, ccextractor
|
||||
can now process files that are being recorded
|
||||
at the same time.
|
||||
@@ -452,9 +500,8 @@ version of CCExtractor.
|
||||
Note: For now, it's only ATSC recordings, not
|
||||
NTSC (analog) recordings.
|
||||
|
||||
|
||||
0.45
|
||||
----
|
||||
0.45 (2008-11-14)
|
||||
-----------------
|
||||
- Added autodetection of DVR-MS files.
|
||||
- Added -asf to force DVR-MS mode.
|
||||
- Added some specific support for DVR-MS
|
||||
@@ -474,8 +521,8 @@ version of CCExtractor.
|
||||
need ccextractor to use GOP timing in large
|
||||
GOPs.
|
||||
|
||||
0.44
|
||||
----
|
||||
0.44 (2008-09-10)
|
||||
-----------------
|
||||
- Added an option to the GUI to process
|
||||
individual files in batch, i.e. call
|
||||
ccextractor once per file. Use it if you
|
||||
@@ -486,8 +533,8 @@ version of CCExtractor.
|
||||
- Several minor bugfixes.
|
||||
- Updated the GUI to add the new options.
|
||||
|
||||
0.43
|
||||
----
|
||||
0.43 (2008-06-20)
|
||||
-----------------
|
||||
- Fixed a bug in the read loop (no less)
|
||||
that caused some files to fail when
|
||||
reading without buffering (which is
|
||||
@@ -495,16 +542,16 @@ version of CCExtractor.
|
||||
- Several improvements in the GUI, such as
|
||||
saving current options as default.
|
||||
|
||||
0.42
|
||||
----
|
||||
0.42 (2008-06-17)
|
||||
-----------------
|
||||
- The option switch "-transcript" has been
|
||||
changed to "--transcript". Also, "-txt"
|
||||
has been added as the short alias.
|
||||
- Windows GUI
|
||||
- Updated help screen
|
||||
|
||||
0.41
|
||||
----
|
||||
0.41 (2008-06-15)
|
||||
-----------------
|
||||
- Default output is now .srt instead of .bin,
|
||||
use -raw if you need the data dump instead of
|
||||
.srt.
|
||||
@@ -516,15 +563,15 @@ version of CCExtractor.
|
||||
there aren't useless. But if they annoy
|
||||
you go ahead...
|
||||
|
||||
0.40
|
||||
----
|
||||
0.40 (2008-05-20)
|
||||
-----------------
|
||||
- Fixed a bug in the sanity check function
|
||||
that caused the Myth branch to abort.
|
||||
- Fixed the OSX build script, it needed a
|
||||
new #define to work.
|
||||
|
||||
0.39
|
||||
----
|
||||
0.39 (2008-05-11)
|
||||
-----------------
|
||||
- Added a -transcript. If used, the output will
|
||||
have no time information. Also, if in roll-up
|
||||
mode there will be no repeated lines.
|
||||
@@ -557,8 +604,8 @@ version of CCExtractor.
|
||||
join the subs), use -ve.
|
||||
|
||||
|
||||
0.36
|
||||
----
|
||||
0.36 (unreleased)
|
||||
-----------------
|
||||
- Fixed bug in SMI, nbsp was missing a ;.
|
||||
- Footer for SAMI files was incorrect (<body> and
|
||||
<sami> tags were being opened again instead of
|
||||
@@ -588,8 +635,8 @@ version of CCExtractor.
|
||||
as usual.
|
||||
|
||||
|
||||
0.35
|
||||
----
|
||||
0.35 (unreleased)
|
||||
-----------------
|
||||
- Added --defaultcolor to the help screen. Code
|
||||
was already in 0.34 but the documentation wasn't
|
||||
updated.
|
||||
@@ -598,8 +645,8 @@ version of CCExtractor.
|
||||
- At the end of the process, a ratio between
|
||||
video length and time to process is displayed.
|
||||
|
||||
0.34
|
||||
----
|
||||
0.34 (2007-06-03)
|
||||
-----------------
|
||||
- Added some basic letter case and capitalization
|
||||
support. For captions that broadcast in ALL
|
||||
UPPERCASE (most of them), ccextractor can now
|
||||
@@ -650,8 +697,8 @@ version of CCExtractor.
|
||||
Number (0.0.0.34 in this version) in case
|
||||
you want to check for version info.
|
||||
|
||||
0.33
|
||||
----
|
||||
0.33 (unreleased)
|
||||
-----------------
|
||||
- Added -scr or --screenfuls, to select the
|
||||
number of screenfuls ccextractor should
|
||||
write before exiting. A screenful is
|
||||
@@ -670,8 +717,8 @@ version of CCExtractor.
|
||||
Use -nofc or --nofontcolor if you don't
|
||||
want these tags.
|
||||
|
||||
0.32
|
||||
----
|
||||
0.32 (unreleased)
|
||||
-----------------
|
||||
- Added -delay ms, which adds (or substracts)
|
||||
a number of milliseconds to all times in
|
||||
.srt/.sami files. For example,
|
||||
@@ -690,8 +737,8 @@ version of CCExtractor.
|
||||
such as from minute 3 to minute 5. Check
|
||||
help screen for exact syntax.
|
||||
|
||||
0.31
|
||||
----
|
||||
0.31 (unreleased)
|
||||
-----------------
|
||||
- Added -dru (direct rollup), which causes
|
||||
roll-up captions to be written as
|
||||
they would on TV instead of line by line.
|
||||
@@ -699,20 +746,20 @@ version of CCExtractor.
|
||||
and ugly too (each line is written many
|
||||
times, two characters at time).
|
||||
|
||||
0.30
|
||||
----
|
||||
0.30 (2007-05-24)
|
||||
-----------------
|
||||
- Fix in extended char decoding, I wasn't
|
||||
replacing the previous char.
|
||||
- When a sequence code was found before
|
||||
having a PTS, reported time was
|
||||
undefined.
|
||||
|
||||
0.29
|
||||
----
|
||||
0.29 (unreleased)
|
||||
-----------------
|
||||
- Minor bugfix.
|
||||
|
||||
0.28
|
||||
----
|
||||
0.28 (unreleased)
|
||||
-----------------
|
||||
- Fixed a buffering related issue. Short version,
|
||||
the first 2 Mb in non-TS mode were being
|
||||
discarded.
|
||||
@@ -721,20 +768,20 @@ version of CCExtractor.
|
||||
they are not part of the .srt "standard"
|
||||
even if McPoodle add them.
|
||||
|
||||
0.27
|
||||
----
|
||||
0.27 (unreleased)
|
||||
-----------------
|
||||
- Modified sanitizing code, it's less aggresive
|
||||
now. Ideally it should mean that characters
|
||||
won't be missed anymore. We'll see.
|
||||
|
||||
0.26
|
||||
----
|
||||
0.26 (unreleased)
|
||||
-----------------
|
||||
- Added -gp (or -goppad) to make ccextractor use
|
||||
GOP timing. Try it for non TS files where
|
||||
subs start OK but desync as the video advances.
|
||||
|
||||
0.25
|
||||
----
|
||||
0.25 (unreleased)
|
||||
-----------------
|
||||
- Format detection is not perfect yet. I've added
|
||||
-nomyth to prevent the MytvTV code path to be
|
||||
called. I've seen apparently correct files that
|
||||
@@ -744,8 +791,8 @@ version of CCExtractor.
|
||||
options will work.
|
||||
|
||||
|
||||
0.24
|
||||
----
|
||||
0.24 (unreleased)
|
||||
-----------------
|
||||
- Fixed a bug that caused dvr-ms (Windows Media Center)
|
||||
files to be incorrectly processed (letters out of
|
||||
order all the time).
|
||||
@@ -759,8 +806,8 @@ version of CCExtractor.
|
||||
still can).
|
||||
|
||||
|
||||
0.22
|
||||
----
|
||||
0.22 (2007-05-15)
|
||||
-----------------
|
||||
- Added text mode handling into decoder, which gets rids
|
||||
of junk when text mode data is present.
|
||||
- Added support for certain (possibly non standard
|
||||
@@ -771,8 +818,8 @@ version of CCExtractor.
|
||||
- Other Minor bug fixes.
|
||||
|
||||
|
||||
0.20
|
||||
----
|
||||
0.20 (2007-05-07)
|
||||
-----------------
|
||||
- Unicode should be decent now.
|
||||
- Added support for Hauppauge PVR 250 cards, and (possibly)
|
||||
many others (bttv) with the same closed caption recording
|
||||
@@ -789,8 +836,8 @@ version of CCExtractor.
|
||||
though. If you have a good CSS for .SAMI files let me
|
||||
know.
|
||||
|
||||
0.19
|
||||
----
|
||||
0.19 (2007-05-03)
|
||||
-----------------
|
||||
- Work on Dish Network streams, timing was completely broken.
|
||||
It's fixed now at least for the samples I have, if it's not
|
||||
completely fixed let me know. Credit for this goes to
|
||||
@@ -801,8 +848,8 @@ version of CCExtractor.
|
||||
- Added Unicode and Latin-1 encoding.
|
||||
|
||||
|
||||
0.17
|
||||
----
|
||||
0.17 (2007-04-29)
|
||||
-----------------
|
||||
- Extraction to .srt is almost complete - works correctly for
|
||||
pop-up and roll-up captions, possibly not yet for paint-on
|
||||
(mostly because I don't have any sample with paint-on captions
|
||||
@@ -810,8 +857,8 @@ version of CCExtractor.
|
||||
- Minor bug fixes.
|
||||
- Automatic TS/non-TS mode detection.
|
||||
|
||||
0.14
|
||||
----
|
||||
0.14 (2007-04-25)
|
||||
-----------------
|
||||
- Work on handling special cases related to the MPEG reference
|
||||
clock: Roll over, jumps, etc.
|
||||
- Modified padding code a bit: In particular, padding occurs
|
||||
@@ -824,8 +871,8 @@ version of CCExtractor.
|
||||
needs to start with a TS header).
|
||||
- Minor bug fixes.
|
||||
|
||||
0.07
|
||||
----
|
||||
0.07 (2007-04-19)
|
||||
-----------------
|
||||
- Added MPEG reference clock parsing.
|
||||
- Added autopadding in TS. Does miracles with timing.
|
||||
- Added video information (as extracted from sequence header).
|
||||
|
||||
58
docs/FFMPEG.TXT
Normal file
58
docs/FFMPEG.TXT
Normal file
@@ -0,0 +1,58 @@
|
||||
Overview
|
||||
========
|
||||
FFmpeg Intigration was done to support multiple encapsulator.
|
||||
|
||||
Dependecy
|
||||
=========
|
||||
FFmpeg library's
|
||||
|
||||
Download and Install FFmpeg on your linux pc.
|
||||
---------------------------------------------
|
||||
|
||||
Download latest source code from following link
|
||||
https://ffmpeg.org/download.html
|
||||
|
||||
then following command to install ffmpeg
|
||||
./configure && make && make install
|
||||
|
||||
Note:If you installed ffmpeg on non standurd location, please change/update your
|
||||
enviorment variable $PATH and $LD_LIBRARY_PATH
|
||||
|
||||
Download and Install FFmpeg on your Windows pc.
|
||||
----------------------------------------------
|
||||
Download prebuild library from following link
|
||||
http://ffmpeg.zeranoe.com/builds/
|
||||
|
||||
You need to download Shared Versions to run the program and Dev Versions to compile.
|
||||
|
||||
How to compile ccextractor
|
||||
==========================
|
||||
|
||||
In Linux
|
||||
--------
|
||||
make ENABLE_FFMPEG=yes
|
||||
|
||||
On Windows
|
||||
----------
|
||||
put the path of libs/include of ffmpeg library in library paths.
|
||||
step 1) In visual studio 2013 right click <Project> and select property.
|
||||
step 2) Select Configuration properties in left panel(column) of property.
|
||||
step 3) Select VC++ Directory.
|
||||
step 4) In the right pane, in the right-hand column of the VC++ Directory property,
|
||||
open the drop-down menu and choose Edit.
|
||||
Step 5) Add path of Directory where you have kept uncompressed library of FFmpeg.
|
||||
|
||||
|
||||
Set preprocessor flag ENABLE_FFMPEG=1
|
||||
Step 1)In visual studio 2013 right click <Project> and select property.
|
||||
Step 2)In the left panel, select Configuration Properties, C/C++, Preprocessor.
|
||||
Step 3)In the right panel, in the right-hand column of the Preprocessor Definitions property, open the drop-down menu and choose Edit.
|
||||
Step 4)In the Preprocessor Definitions dialog box, add ENABLE_FFMPEG=1. Choose OK to save your changes.
|
||||
|
||||
Add library in linker
|
||||
step 1)Open property of project
|
||||
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 all FFmpeg's lib in new line
|
||||
@@ -6,8 +6,8 @@ extract text from images. In the World of Subtile, subtitle stored
|
||||
in bitmap format are common and even neccassary. for converting subtile
|
||||
in bitmap format to subtilte in text format ocr is used.
|
||||
|
||||
Dependecy
|
||||
=========
|
||||
Dependency
|
||||
==========
|
||||
Tesseract (OCR library by google)
|
||||
Leptonica (image processing library)
|
||||
|
||||
@@ -55,7 +55,7 @@ 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
|
||||
@@ -90,7 +90,7 @@ Step 7)Add liblept168.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 extract the tar file and put
|
||||
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.
|
||||
@@ -1,4 +1,4 @@
|
||||
ccextractor, 0.63
|
||||
ccextractor, 0.75
|
||||
-----------------
|
||||
Authors: Carlos Fernández (cfsmp3), Volker Quetschke.
|
||||
Maintainer: cfsmp3
|
||||
@@ -7,12 +7,17 @@ Lots of credit goes to other people, though:
|
||||
McPoodle (author of the original SCC_RIP), Neuron2, and others (see source
|
||||
code).
|
||||
|
||||
Home: http://ccextractor.sourceforge.net
|
||||
Home: http://www.ccextractor.org
|
||||
|
||||
You can subscribe to new releases notifications at freshmeat:
|
||||
|
||||
http://freshmeat.net/projects/ccextractor
|
||||
|
||||
Google Summer of Code 2014 students
|
||||
- Willem van iseghem
|
||||
- Ruslan KuchumoV
|
||||
- Anshul Maheshwari
|
||||
|
||||
License
|
||||
-------
|
||||
GPL 2.0.
|
||||
@@ -67,21 +72,18 @@ If there are Spanish subtitles, one of them should work.
|
||||
|
||||
McPoodle's page
|
||||
---------------
|
||||
http://www.geocities.com/mcpoodle43/SCC_TOOLS/DOCS/SCC_TOOLS.HTML
|
||||
http://www.theneitherworld.com/mcpoodle/SCC_TOOLS/DOCS/SCC_TOOLS.HTML
|
||||
|
||||
Essential CC related information and free (with source) tools.
|
||||
|
||||
Encoding
|
||||
--------
|
||||
This version, in both its Linux and Windows builds generates by
|
||||
default Latin-1 encoded files. You can use -unicode and -utf8
|
||||
if you prefer these encodings (usually it just depends on what
|
||||
your specific player likes).
|
||||
This has changed from the previous UTF-8 default which vobsub
|
||||
can't handle.
|
||||
default Unicode files. You can use -latin1 and -utf8 if you prefer
|
||||
these encodings (usually it just depends on what your specific
|
||||
player likes).
|
||||
|
||||
Future work
|
||||
-----------
|
||||
- Finish EIA-708 decoder
|
||||
- Network support
|
||||
- Please check www.ccextractor.org for news and future work.
|
||||
|
||||
|
||||
28
docs/using_cmake_build.txt
Normal file
28
docs/using_cmake_build.txt
Normal file
@@ -0,0 +1,28 @@
|
||||
For building ccextractor using cmake folllow below steps..
|
||||
|
||||
Step 1) Check you have right version of cmake installed. ( version >= 3.0.2 )
|
||||
We are using CMP0037 policy of cmake which was introduced in 3.0.0
|
||||
since we have tested our system only with cmake version 3.0.2, I would
|
||||
suggest to use 3.0.2 or higher version.
|
||||
|
||||
|
||||
Step 2) create a seprate directory where you want to build the target.
|
||||
In Unix you can do it using follwing commands.
|
||||
~> cd ccextractor
|
||||
~> mkdir build
|
||||
|
||||
Step 3) make the build sytem using cmake
|
||||
~> cmake ../src/
|
||||
|
||||
Step 4) Compile the code.
|
||||
~> make
|
||||
~> make install
|
||||
|
||||
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/
|
||||
|
||||
Hint for looking all the things you want to set from outside
|
||||
cmake -LAH ../src/
|
||||
@@ -1,22 +1,28 @@
|
||||
SHELL = /bin/sh
|
||||
|
||||
CC = gcc
|
||||
CXX = g++
|
||||
|
||||
SYS := $(shell gcc -dumpmachine)
|
||||
CFLAGS = -O3 -std=gnu99
|
||||
INCLUDE = -I../src/gpacmp4/ -I../src/libpng -I../src/zlib
|
||||
ALL_FLAGS = -Wno-write-strings -DGPAC_CONFIG_LINUX -D_FILE_OFFSET_BITS=64
|
||||
LDFLAGS = -lm -zmuldefs
|
||||
INCLUDE = -I../src/gpacmp4/ -I../src/libpng -I../src/zlib -I../src/lib_ccx -I../src/.
|
||||
ALL_FLAGS = -Wno-write-strings -D_FILE_OFFSET_BITS=64
|
||||
LDFLAGS = -lm
|
||||
|
||||
ifneq (, $(findstring linux, $(SYS)))
|
||||
CFLAGS +=-DGPAC_CONFIG_LINUX
|
||||
endif
|
||||
TARGET = ccextractor
|
||||
|
||||
OBJS_DIR = objs
|
||||
VPATH = ../src:../src/gpacmp4:../src/libpng:../src/zlib
|
||||
VPATH = ../src:../src/gpacmp4:../src/libpng:../src/zlib:../src/lib_ccx
|
||||
|
||||
SRCS_DIR = ../src
|
||||
SRCS_C = $(wildcard $(SRCS_DIR)/*.c)
|
||||
OBJS = $(SRCS_C:$(SRCS_DIR)/%.c=$(OBJS_DIR)/%.o)
|
||||
|
||||
SRCS_CCX_DIR = $(SRCS_DIR)/lib_ccx
|
||||
SRCS_CCX = $(wildcard $(SRCS_CCX_DIR)/*.c)
|
||||
OBJS_CCX = $(SRCS_CCX:$(SRCS_CCX_DIR)/%.c=$(OBJS_DIR)/%.o)
|
||||
|
||||
SRCS_PNG_DIR = $(SRCS_DIR)/libpng
|
||||
SRCS_PNG = $(wildcard $(SRCS_PNG_DIR)/*.c)
|
||||
OBJS_PNG = $(SRCS_PNG:$(SRCS_PNG_DIR)/%.c=$(OBJS_DIR)/%.o)
|
||||
@@ -36,9 +42,39 @@ INSTLALL_PROGRAM = $(INSTLALL)
|
||||
DESTDIR = /usr/bin
|
||||
|
||||
ifeq ($(ENABLE_OCR),yes)
|
||||
CFLAGS+=-I/usr/local/include/tesseract -I/usr/local/include/leptonica
|
||||
CFLAGS+=-DENABLE_OCR
|
||||
LDFLAGS+= $(shell pkg-config --libs tesseract)
|
||||
TESS_LDFLAGS+= $(shell pkg-config --libs tesseract)
|
||||
LEPT_LDFLAGS+= $(shell pkg-config --libs lept)
|
||||
|
||||
#error checking of library are there or not
|
||||
ifeq ($(TESS_LDFLAGS),$(EMPTY))
|
||||
$(error **ERROR** "tesseract not found")
|
||||
else
|
||||
#TODO print the version of library found
|
||||
$(info "tesseract found")
|
||||
endif
|
||||
ifeq ($(LEPT_LDFLAGS),$(EMPTY))
|
||||
$(error **ERROR** "leptonica not found")
|
||||
else
|
||||
#TODO print the version of library found
|
||||
$(info "Leptonica found")
|
||||
endif
|
||||
|
||||
CFLAGS += $(shell pkg-config --cflags tesseract)
|
||||
CFLAGS += $(shell pkg-config --cflags lept)
|
||||
LDFLAGS += $(TESS_LDFLAGS)
|
||||
LDFLAGS += $(LEPT_LDFLAGS)
|
||||
endif
|
||||
|
||||
|
||||
ifeq ($(ENABLE_FFMPEG),yes)
|
||||
CFLAGS+=-DENABLE_FFMPEG
|
||||
CFLAGS+= $(shell pkg-config --cflags libavcodec)
|
||||
CFLAGS+= $(shell pkg-config --cflags libavformat)
|
||||
CFLAGS+= $(shell pkg-config --cflags libavutil)
|
||||
LDFLAGS+= $(shell pkg-config --libs libavcodec )
|
||||
LDFLAGS+= $(shell pkg-config --libs libavformat )
|
||||
LDFLAGS+= $(shell pkg-config --libs libavutil )
|
||||
endif
|
||||
|
||||
.PHONY: all
|
||||
@@ -48,8 +84,8 @@ all: objs_dir $(TARGET)
|
||||
objs_dir:
|
||||
mkdir -p $(OBJS_DIR)
|
||||
|
||||
$(TARGET): $(OBJS) $(OBJS_PNG) $(OBJS_GPACMP4) $(OBJS_ZLIB)
|
||||
$(CXX) $(ALL_FLAGS) $(CFLAGS) $(OBJS) $(OBJS_PNG) $(OBJS_GPACMP4) $(OBJS_ZLIB) $(LDFLAGS) -o $@
|
||||
$(TARGET): $(OBJS) $(OBJS_PNG) $(OBJS_GPACMP4) $(OBJS_ZLIB) $(OBJS_CCX)
|
||||
$(CC) $(ALL_FLAGS) $(CFLAGS) $(OBJS) $(OBJS_CCX) $(OBJS_PNG) $(OBJS_GPACMP4) $(OBJS_ZLIB) $(LDFLAGS) -o $@
|
||||
|
||||
$(OBJS_DIR)/%.o: %.c
|
||||
$(CC) -c $(ALL_FLAGS) $(INCLUDE) $(CFLAGS) $< -o $@
|
||||
@@ -60,7 +96,7 @@ $(OBJS_DIR)/%.o: %.cpp
|
||||
.PHONY: clean
|
||||
clean:
|
||||
rm $(TARGET) 2>/dev/null || true
|
||||
rm $(OBJS_PNG) $(OBJS_ZLIB) $(OBJS_GPACMP4) $(OBJS) 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
|
||||
|
||||
@@ -74,7 +110,7 @@ uninstall:
|
||||
|
||||
.PHONY: depend dep
|
||||
depend dep:
|
||||
$(CXX) $(CXXFLAGS) -E -MM $(SRCS_C) $(SRCS_PNG) $(SRCS_ZLIB) \
|
||||
$(CC) $(CFLAGS) -E -MM $(SRCS_C) $(SRCS_PNG) $(SRCS_ZLIB) $(SRCS_CXX) \
|
||||
$(SRCS_GPACMP4_C) $(SRCS_GPACMP4_CPP) |\
|
||||
sed 's/^[a-zA-Z_0-9]*.o/$(OBJS_DIR)\/&/' > .depend
|
||||
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
#!/bin/bash
|
||||
gcc -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
|
||||
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
|
||||
|
||||
2
mac/build.command
Normal file → Executable file
2
mac/build.command
Normal file → Executable file
@@ -1 +1 @@
|
||||
g++ -Dfopen64=fopen -Dopen64=open -Dlseek64=lseek -I../src/gpacmp4 -I ../src/libpng -I ../src/zlib -o ccextractor $(find ../src/ -name '*.cpp') $(find ../src/ -name '*.c')
|
||||
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
|
||||
|
||||
151
src/608.h
151
src/608.h
@@ -1,151 +0,0 @@
|
||||
#ifndef __608_H__
|
||||
|
||||
enum cc_modes
|
||||
{
|
||||
MODE_POPON = 0,
|
||||
MODE_ROLLUP_2 = 1,
|
||||
MODE_ROLLUP_3 = 2,
|
||||
MODE_ROLLUP_4 = 3,
|
||||
MODE_TEXT = 4,
|
||||
MODE_PAINTON = 5,
|
||||
// Fake modes to emulate stuff
|
||||
MODE_FAKE_ROLLUP_1 = 100
|
||||
};
|
||||
|
||||
struct eia608_screen // A CC buffer
|
||||
{
|
||||
unsigned char characters[15][33];
|
||||
unsigned char colors[15][33];
|
||||
unsigned char fonts[15][33]; // Extra char at the end for a 0
|
||||
int row_used[15]; // Any data in row?
|
||||
int empty; // Buffer completely empty?
|
||||
};
|
||||
|
||||
struct s_context_cc608
|
||||
{
|
||||
struct eia608_screen buffer1;
|
||||
struct eia608_screen buffer2;
|
||||
int cursor_row, cursor_column;
|
||||
int visible_buffer;
|
||||
int srt_counter; // Number of subs currently written
|
||||
int screenfuls_counter; // Number of meaningful screenfuls written
|
||||
LLONG current_visible_start_ms; // At what time did the current visible buffer became so?
|
||||
// unsigned current_visible_start_cc; // At what time did the current visible buffer became so?
|
||||
enum cc_modes mode;
|
||||
unsigned char last_c1, last_c2;
|
||||
int channel; // Currently selected channel
|
||||
unsigned char color; // Color we are currently using to write
|
||||
unsigned char font; // Font we are currently using to write
|
||||
int rollup_base_row;
|
||||
LLONG ts_start_of_current_line; /* Time at which the first character for current line was received, =-1 no character received yet */
|
||||
LLONG ts_last_char_received; /* Time at which the last written character was received, =-1 no character received yet */
|
||||
int new_channel; // The new channel after a channel change
|
||||
int my_field; // Used for sanity checks
|
||||
long bytes_processed_608; // To be written ONLY by process_608
|
||||
struct ccx_s_write *out;
|
||||
int have_cursor_position;
|
||||
};
|
||||
|
||||
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];
|
||||
|
||||
int write_cc_buffer_as_srt(struct eia608_screen *data, struct s_context_cc608 *context);
|
||||
void write_stringz_as_srt(char *string, struct s_context_cc608 *context, LLONG ms_start, LLONG ms_end);
|
||||
unsigned get_decoder_line_encoded (unsigned char *buffer, int line_num, struct eia608_screen *data);
|
||||
void capitalize (int line_num, struct eia608_screen *data);
|
||||
void correct_case (int line_num, struct eia608_screen *data);
|
||||
int write_cc_buffer_as_sami(struct eia608_screen *data, struct s_context_cc608 *context);
|
||||
void write_stringz_as_sami(char *string, struct s_context_cc608 *context, LLONG ms_start, LLONG ms_end);
|
||||
int write_cc_buffer_as_smptett(struct eia608_screen *data, struct s_context_cc608 *context);
|
||||
void write_stringz_as_smptett(char *string, struct s_context_cc608 *context, LLONG ms_start, LLONG ms_end);
|
||||
void correct_case (int line_num, struct eia608_screen *data);
|
||||
void capitalize (int line_num, struct eia608_screen *data);
|
||||
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);
|
||||
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);
|
||||
void delete_all_lines_but_current (struct eia608_screen *data, int row);
|
||||
void try_to_add_start_credits(struct s_context_cc608 *context);
|
||||
void try_to_add_end_credits(struct s_context_cc608 *context);
|
||||
void write_cc_buffer_to_gui(struct eia608_screen *data, struct s_context_cc608 *context);
|
||||
|
||||
void handle_end_of_data(struct s_context_cc608 *context);
|
||||
void process608(const unsigned char *data, int length, struct s_context_cc608 *context);
|
||||
void get_char_in_latin_1 (unsigned char *buffer, unsigned char c);
|
||||
void get_char_in_unicode (unsigned char *buffer, unsigned char c);
|
||||
int get_char_in_utf_8 (unsigned char *buffer, unsigned char c);
|
||||
unsigned char cctolower (unsigned char c);
|
||||
unsigned char cctoupper (unsigned char c);
|
||||
int general_608_init (void);
|
||||
LLONG get_visible_end (void);
|
||||
|
||||
#define CC608_SCREEN_WIDTH 32
|
||||
|
||||
#define REQUEST_BUFFER_CAPACITY(length) if (length>enc_buffer_capacity) \
|
||||
{enc_buffer_capacity=length*2; enc_buffer=(unsigned char*) realloc (enc_buffer, enc_buffer_capacity); \
|
||||
if (enc_buffer==NULL) { fatal (EXIT_NOT_ENOUGH_MEMORY, "Not enough memory, bailing out\n"); } \
|
||||
}
|
||||
|
||||
|
||||
enum 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
|
||||
};
|
||||
|
||||
|
||||
enum font_bits
|
||||
{
|
||||
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_RESUMETEXTDISPLAY = 13,
|
||||
COM_ALARMOFF =14,
|
||||
COM_ALARMON = 15,
|
||||
COM_DELETETOENDOFROW = 16,
|
||||
COM_RESUMEDIRECTCAPTIONING = 17,
|
||||
// Non existing commands we insert to have the decoder
|
||||
// special stuff for us.
|
||||
COM_FAKE_RULLUP1 = 18
|
||||
};
|
||||
|
||||
|
||||
|
||||
#define __608_H__
|
||||
#endif
|
||||
@@ -1,438 +0,0 @@
|
||||
#include "ccextractor.h"
|
||||
#include "utility.h"
|
||||
//extern unsigned char encoded_crlf[16];
|
||||
|
||||
// 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 bytes=0;
|
||||
while (*text)
|
||||
{
|
||||
switch (ccx_options.encoding)
|
||||
{
|
||||
case CCX_ENC_UTF_8:
|
||||
case CCX_ENC_LATIN_1:
|
||||
*buffer=*text;
|
||||
bytes++;
|
||||
buffer++;
|
||||
break;
|
||||
case CCX_ENC_UNICODE:
|
||||
*buffer=*text;
|
||||
*(buffer+1)=0;
|
||||
bytes+=2;
|
||||
buffer+=2;
|
||||
break;
|
||||
}
|
||||
text++;
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
|
||||
void correct_case(int line_num,struct eia608_screen *data)
|
||||
{
|
||||
char delim[64] = {
|
||||
' ' ,'\n','\r', 0x89,0x99,
|
||||
'!' , '"', '#', '%' , '&',
|
||||
'\'', '(', ')', ';' , '<',
|
||||
'=' , '>', '?', '[' ,'\\',
|
||||
']' , '*', '+', ',' , '-',
|
||||
'.' , '/', ':', '^' , '_',
|
||||
'{' , '|', '}', '~' ,'\0' };
|
||||
|
||||
char *line = strdup(((char*)data->characters[line_num]));
|
||||
char *oline = (char*)data->characters[line_num];
|
||||
char *c = strtok(line,delim);
|
||||
do
|
||||
{
|
||||
char **index = bsearch(&c,spell_lower,spell_words,sizeof(*spell_lower),string_cmp);
|
||||
|
||||
if(index)
|
||||
{
|
||||
char *correct_c = *(spell_correct + (index - spell_lower));
|
||||
size_t len=strlen (correct_c);
|
||||
memcpy(oline + (c - line),correct_c,len);
|
||||
}
|
||||
} while ( ( c = strtok(NULL,delim) ) != NULL );
|
||||
free(line);
|
||||
}
|
||||
|
||||
void capitalize (int line_num, struct eia608_screen *data)
|
||||
{
|
||||
for (int i=0;i<CC608_SCREEN_WIDTH;i++)
|
||||
{
|
||||
switch (data->characters[line_num][i])
|
||||
{
|
||||
case ' ':
|
||||
case 0x89: // This is a transparent space
|
||||
case '-':
|
||||
break;
|
||||
case '.': // Fallthrough
|
||||
case '?': // Fallthrough
|
||||
case '!':
|
||||
case ':':
|
||||
new_sentence=1;
|
||||
break;
|
||||
default:
|
||||
if (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;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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<CC608_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)
|
||||
{
|
||||
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 (!ccx_options.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 (ccx_options.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
|
||||
}
|
||||
|
||||
unsigned get_decoder_line_encoded_for_gui (unsigned char *buffer, int line_num, struct eia608_screen *data)
|
||||
{
|
||||
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);
|
||||
for (int i=first;i<=last;i++)
|
||||
{
|
||||
get_char_in_latin_1 (buffer,line[i]);
|
||||
buffer++;
|
||||
}
|
||||
*buffer=0;
|
||||
return (unsigned) (buffer-orig); // Return length
|
||||
|
||||
}
|
||||
|
||||
unsigned char *close_tag (unsigned char *buffer, char *tagstack, char tagtype, int *punderlined, int *pitalics, int *pchanged_font)
|
||||
{
|
||||
for (int l=strlen (tagstack)-1; l>=0;l--)
|
||||
{
|
||||
char cur=tagstack[l];
|
||||
switch (cur)
|
||||
{
|
||||
case 'F':
|
||||
buffer+= encode_line (buffer,(unsigned char *) "</font>");
|
||||
(*pchanged_font)--;
|
||||
break;
|
||||
case 'U':
|
||||
buffer+=encode_line (buffer, (unsigned char *) "</u>");
|
||||
(*punderlined)--;
|
||||
break;
|
||||
case 'I':
|
||||
buffer+=encode_line (buffer, (unsigned char *) "</i>");
|
||||
(*pitalics)--;
|
||||
break;
|
||||
}
|
||||
tagstack[l]=0; // Remove from stack
|
||||
if (cur==tagtype) // We closed up to the required tag, done
|
||||
return buffer;
|
||||
}
|
||||
if (tagtype!='A') // All
|
||||
fatal (EXIT_BUG_BUG, "Mismatched tags in encoding, this is a bug, please report");
|
||||
return buffer;
|
||||
}
|
||||
|
||||
unsigned get_decoder_line_encoded (unsigned char *buffer, int line_num, struct eia608_screen *data)
|
||||
{
|
||||
int col = COL_WHITE;
|
||||
int underlined = 0;
|
||||
int italics = 0;
|
||||
int changed_font=0;
|
||||
char tagstack[128]=""; // Keep track of opening/closing tags
|
||||
|
||||
unsigned char *line = data->characters[line_num];
|
||||
unsigned char *orig=buffer; // Keep for debugging
|
||||
int first=0, last=31;
|
||||
if (ccx_options.trim_subs)
|
||||
find_limit_characters(line,&first,&last);
|
||||
for (int i=first;i<=last;i++)
|
||||
{
|
||||
// Handle color
|
||||
int its_col = data->colors[line_num][i];
|
||||
if (its_col != col && !ccx_options.nofontcolor &&
|
||||
!(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);
|
||||
// Add new font tag
|
||||
buffer+=encode_line (buffer, (unsigned char*) color_text[its_col][1]);
|
||||
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*) "\">");
|
||||
}
|
||||
if (color_text[its_col][1][0]) // That means a <font> was added to the buffer
|
||||
{
|
||||
strcat (tagstack,"F");
|
||||
changed_font++;
|
||||
}
|
||||
col = its_col;
|
||||
}
|
||||
// Handle underlined
|
||||
int is_underlined = data->fonts[line_num][i] & FONT_UNDERLINED;
|
||||
if (is_underlined && underlined==0 && !ccx_options.notypesetting) // Open underline
|
||||
{
|
||||
buffer+=encode_line (buffer, (unsigned char *) "<u>");
|
||||
strcat (tagstack,"U");
|
||||
underlined++;
|
||||
}
|
||||
if (is_underlined==0 && underlined && !ccx_options.notypesetting) // Close underline
|
||||
{
|
||||
buffer = close_tag(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_options.notypesetting) // Open italics
|
||||
{
|
||||
buffer+=encode_line (buffer, (unsigned char *) "<i>");
|
||||
strcat (tagstack,"I");
|
||||
italics++;
|
||||
}
|
||||
if (has_ita==0 && italics && !ccx_options.notypesetting) // Close italics
|
||||
{
|
||||
buffer = close_tag(buffer,tagstack,'I',&underlined,&italics,&changed_font);
|
||||
}
|
||||
int bytes=0;
|
||||
switch (ccx_options.encoding)
|
||||
{
|
||||
case CCX_ENC_UTF_8:
|
||||
bytes=get_char_in_utf_8 (buffer,line[i]);
|
||||
break;
|
||||
case CCX_ENC_LATIN_1:
|
||||
get_char_in_latin_1 (buffer,line[i]);
|
||||
bytes=1;
|
||||
break;
|
||||
case CCX_ENC_UNICODE:
|
||||
get_char_in_unicode (buffer,line[i]);
|
||||
bytes=2;
|
||||
break;
|
||||
}
|
||||
buffer+=bytes;
|
||||
}
|
||||
buffer = close_tag(buffer,tagstack,'A',&underlined,&italics,&changed_font);
|
||||
if (underlined || italics || changed_font)
|
||||
fatal (EXIT_BUG_BUG, "Not all tags closed in encoding, this is a bug, please report.\n");
|
||||
*buffer=0;
|
||||
return (unsigned) (buffer-orig); // Return length
|
||||
}
|
||||
|
||||
|
||||
void delete_all_lines_but_current (struct eia608_screen *data, int row)
|
||||
{
|
||||
for (int i=0;i<15;i++)
|
||||
{
|
||||
if (i!=row)
|
||||
{
|
||||
memset(data->characters[i],' ',CC608_SCREEN_WIDTH);
|
||||
data->characters[i][CC608_SCREEN_WIDTH]=0;
|
||||
memset (data->colors[i],ccx_options.cc608_default_color,CC608_SCREEN_WIDTH+1);
|
||||
memset (data->fonts[i],FONT_REGULAR,CC608_SCREEN_WIDTH+1);
|
||||
data->row_used[i]=0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void fprintf_encoded (FILE *fh, const char *string)
|
||||
{
|
||||
REQUEST_BUFFER_CAPACITY(strlen (string)*3);
|
||||
enc_buffer_used=encode_line (enc_buffer,(unsigned char *) string);
|
||||
fwrite (enc_buffer,enc_buffer_used,1,fh);
|
||||
}
|
||||
|
||||
void write_cc_buffer_to_gui(struct eia608_screen *data, struct s_context_cc608 *context)
|
||||
{
|
||||
unsigned h1,m1,s1,ms1;
|
||||
unsigned h2,m2,s2,ms2;
|
||||
LLONG ms_start;
|
||||
int with_data=0;
|
||||
|
||||
for (int i=0;i<15;i++)
|
||||
{
|
||||
if (data->row_used[i])
|
||||
with_data=1;
|
||||
}
|
||||
if (!with_data)
|
||||
return;
|
||||
|
||||
ms_start= context->current_visible_start_ms;
|
||||
|
||||
ms_start+=subs_delay;
|
||||
if (ms_start<0) // Drop screens that because of subs_delay start too early
|
||||
return;
|
||||
int time_reported=0;
|
||||
for (int i=0;i<15;i++)
|
||||
{
|
||||
if (data->row_used[i])
|
||||
{
|
||||
fprintf (stderr, "###SUBTITLE#");
|
||||
if (!time_reported)
|
||||
{
|
||||
LLONG ms_end = get_fts()+subs_delay;
|
||||
mstotime (ms_start,&h1,&m1,&s1,&ms1);
|
||||
mstotime (ms_end-1,&h2,&m2,&s2,&ms2); // -1 To prevent overlapping with next line.
|
||||
// Note, only MM:SS here as we need to save space in the preview window
|
||||
fprintf (stderr, "%02u:%02u#%02u:%02u#",
|
||||
h1*60+m1,s1, h2*60+m2,s2);
|
||||
time_reported=1;
|
||||
}
|
||||
else
|
||||
fprintf (stderr, "##");
|
||||
|
||||
// We don't capitalize here because whatever function that was used
|
||||
// before to write to file already took care of it.
|
||||
int length = get_decoder_line_encoded_for_gui (subline, i, data);
|
||||
fwrite (subline, 1, length, stderr);
|
||||
fwrite ("\n",1,1,stderr);
|
||||
}
|
||||
}
|
||||
fflush (stderr);
|
||||
}
|
||||
|
||||
void try_to_add_end_credits(struct s_context_cc608 *context)
|
||||
{
|
||||
LLONG window, length, st, end;
|
||||
if (context->out->fh == -1)
|
||||
return;
|
||||
window=get_fts()-last_displayed_subs_ms-1;
|
||||
if (window<ccx_options.endcreditsforatleast.time_in_ms) // Won't happen, window is too short
|
||||
return;
|
||||
length=ccx_options.endcreditsforatmost.time_in_ms > window ?
|
||||
window : ccx_options.endcreditsforatmost.time_in_ms;
|
||||
|
||||
st=get_fts()-length-1;
|
||||
end=get_fts();
|
||||
|
||||
switch (ccx_options.write_format)
|
||||
{
|
||||
case CCX_OF_SRT:
|
||||
write_stringz_as_srt(ccx_options.end_credits_text, context, st, end);
|
||||
break;
|
||||
case CCX_OF_SAMI:
|
||||
write_stringz_as_sami(ccx_options.end_credits_text, context, st, end);
|
||||
break;
|
||||
case CCX_OF_SMPTETT:
|
||||
write_stringz_as_smptett(ccx_options.end_credits_text, context, st, end);
|
||||
break ;
|
||||
default:
|
||||
// Do nothing for the rest
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void try_to_add_start_credits(struct s_context_cc608 *context)
|
||||
{
|
||||
LLONG st, end, window, length;
|
||||
LLONG l = context->current_visible_start_ms + subs_delay;
|
||||
// We have a windows from last_displayed_subs_ms to l - we need to see if it fits
|
||||
|
||||
if (l<ccx_options.startcreditsnotbefore.time_in_ms) // Too early
|
||||
return;
|
||||
|
||||
if (last_displayed_subs_ms+1 > ccx_options.startcreditsnotafter.time_in_ms) // Too late
|
||||
return;
|
||||
|
||||
st = ccx_options.startcreditsnotbefore.time_in_ms>(last_displayed_subs_ms+1) ?
|
||||
ccx_options.startcreditsnotbefore.time_in_ms : (last_displayed_subs_ms+1); // When would credits actually start
|
||||
|
||||
end = ccx_options.startcreditsnotafter.time_in_ms<(l-1) ?
|
||||
ccx_options.startcreditsnotafter.time_in_ms : (l-1);
|
||||
|
||||
window = end-st; // Allowable time in MS
|
||||
|
||||
if (ccx_options.startcreditsforatleast.time_in_ms>window) // Window is too short
|
||||
return;
|
||||
|
||||
length=ccx_options.startcreditsforatmost.time_in_ms > window ?
|
||||
window : ccx_options.startcreditsforatmost.time_in_ms;
|
||||
|
||||
dbg_print(CCX_DMT_VERBOSE, "Last subs: %lld Current position: %lld\n",
|
||||
last_displayed_subs_ms, l);
|
||||
dbg_print(CCX_DMT_VERBOSE, "Not before: %lld Not after: %lld\n",
|
||||
ccx_options.startcreditsnotbefore.time_in_ms,
|
||||
ccx_options.startcreditsnotafter.time_in_ms);
|
||||
dbg_print(CCX_DMT_VERBOSE, "Start of window: %lld End of window: %lld\n",st,end);
|
||||
|
||||
if (window>length+2)
|
||||
{
|
||||
// Center in time window
|
||||
LLONG pad=window-length;
|
||||
st+=(pad/2);
|
||||
}
|
||||
end=st+length;
|
||||
switch (ccx_options.write_format)
|
||||
{
|
||||
case CCX_OF_SRT:
|
||||
write_stringz_as_srt(ccx_options.start_credits_text,context,st,end);
|
||||
break;
|
||||
case CCX_OF_SAMI:
|
||||
write_stringz_as_sami(ccx_options.start_credits_text, context, st, end);
|
||||
break;
|
||||
case CCX_OF_SMPTETT:
|
||||
write_stringz_as_smptett(ccx_options.start_credits_text, context, st, end);
|
||||
break;
|
||||
default:
|
||||
// Do nothing for the rest
|
||||
break;
|
||||
}
|
||||
startcredits_displayed=1;
|
||||
return;
|
||||
|
||||
|
||||
}
|
||||
129
src/608_sami.c
129
src/608_sami.c
@@ -1,129 +0,0 @@
|
||||
#include "ccextractor.h"
|
||||
|
||||
void write_stringz_as_sami(char *string, struct s_context_cc608 *context, LLONG ms_start, LLONG ms_end)
|
||||
{
|
||||
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_608, "\r%s\n", str);
|
||||
}
|
||||
enc_buffer_used=encode_line (enc_buffer,(unsigned char *) str);
|
||||
write (context->out->fh, enc_buffer,enc_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_608, "\r");
|
||||
dbg_print(CCX_DMT_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_608, "\r%s\n", str);
|
||||
}
|
||||
enc_buffer_used=encode_line (enc_buffer,(unsigned char *) str);
|
||||
write(context->out->fh, enc_buffer, enc_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_608, "\r%s\n", str);
|
||||
}
|
||||
enc_buffer_used=encode_line (enc_buffer,(unsigned char *) str);
|
||||
write(context->out->fh, enc_buffer, enc_buffer_used);
|
||||
}
|
||||
|
||||
|
||||
|
||||
int write_cc_buffer_as_sami(struct eia608_screen *data, struct s_context_cc608 *context)
|
||||
{
|
||||
LLONG startms, endms;
|
||||
int wrote_something=0;
|
||||
startms = context->current_visible_start_ms;
|
||||
|
||||
startms+=subs_delay;
|
||||
if (startms<0) // Drop screens that because of subs_delay start too early
|
||||
return 0;
|
||||
|
||||
endms = get_visible_end()+subs_delay;
|
||||
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_608, "\r%s\n", str);
|
||||
}
|
||||
enc_buffer_used=encode_line (enc_buffer,(unsigned char *) str);
|
||||
write (context->out->fh, enc_buffer,enc_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_608, "\r");
|
||||
dbg_print(CCX_DMT_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_608, "\r%s\n", str);
|
||||
}
|
||||
enc_buffer_used=encode_line (enc_buffer,(unsigned char *) str);
|
||||
write(context->out->fh, enc_buffer, enc_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_608, "\r%s\n", str);
|
||||
}
|
||||
enc_buffer_used=encode_line (enc_buffer,(unsigned char *) str);
|
||||
write(context->out->fh, enc_buffer, enc_buffer_used);
|
||||
return wrote_something;
|
||||
}
|
||||
@@ -1,156 +0,0 @@
|
||||
#include "ccextractor.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 s_context_cc608 *context, LLONG ms_start, LLONG ms_end)
|
||||
{
|
||||
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_608, "\r%s\n", str);
|
||||
}
|
||||
enc_buffer_used=encode_line (enc_buffer,(unsigned char *) str);
|
||||
write (context->out->fh, enc_buffer,enc_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_608, "\r");
|
||||
dbg_print(CCX_DMT_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_608, "\r%s\n", str);
|
||||
}
|
||||
enc_buffer_used=encode_line (enc_buffer,(unsigned char *) str);
|
||||
write(context->out->fh, enc_buffer, enc_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_608, "\r%s\n", str);
|
||||
}
|
||||
enc_buffer_used=encode_line (enc_buffer,(unsigned char *) str);
|
||||
write (context->out->fh, enc_buffer,enc_buffer_used);
|
||||
sprintf ((char *) str,"</p>\n");
|
||||
}
|
||||
|
||||
|
||||
|
||||
int write_cc_buffer_as_smptett(struct eia608_screen *data, struct s_context_cc608 *context)
|
||||
{
|
||||
unsigned h1,m1,s1,ms1;
|
||||
unsigned h2,m2,s2,ms2;
|
||||
LLONG endms;
|
||||
int wrote_something=0;
|
||||
LLONG startms = context->current_visible_start_ms;
|
||||
|
||||
startms+=subs_delay;
|
||||
if (startms<0) // Drop screens that because of subs_delay start too early
|
||||
return 0;
|
||||
|
||||
endms = get_visible_end()+subs_delay;
|
||||
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_608, "\r%s\n", str);
|
||||
}
|
||||
enc_buffer_used=encode_line (enc_buffer,(unsigned char *) str);
|
||||
write(context->out->fh, enc_buffer, enc_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_608, "\r");
|
||||
dbg_print(CCX_DMT_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_608, "\r%s\n", str);
|
||||
}
|
||||
enc_buffer_used=encode_line (enc_buffer,(unsigned char *) str);
|
||||
write(context->out->fh, enc_buffer, enc_buffer_used);
|
||||
|
||||
if (ccx_options.encoding!=CCX_ENC_UNICODE)
|
||||
{
|
||||
dbg_print(CCX_DMT_608, "\r%s\n", str);
|
||||
}
|
||||
enc_buffer_used=encode_line (enc_buffer,(unsigned char *) str);
|
||||
//write (wb->fh, enc_buffer,enc_buffer_used);
|
||||
|
||||
return wrote_something;
|
||||
}
|
||||
195
src/608_srt.c
195
src/608_srt.c
@@ -1,195 +0,0 @@
|
||||
#include "ccextractor.h"
|
||||
|
||||
|
||||
/* The timing here is not PTS based, but output based, i.e. user delay must be accounted for
|
||||
if there is any */
|
||||
void write_stringz_as_srt (char *string, struct s_context_cc608 *context, LLONG ms_start, LLONG ms_end)
|
||||
{
|
||||
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.
|
||||
char timeline[128];
|
||||
context->srt_counter++;
|
||||
sprintf(timeline, "%u\r\n", context->srt_counter);
|
||||
enc_buffer_used=encode_line (enc_buffer,(unsigned char *) timeline);
|
||||
write(context->out->fh, enc_buffer, enc_buffer_used);
|
||||
sprintf (timeline, "%02u:%02u:%02u,%03u --> %02u:%02u:%02u,%03u\r\n",
|
||||
h1,m1,s1,ms1, h2,m2,s2,ms2);
|
||||
enc_buffer_used=encode_line (enc_buffer,(unsigned char *) timeline);
|
||||
dbg_print(CCX_DMT_608, "\n- - - SRT caption - - -\n");
|
||||
dbg_print(CCX_DMT_608, "%s",timeline);
|
||||
|
||||
write(context->out->fh, enc_buffer, enc_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_srt() - 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_608, "\r");
|
||||
dbg_print(CCX_DMT_608, "%s\n",subline);
|
||||
}
|
||||
write(context->out->fh, el, u);
|
||||
write(context->out->fh, encoded_crlf, encoded_crlf_length);
|
||||
begin+= strlen ((const char *) begin)+1;
|
||||
}
|
||||
|
||||
dbg_print(CCX_DMT_608, "- - - - - - - - - - - -\r\n");
|
||||
|
||||
write(context->out->fh, encoded_crlf, encoded_crlf_length);
|
||||
free(el);
|
||||
}
|
||||
|
||||
int write_cc_buffer_as_srt(struct eia608_screen *data, struct s_context_cc608 *context)
|
||||
{
|
||||
unsigned h1,m1,s1,ms1;
|
||||
unsigned h2,m2,s2,ms2;
|
||||
LLONG ms_start, ms_end;
|
||||
int wrote_something = 0;
|
||||
ms_start = context->current_visible_start_ms;
|
||||
|
||||
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;
|
||||
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 .srt
|
||||
return 0;
|
||||
|
||||
ms_start+=subs_delay;
|
||||
if (ms_start<0) // Drop screens that because of subs_delay start too early
|
||||
return 0;
|
||||
|
||||
ms_end=get_visible_end()+subs_delay;
|
||||
|
||||
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\r\n", context->srt_counter);
|
||||
enc_buffer_used=encode_line (enc_buffer,(unsigned char *) timeline);
|
||||
write(context->out->fh, enc_buffer, enc_buffer_used);
|
||||
sprintf (timeline, "%02u:%02u:%02u,%03u --> %02u:%02u:%02u,%03u\r\n",
|
||||
h1,m1,s1,ms1, h2,m2,s2,ms2);
|
||||
enc_buffer_used=encode_line (enc_buffer,(unsigned char *) timeline);
|
||||
|
||||
dbg_print(CCX_DMT_608, "\n- - - SRT caption ( %d) - - -\n", context->srt_counter);
|
||||
dbg_print(CCX_DMT_608, "%s",timeline);
|
||||
|
||||
write (context->out->fh, enc_buffer,enc_buffer_used);
|
||||
for (int i=0;i<15;i++)
|
||||
{
|
||||
if (data->row_used[i])
|
||||
{
|
||||
if (ccx_options.sentence_cap)
|
||||
{
|
||||
capitalize (i,data);
|
||||
correct_case(i,data);
|
||||
}
|
||||
if (ccx_options.autodash && ccx_options.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);
|
||||
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<CC608_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)
|
||||
write(context->out->fh, "- ", 2);
|
||||
prev_line_start=first;
|
||||
prev_line_end=last;
|
||||
prev_line_center1=center1;
|
||||
prev_line_center2=center2;
|
||||
|
||||
}
|
||||
int length = get_decoder_line_encoded (subline, i, data);
|
||||
if (ccx_options.encoding!=CCX_ENC_UNICODE)
|
||||
{
|
||||
dbg_print(CCX_DMT_608, "\r");
|
||||
dbg_print(CCX_DMT_608, "%s\n",subline);
|
||||
}
|
||||
write(context->out->fh, subline, length);
|
||||
write(context->out->fh, encoded_crlf, encoded_crlf_length);
|
||||
wrote_something=1;
|
||||
// fprintf (wb->fh,encoded_crlf);
|
||||
}
|
||||
}
|
||||
dbg_print(CCX_DMT_608, "- - - - - - - - - - - -\r\n");
|
||||
|
||||
// fprintf (wb->fh, encoded_crlf);
|
||||
write (context->out->fh, encoded_crlf, encoded_crlf_length);
|
||||
return wrote_something;
|
||||
}
|
||||
6
src/CCExtractorConfig.h.in
Normal file
6
src/CCExtractorConfig.h.in
Normal file
@@ -0,0 +1,6 @@
|
||||
/**
|
||||
* the configured options and settings for CCExtractor
|
||||
*/
|
||||
|
||||
#define CCExtractor_VERSION_MAJOR @CCEXTRACTOR_VERSION_MAJOR@
|
||||
#define CCExtractor_VERSION_MINOR @CCEXTRACTOR_VERSION_MINOR@
|
||||
54
src/CMakeLists.txt
Normal file
54
src/CMakeLists.txt
Normal file
@@ -0,0 +1,54 @@
|
||||
cmake_minimum_required (VERSION 3.0.2)
|
||||
|
||||
project (CCExtractor)
|
||||
|
||||
option(WITH_FFMPEG "Build using FFmpeg demuxer and decoder" OFF)
|
||||
#Version number
|
||||
set (CCEXTRACTOR_VERSION_MAJOR 0)
|
||||
set (CCEXTRACTOR_VERSION_MINOR 75)
|
||||
|
||||
# configure a header file to pass some of the CMake settings
|
||||
# to the source code
|
||||
configure_file (
|
||||
"${PROJECT_SOURCE_DIR}/CCExtractorConfig.h.in"
|
||||
"${PROJECT_BINARY_DIR}/CCExtractorConfig.h"
|
||||
)
|
||||
|
||||
|
||||
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)
|
||||
|
||||
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)
|
||||
set (EXTRA_LIBS ${EXTRA_LIBS} ccx)
|
||||
set (EXTRA_LIBS ${EXTRA_LIBS} png)
|
||||
set (EXTRA_LIBS ${EXTRA_LIBS} m)
|
||||
|
||||
########################################################
|
||||
# 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} )
|
||||
|
||||
SET (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DENABLE_FFMPEG")
|
||||
endif (WITH_FFMPEG)
|
||||
|
||||
add_executable(ccextractor ${SOURCEFILE})
|
||||
target_link_libraries (ccextractor ${EXTRA_LIBS})
|
||||
|
||||
install (TARGETS ccextractor DESTINATION bin)
|
||||
@@ -1,25 +0,0 @@
|
||||
/* Functions used by both the 608 and 708 decoders. An effort should be
|
||||
made to reuse, not duplicate, as many functions as possible */
|
||||
|
||||
#include "ccextractor.h"
|
||||
|
||||
/* 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 fts = get_fts();
|
||||
if (fts <= minimum_fts)
|
||||
fts = minimum_fts+1;
|
||||
dbg_print(CCX_DMT_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)
|
||||
{
|
||||
LLONG fts = get_fts();
|
||||
if (fts>minimum_fts)
|
||||
minimum_fts=fts;
|
||||
dbg_print(CCX_DMT_608, "Visible End time=%s\n", print_mstime(fts));
|
||||
return fts;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,605 +0,0 @@
|
||||
#ifndef CCX_CCEXTRACTOR_H
|
||||
#define CCX_CCEXTRACTOR_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <time.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdarg.h>
|
||||
#include <errno.h>
|
||||
|
||||
// compatibility across platforms
|
||||
#include "platform.h"
|
||||
|
||||
#define VERSION "0.70"
|
||||
|
||||
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)
|
||||
|
||||
#include "608.h"
|
||||
#include "708.h"
|
||||
#include "bitstream.h"
|
||||
#include "constants.h"
|
||||
|
||||
#define TS_PMT_MAP_SIZE 128
|
||||
|
||||
struct ccx_boundary_time
|
||||
{
|
||||
int hh,mm,ss;
|
||||
LLONG time_in_ms;
|
||||
int set;
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
// TODO: add more options, and (perhaps) reduce other ccextractor options?
|
||||
int showStartTime, showEndTime; // Show start and/or end time.
|
||||
int showMode; // Show which mode if available (E.G.: POP, RU1, ...)
|
||||
int showCC; // Show which CC channel has been captured.
|
||||
int relativeTimestamp; // Timestamps relative to start of sample or in UTC?
|
||||
int xds; // Show XDS or not
|
||||
int useColors; // Add colors or no colors
|
||||
|
||||
} ccx_transcript_format;
|
||||
|
||||
extern ccx_transcript_format ccx_default_transcript_settings;
|
||||
|
||||
struct ccx_s_options // Options from user parameters
|
||||
{
|
||||
int extract; // Extract 1st, 2nd or both fields
|
||||
int cc_channel; // Channel we want to dump in srt mode
|
||||
int buffer_input;
|
||||
int direct_rollup;
|
||||
int nofontcolor;
|
||||
int notypesetting;
|
||||
struct ccx_boundary_time extraction_start, extraction_end; // Segment we actually process
|
||||
int print_file_reports;
|
||||
|
||||
/* subtitle codec type */
|
||||
enum cxx_code_type codec;
|
||||
enum cxx_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;
|
||||
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 norollup; // If 1, write one line at a time
|
||||
int forced_ru; // 0=Disabled, 1, 2 or 3=max lines in roll-up mode
|
||||
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
|
||||
>0 -> Live stream with a timeout of this value in seconds */
|
||||
int messages_target; // 0 = nowhere (quiet), 1=stdout, 2=stderr
|
||||
/* Levenshtein's parameters, for string comparison */
|
||||
int levdistmincnt, levdistmaxpct; // Means 2 fails or less is "the same", 10% or less is also "the same"
|
||||
int investigate_packets; // Look for captions in all packets when everything else fails
|
||||
int fullbin; // Disable pruning of padding cc blocks
|
||||
int nosync; // Disable syncing
|
||||
unsigned 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
|
||||
/* MP4 related stuff */
|
||||
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?
|
||||
unsigned teletext_mode; // 0=Disabled, 1 = Not found, 2=Found
|
||||
ccx_transcript_format transcript_settings; // Keeps the settings for generating transcript output files.
|
||||
char millis_separator;
|
||||
LLONG screens_to_process; // How many screenfuls we want?
|
||||
enum ccx_encoding_type encoding;
|
||||
enum ccx_output_format write_format; // 0=Raw, 1=srt, 2=SMI
|
||||
enum ccx_output_date_format date_format;
|
||||
enum color_code cc608_default_color;
|
||||
char *output_filename;
|
||||
char *out_elementarystream_filename;
|
||||
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 */
|
||||
in_addr_t udpaddr;
|
||||
unsigned udpport; // Non-zero => Listen for UDP packets on this port, no files.
|
||||
int line_terminator_lf; // 0 = CRLF, 1=LF
|
||||
int noautotimeref; // Do NOT set time automatically?
|
||||
enum ccx_datasource input_source; // Files, stdin or network
|
||||
|
||||
};
|
||||
|
||||
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 ccx_s_write
|
||||
{
|
||||
int fh;
|
||||
char *filename;
|
||||
void* spupng_data;
|
||||
};
|
||||
|
||||
|
||||
struct gop_time_code
|
||||
{
|
||||
int drop_frame_flag;
|
||||
int time_code_hours;
|
||||
int time_code_minutes;
|
||||
int marker_bit;
|
||||
int time_code_seconds;
|
||||
int time_code_pictures;
|
||||
int inited;
|
||||
LLONG ms;
|
||||
};
|
||||
|
||||
|
||||
/* Report information */
|
||||
#define SUB_STREAMS_CNT 10
|
||||
struct file_report_t
|
||||
{
|
||||
unsigned program_cnt;
|
||||
unsigned width;
|
||||
unsigned height;
|
||||
unsigned aspect_ratio;
|
||||
unsigned frame_rate;
|
||||
unsigned xds : 1;
|
||||
unsigned cc_channels_608[4];
|
||||
unsigned services708[63];
|
||||
unsigned dvb_sub_pid[SUB_STREAMS_CNT];
|
||||
unsigned tlt_sub_pid[SUB_STREAMS_CNT];
|
||||
unsigned mp4_cc_track_cnt;
|
||||
} file_report;
|
||||
|
||||
|
||||
// Stuff for telcc.c
|
||||
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
|
||||
double offset; // time offset in seconds
|
||||
uint8_t bom : 1; // print UTF-8 BOM characters at the beginning of output
|
||||
uint8_t nonempty : 1; // produce at least one (dummy) frame
|
||||
// uint8_t se_mode : 1; // search engine compatible mode => Uses CCExtractor's write_format
|
||||
// uint64_t utc_refvalue; // UTC referential value => Moved to CCExtractor global, so can be used for 608 too
|
||||
uint16_t user_page; // Page selected by user, which MIGHT be different to 'page' depending on autodetection stuff
|
||||
};
|
||||
|
||||
#define buffered_skip(bytes) if (bytes<=bytesinbuffer-filebuffer_pos) { \
|
||||
filebuffer_pos+=bytes; \
|
||||
result=bytes; \
|
||||
} else result=buffered_read_opt (NULL,bytes);
|
||||
|
||||
#define buffered_read(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 (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(buffer) if (bytesinbuffer-filebuffer_pos) { \
|
||||
if (buffer) { *buffer=filebuffer[filebuffer_pos]; \
|
||||
filebuffer_pos++; \
|
||||
result=1; } \
|
||||
} else result=buffered_read_opt (buffer,1);
|
||||
|
||||
extern LLONG buffered_read_opt (unsigned char *buffer, unsigned int bytes);
|
||||
|
||||
//params.c
|
||||
void parse_parameters (int argc, char *argv[]);
|
||||
void usage (void);
|
||||
int atoi_hex (char *s);
|
||||
int stringztoms (const char *s, struct ccx_boundary_time *bt);
|
||||
|
||||
// general_loop.c
|
||||
void position_sanity_check ();
|
||||
int init_file_buffer( void );
|
||||
LLONG ps_getmoredata( void );
|
||||
LLONG general_getmoredata( void );
|
||||
void raw_loop (void);
|
||||
LLONG process_raw (void);
|
||||
void general_loop(void);
|
||||
void processhex (char *filename);
|
||||
void rcwt_loop( void );
|
||||
|
||||
#ifndef __cplusplus
|
||||
#define false 0
|
||||
#define true 1
|
||||
#endif
|
||||
|
||||
// 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_xds_program_name (const char *program_name);
|
||||
void activity_xds_network_call_letters (const char *program_name);
|
||||
void activity_xds_program_identification_number (unsigned minutes, unsigned hours, unsigned date, unsigned month);
|
||||
void activity_xds_program_description (int line_num, const char *program_desc);
|
||||
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( void );
|
||||
|
||||
// wtv_functions.c
|
||||
LLONG wtv_getmoredata( void );
|
||||
|
||||
// avc_functions.c
|
||||
LLONG process_avc (unsigned char *avcbuf, LLONG avcbuflen);
|
||||
void init_avc(void);
|
||||
|
||||
// es_functions.c
|
||||
LLONG process_m2v (unsigned char *data, LLONG length);
|
||||
|
||||
extern unsigned top_field_first;
|
||||
|
||||
// es_userdata.c
|
||||
int user_data(struct bitstream *ustream, int udtype);
|
||||
|
||||
// bitstream.c - see bitstream.h
|
||||
|
||||
// 608.c
|
||||
int write_cc_buffer(struct s_context_cc608 *context);
|
||||
unsigned char *debug_608toASC (unsigned char *ccdata, int channel);
|
||||
|
||||
|
||||
// cc_decoders_common.c
|
||||
LLONG get_visible_start (void);
|
||||
LLONG get_visible_end (void);
|
||||
|
||||
// file_functions.c
|
||||
LLONG getfilesize (int in);
|
||||
LLONG gettotalfilessize (void);
|
||||
void prepare_for_new_file (void);
|
||||
void close_input_file (void);
|
||||
int switch_to_next_file (LLONG bytesinbuffer);
|
||||
int init_sockets (void);
|
||||
void return_to_buffer (unsigned char *buffer, unsigned int bytes);
|
||||
|
||||
// timing.c
|
||||
void set_fts(void);
|
||||
LLONG get_fts(void);
|
||||
LLONG get_fts_max(void);
|
||||
char *print_mstime( LLONG mstime );
|
||||
char *print_mstime2buf( LLONG mstime , char *buf );
|
||||
void print_debug_timing( void );
|
||||
int gop_accepted(struct gop_time_code* g );
|
||||
void calculate_ms_gop_time (struct gop_time_code *g);
|
||||
|
||||
// sequencing.c
|
||||
void init_hdcc (void);
|
||||
void store_hdcc(unsigned char *cc_data, int cc_count, int sequence_number, LLONG current_fts);
|
||||
void anchor_hdcc(int seq);
|
||||
void process_hdcc (void);
|
||||
int do_cb (unsigned char *cc_block);
|
||||
|
||||
// mp4.c
|
||||
int processmp4 (char *file);
|
||||
|
||||
// params_dump.c
|
||||
void params_dump(void);
|
||||
void print_file_report(void);
|
||||
|
||||
// output.c
|
||||
void init_write (struct ccx_s_write *wb);
|
||||
void writeraw (const unsigned char *data, int length, struct ccx_s_write *wb);
|
||||
void writedata(const unsigned char *data, int length, struct s_context_cc608 *context);
|
||||
void flushbuffer (struct ccx_s_write *wb, int closefile);
|
||||
void printdata (const unsigned char *data1, int length1,const unsigned char *data2, int length2);
|
||||
void writercwtdata (const unsigned char *data);
|
||||
|
||||
// stream_functions.c
|
||||
void detect_stream_type (void);
|
||||
int detect_myth( void );
|
||||
int read_video_pes_header (unsigned char *header, int *headerlength, int sbuflen);
|
||||
int read_pts_pes(unsigned char*header, int len);
|
||||
|
||||
// ts_functions.c
|
||||
void init_ts( void );
|
||||
int ts_readpacket(void);
|
||||
long ts_readstream(void);
|
||||
LLONG ts_getmoredata( void );
|
||||
int write_section(struct ts_payload *payload, unsigned char*buf, int size, int pos);
|
||||
int parse_PMT (unsigned char *buf,int len, int pos);
|
||||
int parse_PAT (void);
|
||||
|
||||
// myth.c
|
||||
void myth_loop(void);
|
||||
|
||||
// mp4_bridge2bento4.c
|
||||
void mp4_loop (char *filename);
|
||||
|
||||
// xds.c
|
||||
void process_xds_bytes (const unsigned char hi, int lo);
|
||||
void do_end_of_xds (unsigned char expected_checksum);
|
||||
void xds_init();
|
||||
|
||||
// ccextractor.c
|
||||
LLONG calculate_gop_mstime (struct gop_time_code *g);
|
||||
void set_fts(void);
|
||||
char *print_mstime( LLONG mstime );
|
||||
void print_debug_timing( void );
|
||||
int switch_to_next_file (LLONG bytesinbuffer);
|
||||
|
||||
// utility.c
|
||||
void fatal(int exit_code, const char *fmt, ...);
|
||||
void dvprint(const char *fmt, ...);
|
||||
void mprint (const char *fmt, ...);
|
||||
void subsprintf (const char *fmt, ...);
|
||||
void dbg_print(LLONG mask, const char *fmt, ...);
|
||||
void fdprintf (int fd, const char *fmt, ...);
|
||||
void init_boundary_time (struct ccx_boundary_time *bt);
|
||||
void sleep_secs (int secs);
|
||||
void dump (LLONG mask, unsigned char *start, int l, unsigned long abs_start, unsigned clear_high_bit);
|
||||
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 millis_to_date (uint64_t timestamp, char *buffer) ;
|
||||
int levenshtein_dist (const uint64_t *s1, const uint64_t *s2, unsigned s1len, unsigned s2len);
|
||||
|
||||
void init_context_cc608(struct s_context_cc608 *data, int field);
|
||||
unsigned encode_line (unsigned char *buffer, unsigned char *text);
|
||||
void buffered_seek (int offset);
|
||||
void write_subtitle_file_header(struct ccx_s_write *out);
|
||||
void write_subtitle_file_footer(struct ccx_s_write *out);
|
||||
extern void build_parity_table(void);
|
||||
|
||||
void tlt_process_pes_packet(uint8_t *buffer, uint16_t size) ;
|
||||
void telxcc_init(void);
|
||||
void telxcc_close(void);
|
||||
void mstotime(LLONG milli, unsigned *hours, unsigned *minutes,
|
||||
unsigned *seconds, unsigned *ms);
|
||||
|
||||
extern struct gop_time_code gop_time, first_gop_time, printed_gop;
|
||||
extern int gop_rollover;
|
||||
extern LLONG min_pts, sync_pts, current_pts;
|
||||
extern unsigned rollover_bits;
|
||||
extern uint32_t global_timestamp, min_global_timestamp;
|
||||
extern int global_timestamp_inited;
|
||||
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)
|
||||
// Count 608 (per field) and 708 blocks since last set_fts() call
|
||||
extern int cb_field1, cb_field2, cb_708;
|
||||
extern int saw_caption_block;
|
||||
|
||||
|
||||
extern unsigned char *buffer;
|
||||
extern LLONG past;
|
||||
extern LLONG total_inputsize, total_past; // Only in binary concat mode
|
||||
|
||||
extern char **inputfile;
|
||||
extern int current_file;
|
||||
extern LLONG result; // Number of bytes read/skipped in last read operation
|
||||
|
||||
|
||||
extern struct sockaddr_in servaddr, cliaddr;
|
||||
|
||||
extern int strangeheader;
|
||||
|
||||
extern unsigned char startbytes[STARTBYTESLENGTH];
|
||||
extern unsigned int startbytes_pos;
|
||||
extern int startbytes_avail; // Needs to be able to hold -1 result.
|
||||
|
||||
extern unsigned char *pesheaderbuf;
|
||||
extern int pts_set; //0 = No, 1 = received, 2 = min_pts set
|
||||
extern unsigned long long max_dif;
|
||||
|
||||
extern int MPEG_CLOCK_FREQ; // This is part of the standard
|
||||
|
||||
extern unsigned pts_big_change;
|
||||
extern unsigned total_frames_count;
|
||||
extern unsigned total_pulldownfields;
|
||||
extern unsigned total_pulldownframes;
|
||||
|
||||
extern int CaptionGap;
|
||||
|
||||
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 struct s_context_cc608 context_cc608_field_1, context_cc608_field_2;
|
||||
|
||||
extern const char *desc[256];
|
||||
|
||||
extern FILE *fh_out_elementarystream;
|
||||
extern int infd;
|
||||
extern int false_pict_header;
|
||||
|
||||
extern int stat_numuserheaders;
|
||||
extern int stat_dvdccheaders;
|
||||
extern int stat_scte20ccheaders;
|
||||
extern int stat_replay5000headers;
|
||||
extern int stat_replay4000headers;
|
||||
extern int stat_dishheaders;
|
||||
extern int stat_hdtv;
|
||||
extern int stat_divicom;
|
||||
extern enum ccx_stream_mode_enum stream_mode;
|
||||
extern int cc_stats[4];
|
||||
extern LLONG inputsize;
|
||||
|
||||
extern LLONG subs_delay;
|
||||
extern int startcredits_displayed, end_credits_displayed;
|
||||
extern LLONG last_displayed_subs_ms;
|
||||
extern int processed_enough;
|
||||
extern unsigned char usercolor_rgb[8];
|
||||
|
||||
extern const char *extension;
|
||||
extern long FILEBUFFERSIZE; // Uppercase because it used to be a define
|
||||
extern struct ccx_s_options ccx_options;
|
||||
extern int temp_debug;
|
||||
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 double current_fps;
|
||||
|
||||
extern int end_of_file;
|
||||
extern LLONG inbuf;
|
||||
extern enum ccx_bufferdata_type bufferdatatype; // Can be CCX_BUFFERDATA_TYPE_RAW or CCX_BUFFERDATA_TYPE_PES
|
||||
|
||||
extern unsigned top_field_first;
|
||||
|
||||
extern int firstcall;
|
||||
extern LLONG minimum_fts; // No screen should start before this FTS
|
||||
|
||||
#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 int current_field;
|
||||
|
||||
extern int last_reported_progress;
|
||||
extern int cc_to_stdout;
|
||||
|
||||
extern unsigned hauppauge_warning_shown;
|
||||
extern unsigned char *subline;
|
||||
extern int saw_gop_header;
|
||||
extern int max_gop_length;
|
||||
extern int last_gop_length;
|
||||
extern int frames_since_last_gop;
|
||||
extern LLONG fts_at_gop_start;
|
||||
extern int frames_since_ref_time;
|
||||
extern enum ccx_stream_mode_enum auto_stream;
|
||||
extern int num_input_files;
|
||||
extern char *basefilename;
|
||||
extern int do_cea708; // Process 708 data?
|
||||
extern int cea708services[63]; // [] -> 1 for services to be processed
|
||||
extern struct ccx_s_write wbout1, wbout2, *wbxdsout;
|
||||
|
||||
extern char **spell_lower;
|
||||
extern char **spell_correct;
|
||||
extern int spell_words;
|
||||
extern int spell_capacity;
|
||||
|
||||
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 enum ccx_frame_type current_picture_coding_type;
|
||||
extern int current_tref; // Store temporal reference of current frame
|
||||
|
||||
extern int cc608_parity_table[256]; // From myth
|
||||
|
||||
// From ts_functions
|
||||
extern unsigned cap_stream_type;
|
||||
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
|
||||
*/
|
||||
|
||||
#define EXIT_OK 0
|
||||
#define EXIT_NO_INPUT_FILES 2
|
||||
#define EXIT_TOO_MANY_INPUT_FILES 3
|
||||
#define EXIT_INCOMPATIBLE_PARAMETERS 4
|
||||
#define EXIT_FILE_CREATION_FAILED 5
|
||||
#define EXIT_UNABLE_TO_DETERMINE_FILE_SIZE 6
|
||||
#define EXIT_MALFORMED_PARAMETER 7
|
||||
#define EXIT_READ_ERROR 8
|
||||
#define EXIT_UNSUPPORTED 9
|
||||
#define EXIT_NOT_CLASSIFIED 300
|
||||
#define EXIT_NOT_ENOUGH_MEMORY 500
|
||||
#define EXIT_ERROR_IN_CAPITALIZATION_FILE 501
|
||||
#define EXIT_BUFFER_FULL 502
|
||||
#define EXIT_BUG_BUG 1000
|
||||
#define EXIT_MISSING_ASF_HEADER 1001
|
||||
#define EXIT_MISSING_RCWT_HEADER 1002
|
||||
|
||||
extern int PIDs_seen[65536];
|
||||
extern struct PMT_entry *PIDs_programs[65536];
|
||||
|
||||
extern LLONG ts_start_of_xds;
|
||||
//extern int timestamps_on_transcript;
|
||||
|
||||
extern unsigned teletext_mode;
|
||||
|
||||
#define MAX_TLT_PAGES 1000
|
||||
extern short int seen_sub_page[MAX_TLT_PAGES];
|
||||
|
||||
extern uint64_t utc_refvalue; // UTC referential value
|
||||
extern struct ccx_s_teletext_config tlt_config;
|
||||
extern uint32_t tlt_packet_counter;
|
||||
extern uint32_t tlt_frames_produced;
|
||||
|
||||
#endif
|
||||
@@ -25,7 +25,7 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include "../disable_warnings.h"
|
||||
#include "disable_warnings.h"
|
||||
#include <gpac/setup.h>
|
||||
|
||||
#ifndef GPAC_DISABLE_AVILIB
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
*/
|
||||
#include "../disable_warnings.h"
|
||||
#include "disable_warnings.h"
|
||||
#include <gpac/internal/isomedia_dev.h>
|
||||
|
||||
#ifndef GPAC_DISABLE_ISOM
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
*/
|
||||
#include "../disable_warnings.h"
|
||||
#include "disable_warnings.h"
|
||||
#include <gpac/internal/isomedia_dev.h>
|
||||
|
||||
#ifndef GPAC_DISABLE_ISOM
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
*/
|
||||
#include "../disable_warnings.h"
|
||||
#include "disable_warnings.h"
|
||||
#include <gpac/internal/isomedia_dev.h>
|
||||
#include <gpac/network.h>
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
*/
|
||||
#include "../disable_warnings.h"
|
||||
#include "disable_warnings.h"
|
||||
#include <gpac/internal/odf_dev.h>
|
||||
#include <gpac/constants.h>
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
*/
|
||||
#include "../disable_warnings.h"
|
||||
#include "disable_warnings.h"
|
||||
#include <gpac/tools.h>
|
||||
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include "../disable_warnings.h"
|
||||
#include "disable_warnings.h"
|
||||
#include <gpac/internal/odf_dev.h>
|
||||
|
||||
#ifndef GPAC_MINIMAL_ODF
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
*/
|
||||
#include "../disable_warnings.h"
|
||||
#include "disable_warnings.h"
|
||||
#include <gpac/internal/isomedia_dev.h>
|
||||
#include <gpac/network.h>
|
||||
#ifndef _WIN32
|
||||
@@ -398,23 +398,6 @@ GF_ISOFile *gf_isom_open_file(const char *fileName, u32 OpenMode, const char *tm
|
||||
return mov;
|
||||
}
|
||||
|
||||
#ifndef WIN32
|
||||
void gf_utc_time_since_1970(u32 *sec, u32 *msec)
|
||||
{
|
||||
#if defined (WIN32) && !defined(_WIN32_WCE)
|
||||
struct _timeb tb;
|
||||
_ftime( &tb );
|
||||
*sec = (u32) tb.time;
|
||||
*msec = tb.millitm;
|
||||
#else
|
||||
struct timeval tv;
|
||||
gettimeofday(&tv, NULL);
|
||||
*sec = tv.tv_sec;
|
||||
*msec = tv.tv_usec/1000;
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
u64 gf_isom_get_mp4time()
|
||||
{
|
||||
u32 calctime, msec;
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include "../disable_warnings.h"
|
||||
#include "disable_warnings.h"
|
||||
#include <gpac/internal/isomedia_dev.h>
|
||||
#include <gpac/constants.h>
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
*/
|
||||
#include "../disable_warnings.h"
|
||||
#include "disable_warnings.h"
|
||||
#include <gpac/internal/isomedia_dev.h>
|
||||
|
||||
#if !defined(GPAC_DISABLE_ISOM) && !defined(GPAC_DISABLE_ISOM_WRITE)
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
*/
|
||||
#include "../disable_warnings.h"
|
||||
#include "disable_warnings.h"
|
||||
#include <gpac/internal/isomedia_dev.h>
|
||||
|
||||
#if !defined(GPAC_DISABLE_ISOM) && !defined(GPAC_DISABLE_ISOM_WRITE)
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
*/
|
||||
#include "../disable_warnings.h"
|
||||
#include "disable_warnings.h"
|
||||
#include <gpac/internal/isomedia_dev.h>
|
||||
#include <gpac/constants.h>
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include "../disable_warnings.h"
|
||||
#include "disable_warnings.h"
|
||||
#include <gpac/internal/isomedia_dev.h>
|
||||
|
||||
#ifndef GPAC_DISABLE_ISOM
|
||||
|
||||
@@ -3,9 +3,12 @@
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <gpac/isomedia.h>
|
||||
#include "../ccextractor.h"
|
||||
#include "lib_ccx.h"
|
||||
#include "utility.h"
|
||||
#include "ccx_encoders_common.h"
|
||||
#include "ccx_common_option.h"
|
||||
|
||||
void do_NAL (unsigned char *NALstart, LLONG NAL_length); // From avc_functions.c
|
||||
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
|
||||
|
||||
static short bswap16(short v)
|
||||
@@ -24,14 +27,14 @@ static struct {
|
||||
unsigned type[32];
|
||||
}s_nalu_stats;
|
||||
|
||||
static int process_avc_sample(u32 timescale, GF_AVCConfig* c, GF_ISOSample* s)
|
||||
static int process_avc_sample(struct lib_ccx_ctx *ctx, u32 timescale, GF_AVCConfig* c, GF_ISOSample* s, struct cc_subtitle *sub)
|
||||
{
|
||||
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
|
||||
|
||||
if (pts_set==0)
|
||||
|
||||
if (pts_set==0)
|
||||
pts_set=1;
|
||||
set_fts();
|
||||
|
||||
@@ -59,19 +62,20 @@ static int process_avc_sample(u32 timescale, GF_AVCConfig* c, GF_ISOSample* s)
|
||||
temp_debug=0;
|
||||
|
||||
if (nal_length>0)
|
||||
do_NAL ((unsigned char *) &(s->data[i]) ,nal_length);
|
||||
do_NAL (ctx, (unsigned char *) &(s->data[i]) ,nal_length, sub);
|
||||
i += nal_length;
|
||||
} // outer for
|
||||
assert(i == s->dataLength);
|
||||
|
||||
return status;
|
||||
}
|
||||
static int process_xdvb_track(const char* basename, GF_ISOFile* f, u32 track)
|
||||
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;
|
||||
if((sample_count = gf_isom_get_sample_count(f, track)) < 1){
|
||||
if((sample_count = gf_isom_get_sample_count(f, track)) < 1)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -79,29 +83,30 @@ static int process_xdvb_track(const char* basename, GF_ISOFile* f, u32 track)
|
||||
|
||||
status = 0;
|
||||
|
||||
for(i = 0; i < sample_count; i++){
|
||||
for(i = 0; i < sample_count; i++)
|
||||
{
|
||||
u32 sdi;
|
||||
|
||||
GF_ISOSample* s = gf_isom_get_sample(f, track, i + 1, &sdi);
|
||||
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();
|
||||
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();
|
||||
|
||||
process_m2v ((unsigned char *) s->data,s->dataLength);
|
||||
process_m2v (ctx, (unsigned char *) s->data,s->dataLength, sub);
|
||||
gf_isom_sample_del(&s);
|
||||
}
|
||||
|
||||
int progress = (int) ((i*100) / sample_count);
|
||||
if (last_reported_progress != progress)
|
||||
{
|
||||
int cur_sec = (int) (get_fts() / 1000);
|
||||
activity_progress(progress, cur_sec/60, cur_sec%60);
|
||||
last_reported_progress = progress;
|
||||
}
|
||||
int progress = (int) ((i*100) / sample_count);
|
||||
if (ctx->last_reported_progress != progress)
|
||||
{
|
||||
int cur_sec = (int) (get_fts() / 1000);
|
||||
activity_progress(progress, cur_sec/60, cur_sec%60);
|
||||
ctx->last_reported_progress = progress;
|
||||
}
|
||||
}
|
||||
int cur_sec = (int) (get_fts() / 1000);
|
||||
activity_progress(100, cur_sec/60, cur_sec%60);
|
||||
@@ -109,13 +114,14 @@ static int process_xdvb_track(const char* basename, GF_ISOFile* f, u32 track)
|
||||
return status;
|
||||
}
|
||||
|
||||
static int process_avc_track(const char* basename, GF_ISOFile* f, u32 track)
|
||||
static int process_avc_track(struct lib_ccx_ctx *ctx, const char* basename, GF_ISOFile* f, u32 track, struct cc_subtitle *sub)
|
||||
{
|
||||
u32 timescale, i, sample_count, last_sdi = 0;
|
||||
int status;
|
||||
GF_AVCConfig* c = NULL;
|
||||
|
||||
if((sample_count = gf_isom_get_sample_count(f, track)) < 1){
|
||||
if((sample_count = gf_isom_get_sample_count(f, track)) < 1)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -123,19 +129,24 @@ static int process_avc_track(const char* basename, GF_ISOFile* f, u32 track)
|
||||
|
||||
status = 0;
|
||||
|
||||
for(i = 0; i < sample_count; i++){
|
||||
for(i = 0; i < sample_count; i++)
|
||||
{
|
||||
u32 sdi;
|
||||
|
||||
GF_ISOSample* s = gf_isom_get_sample(f, track, i + 1, &sdi);
|
||||
|
||||
if(s != NULL){
|
||||
if(sdi != last_sdi){
|
||||
if(c != NULL){
|
||||
if(s != NULL)
|
||||
{
|
||||
if(sdi != last_sdi)
|
||||
{
|
||||
if(c != NULL)
|
||||
{
|
||||
gf_odf_avc_cfg_del(c);
|
||||
c = NULL;
|
||||
}
|
||||
|
||||
if((c = gf_isom_avc_config_get(f, track, sdi)) == NULL){
|
||||
if((c = gf_isom_avc_config_get(f, track, sdi)) == NULL)
|
||||
{
|
||||
gf_isom_sample_del(&s);
|
||||
status = -1;
|
||||
break;
|
||||
@@ -144,27 +155,29 @@ static int process_avc_track(const char* basename, GF_ISOFile* f, u32 track)
|
||||
last_sdi = sdi;
|
||||
}
|
||||
|
||||
status = process_avc_sample(timescale, c, s);
|
||||
status = process_avc_sample(ctx, timescale, c, s, sub);
|
||||
|
||||
gf_isom_sample_del(&s);
|
||||
|
||||
if(status != 0){
|
||||
if(status != 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int progress = (int) ((i*100) / sample_count);
|
||||
if (last_reported_progress != progress)
|
||||
{
|
||||
int cur_sec = (int) (get_fts() / 1000);
|
||||
activity_progress(progress, cur_sec/60, cur_sec%60);
|
||||
last_reported_progress = progress;
|
||||
}
|
||||
int progress = (int) ((i*100) / sample_count);
|
||||
if (ctx->last_reported_progress != progress)
|
||||
{
|
||||
int cur_sec = (int) (get_fts() / 1000);
|
||||
activity_progress(progress, cur_sec/60, cur_sec%60);
|
||||
ctx->last_reported_progress = progress;
|
||||
}
|
||||
}
|
||||
int cur_sec = (int) (get_fts() / 1000);
|
||||
activity_progress(100, cur_sec/60, cur_sec%60);
|
||||
|
||||
if(c != NULL){
|
||||
if(c != NULL)
|
||||
{
|
||||
gf_odf_avc_cfg_del(c);
|
||||
c = NULL;
|
||||
}
|
||||
@@ -180,19 +193,25 @@ static int process_avc_track(const char* basename, GF_ISOFile* f, u32 track)
|
||||
if track is AVC track
|
||||
for each sample in track
|
||||
for each NALU in sample
|
||||
send to avc.cpp for processing
|
||||
send to avc.c for processing
|
||||
close(media)
|
||||
}
|
||||
|
||||
*/
|
||||
int processmp4 (char *file)
|
||||
int processmp4 (struct lib_ccx_ctx *ctx, char *file,void *enc_ctx)
|
||||
{
|
||||
GF_ISOFile* f;
|
||||
u32 i, j, track_count, avc_track_count, cc_track_count;
|
||||
|
||||
mprint("opening \'%s\': ", file);
|
||||
struct cc_subtitle dec_sub;
|
||||
|
||||
if((f = gf_isom_open(file, GF_ISOM_OPEN_READ, NULL)) == NULL){
|
||||
memset(&dec_sub,0,sizeof(dec_sub));
|
||||
mprint("opening \'%s\': ", file);
|
||||
#ifdef MP4_DEBUG
|
||||
gf_log_set_tool_level(GF_LOG_CONTAINER,GF_LOG_DEBUG);
|
||||
#endif
|
||||
|
||||
if((f = gf_isom_open(file, GF_ISOM_OPEN_READ, NULL)) == NULL)
|
||||
{
|
||||
mprint("failed to open\n");
|
||||
return -2;
|
||||
}
|
||||
@@ -218,7 +237,8 @@ int processmp4 (char *file)
|
||||
avc_track_count++;
|
||||
}
|
||||
|
||||
for(i = 0; i < track_count; i++){
|
||||
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);
|
||||
|
||||
@@ -226,10 +246,17 @@ int processmp4 (char *file)
|
||||
{
|
||||
if (cc_track_count && !ccx_options.mp4vidtrack)
|
||||
continue;
|
||||
if(process_xdvb_track(file, f, i + 1) != 0){
|
||||
if(process_xdvb_track(ctx, file, f, i + 1, &dec_sub) != 0)
|
||||
{
|
||||
mprint("error\n");
|
||||
return -3;
|
||||
} }
|
||||
}
|
||||
if(dec_sub.got_output)
|
||||
{
|
||||
encode_sub(enc_ctx, &dec_sub);
|
||||
dec_sub.got_output = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if( type == GF_ISOM_MEDIA_VISUAL && subtype == GF_ISOM_SUBTYPE_AVC_H264)
|
||||
{
|
||||
@@ -240,15 +267,21 @@ int processmp4 (char *file)
|
||||
{
|
||||
for (j=0; j<gf_list_count(cnf->sequenceParameterSets);j++)
|
||||
{
|
||||
GF_AVCConfigSlot* seqcnf=(GF_AVCConfigSlot* )gf_list_get(cnf->sequenceParameterSets,j);
|
||||
do_NAL ((unsigned char *) seqcnf->data,seqcnf->size);
|
||||
GF_AVCConfigSlot* seqcnf=(GF_AVCConfigSlot* )gf_list_get(cnf->sequenceParameterSets,j);
|
||||
do_NAL (ctx, (unsigned char *) seqcnf->data, seqcnf->size, &dec_sub);
|
||||
}
|
||||
}
|
||||
|
||||
if(process_avc_track(file, f, i + 1) != 0){
|
||||
if(process_avc_track(ctx, file, f, i + 1, &dec_sub) != 0)
|
||||
{
|
||||
mprint("error\n");
|
||||
return -3;
|
||||
}
|
||||
if(dec_sub.got_output)
|
||||
{
|
||||
encode_sub(enc_ctx, &dec_sub);
|
||||
dec_sub.got_output = 0;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -257,16 +290,20 @@ int processmp4 (char *file)
|
||||
if (avc_track_count && ccx_options.mp4vidtrack)
|
||||
continue;
|
||||
|
||||
/* unsigned num_streams = gf_isom_get_sample_description_count (f,i+1); */
|
||||
#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);
|
||||
// u64 duration = gf_isom_get_media_duration(f,i+1);
|
||||
/* mprint ("%u streams\n",num_streams);
|
||||
#ifdef MP$DEBUG
|
||||
u64 duration = gf_isom_get_media_duration(f,i+1);
|
||||
mprint ("%u streams\n",num_streams);
|
||||
mprint ("%u sample counts\n",num_samples);
|
||||
mprint ("%u timescale\n",(unsigned) timescale);
|
||||
mprint ("%u duration\n",(unsigned) duration); */
|
||||
mprint ("%u duration\n",(unsigned) duration);
|
||||
#endif
|
||||
for (unsigned k = 0; k <num_samples; k++)
|
||||
{
|
||||
u32 StreamDescriptionIndex;
|
||||
@@ -281,48 +318,59 @@ int processmp4 (char *file)
|
||||
ProcessingStreamDescriptionIndex=StreamDescriptionIndex;
|
||||
if (sample==NULL)
|
||||
continue;
|
||||
// mprint ("Data length: %lu\n",sample->dataLength);
|
||||
/* const LLONG timestamp = (LLONG )((sample->DTS + sample->CTS_Offset) * 1000) / timescale; */
|
||||
#ifdef DEBUG
|
||||
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();
|
||||
|
||||
// Change by Willem
|
||||
|
||||
// Apparently the first 4 bytes are the sample length, and then comes 'cdat', and then the data itself
|
||||
/*if (sample->dataLength>8 && strncmp (sample->data+4, "cdat", 4)==0)
|
||||
int atomStart = 0;
|
||||
// process Atom by Atom
|
||||
while (atomStart < sample->dataLength)
|
||||
{
|
||||
//dump (256,( unsigned char *) sample->data+8,sample->dataLength-8,0, 1);
|
||||
process608((const unsigned char *)sample->data + 8, sample->dataLength - 8, &context_cc608_field_1);
|
||||
}*/
|
||||
|
||||
// Based on https://developer.apple.com/library/prerelease/mac/documentation/QuickTime/QTFF/QTFFChap3/qtff3.html#//apple_ref/doc/uid/TP40000939-CH205-SW87
|
||||
// An atom consists of the Atom size, Atom Type and the data.
|
||||
// First 4 bytes are length in bytes of this atom
|
||||
// byte 5-8 are the atom type. Should be either cdat or cdt2
|
||||
// byte 9-x are actual data.
|
||||
// This means a sample can contain multiple atoms!
|
||||
if (sample->dataLength > 8 && strncmp(sample->data + 4, "cdat", 4) == 0){ // The format of the closed captioning sample data is a sequence of one or more atoms, one of which must be a 'cdat' atom.
|
||||
int atomStart = 0;
|
||||
// process Atom by Atom
|
||||
while (atomStart < sample->dataLength){
|
||||
unsigned int atomLength = (unsigned char)sample->data[atomStart] << 24 | (unsigned char)sample->data[atomStart + 1] << 16 | (unsigned char)sample->data[atomStart + 2] << 8 | (unsigned char)sample->data[atomStart + 3];
|
||||
if (atomLength > 8 && (strncmp(sample->data + atomStart + 4, "cdat", 4) == 0 || strncmp(sample->data + atomStart + 4, "cdt2", 4) == 0)){
|
||||
dump(256, (unsigned char *)sample->data +atomStart+ 8, atomLength - 8, 0, 1);
|
||||
process608((const unsigned char *)sample->data + atomStart + 8, atomLength - 8, &context_cc608_field_1);
|
||||
}
|
||||
atomStart += atomLength;
|
||||
char *data = sample->data + atomStart;
|
||||
unsigned int atomLength = RB32(data);
|
||||
if(atomLength < 8 || atomLength > sample->dataLength)
|
||||
{
|
||||
mprint ("Invalid atom.\n");
|
||||
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);
|
||||
#endif
|
||||
do
|
||||
{
|
||||
ret = process608((unsigned char*)data, len, ctx->dec_ctx->context_cc608_field_1, &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 (last_reported_progress != progress)
|
||||
if (ctx->last_reported_progress != progress)
|
||||
{
|
||||
int cur_sec = (int) (get_fts() / 1000);
|
||||
activity_progress(progress, cur_sec/60, cur_sec%60);
|
||||
last_reported_progress = progress;
|
||||
activity_progress(progress, cur_sec/60, cur_sec%60);
|
||||
ctx->last_reported_progress = progress;
|
||||
}
|
||||
}
|
||||
int cur_sec = (int) (get_fts() / 1000);
|
||||
@@ -336,9 +384,12 @@ int processmp4 (char *file)
|
||||
f = NULL;
|
||||
mprint ("ok\n");
|
||||
|
||||
if(avc_track_count == 0){
|
||||
if(avc_track_count == 0)
|
||||
{
|
||||
mprint("Found no AVC track(s). ", file);
|
||||
}else{
|
||||
}
|
||||
else
|
||||
{
|
||||
mprint("Found %d AVC track(s). ", avc_track_count);
|
||||
}
|
||||
if (cc_track_count)
|
||||
@@ -346,9 +397,9 @@ int processmp4 (char *file)
|
||||
else
|
||||
mprint ("found no dedicated CC track(s).\n");
|
||||
|
||||
file_report.mp4_cc_track_cnt = cc_track_count;
|
||||
ctx->freport.mp4_cc_track_cnt = cc_track_count;
|
||||
if (ccx_options.print_file_reports)
|
||||
print_file_report();
|
||||
print_file_report(ctx);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
*/
|
||||
#include "../disable_warnings.h"
|
||||
#include "disable_warnings.h"
|
||||
#include <gpac/tools.h>
|
||||
|
||||
#if defined(_WIN32_WCE)
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
*/
|
||||
#include "../disable_warnings.h"
|
||||
#include "disable_warnings.h"
|
||||
#include <gpac/internal/isomedia_dev.h>
|
||||
|
||||
#ifndef GPAC_DISABLE_ISOM
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
*/
|
||||
#include "../disable_warnings.h"
|
||||
#include "disable_warnings.h"
|
||||
#include <gpac/internal/isomedia_dev.h>
|
||||
#include <gpac/constants.h>
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
*/
|
||||
#include "../disable_warnings.h"
|
||||
#include "disable_warnings.h"
|
||||
#include <gpac/internal/isomedia_dev.h>
|
||||
#include <gpac/constants.h>
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
*/
|
||||
#include "../disable_warnings.h"
|
||||
#include "disable_warnings.h"
|
||||
#include <gpac/network.h>
|
||||
|
||||
/* the length of the URL separator ("://" || "|//") */
|
||||
|
||||
204
src/lib_ccx/608_sami.c
Normal file
204
src/lib_ccx/608_sami.c
Normal file
@@ -0,0 +1,204 @@
|
||||
#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;
|
||||
}
|
||||
225
src/lib_ccx/608_smptett.c
Normal file
225
src/lib_ccx/608_smptett.c
Normal file
@@ -0,0 +1,225 @@
|
||||
#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;
|
||||
}
|
||||
@@ -116,7 +116,7 @@ spupng_write_png(struct spupng_t *sp, struct eia608_screen* data,
|
||||
return 1;
|
||||
}
|
||||
|
||||
int
|
||||
int
|
||||
spupng_export_png(struct spupng_t *sp, struct eia608_screen* data)
|
||||
{
|
||||
png_structp png_ptr;
|
||||
@@ -124,7 +124,7 @@ spupng_export_png(struct spupng_t *sp, struct eia608_screen* data)
|
||||
png_bytep *row_pointer;
|
||||
png_bytep image;
|
||||
int ww, wh, rowstride, row_adv;
|
||||
int row;
|
||||
int row;
|
||||
|
||||
assert ((sizeof(png_byte) == sizeof(uint8_t))
|
||||
&& (sizeof(*image) == sizeof(uint8_t)));
|
||||
@@ -194,9 +194,9 @@ unknown_error:
|
||||
|
||||
int
|
||||
spupng_write_ccbuffer(struct spupng_t *sp, struct eia608_screen* data,
|
||||
struct s_context_cc608 *context)
|
||||
struct encoder_ctx *context)
|
||||
{
|
||||
LLONG ms_start = context->current_visible_start_ms + subs_delay;
|
||||
LLONG ms_start = data->start_time + context->subs_delay;
|
||||
if (ms_start < 0)
|
||||
{
|
||||
dbg_print(CCX_DMT_VERBOSE, "Negative start\n");
|
||||
@@ -220,17 +220,17 @@ spupng_write_ccbuffer(struct spupng_t *sp, struct eia608_screen* data,
|
||||
return 0;
|
||||
}
|
||||
|
||||
LLONG ms_end = get_visible_end() + subs_delay;
|
||||
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(EXIT_FILE_CREATION_FAILED, "Cannot open %s: %s\n",
|
||||
fatal(CCX_COMMON_EXIT_FILE_CREATION_FAILED, "Cannot open %s: %s\n",
|
||||
sp->pngfile, strerror(errno));
|
||||
}
|
||||
if (!spupng_export_png(sp, data))
|
||||
{
|
||||
fatal(EXIT_FILE_CREATION_FAILED, "Cannot write %s: %s\n",
|
||||
fatal(CCX_COMMON_EXIT_FILE_CREATION_FAILED, "Cannot write %s: %s\n",
|
||||
sp->pngfile, strerror(errno));
|
||||
}
|
||||
fclose(sp->fppng);
|
||||
@@ -259,15 +259,15 @@ spupng_write_ccbuffer(struct spupng_t *sp, struct eia608_screen* data,
|
||||
break;
|
||||
}
|
||||
}
|
||||
strncat(str,(const char*)subline,256);
|
||||
strncat(str,"\n",256);
|
||||
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 s_context_cc608 *context)
|
||||
int write_cc_buffer_as_spupng(struct eia608_screen *data,struct encoder_ctx *context)
|
||||
{
|
||||
if (0 != context->out->spupng_data)
|
||||
{
|
||||
@@ -276,4 +276,3 @@ int write_cc_buffer_as_spupng(struct eia608_screen *data,struct s_context_cc608
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
#ifndef __608_SPUPNG_H__
|
||||
|
||||
#include "ccextractor.h"
|
||||
#include "lib_ccx.h"
|
||||
#include "spupng_encoder.h"
|
||||
#include "ccx_encoders_common.h"
|
||||
|
||||
|
||||
int write_cc_buffer_as_spupng(struct eia608_screen *data,struct s_context_cc608 *context);
|
||||
int write_cc_buffer_as_spupng(struct eia608_screen *data,struct encoder_ctx *context);
|
||||
|
||||
#endif /* __608_SPUPNG_H__ */
|
||||
262
src/lib_ccx/608_srt.c
Normal file
262
src/lib_ccx/608_srt.c
Normal file
@@ -0,0 +1,262 @@
|
||||
#include "lib_ccx.h"
|
||||
#include "ccx_common_option.h"
|
||||
#include "ccx_encoders_common.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 */
|
||||
void 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;
|
||||
|
||||
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);
|
||||
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);
|
||||
dbg_print(CCX_DMT_DECODER_608, "\n- - - SRT caption - - -\n");
|
||||
dbg_print(CCX_DMT_DECODER_608, "%s",timeline);
|
||||
|
||||
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_srt() - 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_crlf, 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);
|
||||
free(el);
|
||||
free(unescaped);
|
||||
}
|
||||
|
||||
int write_cc_bitmap_as_srt(struct cc_subtitle *sub, struct encoder_ctx *context)
|
||||
{
|
||||
int ret = 0;
|
||||
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
|
||||
|
||||
if (context->prev_start != -1 && (sub->flags & SUB_EOD_MARKER))
|
||||
{
|
||||
ms_start = context->prev_start;
|
||||
ms_end = sub->start_time;
|
||||
}
|
||||
else if ( !(sub->flags & SUB_EOD_MARKER))
|
||||
{
|
||||
ms_start = sub->start_time;
|
||||
ms_end = sub->end_time;
|
||||
}
|
||||
else if (context->prev_start == -1 && (sub->flags & SUB_EOD_MARKER))
|
||||
{
|
||||
ms_start = 1;
|
||||
ms_end = sub->start_time;
|
||||
}
|
||||
|
||||
if(sub->nb_data == 0 )
|
||||
return 0;
|
||||
|
||||
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))
|
||||
{
|
||||
if (context->prev_start != -1 || !(sub->flags & SUB_EOD_MARKER))
|
||||
{
|
||||
mstotime (ms_start,&h1,&m1,&s1,&ms1);
|
||||
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);
|
||||
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);
|
||||
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);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
sub->nb_data = 0;
|
||||
freep(&sub->data);
|
||||
return ret;
|
||||
|
||||
}
|
||||
int write_cc_buffer_as_srt(struct eia608_screen *data, struct encoder_ctx *context)
|
||||
{
|
||||
int used;
|
||||
unsigned h1,m1,s1,ms1;
|
||||
unsigned h2,m2,s2,ms2;
|
||||
LLONG ms_start, ms_end;
|
||||
int wrote_something = 0;
|
||||
ms_start = data->start_time;
|
||||
|
||||
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;
|
||||
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 .srt
|
||||
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.
|
||||
char timeline[128];
|
||||
context->srt_counter++;
|
||||
sprintf(timeline, "%u%s", context->srt_counter, encoded_crlf);
|
||||
used = encode_line(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);
|
||||
|
||||
dbg_print(CCX_DMT_DECODER_608, "\n- - - SRT caption ( %d) - - -\n", context->srt_counter);
|
||||
dbg_print(CCX_DMT_DECODER_608, "%s",timeline);
|
||||
|
||||
write (context->out->fh, context->buffer, used);
|
||||
for (int i=0;i<15;i++)
|
||||
{
|
||||
if (data->row_used[i])
|
||||
{
|
||||
if (ccx_options.sentence_cap)
|
||||
{
|
||||
capitalize (i,data);
|
||||
correct_case(i,data);
|
||||
}
|
||||
if (ccx_options.autodash && ccx_options.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);
|
||||
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)
|
||||
write(context->out->fh, "- ", 2);
|
||||
prev_line_start=first;
|
||||
prev_line_end=last;
|
||||
prev_line_center1=center1;
|
||||
prev_line_center2=center2;
|
||||
|
||||
}
|
||||
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);
|
||||
write(context->out->fh, encoded_crlf, encoded_crlf_length);
|
||||
wrote_something=1;
|
||||
// fprintf (wb->fh,encoded_crlf);
|
||||
}
|
||||
}
|
||||
dbg_print(CCX_DMT_DECODER_608, "- - - - - - - - - - - -\r\n");
|
||||
|
||||
// fprintf (wb->fh, encoded_crlf);
|
||||
write (context->out->fh, encoded_crlf, encoded_crlf_length);
|
||||
return wrote_something;
|
||||
}
|
||||
@@ -4,10 +4,10 @@ EIA-708, SO INTERNALLY WE USE THIS TABLE (FOR CONVENIENCE)
|
||||
|
||||
00-1F -> Characters that are in the G2 group in 20-3F,
|
||||
except for 06, which is used for the closed captions
|
||||
sign "CC" which is defined in group G3 as 00. (this
|
||||
sign "CC" which is defined in group G3 as 00. (this
|
||||
is by the article 33).
|
||||
20-7F -> Group G0 as is - corresponds to the ASCII code
|
||||
80-9F -> Characters that are in the G2 group in 60-7F
|
||||
20-7F -> Group G0 as is - corresponds to the ASCII code
|
||||
80-9F -> Characters that are in the G2 group in 60-7F
|
||||
(there are several blank characters here, that's OK)
|
||||
A0-FF -> Group G1 as is - non-English characters and symbols
|
||||
*/
|
||||
@@ -31,7 +31,7 @@ unsigned char get_internal_from_G2 (unsigned char g2_char)
|
||||
if (g2_char>=0x60 && g2_char<=0x7F)
|
||||
return g2_char+0x20;
|
||||
// Rest unmapped, so we return a blank space
|
||||
return 0x20;
|
||||
return 0x20;
|
||||
}
|
||||
|
||||
// TODO: Probably not right
|
||||
@@ -39,7 +39,7 @@ unsigned char get_internal_from_G2 (unsigned char g2_char)
|
||||
unsigned char get_internal_from_G3 (unsigned char g3_char)
|
||||
{
|
||||
if (g3_char==0xa0) // The "CC" (closed captions) sign
|
||||
return 0x06;
|
||||
return 0x06;
|
||||
// Rest unmapped, so we return a blank space
|
||||
return 0x20;
|
||||
return 0x20;
|
||||
}
|
||||
27
src/lib_ccx/CMakeLists.txt
Normal file
27
src/lib_ccx/CMakeLists.txt
Normal file
@@ -0,0 +1,27 @@
|
||||
cmake_policy(SET CMP0037 NEW)
|
||||
|
||||
SET (CMAKE_C_FLAGS "-O0 -Wall -g -std=gnu99")
|
||||
|
||||
if (WITH_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})
|
||||
|
||||
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"
|
||||
"Description: Closed Caption Extraction library\n"
|
||||
"Version: 0.75\n"
|
||||
"Cflags: -I\${includedir}/\n"
|
||||
"Libs: -L\${libdir} -lccx -lpng\n"
|
||||
"Libs.private: -lpng\n" )
|
||||
|
||||
install (TARGETS ccx DESTINATION lib)
|
||||
install (FILES ${HeaderFiles} DESTINATION include)
|
||||
install (FILES ccx.pc DESTINATION lib/pkgconfig )
|
||||
@@ -1,7 +1,8 @@
|
||||
/* This file contains functions that report the user of the GUI of
|
||||
relevant events. */
|
||||
|
||||
#include "ccextractor.h"
|
||||
#include "lib_ccx.h"
|
||||
#include "ccx_common_option.h"
|
||||
|
||||
static int credits_shown=0;
|
||||
unsigned long net_activity_gui=0;
|
||||
@@ -29,71 +30,59 @@ void activity_input_file_open (const char *filename)
|
||||
if (ccx_options.gui_mode_reports)
|
||||
{
|
||||
fprintf (stderr, "###INPUTFILEOPEN#%s\n", filename);
|
||||
fflush (stderr);
|
||||
fflush (stderr);
|
||||
}
|
||||
}
|
||||
|
||||
void activity_xds_program_identification_number (unsigned minutes, unsigned hours, unsigned date, unsigned month)
|
||||
{
|
||||
if (ccx_options.gui_mode_reports)
|
||||
{
|
||||
fprintf (stderr, "###XDSPROGRAMIDENTIFICATIONNUMBER#%u#%u#%u#%u\n", minutes,hours,date,month);
|
||||
fflush (stderr);
|
||||
}
|
||||
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;
|
||||
}
|
||||
fflush(stderr);
|
||||
va_end(args);
|
||||
}
|
||||
}
|
||||
|
||||
void activity_xds_network_call_letters (const char *program_name)
|
||||
{
|
||||
if (ccx_options.gui_mode_reports)
|
||||
{
|
||||
fprintf (stderr, "###XDSNETWORKCALLLETTERS#%s\n", program_name);
|
||||
fflush (stderr);
|
||||
}
|
||||
}
|
||||
|
||||
void activity_xds_program_name (const char *program_name)
|
||||
{
|
||||
if (ccx_options.gui_mode_reports)
|
||||
{
|
||||
fprintf (stderr, "###XDSPROGRAMNAME#%s\n", program_name);
|
||||
fflush (stderr);
|
||||
}
|
||||
}
|
||||
|
||||
void activity_xds_program_description (int line_num, const char *program_desc)
|
||||
{
|
||||
if (ccx_options.gui_mode_reports)
|
||||
{
|
||||
fprintf (stderr, "###XDSPROGRAMDESC#%d#%s\n", line_num, program_desc);
|
||||
fflush (stderr);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void activity_video_info (int hor_size,int vert_size,
|
||||
void activity_video_info (int hor_size,int vert_size,
|
||||
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);
|
||||
fflush (stderr);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void activity_message (const char *fmt, ...)
|
||||
{
|
||||
if (ccx_options.gui_mode_reports)
|
||||
{
|
||||
va_list args;
|
||||
va_list args;
|
||||
fprintf (stderr, "###MESSAGE#");
|
||||
va_start(args, fmt);
|
||||
fprintf(stderr, fmt, args);
|
||||
fprintf(stderr, "\n");
|
||||
va_end(args);
|
||||
fflush (stderr);
|
||||
fflush (stderr);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -102,7 +91,7 @@ void activity_input_file_closed (void)
|
||||
if (ccx_options.gui_mode_reports)
|
||||
{
|
||||
fprintf (stderr, "###INPUTFILECLOSED\n");
|
||||
fflush (stderr);
|
||||
fflush (stderr);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -111,7 +100,7 @@ void activity_program_number (unsigned program_number)
|
||||
if (ccx_options.gui_mode_reports)
|
||||
{
|
||||
fprintf (stderr, "###TSPROGRAMNUMBER#%u\n",program_number);
|
||||
fflush (stderr);
|
||||
fflush (stderr);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -141,7 +130,6 @@ void activity_header (void)
|
||||
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");
|
||||
mprint ("--------------------------------------------------------------------------\n");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "ccextractor.h"
|
||||
#include "lib_ccx.h"
|
||||
#include "ccx_common_option.h"
|
||||
#include "asf_constants.h"
|
||||
|
||||
// Indicate first / subsequent calls to asf_getmoredata()
|
||||
@@ -28,7 +29,7 @@ uint32_t asf_readval(void *val, int ltype)
|
||||
rval = *((uint32_t*)val);
|
||||
break;
|
||||
default:
|
||||
fatal (EXIT_BUG_BUG, "Wrong type ...\n");
|
||||
fatal (CCX_COMMON_EXIT_BUG_BUG, "Wrong type ...\n");
|
||||
break;
|
||||
}
|
||||
return rval;
|
||||
@@ -36,7 +37,7 @@ uint32_t asf_readval(void *val, int ltype)
|
||||
|
||||
char *guidstr(void *val)
|
||||
{
|
||||
static char sbuf[32];
|
||||
static char sbuf[40];
|
||||
|
||||
sprintf(sbuf,"%08lX-%04X-%04X-",
|
||||
(long)*((uint32_t*)((char*)val+0)),
|
||||
@@ -60,7 +61,7 @@ asf_data asf_data_container;
|
||||
* When the function is called the next time it continues to read
|
||||
* where it stopped before, static variables make sure that parameters
|
||||
* are remembered between calls. */
|
||||
LLONG asf_getmoredata(void)
|
||||
LLONG asf_getmoredata(struct lib_ccx_ctx *ctx)
|
||||
{
|
||||
int enough = 0;
|
||||
int payload_read = 0;
|
||||
@@ -81,14 +82,14 @@ LLONG asf_getmoredata(void)
|
||||
// Data Object Loop
|
||||
int datapacketlength = 0; // Collect the read header bytes
|
||||
|
||||
// Payload parsing information
|
||||
// Payload parsing information
|
||||
int SequenceType = 0; // ASF
|
||||
int PaddingLType = 0; // ASF
|
||||
uint32_t Sequence = 0;
|
||||
int PaddingLType = 0; // ASF
|
||||
uint32_t Sequence = 0;
|
||||
uint32_t SendTime = 0;
|
||||
|
||||
int payloadparsersize = 0; // Infered (PacketLType + SequenceType + PaddingLType + 6);
|
||||
|
||||
|
||||
uint32_t OffsetMediaLength = 0; // ASF
|
||||
uint32_t ReplicatedLength = 0; // ASF
|
||||
|
||||
@@ -140,7 +141,7 @@ LLONG asf_getmoredata(void)
|
||||
.StreamNumberLType = 0,
|
||||
.PacketLength = 0,
|
||||
.PaddingLength = 0
|
||||
};
|
||||
};
|
||||
// Initialize the Payload Extension System
|
||||
for(int stream=0; stream<STREAMNUM; stream++)
|
||||
{
|
||||
@@ -151,8 +152,8 @@ LLONG asf_getmoredata(void)
|
||||
asf_data_container.PayloadExtPTSEntry[stream] = -1;
|
||||
}
|
||||
|
||||
buffered_read(asf_data_container.parsebuf,30);
|
||||
past+=result;
|
||||
buffered_read(ctx, asf_data_container.parsebuf,30);
|
||||
ctx->past+=result;
|
||||
if (result!=30)
|
||||
{
|
||||
mprint("Premature end of file!\n");
|
||||
@@ -173,7 +174,7 @@ LLONG asf_getmoredata(void)
|
||||
dbg_print(CCX_DMT_PARSE, "Length: %lld\n", asf_data_container.HeaderObjectSize);
|
||||
dbg_print(CCX_DMT_PARSE,"\nNumber of header objects: %ld\n",
|
||||
(long)*((uint32_t*)(asf_data_container.parsebuf + 24)));
|
||||
|
||||
|
||||
|
||||
if (asf_data_container.HeaderObjectSize > asf_data_container.parsebufsize) {
|
||||
asf_data_container.parsebuf = (unsigned char*)realloc(asf_data_container.parsebuf, (size_t)asf_data_container.HeaderObjectSize);
|
||||
@@ -185,8 +186,8 @@ LLONG asf_getmoredata(void)
|
||||
curpos = asf_data_container.parsebuf + 30;
|
||||
getbytes = asf_data_container.HeaderObjectSize - 30;
|
||||
|
||||
buffered_read(curpos, (int) getbytes);
|
||||
past+=result;
|
||||
buffered_read(ctx, curpos, (int) getbytes);
|
||||
ctx->past+=result;
|
||||
if (result!=getbytes)
|
||||
{
|
||||
mprint("Premature end of file!\n");
|
||||
@@ -202,7 +203,7 @@ LLONG asf_getmoredata(void)
|
||||
|
||||
if( !memcmp(curpos, ASF_FILE_PROPERTIES, 16 ) )
|
||||
{
|
||||
// Mandatory Object, only one.
|
||||
// Mandatory Object, only one.
|
||||
dbg_print(CCX_DMT_PARSE, "\nFile Properties Object (size: %lld)\n", hpobjectsize);
|
||||
|
||||
asf_data_container.FileSize = *((int64_t*)(curpos + 40));
|
||||
@@ -211,11 +212,11 @@ LLONG asf_getmoredata(void)
|
||||
SeekableFlag = 0x2 & curpos[88];
|
||||
MinPacketSize = *((uint32_t*)(curpos+92));
|
||||
MaxPacketSize = *((uint32_t*)(curpos+96));
|
||||
|
||||
|
||||
dbg_print(CCX_DMT_PARSE, "FileSize: %lld Packet count: %lld\n", asf_data_container.FileSize, DataPacketsCount);
|
||||
dbg_print(CCX_DMT_PARSE, "Broadcast: %d - Seekable: %d\n", BroadcastFlag, SeekableFlag);
|
||||
dbg_print(CCX_DMT_PARSE, "MiDPS: %d MaDPS: %d\n", MinPacketSize, MaxPacketSize);
|
||||
|
||||
|
||||
}
|
||||
else if( !memcmp(curpos,ASF_STREAM_PROPERTIES, 16 ) )
|
||||
{
|
||||
@@ -225,7 +226,7 @@ LLONG asf_getmoredata(void)
|
||||
asf_data_container.StreamProperties.VideoStreamNumber = *(curpos + 72) & 0x7F;
|
||||
dbg_print(CCX_DMT_PARSE, "Stream Type: ASF_Video_Media\n");
|
||||
dbg_print(CCX_DMT_PARSE, "Video Stream Number: %d\n", asf_data_container.StreamProperties.VideoStreamNumber);
|
||||
|
||||
|
||||
}
|
||||
else if( !memcmp(curpos+24, ASF_AUDIO_MEDIA, 16 ) )
|
||||
{
|
||||
@@ -237,7 +238,7 @@ LLONG asf_getmoredata(void)
|
||||
{
|
||||
dbg_print(CCX_DMT_PARSE, "Stream Type: %s\n",
|
||||
guidstr(curpos+24));
|
||||
dbg_print(CCX_DMT_PARSE, "Stream Number: %d\n", *(curpos+72) & 0x7F );
|
||||
dbg_print(CCX_DMT_PARSE, "Stream Number: %d\n", *(curpos+72) & 0x7F );
|
||||
}
|
||||
}
|
||||
else if( !memcmp(curpos,ASF_HEADER_EXTENSION, 16 ) )
|
||||
@@ -273,7 +274,7 @@ LLONG asf_getmoredata(void)
|
||||
StreamNumber, StreamNameCount, PayloadExtensionSystemCount);
|
||||
|
||||
if ( StreamNumber >= STREAMNUM )
|
||||
fatal(EXIT_BUG_BUG, "STREAMNUM too small. Send bug report!/n");
|
||||
fatal(CCX_COMMON_EXIT_BUG_BUG, "STREAMNUM too small. Send bug report!/n");
|
||||
|
||||
for(int i=0; i<StreamNameCount; i++)
|
||||
{
|
||||
@@ -285,7 +286,7 @@ LLONG asf_getmoredata(void)
|
||||
int extensionsysteminfolength;
|
||||
|
||||
if ( PayloadExtensionSystemCount > PAYEXTNUM )
|
||||
fatal(EXIT_BUG_BUG, "PAYEXTNUM too small. Send bug report!/n");
|
||||
fatal(CCX_COMMON_EXIT_BUG_BUG, "PAYEXTNUM too small. Send bug report!/n");
|
||||
|
||||
for(int i=0; i<PayloadExtensionSystemCount; i++)
|
||||
{
|
||||
@@ -439,7 +440,7 @@ LLONG asf_getmoredata(void)
|
||||
dbg_print(CCX_DMT_PARSE, "%02X:",*((unsigned char*)(edescval+ii)));
|
||||
}
|
||||
if (DescriptorValueLength>8)
|
||||
dbg_print(CCX_DMT_PARSE, "skipped %d more",DescriptorValueLength-8);
|
||||
dbg_print(CCX_DMT_PARSE, "skipped %d more",DescriptorValueLength-8);
|
||||
dbg_print(CCX_DMT_PARSE, " (BYTES)\n");
|
||||
break;
|
||||
case 2: // BOOL
|
||||
@@ -455,10 +456,10 @@ LLONG asf_getmoredata(void)
|
||||
dbg_print(CCX_DMT_PARSE, "%u (WORD)\n",(int)*((uint16_t*)edescval));
|
||||
break;
|
||||
default:
|
||||
fatal(EXIT_BUG_BUG, "Wrong type ...\n");
|
||||
fatal(CCX_COMMON_EXIT_BUG_BUG, "Wrong type ...\n");
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
if(!memcmp(econtentpos+2, L"WM/VideoClosedCaptioning"
|
||||
,DescriptorNameLength))
|
||||
{
|
||||
@@ -534,8 +535,8 @@ LLONG asf_getmoredata(void)
|
||||
asf_data_container.PacketSize = MinPacketSize;
|
||||
|
||||
// Now the Data Object, except for the packages
|
||||
buffered_read(asf_data_container.parsebuf, 50); // No realloc needed.
|
||||
past+=result;
|
||||
buffered_read(ctx, asf_data_container.parsebuf, 50); // No realloc needed.
|
||||
ctx->past+=result;
|
||||
if (result!=50)
|
||||
{
|
||||
mprint("Premature end of file!\n");
|
||||
@@ -557,7 +558,7 @@ LLONG asf_getmoredata(void)
|
||||
asf_data_container.TotalDataPackets = *((uint32_t*)(asf_data_container.parsebuf + 40));
|
||||
dbg_print(CCX_DMT_PARSE, "Size: %lld\n", asf_data_container.DataObjectSize);
|
||||
dbg_print(CCX_DMT_PARSE, "Number of data packets: %ld\n", (long)asf_data_container.TotalDataPackets);
|
||||
|
||||
|
||||
|
||||
reentry = 0; // Make sure we read the Data Packet Headers
|
||||
} // End of if (firstcall)
|
||||
@@ -576,8 +577,8 @@ LLONG asf_getmoredata(void)
|
||||
dbg_print(CCX_DMT_PARSE, "\nReading packet %d/%d\n", asf_data_container.datapacketcur + 1, asf_data_container.TotalDataPackets);
|
||||
|
||||
// First packet
|
||||
buffered_read(asf_data_container.parsebuf, 1); // No realloc needed.
|
||||
past+=result;
|
||||
buffered_read(ctx, asf_data_container.parsebuf, 1); // No realloc needed.
|
||||
ctx->past+=result;
|
||||
asf_data_container.dobjectread += result;
|
||||
if (result!=1)
|
||||
{
|
||||
@@ -593,8 +594,8 @@ LLONG asf_getmoredata(void)
|
||||
{
|
||||
fatal(EXIT_NOT_CLASSIFIED, "Error Correction Length Type not 00 - reserved - aborting ...\n");
|
||||
}
|
||||
buffered_read(asf_data_container.parsebuf + 1, ecdatalength);
|
||||
past+=result;
|
||||
buffered_read(ctx, asf_data_container.parsebuf + 1, ecdatalength);
|
||||
ctx->past+=result;
|
||||
asf_data_container.dobjectread += result;
|
||||
if (result!=ecdatalength)
|
||||
{
|
||||
@@ -614,8 +615,8 @@ LLONG asf_getmoredata(void)
|
||||
}
|
||||
|
||||
// Now payload parsing information
|
||||
buffered_read(asf_data_container.parsebuf + ecinfo, 2 - ecinfo); // No realloc needed
|
||||
past+=result;
|
||||
buffered_read(ctx, asf_data_container.parsebuf + ecinfo, 2 - ecinfo); // No realloc needed
|
||||
ctx->past+=result;
|
||||
asf_data_container.dobjectread += result;
|
||||
if (result!=2)
|
||||
{
|
||||
@@ -650,8 +651,8 @@ LLONG asf_getmoredata(void)
|
||||
|
||||
payloadparsersize = asf_data_container.PacketLType + SequenceType + PaddingLType + 6;
|
||||
|
||||
buffered_read(asf_data_container.parsebuf + 2, payloadparsersize); // No realloc needed
|
||||
past+=result;
|
||||
buffered_read(ctx, asf_data_container.parsebuf + 2, payloadparsersize); // No realloc needed
|
||||
ctx->past+=result;
|
||||
asf_data_container.dobjectread += result;
|
||||
if (result!=payloadparsersize)
|
||||
{
|
||||
@@ -688,8 +689,8 @@ LLONG asf_getmoredata(void)
|
||||
{
|
||||
unsigned char plheader[1];
|
||||
|
||||
buffered_read(plheader, 1);
|
||||
past+=result;
|
||||
buffered_read(ctx, plheader, 1);
|
||||
ctx->past+=result;
|
||||
asf_data_container.dobjectread += result;
|
||||
if (result!=1)
|
||||
{
|
||||
@@ -726,9 +727,9 @@ LLONG asf_getmoredata(void)
|
||||
dbg_print(CCX_DMT_PARSE, "\nMultiple payloads %d/%d\n", asf_data_container.payloadcur + 1, asf_data_container.NumberOfPayloads);
|
||||
|
||||
int payloadheadersize = 1 + asf_data_container.MediaNumberLType + asf_data_container.OffsetMediaLType + asf_data_container.ReplicatedLType;
|
||||
|
||||
buffered_read(asf_data_container.parsebuf, payloadheadersize); // No realloc needed
|
||||
past+=result;
|
||||
|
||||
buffered_read(ctx, asf_data_container.parsebuf, payloadheadersize); // No realloc needed
|
||||
ctx->past+=result;
|
||||
asf_data_container.dobjectread += result;
|
||||
if (result!=payloadheadersize)
|
||||
{
|
||||
@@ -737,7 +738,7 @@ LLONG asf_getmoredata(void)
|
||||
return payload_read;
|
||||
}
|
||||
datapacketlength+=payloadheadersize;
|
||||
|
||||
|
||||
asf_data_container.PayloadStreamNumber = *asf_data_container.parsebuf & 0x7F;
|
||||
asf_data_container.KeyFrame = (*asf_data_container.parsebuf & 0x80) && 1;
|
||||
|
||||
@@ -755,8 +756,8 @@ LLONG asf_getmoredata(void)
|
||||
fatal(EXIT_NOT_ENOUGH_MEMORY, "Out of memory");
|
||||
asf_data_container.parsebufsize = ReplicatedLength;
|
||||
}
|
||||
buffered_read(asf_data_container.parsebuf, (long)ReplicatedLength);
|
||||
past+=result;
|
||||
buffered_read(ctx, asf_data_container.parsebuf, (long)ReplicatedLength);
|
||||
ctx->past+=result;
|
||||
asf_data_container.dobjectread += result;
|
||||
if (result!=ReplicatedLength)
|
||||
{
|
||||
@@ -804,7 +805,7 @@ LLONG asf_getmoredata(void)
|
||||
// unknown = *((uint32_t*)(reppos+4));
|
||||
rtStart = *((int64_t*)(reppos+8));
|
||||
rtEnd = *((int64_t*)(reppos+16));
|
||||
|
||||
|
||||
//printf("dwVersion: %d unknown: 0x%04X\n", dwVersion, unknown);
|
||||
}
|
||||
|
||||
@@ -823,7 +824,7 @@ LLONG asf_getmoredata(void)
|
||||
print_mstime(PresentationTimems));
|
||||
dbg_print(CCX_DMT_PARSE," dvr-ms PTS: %s+%lld\n",
|
||||
print_mstime(rtStart/10000), (rtEnd-rtStart)/10000);
|
||||
|
||||
|
||||
datapacketlength+=ReplicatedLength;
|
||||
|
||||
// Only multiple payload packages have this value
|
||||
@@ -831,8 +832,8 @@ LLONG asf_getmoredata(void)
|
||||
{
|
||||
unsigned char plheader[4];
|
||||
|
||||
buffered_read(plheader, asf_data_container.PayloadLType);
|
||||
past+=result;
|
||||
buffered_read(ctx, plheader, asf_data_container.PayloadLType);
|
||||
ctx->past+=result;
|
||||
asf_data_container.dobjectread += result;
|
||||
if (result != asf_data_container.PayloadLType)
|
||||
{
|
||||
@@ -918,9 +919,9 @@ LLONG asf_getmoredata(void)
|
||||
// a jump this needs synchronizing, otherwise it was
|
||||
// just a gap in the captions.
|
||||
if (!asf_data_container.StreamProperties.VideoJump)
|
||||
CaptionGap = 1;
|
||||
ccx_common_timing_settings.disable_sync_check = 1;
|
||||
else
|
||||
CaptionGap = 0;
|
||||
ccx_common_timing_settings.disable_sync_check = 0;
|
||||
}
|
||||
asf_data_container.StreamProperties.VideoJump = 0;
|
||||
}
|
||||
@@ -955,7 +956,7 @@ LLONG asf_getmoredata(void)
|
||||
dbg_print(CCX_DMT_PARSE, "\nVideo stream object");
|
||||
dbg_print(CCX_DMT_PARSE, " read with PTS: %s\n",
|
||||
print_mstime(asf_data_container.StreamProperties.currDecodeStreamPTS));
|
||||
|
||||
|
||||
|
||||
// Enough for now
|
||||
enough = 1;
|
||||
@@ -973,11 +974,11 @@ LLONG asf_getmoredata(void)
|
||||
int want = (long)((BUFSIZE - inbuf)>asf_data_container.PayloadLength ?
|
||||
asf_data_container.PayloadLength : (BUFSIZE - inbuf));
|
||||
if (want < (long)asf_data_container.PayloadLength)
|
||||
fatal(EXIT_BUG_BUG, "Buffer size to small for ASF payload!\nPlease file a bug report!\n");
|
||||
buffered_read (buffer+inbuf,want);
|
||||
fatal(CCX_COMMON_EXIT_BUG_BUG, "Buffer size to small for ASF payload!\nPlease file a bug report!\n");
|
||||
buffered_read (ctx, ctx->buffer+inbuf,want);
|
||||
payload_read+=(int) result;
|
||||
inbuf+=result;
|
||||
past+=result;
|
||||
ctx->past+=result;
|
||||
if (result != asf_data_container.PayloadLength)
|
||||
{
|
||||
mprint("Premature end of file!\n");
|
||||
@@ -990,8 +991,8 @@ LLONG asf_getmoredata(void)
|
||||
{
|
||||
// Skip non-cc data
|
||||
dbg_print(CCX_DMT_PARSE, "Skipping Stream #%d data ...\n", asf_data_container.PayloadStreamNumber);
|
||||
buffered_skip((int)asf_data_container.PayloadLength);
|
||||
past+=result;
|
||||
buffered_skip(ctx, (int)asf_data_container.PayloadLength);
|
||||
ctx->past+=result;
|
||||
if (result != asf_data_container.PayloadLength)
|
||||
{
|
||||
mprint("Premature end of file!\n");
|
||||
@@ -1000,7 +1001,7 @@ LLONG asf_getmoredata(void)
|
||||
}
|
||||
asf_data_container.dobjectread += result;
|
||||
}
|
||||
|
||||
|
||||
asf_data_container.payloadcur++;
|
||||
}
|
||||
if (enough)
|
||||
@@ -1008,8 +1009,8 @@ LLONG asf_getmoredata(void)
|
||||
|
||||
// Skip padding bytes
|
||||
dbg_print(CCX_DMT_PARSE, "Skip %d padding\n", asf_data_container.PaddingLength);
|
||||
buffered_skip((long)asf_data_container.PaddingLength);
|
||||
past+=result;
|
||||
buffered_skip(ctx, (long)asf_data_container.PaddingLength);
|
||||
ctx->past+=result;
|
||||
if (result != asf_data_container.PaddingLength)
|
||||
{
|
||||
mprint("Premature end of file!\n");
|
||||
@@ -1028,8 +1029,8 @@ LLONG asf_getmoredata(void)
|
||||
|
||||
// Skip the rest of the file
|
||||
dbg_print(CCX_DMT_PARSE, "Skip the rest: %d\n", (int)(asf_data_container.FileSize - asf_data_container.HeaderObjectSize - asf_data_container.DataObjectSize));
|
||||
buffered_skip((int)(asf_data_container.FileSize - asf_data_container.HeaderObjectSize - asf_data_container.DataObjectSize));
|
||||
past+=result;
|
||||
buffered_skip(ctx, (int)(asf_data_container.FileSize - asf_data_container.HeaderObjectSize - asf_data_container.DataObjectSize));
|
||||
ctx->past+=result;
|
||||
// Don not set end_of_file (although it is true) as this would
|
||||
// produce an premature end error.
|
||||
//end_of_file=1;
|
||||
@@ -1,7 +1,9 @@
|
||||
#include "ccextractor.h"
|
||||
#include "lib_ccx.h"
|
||||
#include "ccx_common_option.h"
|
||||
#include "utility.h"
|
||||
#include <math.h>
|
||||
|
||||
// Functions to parse a AVC/H.264 data stream, see ISO/IEC 14496-10
|
||||
// Functions to parse a AVC/H.264 data stream, see ISO/IEC 14496-10
|
||||
|
||||
int ccblocks_in_avc_total=0;
|
||||
int ccblocks_in_avc_lost=0;
|
||||
@@ -12,7 +14,7 @@ static void sei_rbsp (unsigned char *seibuf, unsigned char *seiend);
|
||||
static unsigned char *sei_message (unsigned char *seibuf, unsigned char *seiend);
|
||||
static void user_data_registered_itu_t_t35 (unsigned char *userbuf, unsigned char *userend);
|
||||
static void seq_parameter_set_rbsp (unsigned char *seqbuf, unsigned char *seqend);
|
||||
static void slice_header (unsigned char *heabuf, unsigned char *heaend, int nal_unit_type);
|
||||
static void slice_header (struct lib_ccx_ctx *ctx, unsigned char *heabuf, unsigned char *heaend, int nal_unit_type, struct cc_subtitle *sub);
|
||||
|
||||
static unsigned char cc_count;
|
||||
// buffer to hold cc data
|
||||
@@ -44,15 +46,15 @@ void init_avc(void)
|
||||
cc_data = (unsigned char*)malloc(1024);
|
||||
}
|
||||
|
||||
void do_NAL (unsigned char *NALstart, LLONG NAL_length)
|
||||
void do_NAL (struct lib_ccx_ctx *ctx, unsigned char *NALstart, LLONG NAL_length, struct cc_subtitle *sub)
|
||||
{
|
||||
unsigned char *NALstop;
|
||||
unsigned nal_unit_type = *NALstart & 0x1F;
|
||||
|
||||
NALstop = NAL_length+NALstart;
|
||||
NALstop = NAL_length+NALstart;
|
||||
NALstop = remove_03emu(NALstart+1, NALstop); // Add +1 to NALstop for TS, without it for MP4. Still don't know why
|
||||
|
||||
if (NALstop==NULL) // remove_03emu failed.
|
||||
if (NALstop==NULL) // remove_03emu failed.
|
||||
{
|
||||
mprint ("\rNotice: NAL of type %u had to be skipped because remove_03emu failed.\n", nal_unit_type);
|
||||
return;
|
||||
@@ -66,22 +68,22 @@ void do_NAL (unsigned char *NALstart, LLONG NAL_length)
|
||||
{
|
||||
// Found sequence parameter set
|
||||
// We need this to parse NAL type 1 (CCX_NAL_TYPE_CODED_SLICE_NON_IDR_PICTURE_1)
|
||||
num_nal_unit_type_7++;
|
||||
num_nal_unit_type_7++;
|
||||
seq_parameter_set_rbsp(NALstart+1, NALstop);
|
||||
got_seq_para = 1;
|
||||
}
|
||||
else if ( got_seq_para && (nal_unit_type == CCX_NAL_TYPE_CODED_SLICE_NON_IDR_PICTURE_1 ||
|
||||
else if ( got_seq_para && (nal_unit_type == CCX_NAL_TYPE_CODED_SLICE_NON_IDR_PICTURE_1 ||
|
||||
nal_unit_type == CCX_NAL_TYPE_CODED_SLICE_IDR_PICTURE)) // Only if nal_unit_type=1
|
||||
{
|
||||
// Found coded slice of a non-IDR picture
|
||||
// Found coded slice of a non-IDR picture
|
||||
// We only need the slice header data, no need to implement
|
||||
// slice_layer_without_partitioning_rbsp( );
|
||||
slice_header(NALstart+1, NALstop, nal_unit_type);
|
||||
slice_header(ctx, NALstart+1, NALstop, nal_unit_type, sub);
|
||||
}
|
||||
else if ( got_seq_para && nal_unit_type == CCX_NAL_TYPE_SEI )
|
||||
{
|
||||
// Found SEI (used for subtitles)
|
||||
//set_fts(); // FIXME - check this!!!
|
||||
//set_fts(); // FIXME - check this!!!
|
||||
sei_rbsp(NALstart+1, NALstop);
|
||||
}
|
||||
else if ( got_seq_para && nal_unit_type == CCX_NAL_TYPE_PICTURE_PARAMETER_SET )
|
||||
@@ -99,7 +101,7 @@ void do_NAL (unsigned char *NALstart, LLONG NAL_length)
|
||||
|
||||
// Process inbuf bytes in buffer holding and AVC (H.264) video stream.
|
||||
// The number of processed bytes is returned.
|
||||
LLONG process_avc (unsigned char *avcbuf, LLONG avcbuflen)
|
||||
LLONG process_avc (struct lib_ccx_ctx *ctx, unsigned char *avcbuf, LLONG avcbuflen ,struct cc_subtitle *sub)
|
||||
{
|
||||
unsigned char *bpos = avcbuf;
|
||||
unsigned char *NALstart;
|
||||
@@ -108,16 +110,16 @@ LLONG process_avc (unsigned char *avcbuf, LLONG avcbuflen)
|
||||
// At least 5 bytes are needed for a NAL unit
|
||||
if(avcbuflen <= 5)
|
||||
{
|
||||
fatal(EXIT_BUG_BUG,
|
||||
fatal(CCX_COMMON_EXIT_BUG_BUG,
|
||||
"NAL unit need at last 5 bytes ...");
|
||||
}
|
||||
}
|
||||
|
||||
// Warning there should be only leading zeros, nothing else
|
||||
// Warning there should be only leading zeros, nothing else
|
||||
if( !(bpos[0]==0x00 && bpos[1]==0x00) )
|
||||
{
|
||||
fatal(EXIT_BUG_BUG,
|
||||
fatal(CCX_COMMON_EXIT_BUG_BUG,
|
||||
"Broken AVC stream - no 0x0000 ...");
|
||||
}
|
||||
}
|
||||
bpos = bpos+2;
|
||||
|
||||
int firstloop=1; // Check for valid start code at buffer start
|
||||
@@ -138,7 +140,7 @@ LLONG process_avc (unsigned char *avcbuf, LLONG avcbuflen)
|
||||
else if(firstloop && *bpos != 0x00)
|
||||
{
|
||||
// Not 0x00 or 0x01
|
||||
fatal(EXIT_BUG_BUG,
|
||||
fatal(CCX_COMMON_EXIT_BUG_BUG,
|
||||
"Broken AVC stream - no 0x00 ...");
|
||||
}
|
||||
bpos++;
|
||||
@@ -193,7 +195,7 @@ LLONG process_avc (unsigned char *avcbuf, LLONG avcbuflen)
|
||||
if(*NALstart & 0x80)
|
||||
{
|
||||
dump(CCX_DMT_GENERIC_NOTICES, NALstart-4,10, 0, 0);
|
||||
fatal(EXIT_BUG_BUG,
|
||||
fatal(CCX_COMMON_EXIT_BUG_BUG,
|
||||
"Broken AVC stream - forbidden_zero_bit not zero ...");
|
||||
}
|
||||
|
||||
@@ -202,17 +204,17 @@ LLONG process_avc (unsigned char *avcbuf, LLONG avcbuflen)
|
||||
|
||||
dvprint("BEGIN NAL unit type: %d length %d zeros: %d ref_idc: %d - Buffered captions before: %d\n",
|
||||
nal_unit_type, NALstop-NALstart-1, zeropad, nal_ref_idc, !cc_buffer_saved);
|
||||
|
||||
do_NAL (NALstart, NALstop-NALstart);
|
||||
|
||||
do_NAL (ctx, NALstart, NALstop-NALstart, sub);
|
||||
|
||||
dvprint("END NAL unit type: %d length %d zeros: %d ref_idc: %d - Buffered captions after: %d\n",
|
||||
nal_unit_type, NALstop-NALstart-1, zeropad, nal_ref_idc, !cc_buffer_saved);
|
||||
}
|
||||
}
|
||||
|
||||
return avcbuflen;
|
||||
}
|
||||
|
||||
#define ZEROBYTES_SHORTSTARTCODE 2
|
||||
#define ZEROBYTES_SHORTSTARTCODE 2
|
||||
|
||||
// Copied for reference decoder, see if it behaves different that Volker's code
|
||||
int EBSPtoRBSP(unsigned char *streamBuffer, int end_bytepos, int begin_bytepos)
|
||||
@@ -227,9 +229,9 @@ int EBSPtoRBSP(unsigned char *streamBuffer, int end_bytepos, int begin_bytepos)
|
||||
|
||||
for(i = begin_bytepos; i < end_bytepos; ++i)
|
||||
{ //starting from begin_bytepos to avoid header information
|
||||
//in NAL unit, 0x000000, 0x000001 or 0x000002 shall not occur at any byte-aligned position
|
||||
if(count == ZEROBYTES_SHORTSTARTCODE && streamBuffer[i] < 0x03)
|
||||
return -1;
|
||||
//in NAL unit, 0x000000, 0x000001 or 0x000002 shall not occur at any byte-aligned position
|
||||
if(count == ZEROBYTES_SHORTSTARTCODE && streamBuffer[i] < 0x03)
|
||||
return -1;
|
||||
if(count == ZEROBYTES_SHORTSTARTCODE && streamBuffer[i] == 0x03)
|
||||
{
|
||||
//check the 4th byte after 0x000003, except when cabac_zero_word is used, in which case the last three bytes of this NAL unit must be 0x000003
|
||||
@@ -260,8 +262,8 @@ typedef unsigned int u32;
|
||||
u32 avc_remove_emulation_bytes(const unsigned char *buffer_src, unsigned char *buffer_dst, u32 nal_size) ;
|
||||
|
||||
unsigned char *remove_03emu(unsigned char *from, unsigned char *to)
|
||||
{
|
||||
int num=to-from;
|
||||
{
|
||||
int num=to-from;
|
||||
int newsize = EBSPtoRBSP (from,num,0); //TODO: Do something if newsize == -1 (broken NAL)
|
||||
if (newsize==-1)
|
||||
return NULL;
|
||||
@@ -282,7 +284,7 @@ void sei_rbsp (unsigned char *seibuf, unsigned char *seiend)
|
||||
{
|
||||
if(*tbuf != 0x80)
|
||||
mprint("Strange rbsp_trailing_bits value: %02X\n",*tbuf);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO: This really really looks bad
|
||||
@@ -305,33 +307,33 @@ unsigned char *sei_message (unsigned char *seibuf, unsigned char *seiend)
|
||||
payloadType+=255;
|
||||
seibuf++;
|
||||
}
|
||||
payloadType += *seibuf;
|
||||
payloadType += *seibuf;
|
||||
seibuf++;
|
||||
|
||||
|
||||
|
||||
int payloadSize = 0;
|
||||
while (*seibuf==0xff)
|
||||
{
|
||||
payloadSize+=255;
|
||||
seibuf++;
|
||||
}
|
||||
payloadSize += *seibuf;
|
||||
payloadSize += *seibuf;
|
||||
seibuf++;
|
||||
|
||||
|
||||
int broken=0;
|
||||
unsigned char *paystart = seibuf;
|
||||
seibuf+=payloadSize;
|
||||
|
||||
|
||||
dvprint("Payload type: %d size: %d - ", payloadType, payloadSize);
|
||||
if(seibuf > seiend )
|
||||
if(seibuf > seiend )
|
||||
{
|
||||
// TODO: What do we do here?
|
||||
broken=1;
|
||||
if (payloadType==4)
|
||||
if (payloadType==4)
|
||||
{
|
||||
dbg_print(CCX_DMT_VERBOSE, "Warning: Subtitles payload seems incorrect (too long), continuing but it doesn't look good..");
|
||||
}
|
||||
else
|
||||
else
|
||||
{
|
||||
dbg_print(CCX_DMT_VERBOSE, "Warning: Non-subtitles payload seems incorrect (too long), continuing but it doesn't look good..");
|
||||
}
|
||||
@@ -352,7 +354,7 @@ void copy_ccdata_to_buffer (char *source, int new_cc_count)
|
||||
mprint ("Warning: Probably loss of CC data, unsaved buffer being rewritten\n");
|
||||
ccblocks_in_avc_lost++;
|
||||
}
|
||||
memcpy(cc_data+cc_count*3, source, new_cc_count*3+1);
|
||||
memcpy(cc_data+cc_count*3, source, new_cc_count*3+1);
|
||||
cc_count+=new_cc_count;
|
||||
cc_buffer_saved=0;
|
||||
}
|
||||
@@ -393,7 +395,7 @@ void user_data_registered_itu_t_t35 (unsigned char *userbuf, unsigned char *user
|
||||
}
|
||||
|
||||
switch (itu_t_35_provider_code)
|
||||
{
|
||||
{
|
||||
case 0x0031: // ANSI/SCTE 128
|
||||
dbg_print(CCX_DMT_VERBOSE, "Caption block in ANSI/SCTE 128...");
|
||||
if (*tbuf==0x47 && *(tbuf+1)==0x41 && *(tbuf+2)==0x39 && *(tbuf+3)==0x34) // ATSC1_data() - GA94
|
||||
@@ -419,14 +421,14 @@ void user_data_registered_itu_t_t35 (unsigned char *userbuf, unsigned char *user
|
||||
*/
|
||||
local_cc_count= *tbuf & 0x1F;
|
||||
process_cc_data_flag = (*tbuf & 0x40) >> 6;
|
||||
|
||||
|
||||
/* if (!process_cc_data_flag)
|
||||
{
|
||||
mprint ("process_cc_data_flag == 0, skipping this caption block.\n");
|
||||
break;
|
||||
} */
|
||||
/*
|
||||
The following tests are not passed in Comcast's sample videos. *tbuf here is always 0x41.
|
||||
The following tests are not passed in Comcast's sample videos. *tbuf here is always 0x41.
|
||||
if (! (*tbuf & 0x80)) // First bit must be 1
|
||||
{
|
||||
printf ("Fixed bit should be 1, but it's 0 - skipping this caption block.\n");
|
||||
@@ -450,15 +452,15 @@ void user_data_registered_itu_t_t35 (unsigned char *userbuf, unsigned char *user
|
||||
|
||||
/* TODO: I don't think we have user_data_len here
|
||||
if (cc_count*3+3 != user_data_len)
|
||||
fatal(EXIT_BUG_BUG,
|
||||
fatal(CCX_COMMON_EXIT_BUG_BUG,
|
||||
"Syntax problem: user_data_len != cc_count*3+3."); */
|
||||
|
||||
// Enough room for CC captions?
|
||||
if (cc_tmpdata+local_cc_count*3 >= userend)
|
||||
fatal(EXIT_BUG_BUG,
|
||||
fatal(CCX_COMMON_EXIT_BUG_BUG,
|
||||
"Syntax problem: Too many caption blocks.");
|
||||
if (cc_tmpdata[local_cc_count*3]!=0xFF)
|
||||
fatal(EXIT_BUG_BUG,
|
||||
fatal(CCX_COMMON_EXIT_BUG_BUG,
|
||||
"Syntax problem: Final 0xFF marker missing.");
|
||||
|
||||
// Save the data and process once we know the sequence number
|
||||
@@ -478,19 +480,19 @@ void user_data_registered_itu_t_t35 (unsigned char *userbuf, unsigned char *user
|
||||
default:
|
||||
dbg_print(CCX_DMT_VERBOSE, "SCTE/ATSC reserved.\n");
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
else if (*tbuf==0x44 && *(tbuf+1)==0x54 && *(tbuf+2)==0x47 && *(tbuf+3)==0x31) // afd_data() - DTG1
|
||||
{
|
||||
;
|
||||
// Active Format Description Data. Actually unrelated to captions. Left
|
||||
// here in case we want to do some reporting eventually. From specs:
|
||||
// "Active Format Description (AFD) should be included in video user
|
||||
// data whenever the rectangular picture area containing useful
|
||||
// "Active Format Description (AFD) should be included in video user
|
||||
// data whenever the rectangular picture area containing useful
|
||||
// information does not extend to the full height or width of the coded
|
||||
// frame. AFD data may also be included in user data when the
|
||||
// frame. AFD data may also be included in user data when the
|
||||
// rectangular picture area containing
|
||||
// useful information extends to the fullheight and width of the
|
||||
// useful information extends to the fullheight and width of the
|
||||
// coded frame."
|
||||
}
|
||||
else
|
||||
@@ -505,7 +507,7 @@ void user_data_registered_itu_t_t35 (unsigned char *userbuf, unsigned char *user
|
||||
if(user_data_type_code != 0x03)
|
||||
{
|
||||
dbg_print(CCX_DMT_VERBOSE, "Not supported user_data_type_code: %02x\n",
|
||||
user_data_type_code);
|
||||
user_data_type_code);
|
||||
return;
|
||||
}
|
||||
tbuf++;
|
||||
@@ -522,15 +524,15 @@ void user_data_registered_itu_t_t35 (unsigned char *userbuf, unsigned char *user
|
||||
cc_tmpdata = tbuf+2;
|
||||
|
||||
if (local_cc_count*3+3 != user_data_len)
|
||||
fatal(EXIT_BUG_BUG,
|
||||
fatal(CCX_COMMON_EXIT_BUG_BUG,
|
||||
"Syntax problem: user_data_len != cc_count*3+3.");
|
||||
|
||||
// Enough room for CC captions?
|
||||
if (cc_tmpdata+local_cc_count*3 >= userend)
|
||||
fatal(EXIT_BUG_BUG,
|
||||
fatal(CCX_COMMON_EXIT_BUG_BUG,
|
||||
"Syntax problem: Too many caption blocks.");
|
||||
if (cc_tmpdata[local_cc_count*3]!=0xFF)
|
||||
fatal(EXIT_BUG_BUG,
|
||||
fatal(CCX_COMMON_EXIT_BUG_BUG,
|
||||
"Syntax problem: Final 0xFF marker missing.");
|
||||
|
||||
// Save the data and process once we know the sequence number
|
||||
@@ -563,178 +565,250 @@ void seq_parameter_set_rbsp (unsigned char *seqbuf, unsigned char *seqend)
|
||||
|
||||
dvprint("SEQUENCE PARAMETER SET (bitlen: %lld)\n", q1.bitsleft);
|
||||
tmp=u(&q1,8);
|
||||
dvprint("profile_idc= %llX\n", tmp);
|
||||
LLONG profile_idc = tmp;
|
||||
dvprint("profile_idc= % 4lld (%#llX)\n",tmp,tmp);
|
||||
tmp=u(&q1,1);
|
||||
dvprint("constraint_set0_flag= %llX\n", tmp);
|
||||
dvprint("constraint_set0_flag= % 4lld (%#llX)\n",tmp,tmp);
|
||||
tmp=u(&q1,1);
|
||||
dvprint("constraint_set1_flag= %llX\n", tmp);
|
||||
dvprint("constraint_set1_flag= % 4lld (%#llX)\n",tmp,tmp);
|
||||
tmp=u(&q1,1);
|
||||
dvprint("constraint_set2_flag= %llX\n", tmp);
|
||||
tmp=u(&q1,5);
|
||||
dvprint("reserved= %llX\n", tmp);
|
||||
dvprint("constraint_set2_flag= % 4lld (%#llX)\n",tmp,tmp);
|
||||
tmp = u(&q1, 1);
|
||||
dvprint("constraint_set3_flag= % 4lld (%#llX)\n",tmp,tmp);
|
||||
tmp = u(&q1, 1);
|
||||
dvprint("constraint_set4_flag= % 4lld (%#llX)\n",tmp,tmp);
|
||||
tmp = u(&q1, 1);
|
||||
dvprint("constraint_set5_flag= % 4lld (%#llX)\n",tmp,tmp);
|
||||
tmp=u(&q1,2);
|
||||
dvprint("reserved= % 4lld (%#llX)\n",tmp,tmp);
|
||||
tmp=u(&q1,8);
|
||||
dvprint("level_idc= %llX\n", tmp);
|
||||
dvprint("level_idc= % 4lld (%#llX)\n",tmp,tmp);
|
||||
seq_parameter_set_id = ue(&q1);
|
||||
dvprint("seq_parameter_set_id= %llX\n", seq_parameter_set_id);
|
||||
log2_max_frame_num = (int)ue(&q1)+4;
|
||||
dvprint("log2_max_frame_num4= %X\n", log2_max_frame_num);
|
||||
dvprint("seq_parameter_set_id= % 4lld (%#llX)\n", seq_parameter_set_id,seq_parameter_set_id);
|
||||
if (profile_idc == 100 || profile_idc == 110 || profile_idc == 122
|
||||
|| profile_idc == 244 || profile_idc == 44 || profile_idc == 83
|
||||
|| profile_idc == 86 || profile_idc == 118 || profile_idc == 128){
|
||||
LLONG chroma_format_idc = ue(&q1);
|
||||
dvprint("chroma_format_idc= % 4lld (%#llX)\n", chroma_format_idc,chroma_format_idc);
|
||||
if (chroma_format_idc == 3){
|
||||
tmp = u(&q1, 1);
|
||||
dvprint("separate_colour_plane_flag= % 4lld (%#llX)\n", tmp, tmp);
|
||||
}
|
||||
tmp = ue(&q1);
|
||||
dvprint("bit_depth_luma_minus8= % 4lld (%#llX)\n", tmp, tmp);
|
||||
tmp = ue(&q1);
|
||||
dvprint("bit_depth_chroma_minus8= % 4lld (%#llX)\n", tmp, tmp);
|
||||
tmp = u(&q1,1);
|
||||
dvprint("qpprime_y_zero_transform_bypass_flag= % 4lld (%#llX)\n", tmp, tmp);
|
||||
tmp = u(&q1, 1);
|
||||
dvprint("seq_scaling_matrix_present_flag= % 4lld (%#llX)\n", tmp, tmp);
|
||||
if (tmp == 1){
|
||||
// WVI: untested, just copied from specs.
|
||||
for (int i = 0; i < ((chroma_format_idc != 3) ? 8 : 12); i++){
|
||||
tmp = u(&q1, 1);
|
||||
dvprint("seq_scaling_list_present_flag[%d]= % 4lld (%#llX)\n",i,tmp, tmp);
|
||||
if (tmp){
|
||||
// We use a "dummy"/slimmed-down replacement here. Actual/full code can be found in the spec (ISO/IEC 14496-10:2012(E)) chapter 7.3.2.1.1.1 - Scaling list syntax
|
||||
if (i < 6){
|
||||
// Scaling list size 16
|
||||
// TODO: replace with full scaling list implementation?
|
||||
int nextScale = 8;
|
||||
int lastScale = 8;
|
||||
for (int j = 0; j < 16; j++){
|
||||
if (nextScale != 0){
|
||||
int64_t delta_scale = se(&q1);
|
||||
nextScale = (lastScale + delta_scale + 256) % 256;
|
||||
}
|
||||
lastScale = (nextScale == 0) ? lastScale : nextScale;
|
||||
}
|
||||
// END of TODO
|
||||
}
|
||||
else {
|
||||
// Scaling list size 64
|
||||
// TODO: replace with full scaling list implementation?
|
||||
int nextScale = 8;
|
||||
int lastScale = 8;
|
||||
for (int j = 0; j < 64; j++){
|
||||
if (nextScale != 0){
|
||||
int64_t delta_scale = se(&q1);
|
||||
nextScale = (lastScale + delta_scale + 256) % 256;
|
||||
}
|
||||
lastScale = (nextScale == 0) ? lastScale : nextScale;
|
||||
}
|
||||
// END of TODO
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
log2_max_frame_num = (int)ue(&q1);
|
||||
dvprint("log2_max_frame_num4_minus4= % 4d (%#X)\n", log2_max_frame_num,log2_max_frame_num);
|
||||
log2_max_frame_num += 4; // 4 is added due to the formula.
|
||||
pic_order_cnt_type = (int)ue(&q1);
|
||||
dvprint("pic_order_cnt_type= %X\n", pic_order_cnt_type);
|
||||
dvprint("pic_order_cnt_type= % 4d (%#X)\n", pic_order_cnt_type,pic_order_cnt_type);
|
||||
if( pic_order_cnt_type == 0 )
|
||||
{
|
||||
log2_max_pic_order_cnt_lsb = (int)ue(&q1)+4;
|
||||
dvprint("log2_max_pic_order_cnt_lsb= %X\n", log2_max_pic_order_cnt_lsb);
|
||||
log2_max_pic_order_cnt_lsb = (int)ue(&q1);
|
||||
dvprint("log2_max_pic_order_cnt_lsb_minus4= % 4d (%#X)\n", log2_max_pic_order_cnt_lsb,log2_max_pic_order_cnt_lsb);
|
||||
log2_max_pic_order_cnt_lsb += 4; // 4 is added due to formula.
|
||||
}
|
||||
else if( pic_order_cnt_type == 1 )
|
||||
{
|
||||
// CFS: Untested, just copied from specs.
|
||||
tmp= u(&q1,1);
|
||||
dvprint("delta_pic_order_always_zero_flag= %llX\n", tmp);
|
||||
dvprint("delta_pic_order_always_zero_flag= % 4lld (%#llX)\n",tmp,tmp);
|
||||
tmp = se(&q1);
|
||||
dvprint("offset_for_non_ref_pic= %llX\n", tmp);
|
||||
dvprint("offset_for_non_ref_pic= % 4lld (%#llX)\n",tmp,tmp);
|
||||
tmp = se(&q1);
|
||||
dvprint("offset_for_top_to_bottom_field %llX\n", tmp);
|
||||
dvprint("offset_for_top_to_bottom_field % 4lld (%#llX)\n",tmp,tmp);
|
||||
LLONG num_ref_frame_in_pic_order_cnt_cycle = ue (&q1);
|
||||
dvprint("num_ref_frame_in_pic_order_cnt_cycle %llX\n", num_ref_frame_in_pic_order_cnt_cycle);
|
||||
dvprint("num_ref_frame_in_pic_order_cnt_cycle % 4lld (%#llX)\n", num_ref_frame_in_pic_order_cnt_cycle,num_ref_frame_in_pic_order_cnt_cycle);
|
||||
for (int i=0; i<num_ref_frame_in_pic_order_cnt_cycle; i++)
|
||||
{
|
||||
tmp=se(&q1);
|
||||
dvprint("offset_for_ref_frame [%d / %d] = %llX\n", i, num_ref_frame_in_pic_order_cnt_cycle, tmp);
|
||||
tmp=se(&q1);
|
||||
dvprint("offset_for_ref_frame [%d / %d] = % 4lld (%#llX)\n", i, num_ref_frame_in_pic_order_cnt_cycle, tmp,tmp);
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
// CFS: Looks like nothing needs to be parsed for pic_order_cnt_type==2 ?
|
||||
// fatal(EXIT_BUG_BUG, "AVC: pic_order_cnt_type > 1 is not yet supported.");
|
||||
// Nothing needs to be parsed when pic_order_cnt_type == 2
|
||||
}
|
||||
|
||||
tmp=ue(&q1);
|
||||
dvprint("num_ref_frames= %llX\n", tmp);
|
||||
|
||||
dvprint("max_num_ref_frames= % 4lld (%#llX)\n",tmp,tmp);
|
||||
tmp=u(&q1,1);
|
||||
dvprint("gaps allowed= %llX\n", tmp);
|
||||
dvprint("gaps_in_frame_num_value_allowed_flag= % 4lld (%#llX)\n",tmp,tmp);
|
||||
tmp=ue(&q1);
|
||||
dvprint("pic_width_in_mbs_minus1= %llX\n", tmp);
|
||||
dvprint("pic_width_in_mbs_minus1= % 4lld (%#llX)\n",tmp,tmp);
|
||||
tmp=ue(&q1);
|
||||
dvprint("pic_height_in_map_units_minus1= %llX\n", tmp);
|
||||
dvprint("pic_height_in_map_units_minus1= % 4lld (%#llX)\n",tmp,tmp);
|
||||
frame_mbs_only_flag = (int)u(&q1,1);
|
||||
dvprint("frame_mbs_only_flag= %X\n", frame_mbs_only_flag);
|
||||
dvprint("frame_mbs_only_flag= % 4d (%#X)\n", frame_mbs_only_flag,frame_mbs_only_flag);
|
||||
if ( !frame_mbs_only_flag )
|
||||
{
|
||||
tmp=u(&q1,1);
|
||||
dvprint("mb_adaptive_fr_fi_flag= %llX\n", tmp);
|
||||
dvprint("mb_adaptive_fr_fi_flag= % 4lld (%#llX)\n",tmp,tmp);
|
||||
}
|
||||
tmp=u(&q1,1);
|
||||
dvprint("direct_8x8_inference_f= %llX\n", tmp);
|
||||
dvprint("direct_8x8_inference_f= % 4lld (%#llX)\n",tmp,tmp);
|
||||
tmp=u(&q1,1);
|
||||
dvprint("frame_cropping_flag= %llX\n", tmp);
|
||||
dvprint("frame_cropping_flag= % 4lld (%#llX)\n",tmp,tmp);
|
||||
if ( tmp )
|
||||
{
|
||||
tmp=ue(&q1);
|
||||
dvprint("frame_crop_left_offset= %llX\n", tmp);
|
||||
dvprint("frame_crop_left_offset= % 4lld (%#llX)\n",tmp,tmp);
|
||||
tmp=ue(&q1);
|
||||
dvprint("frame_crop_right_offset= %llX\n", tmp);
|
||||
dvprint("frame_crop_right_offset= % 4lld (%#llX)\n",tmp,tmp);
|
||||
tmp=ue(&q1);
|
||||
dvprint("frame_crop_top_offset= %llX\n", tmp);
|
||||
dvprint("frame_crop_top_offset= % 4lld (%#llX)\n",tmp,tmp);
|
||||
tmp=ue(&q1);
|
||||
dvprint("frame_crop_bottom_offset= %llX\n", tmp);
|
||||
dvprint("frame_crop_bottom_offset= % 4lld (%#llX)\n",tmp,tmp);
|
||||
}
|
||||
tmp=u(&q1,1);
|
||||
dvprint("vui_parameters_present= %llX\n", tmp);
|
||||
dvprint("vui_parameters_present= % 4lld (%#llX)\n",tmp,tmp);
|
||||
if ( tmp )
|
||||
{
|
||||
dvprint("\nVUI parameters\n");
|
||||
tmp=u(&q1,1);
|
||||
dvprint("aspect_ratio_info_pres= %llX\n", tmp);
|
||||
dvprint("aspect_ratio_info_pres= % 4lld (%#llX)\n",tmp,tmp);
|
||||
if ( tmp )
|
||||
{
|
||||
tmp=u(&q1,8);
|
||||
dvprint("aspect_ratio_idc= %llX\n", tmp);
|
||||
dvprint("aspect_ratio_idc= % 4lld (%#llX)\n",tmp,tmp);
|
||||
if ( tmp == 255 )
|
||||
{
|
||||
tmp=u(&q1,16);
|
||||
dvprint("sar_width= %llX\n", tmp);
|
||||
dvprint("sar_width= % 4lld (%#llX)\n",tmp,tmp);
|
||||
tmp=u(&q1,16);
|
||||
dvprint("sar_height= %llX\n", tmp);
|
||||
dvprint("sar_height= % 4lld (%#llX)\n",tmp,tmp);
|
||||
}
|
||||
}
|
||||
tmp=u(&q1,1);
|
||||
dvprint("overscan_info_pres_flag= %llX\n", tmp);
|
||||
dvprint("overscan_info_pres_flag= % 4lld (%#llX)\n",tmp,tmp);
|
||||
if ( tmp )
|
||||
{
|
||||
tmp=u(&q1,1);
|
||||
dvprint("overscan_appropriate_flag= %llX\n", tmp);
|
||||
dvprint("overscan_appropriate_flag= % 4lld (%#llX)\n",tmp,tmp);
|
||||
}
|
||||
tmp=u(&q1,1);
|
||||
dvprint("video_signal_type_present_flag= %llX\n", tmp);
|
||||
dvprint("video_signal_type_present_flag= % 4lld (%#llX)\n",tmp,tmp);
|
||||
if ( tmp )
|
||||
{
|
||||
tmp=u(&q1,3);
|
||||
dvprint("video_format= %llX\n", tmp);
|
||||
dvprint("video_format= % 4lld (%#llX)\n",tmp,tmp);
|
||||
tmp=u(&q1,1);
|
||||
dvprint("video_full_range_flag= %llX\n", tmp);
|
||||
dvprint("video_full_range_flag= % 4lld (%#llX)\n",tmp,tmp);
|
||||
tmp=u(&q1,1);
|
||||
dvprint("colour_description_present_flag= %llX\n", tmp);
|
||||
dvprint("colour_description_present_flag= % 4lld (%#llX)\n",tmp,tmp);
|
||||
if ( tmp )
|
||||
{
|
||||
tmp=u(&q1,8);
|
||||
dvprint("colour_primaries= %llX\n", tmp);
|
||||
dvprint("colour_primaries= % 4lld (%#llX)\n",tmp,tmp);
|
||||
tmp=u(&q1,8);
|
||||
dvprint("transfer_characteristics= %llX\n", tmp);
|
||||
dvprint("transfer_characteristics= % 4lld (%#llX)\n",tmp,tmp);
|
||||
tmp=u(&q1,8);
|
||||
dvprint("matrix_coefficients= %llX\n", tmp);
|
||||
dvprint("matrix_coefficients= % 4lld (%#llX)\n",tmp,tmp);
|
||||
}
|
||||
}
|
||||
tmp=u(&q1,1);
|
||||
dvprint("chroma_loc_info_present_flag= %llX\n", tmp);
|
||||
dvprint("chroma_loc_info_present_flag= % 4lld (%#llX)\n",tmp,tmp);
|
||||
if ( tmp )
|
||||
{
|
||||
tmp=ue(&q1);
|
||||
dvprint("chroma_sample_loc_type_top_field= %llX\n", tmp);
|
||||
dvprint("chroma_sample_loc_type_top_field= % 4lld (%#llX)\n",tmp,tmp);
|
||||
tmp=ue(&q1);
|
||||
dvprint("chroma_sample_loc_type_bottom_field= %llX\n", tmp);
|
||||
dvprint("chroma_sample_loc_type_bottom_field= % 4lld (%#llX)\n",tmp,tmp);
|
||||
}
|
||||
tmp=u(&q1,1);
|
||||
dvprint("timing_info_present_flag= %llX\n", tmp);
|
||||
dvprint("timing_info_present_flag= % 4lld (%#llX)\n",tmp,tmp);
|
||||
if ( tmp )
|
||||
{
|
||||
tmp=u(&q1,32);
|
||||
dvprint("num_units_in_tick= %llX\n", tmp);
|
||||
LLONG num_units_in_tick = tmp;
|
||||
dvprint("num_units_in_tick= % 4lld (%#llX)\n",tmp,tmp);
|
||||
tmp=u(&q1,32);
|
||||
dvprint("time_scale= %llX\n", tmp);
|
||||
LLONG time_scale = tmp;
|
||||
dvprint("time_scale= % 4lld (%#llX)\n",tmp,tmp);
|
||||
tmp=u(&q1,1);
|
||||
dvprint("fixed_frame_rate_flag= %llX\n", tmp);
|
||||
int fixed_frame_rate_flag = (int) tmp;
|
||||
dvprint("fixed_frame_rate_flag= % 4lld (%#llX)\n",tmp,tmp);
|
||||
// Change: use num_units_in_tick and time_scale to calculate FPS. (ISO/IEC 14496-10:2012(E), page 397 & further)
|
||||
if (fixed_frame_rate_flag){
|
||||
double clock_tick = (double) num_units_in_tick / time_scale;
|
||||
dvprint("clock_tick= %f\n", clock_tick);
|
||||
current_fps = (double)time_scale / (2 * num_units_in_tick); // Based on formula D-2, p. 359 of the ISO/IEC 14496-10:2012(E) spec.
|
||||
mprint("Changed fps using NAL to: %f\n", current_fps);
|
||||
}
|
||||
}
|
||||
tmp=u(&q1,1);
|
||||
dvprint("nal_hrd_parameters_present_flag= %llX\n", tmp);
|
||||
dvprint("nal_hrd_parameters_present_flag= % 4lld (%#llX)\n",tmp,tmp);
|
||||
if ( tmp )
|
||||
{
|
||||
dvprint ("nal_hrd. Not implemented for now. Hopefully not needed. Skiping rest of NAL\n");
|
||||
dvprint ("nal_hrd. Not implemented for now. Hopefully not needed. Skiping rest of NAL\n");
|
||||
//printf("Boom nal_hrd\n");
|
||||
// exit(1);
|
||||
num_nal_hrd++;
|
||||
return;
|
||||
}
|
||||
}
|
||||
tmp1=u(&q1,1);
|
||||
dvprint("vcl_hrd_parameters_present_flag= %llX\n", tmp1);
|
||||
if ( tmp )
|
||||
{
|
||||
// TODO.
|
||||
mprint ("vcl_hrd. Not implemented for now. Hopefully not needed. Skiping rest of NAL\n");
|
||||
mprint ("vcl_hrd. Not implemented for now. Hopefully not needed. Skiping rest of NAL\n");
|
||||
num_vcl_hrd++;
|
||||
// exit(1);
|
||||
}
|
||||
}
|
||||
if ( tmp || tmp1 )
|
||||
{
|
||||
tmp=u(&q1,1);
|
||||
dvprint("low_delay_hrd_flag= %llX\n", tmp);
|
||||
dvprint("low_delay_hrd_flag= % 4lld (%#llX)\n",tmp,tmp);
|
||||
return;
|
||||
}
|
||||
tmp=u(&q1,1);
|
||||
dvprint("pic_struct_present_flag= %llX\n", tmp);
|
||||
dvprint("pic_struct_present_flag= % 4lld (%#llX)\n",tmp,tmp);
|
||||
tmp=u(&q1,1);
|
||||
dvprint("bitstream_restriction_flag= %llX\n", tmp);
|
||||
dvprint("bitstream_restriction_flag= % 4lld (%#llX)\n",tmp,tmp);
|
||||
// ..
|
||||
// The hope was to find the GOP length in max_dec_frame_buffering, but
|
||||
// it was not set in the testfile. Ignore the rest here, it's
|
||||
@@ -745,7 +819,7 @@ void seq_parameter_set_rbsp (unsigned char *seqbuf, unsigned char *seqend)
|
||||
|
||||
|
||||
// Process slice header in AVC data.
|
||||
void slice_header (unsigned char *heabuf, unsigned char *heaend, int nal_unit_type)
|
||||
void slice_header (struct lib_ccx_ctx *ctx, unsigned char *heabuf, unsigned char *heaend, int nal_unit_type, struct cc_subtitle *sub)
|
||||
{
|
||||
LLONG tmp;
|
||||
struct bitstream q1;
|
||||
@@ -772,11 +846,11 @@ void slice_header (unsigned char *heabuf, unsigned char *heaend, int nal_unit_ty
|
||||
|
||||
dvprint("\nSLICE HEADER\n");
|
||||
tmp=ue(&q1);
|
||||
dvprint("first_mb_in_slice= %llX\n", tmp);
|
||||
dvprint("first_mb_in_slice= % 4lld (%#llX)\n",tmp,tmp);
|
||||
slice_type=ue(&q1);
|
||||
dvprint("slice_type= %llX\n", slice_type);
|
||||
tmp=ue(&q1);
|
||||
dvprint("pic_parameter_set_id= %llX\n", tmp);
|
||||
dvprint("pic_parameter_set_id= % 4lld (%#llX)\n",tmp,tmp);
|
||||
|
||||
lastframe_num = frame_num;
|
||||
int maxframe_num = (int) ((1<<log2_max_frame_num) - 1);
|
||||
@@ -808,7 +882,7 @@ void slice_header (unsigned char *heabuf, unsigned char *heaend, int nal_unit_ty
|
||||
if( nal_unit_type == 5 )
|
||||
{
|
||||
tmp=ue(&q1);
|
||||
dvprint("idr_pic_id= %llX\n", tmp);
|
||||
dvprint("idr_pic_id= % 4lld (%#llX)\n",tmp,tmp);
|
||||
//TODO
|
||||
}
|
||||
if( pic_order_cnt_type == 0 )
|
||||
@@ -818,7 +892,7 @@ void slice_header (unsigned char *heabuf, unsigned char *heaend, int nal_unit_ty
|
||||
}
|
||||
if( pic_order_cnt_type == 1 )
|
||||
{
|
||||
fatal(EXIT_BUG_BUG, "AVC: pic_order_cnt_type == 1 not yet supported.");
|
||||
fatal(CCX_COMMON_EXIT_BUG_BUG, "AVC: pic_order_cnt_type == 1 not yet supported.");
|
||||
}
|
||||
#if 0
|
||||
else
|
||||
@@ -831,7 +905,7 @@ void slice_header (unsigned char *heabuf, unsigned char *heaend, int nal_unit_ty
|
||||
FrameNumOffset = lastframe_num + maxframe_num;
|
||||
else
|
||||
FrameNumOffset = lastframe_num;
|
||||
|
||||
|
||||
LLONG tempPicOrderCnt=0;
|
||||
if (IdrPicFlag == 1)
|
||||
tempPicOrderCnt=0;
|
||||
@@ -851,9 +925,9 @@ void slice_header (unsigned char *heabuf, unsigned char *heaend, int nal_unit_ty
|
||||
else
|
||||
TopFieldOrderCnt = tempPicOrderCnt;
|
||||
|
||||
//pic_order_cnt_lsb=tempPicOrderCnt;
|
||||
//pic_order_cnt_lsb=tempPicOrderCnt;
|
||||
//pic_order_cnt_lsb=u(&q1,tempPicOrderCnt);
|
||||
//fatal(EXIT_BUG_BUG, "AVC: pic_order_cnt_type != 0 not yet supported.");
|
||||
//fatal(CCX_COMMON_EXIT_BUG_BUG, "AVC: pic_order_cnt_type != 0 not yet supported.");
|
||||
//TODO
|
||||
// Calculate picture order count (POC) according to 8.2.1
|
||||
}
|
||||
@@ -893,11 +967,11 @@ void slice_header (unsigned char *heabuf, unsigned char *heaend, int nal_unit_ty
|
||||
|
||||
// Sometimes two P-slices follow each other, see garbled_dishHD.mpg,
|
||||
// in this case we only treat the first as a reference pic
|
||||
if (isref && frames_since_last_gop <= 3) // Used to be == 1, but the sample file
|
||||
if (isref && ctx->frames_since_last_gop <= 3) // Used to be == 1, but the sample file
|
||||
{ // 2014 SugarHouse Casino Mummers Parade Fancy Brigades_new.ts was garbled
|
||||
// Probably doing a proper PTS sort would be a better solution.
|
||||
isref = 0;
|
||||
dbg_print(CCX_DMT_TIME, "Ignoring this reference pic.\n");
|
||||
dbg_print(CCX_DMT_TIME, "Ignoring this reference pic.\n");
|
||||
}
|
||||
|
||||
// if slices are buffered - flush
|
||||
@@ -910,10 +984,10 @@ void slice_header (unsigned char *heabuf, unsigned char *heaend, int nal_unit_ty
|
||||
// Flush buffered cc blocks before doing the housekeeping
|
||||
if (has_ccdata_buffered)
|
||||
{
|
||||
process_hdcc();
|
||||
process_hdcc(ctx, sub);
|
||||
}
|
||||
last_gop_length = frames_since_last_gop;
|
||||
frames_since_last_gop=0;
|
||||
ctx->last_gop_length = ctx->frames_since_last_gop;
|
||||
ctx->frames_since_last_gop=0;
|
||||
last_gop_maxtref = maxtref;
|
||||
maxtref = 0;
|
||||
lastmaxidx = maxidx;
|
||||
@@ -963,11 +1037,11 @@ void slice_header (unsigned char *heabuf, unsigned char *heaend, int nal_unit_ty
|
||||
// Set maxtref
|
||||
if( current_tref > maxtref ) {
|
||||
maxtref = current_tref;
|
||||
}
|
||||
}
|
||||
// Now an ugly workaround where pic_order_cnt_lsb increases in
|
||||
// steps of two. The 1.5 is an approximation, it should be:
|
||||
// last_gop_maxtref+1 == last_gop_length*2
|
||||
if ( last_gop_maxtref > last_gop_length*1.5 ) {
|
||||
if ( last_gop_maxtref > ctx->last_gop_length*1.5 ) {
|
||||
current_tref = current_tref/2;
|
||||
}
|
||||
}
|
||||
@@ -1006,7 +1080,7 @@ void slice_header (unsigned char *heabuf, unsigned char *heaend, int nal_unit_ty
|
||||
}
|
||||
if ( lastmaxidx == -1) {
|
||||
// Set temporal reference to zero on minimal index and in the first GOP
|
||||
// to avoid setting a wrong fts_offset
|
||||
// to avoid setting a wrong fts_offset
|
||||
current_tref = 0;
|
||||
}
|
||||
}
|
||||
@@ -1026,7 +1100,7 @@ void slice_header (unsigned char *heabuf, unsigned char *heaend, int nal_unit_ty
|
||||
(unsigned) (sync_pts));
|
||||
dbg_print(CCX_DMT_TIME, " - %s since GOP: %2u",
|
||||
slice_types[slice_type],
|
||||
(unsigned) (frames_since_last_gop));
|
||||
(unsigned) (ctx->frames_since_last_gop));
|
||||
dbg_print(CCX_DMT_TIME, " b:%lld frame# %lld\n", bottom_field_flag, frame_num);
|
||||
|
||||
// sync_pts is (was) set when current_tref was zero
|
||||
@@ -1040,13 +1114,11 @@ void slice_header (unsigned char *heabuf, unsigned char *heaend, int nal_unit_ty
|
||||
}
|
||||
|
||||
total_frames_count++;
|
||||
frames_since_last_gop++;
|
||||
ctx->frames_since_last_gop++;
|
||||
|
||||
store_hdcc(cc_data, cc_count, curridx, fts_now);
|
||||
store_hdcc(ctx, cc_data, cc_count, curridx, fts_now, sub);
|
||||
cc_buffer_saved=1; // CFS: store_hdcc supposedly saves the CC buffer to a sequence buffer
|
||||
cc_count=0;
|
||||
|
||||
//exit(1);
|
||||
}
|
||||
|
||||
// max_dec_frame_buffering .. Max frames in buffer
|
||||
@@ -1,4 +1,4 @@
|
||||
#include "ccextractor.h"
|
||||
#include "lib_ccx.h"
|
||||
|
||||
// Hold functions to read streams on a bit or byte oriented basis
|
||||
// plus some data related helper functions.
|
||||
@@ -22,7 +22,7 @@ int init_bitstream(struct bitstream *bstr, unsigned char *start, unsigned char *
|
||||
bstr->_i_bpos = 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!");
|
||||
return 1;
|
||||
@@ -40,11 +40,11 @@ uint64_t next_bits(struct bitstream *bstr, unsigned bnum)
|
||||
uint64_t res = 0;
|
||||
|
||||
if(bnum > 64)
|
||||
fatal(EXIT_BUG_BUG, "next_bits: 64 is maximum bit number, argument: %u!", bnum);
|
||||
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(EXIT_BUG_BUG, "next_bits: bitstream has negative length!");
|
||||
fatal(CCX_COMMON_EXIT_BUG_BUG, "next_bits: bitstream has negative length!");
|
||||
|
||||
// Keep a negative bitstream.bitsleft, but correct it.
|
||||
if (bstr->bitsleft <= 0)
|
||||
@@ -67,7 +67,7 @@ uint64_t next_bits(struct bitstream *bstr, unsigned bnum)
|
||||
|
||||
if(vbit < 1 || vbit > 8)
|
||||
{
|
||||
fatal(EXIT_BUG_BUG, "next_bits: Illegal bit position value %d!", vbit);
|
||||
fatal(CCX_COMMON_EXIT_BUG_BUG, "next_bits: Illegal bit position value %d!", vbit);
|
||||
}
|
||||
|
||||
while( 1 )
|
||||
@@ -75,7 +75,7 @@ uint64_t next_bits(struct bitstream *bstr, unsigned bnum)
|
||||
if(vpos >= bstr->end)
|
||||
{
|
||||
// We should not get here ...
|
||||
fatal(EXIT_BUG_BUG, "next_bits: Reading after end of data ...");
|
||||
fatal(CCX_COMMON_EXIT_BUG_BUG, "next_bits: Reading after end of data ...");
|
||||
}
|
||||
|
||||
res |= (*vpos & (0x01 << (vbit-1)) ? 1 : 0);
|
||||
@@ -130,7 +130,7 @@ int skip_bits(struct bitstream *bstr, unsigned bnum)
|
||||
{
|
||||
// Sanity check
|
||||
if(bstr->end - bstr->pos < 0)
|
||||
fatal(EXIT_BUG_BUG, "skip_bits: bitstream has negative length!");
|
||||
fatal(CCX_COMMON_EXIT_BUG_BUG, "skip_bits: bitstream has negative length!");
|
||||
|
||||
// Keep a negative bstr->bitsleft, but correct it.
|
||||
if (bstr->bitsleft < 0)
|
||||
@@ -167,13 +167,13 @@ int is_byte_aligned(struct bitstream *bstr)
|
||||
{
|
||||
// Sanity check
|
||||
if(bstr->end - bstr->pos < 0)
|
||||
fatal(EXIT_BUG_BUG, "is_byte_aligned: bitstream has negative length!");
|
||||
fatal(CCX_COMMON_EXIT_BUG_BUG, "is_byte_aligned: bitstream has negative length!");
|
||||
|
||||
int vbit = bstr->bpos;
|
||||
|
||||
if(vbit == 0 || vbit > 8)
|
||||
{
|
||||
fatal(EXIT_BUG_BUG, "is_byte_aligned: Illegal bit position value %d!\n", vbit);
|
||||
fatal(CCX_COMMON_EXIT_BUG_BUG, "is_byte_aligned: Illegal bit position value %d!\n", vbit);
|
||||
}
|
||||
|
||||
if (vbit == 8)
|
||||
@@ -188,13 +188,13 @@ void make_byte_aligned(struct bitstream *bstr)
|
||||
{
|
||||
// Sanity check
|
||||
if(bstr->end - bstr->pos < 0)
|
||||
fatal(EXIT_BUG_BUG, "make_byte_aligned: bitstream has negative length!");
|
||||
fatal(CCX_COMMON_EXIT_BUG_BUG, "make_byte_aligned: bitstream has negative length!");
|
||||
|
||||
int vbit = bstr->bpos;
|
||||
|
||||
if(vbit == 0 || vbit > 8)
|
||||
{
|
||||
fatal(EXIT_BUG_BUG, "make_byte_aligned: Illegal bit position value %d!\n", vbit);
|
||||
fatal(CCX_COMMON_EXIT_BUG_BUG, "make_byte_aligned: Illegal bit position value %d!\n", vbit);
|
||||
}
|
||||
|
||||
// Keep a negative bstr->bitsleft, but correct it.
|
||||
@@ -226,7 +226,7 @@ unsigned char *next_bytes(struct bitstream *bstr, unsigned bynum)
|
||||
{
|
||||
// Sanity check
|
||||
if(bstr->end - bstr->pos < 0)
|
||||
fatal(EXIT_BUG_BUG, "next_bytes: bitstream has negative length!");
|
||||
fatal(CCX_COMMON_EXIT_BUG_BUG, "next_bytes: bitstream has negative length!");
|
||||
|
||||
// Keep a negative bstr->bitsleft, but correct it.
|
||||
if (bstr->bitsleft < 0)
|
||||
@@ -294,17 +294,17 @@ uint64_t bitstream_get_num(struct bitstream *bstr, unsigned bytes, int advance)
|
||||
case 8:
|
||||
break;
|
||||
default:
|
||||
fatal (EXIT_BUG_BUG, "bitstream_get_num: Illegal precision value [%u]!",
|
||||
fatal (CCX_COMMON_EXIT_BUG_BUG, "bitstream_get_num: Illegal precision value [%u]!",
|
||||
bytes);
|
||||
break;
|
||||
}
|
||||
for (unsigned i=0;i<bytes;i++)
|
||||
{
|
||||
unsigned char *ucpos=((unsigned char *)bpos) +bytes-i-1; // Read backwards
|
||||
unsigned char uc=*ucpos;
|
||||
unsigned char *ucpos=((unsigned char *)bpos) +bytes-i-1; // Read backwards
|
||||
unsigned char uc=*ucpos;
|
||||
rval=(rval<<8) + uc;
|
||||
}
|
||||
return rval;
|
||||
return rval;
|
||||
}
|
||||
|
||||
|
||||
@@ -329,7 +329,7 @@ int64_t se(struct bitstream *bstr)
|
||||
int64_t res = 0;
|
||||
|
||||
res = ue(bstr);
|
||||
|
||||
|
||||
// The following function might truncate when res+1 overflows
|
||||
//res = (res+1)/2 * (res % 2 ? 1 : -1);
|
||||
// Use this:
|
||||
@@ -3,8 +3,8 @@
|
||||
void get_char_in_latin_1 (unsigned char *buffer, unsigned char c)
|
||||
{
|
||||
unsigned char c1='?';
|
||||
if (c<0x80)
|
||||
{
|
||||
if (c<0x80)
|
||||
{
|
||||
// Regular line-21 character set, mostly ASCII except these exceptions
|
||||
switch (c)
|
||||
{
|
||||
@@ -16,7 +16,7 @@ void get_char_in_latin_1 (unsigned char *buffer, unsigned char c)
|
||||
break;
|
||||
case 0x5e: // lowercase i, acute accent
|
||||
c1=0xed;
|
||||
break;
|
||||
break;
|
||||
case 0x5f: // lowercase o, acute accent
|
||||
c1=0xf3;
|
||||
break;
|
||||
@@ -45,25 +45,25 @@ void get_char_in_latin_1 (unsigned char *buffer, unsigned char c)
|
||||
switch (c)
|
||||
{
|
||||
// THIS BLOCK INCLUDES THE 16 EXTENDED (TWO-BYTE) LINE 21 CHARACTERS
|
||||
// THAT COME FROM HI BYTE=0x11 AND LOW BETWEEN 0x30 AND 0x3F
|
||||
// THAT COME FROM HI BYTE=0x11 AND LOW BETWEEN 0x30 AND 0x3F
|
||||
case 0x80: // Registered symbol (R)
|
||||
c1=0xae;
|
||||
break;
|
||||
break;
|
||||
case 0x81: // degree sign
|
||||
c1=0xb0;
|
||||
break;
|
||||
case 0x82: // 1/2 symbol
|
||||
case 0x82: // 1/2 symbol
|
||||
c1=0xbd;
|
||||
break;
|
||||
case 0x83: // Inverted (open) question mark
|
||||
case 0x83: // Inverted (open) question mark
|
||||
c1=0xbf;
|
||||
break;
|
||||
case 0x84: // Trademark symbol (TM) - Does not exist in Latin 1
|
||||
break;
|
||||
case 0x85: // Cents symbol
|
||||
break;
|
||||
case 0x85: // Cents symbol
|
||||
c1=0xa2;
|
||||
break;
|
||||
case 0x86: // Pounds sterling
|
||||
case 0x86: // Pounds sterling
|
||||
c1=0xa3;
|
||||
break;
|
||||
case 0x87: // Music note - Not in latin 1, so we use 'pilcrow'
|
||||
@@ -73,7 +73,7 @@ void get_char_in_latin_1 (unsigned char *buffer, unsigned char c)
|
||||
c1=0xe0;
|
||||
break;
|
||||
case 0x89: // transparent space, we make it regular
|
||||
c1=0x20;
|
||||
c1=0x20;
|
||||
break;
|
||||
case 0x8a: // lowercase e, grave accent
|
||||
c1=0xe8;
|
||||
@@ -82,7 +82,7 @@ void get_char_in_latin_1 (unsigned char *buffer, unsigned char c)
|
||||
c1=0xe2;
|
||||
break;
|
||||
case 0x8c: // lowercase e, circumflex accent
|
||||
c1=0xea;
|
||||
c1=0xea;
|
||||
break;
|
||||
case 0x8d: // lowercase i, circumflex accent
|
||||
c1=0xee;
|
||||
@@ -114,19 +114,19 @@ void get_char_in_latin_1 (unsigned char *buffer, unsigned char c)
|
||||
c1=0xfc;
|
||||
break;
|
||||
case 0x96: // apostrophe
|
||||
c1=0x27;
|
||||
c1=0x27;
|
||||
break;
|
||||
case 0x97: // inverted exclamation mark
|
||||
case 0x97: // inverted exclamation mark
|
||||
c1=0xa1;
|
||||
break;
|
||||
case 0x98: // asterisk
|
||||
c1=0x2a;
|
||||
c1=0x2a;
|
||||
break;
|
||||
case 0x99: // apostrophe (yes, duped). See CCADI source code.
|
||||
c1=0x27;
|
||||
c1=0x27;
|
||||
break;
|
||||
case 0x9a: // em dash
|
||||
c1=0x2d;
|
||||
c1=0x2d;
|
||||
break;
|
||||
case 0x9b: // copyright sign
|
||||
c1=0xa9;
|
||||
@@ -137,17 +137,17 @@ void get_char_in_latin_1 (unsigned char *buffer, unsigned char c)
|
||||
c1=0x2e;
|
||||
break;
|
||||
case 0x9e: // Quoatation mark
|
||||
c1=0x22;
|
||||
c1=0x22;
|
||||
break;
|
||||
case 0x9f: // Quoatation mark
|
||||
c1=0x22;
|
||||
c1=0x22;
|
||||
break;
|
||||
case 0xa0: // uppercase A, grave accent
|
||||
c1=0xc0;
|
||||
break;
|
||||
case 0xa1: // uppercase A, circumflex
|
||||
c1=0xc2;
|
||||
break;
|
||||
break;
|
||||
case 0xa2: // uppercase C with cedilla
|
||||
c1=0xc7;
|
||||
break;
|
||||
@@ -223,7 +223,7 @@ void get_char_in_latin_1 (unsigned char *buffer, unsigned char c)
|
||||
c1=0x7b;
|
||||
break;
|
||||
case 0xba: // Closing curly brace
|
||||
c1=0x7d;
|
||||
c1=0x7d;
|
||||
break;
|
||||
case 0xbb: // Backslash
|
||||
c1=0x5c;
|
||||
@@ -234,17 +234,17 @@ void get_char_in_latin_1 (unsigned char *buffer, unsigned char c)
|
||||
case 0xbd: // Underscore
|
||||
c1=0x5f;
|
||||
break;
|
||||
case 0xbe: // Pipe (broken bar)
|
||||
case 0xbe: // Pipe (broken bar)
|
||||
c1=0xa6;
|
||||
break;
|
||||
case 0xbf: // Tilde
|
||||
c1=0x7e;
|
||||
c1=0x7e;
|
||||
break;
|
||||
case 0xc0: // Uppercase A, umlaut
|
||||
case 0xc0: // Uppercase A, umlaut
|
||||
c1=0xc4;
|
||||
break;
|
||||
case 0xc1: // Lowercase A, umlaut
|
||||
c1=0xe3;
|
||||
c1=0xe3;
|
||||
break;
|
||||
case 0xc2: // Uppercase O, umlaut
|
||||
c1=0xd6;
|
||||
@@ -260,10 +260,10 @@ void get_char_in_latin_1 (unsigned char *buffer, unsigned char c)
|
||||
break;
|
||||
case 0xc6: // Currency symbol
|
||||
c1=0xa4;
|
||||
break;
|
||||
break;
|
||||
case 0xc7: // Vertical bar
|
||||
c1=0x7c;
|
||||
break;
|
||||
break;
|
||||
case 0xc8: // Uppercase A, ring
|
||||
c1=0xc5;
|
||||
break;
|
||||
@@ -284,7 +284,7 @@ void get_char_in_latin_1 (unsigned char *buffer, unsigned char c)
|
||||
*buffer='?'; // I'll do it eventually, I promise
|
||||
break; // This are weird chars anyway
|
||||
}
|
||||
*buffer=c1;
|
||||
*buffer=c1;
|
||||
}
|
||||
|
||||
void get_char_in_unicode (unsigned char *buffer, unsigned char c)
|
||||
@@ -292,7 +292,7 @@ void get_char_in_unicode (unsigned char *buffer, unsigned char c)
|
||||
unsigned char c1,c2;
|
||||
switch (c)
|
||||
{
|
||||
case 0x84: // Trademark symbol (TM)
|
||||
case 0x84: // Trademark symbol (TM)
|
||||
c2=0x21;
|
||||
c1=0x22;
|
||||
break;
|
||||
@@ -307,7 +307,7 @@ void get_char_in_unicode (unsigned char *buffer, unsigned char c)
|
||||
case 0xcc: // Upper left corner
|
||||
c2=0x23;
|
||||
c1=0x1c;
|
||||
break;
|
||||
break;
|
||||
case 0xcd: // Upper right corner
|
||||
c2=0x23;
|
||||
c1=0x1d;
|
||||
@@ -320,7 +320,7 @@ void get_char_in_unicode (unsigned char *buffer, unsigned char c)
|
||||
c2=0x23;
|
||||
c1=0x1f;
|
||||
break;
|
||||
default: // Everything else, same as latin-1 followed by 00
|
||||
default: // Everything else, same as latin-1 followed by 00
|
||||
get_char_in_latin_1 (&c1,c);
|
||||
c2=0;
|
||||
break;
|
||||
@@ -384,10 +384,10 @@ int get_char_in_utf_8 (unsigned char *buffer, unsigned char c) // Returns number
|
||||
switch (c)
|
||||
{
|
||||
// THIS BLOCK INCLUDES THE 16 EXTENDED (TWO-BYTE) LINE 21 CHARACTERS
|
||||
// THAT COME FROM HI BYTE=0x11 AND LOW BETWEEN 0x30 AND 0x3F
|
||||
// THAT COME FROM HI BYTE=0x11 AND LOW BETWEEN 0x30 AND 0x3F
|
||||
case 0x80: // Registered symbol (R)
|
||||
*buffer=0xc2;
|
||||
*(buffer+1)=0xae;
|
||||
*(buffer+1)=0xae;
|
||||
return 2;
|
||||
case 0x81: // degree sign
|
||||
*buffer=0xc2;
|
||||
@@ -414,7 +414,7 @@ int get_char_in_utf_8 (unsigned char *buffer, unsigned char c) // Returns number
|
||||
*buffer=0xc2;
|
||||
*(buffer+1)=0xa3;
|
||||
return 2;
|
||||
case 0x87: // Music note
|
||||
case 0x87: // Music note
|
||||
*buffer=0xe2;
|
||||
*(buffer+1)=0x99;
|
||||
*(buffer+2)=0xaa;
|
||||
@@ -424,7 +424,7 @@ int get_char_in_utf_8 (unsigned char *buffer, unsigned char c) // Returns number
|
||||
*(buffer+1)=0xa0;
|
||||
return 2;
|
||||
case 0x89: // transparent space, we make it regular
|
||||
*buffer=0x20;
|
||||
*buffer=0x20;
|
||||
return 1;
|
||||
case 0x8a: // lowercase e, grave accent
|
||||
*buffer=0xc3;
|
||||
@@ -477,20 +477,20 @@ int get_char_in_utf_8 (unsigned char *buffer, unsigned char c) // Returns number
|
||||
*(buffer+1)=0xbc;
|
||||
return 2;
|
||||
case 0x96: // apostrophe
|
||||
*buffer=0x27;
|
||||
*buffer=0x27;
|
||||
return 1;
|
||||
case 0x97: // inverted exclamation mark
|
||||
*buffer=0xc2;
|
||||
*(buffer+1)=0xa1;
|
||||
return 2;
|
||||
case 0x98: // asterisk
|
||||
*buffer=0x2a;
|
||||
*buffer=0x2a;
|
||||
return 1;
|
||||
case 0x99: // Plain single quote
|
||||
*buffer=0x27;
|
||||
*buffer=0x27;
|
||||
return 1;
|
||||
case 0x9a: // em dash
|
||||
*buffer=0xe2;
|
||||
*buffer=0xe2;
|
||||
*(buffer+1)=0x80;
|
||||
*(buffer+2)=0x94;
|
||||
return 3;
|
||||
@@ -498,23 +498,23 @@ int get_char_in_utf_8 (unsigned char *buffer, unsigned char c) // Returns number
|
||||
*buffer=0xc2;
|
||||
*(buffer+1)=0xa9;
|
||||
return 2;
|
||||
case 0x9c: // Service mark
|
||||
*buffer=0xe2;
|
||||
case 0x9c: // Service mark
|
||||
*buffer=0xe2;
|
||||
*(buffer+1)=0x84;
|
||||
*(buffer+2)=0xa0;
|
||||
return 3;
|
||||
case 0x9d: // Round bullet
|
||||
*buffer=0xe2;
|
||||
*buffer=0xe2;
|
||||
*(buffer+1)=0x80;
|
||||
*(buffer+2)=0xa2;
|
||||
return 3;
|
||||
case 0x9e: // Opening double quotes
|
||||
*buffer=0xe2;
|
||||
*buffer=0xe2;
|
||||
*(buffer+1)=0x80;
|
||||
*(buffer+2)=0x9c;
|
||||
return 3;
|
||||
case 0x9f: // Closing double quotes
|
||||
*buffer=0xe2;
|
||||
*buffer=0xe2;
|
||||
*(buffer+1)=0x80;
|
||||
*(buffer+2)=0x9d;
|
||||
return 3;
|
||||
@@ -624,7 +624,7 @@ int get_char_in_utf_8 (unsigned char *buffer, unsigned char c) // Returns number
|
||||
*buffer=0x7b;
|
||||
return 1;
|
||||
case 0xba: // Closing curly brace
|
||||
*buffer=0x7d;
|
||||
*buffer=0x7d;
|
||||
return 1;
|
||||
case 0xbb: // Backslash
|
||||
*buffer=0x5c;
|
||||
@@ -636,26 +636,26 @@ int get_char_in_utf_8 (unsigned char *buffer, unsigned char c) // Returns number
|
||||
*buffer=0x5f;
|
||||
return 1;
|
||||
case 0xbe: // Pipe (broken bar)
|
||||
*buffer=0xc2;
|
||||
*buffer=0xc2;
|
||||
*(buffer+1)=0xa6;
|
||||
return 2;
|
||||
case 0xbf: // Tilde
|
||||
*buffer=0x7e; // Not sure
|
||||
return 1;
|
||||
case 0xc0: // Uppercase A, umlaut
|
||||
*buffer=0xc3;
|
||||
*buffer=0xc3;
|
||||
*(buffer+1)=0x84;
|
||||
return 2;
|
||||
case 0xc1: // Lowercase A, umlaut
|
||||
*buffer=0xc3;
|
||||
*buffer=0xc3;
|
||||
*(buffer+1)=0xa4;
|
||||
return 2;
|
||||
case 0xc2: // Uppercase O, umlaut
|
||||
*buffer=0xc3;
|
||||
*buffer=0xc3;
|
||||
*(buffer+1)=0x96;
|
||||
return 2;
|
||||
case 0xc3: // Lowercase o, umlaut
|
||||
*buffer=0xc3;
|
||||
*buffer=0xc3;
|
||||
*(buffer+1)=0xb6;
|
||||
return 2;
|
||||
case 0xc4: // Esszett (sharp S)
|
||||
@@ -671,7 +671,7 @@ int get_char_in_utf_8 (unsigned char *buffer, unsigned char c) // Returns number
|
||||
*(buffer+1)=0xa4;
|
||||
return 2;
|
||||
case 0xc7: // Vertical bar
|
||||
*buffer=0x7c;
|
||||
*buffer=0x7c;
|
||||
return 1;
|
||||
case 0xc8: // Uppercase A, ring
|
||||
*buffer=0xc3;
|
||||
@@ -709,7 +709,7 @@ int get_char_in_utf_8 (unsigned char *buffer, unsigned char c) // Returns number
|
||||
*(buffer+1)=0x8c;
|
||||
*(buffer+2)=0x9f;
|
||||
return 3;
|
||||
default: //
|
||||
default: //
|
||||
*buffer='?'; // I'll do it eventually, I promise
|
||||
return 1; // This are weird chars anyway
|
||||
}
|
||||
@@ -726,35 +726,35 @@ unsigned char cctolower (unsigned char c)
|
||||
case 0x90: // capital letter A with acute
|
||||
return 0x2a;
|
||||
case 0x91: // capital letter E with acute
|
||||
return 0x5c;
|
||||
return 0x5c;
|
||||
case 0x92: // capital letter O with acute
|
||||
return 0x5f;
|
||||
return 0x5f;
|
||||
case 0x93: // capital letter U with acute
|
||||
return 0x60;
|
||||
return 0x60;
|
||||
case 0xa2: // uppercase C with cedilla
|
||||
return 0x7b;
|
||||
return 0x7b;
|
||||
case 0xa0: // uppercase A, grave accent
|
||||
return 0x88;
|
||||
return 0x88;
|
||||
case 0xa3: // uppercase E, grave accent
|
||||
return 0x8a;
|
||||
return 0x8a;
|
||||
case 0xa1: // uppercase A, circumflex
|
||||
return 0x8b;
|
||||
return 0x8b;
|
||||
case 0xa4: // uppercase E, circumflex
|
||||
return 0x8c;
|
||||
return 0x8c;
|
||||
case 0xa7: // uppercase I, circumflex
|
||||
return 0x8d;
|
||||
return 0x8d;
|
||||
case 0xaa: // uppercase O, circumflex
|
||||
return 0x8e;
|
||||
return 0x8e;
|
||||
case 0xad: // uppercase U, circumflex
|
||||
return 0x8f;
|
||||
return 0x8f;
|
||||
case 0x94: // capital letter U with diaresis
|
||||
return 0x95;
|
||||
return 0x95;
|
||||
case 0xa5: // capital letter E with diaresis
|
||||
return 0xa6;
|
||||
return 0xa6;
|
||||
case 0xa8: // uppercase I, with diaresis
|
||||
return 0xa9;
|
||||
return 0xa9;
|
||||
case 0xab: // uppercase U, grave accent
|
||||
return 0xac;
|
||||
return 0xac;
|
||||
case 0xb0: // Uppercase A, tilde
|
||||
return 0xb1;
|
||||
case 0xb2: // Uppercase I, acute accent
|
||||
@@ -820,21 +820,21 @@ unsigned char cctoupper (unsigned char c)
|
||||
case 0xac: // lowercase u, grave accent
|
||||
return 0xab;
|
||||
case 0xb1: // Lowercase a, tilde
|
||||
return 0xb0;
|
||||
return 0xb0;
|
||||
case 0xb4: // Lowercase i, grave accent
|
||||
return 0xb3;
|
||||
case 0xb6: // Lowercase o, grave accent
|
||||
return 0xb5;
|
||||
case 0xb8: // Lowercase o, tilde
|
||||
return 0xb5;
|
||||
case 0xb8: // Lowercase o, tilde
|
||||
return 0xb7;
|
||||
case 0xc1: // Lowercase A, umlaut
|
||||
return 0xc0;
|
||||
case 0xc1: // Lowercase A, umlaut
|
||||
return 0xc0;
|
||||
case 0xc3: // Lowercase o, umlaut
|
||||
return 0xc2;
|
||||
case 0xc9: // Lowercase A, ring
|
||||
return 0xc8;
|
||||
return 0xc8;
|
||||
case 0xcb: // Lowercase o, slash
|
||||
return 0xca;
|
||||
return 0xca;
|
||||
}
|
||||
return c;
|
||||
}
|
||||
10
src/lib_ccx/ccx_common_char_encoding.h
Normal file
10
src/lib_ccx/ccx_common_char_encoding.h
Normal file
@@ -0,0 +1,10 @@
|
||||
#ifndef __CCX_COMMON_CHAR_ENCODING__
|
||||
#define __CCX_COMMON_CHAR_ENCODING__
|
||||
|
||||
void get_char_in_latin_1(unsigned char *buffer, unsigned char c);
|
||||
void get_char_in_unicode(unsigned char *buffer, unsigned char c);
|
||||
int get_char_in_utf_8(unsigned char *buffer, unsigned char c);
|
||||
unsigned char cctolower(unsigned char c);
|
||||
unsigned char cctoupper(unsigned char c);
|
||||
|
||||
#endif
|
||||
65
src/lib_ccx/ccx_common_common.c
Normal file
65
src/lib_ccx/ccx_common_common.c
Normal file
@@ -0,0 +1,65 @@
|
||||
#include "ccx_common_common.h"
|
||||
|
||||
/* printf() for fd instead of FILE*, since dprintf is not portable */
|
||||
void fdprintf(int fd, const char *fmt, ...)
|
||||
{
|
||||
/* Guess we need no more than 100 bytes. */
|
||||
int n, size = 100;
|
||||
char *p, *np;
|
||||
va_list ap;
|
||||
|
||||
if (fd < 0)
|
||||
return;
|
||||
if ((p = (char *)malloc(size)) == NULL)
|
||||
return;
|
||||
|
||||
while (1)
|
||||
{
|
||||
/* Try to print in the allocated space. */
|
||||
va_start(ap, fmt);
|
||||
n = vsnprintf(p, size, fmt, ap);
|
||||
va_end(ap);
|
||||
/* If that worked, return the string. */
|
||||
if (n > -1 && n < size)
|
||||
{
|
||||
write(fd, p, n);
|
||||
free(p);
|
||||
return;
|
||||
}
|
||||
/* Else try again with more space. */
|
||||
if (n > -1) /* glibc 2.1 */
|
||||
size = n + 1; /* precisely what is needed */
|
||||
else /* glibc 2.0 */
|
||||
size *= 2; /* twice the old size */
|
||||
if ((np = (char *)realloc(p, size)) == NULL)
|
||||
{
|
||||
free(p);
|
||||
return;
|
||||
}
|
||||
else {
|
||||
p = np;
|
||||
}
|
||||
}
|
||||
}
|
||||
/* Converts the given milli to separate hours,minutes,seconds and ms variables */
|
||||
void mstotime(LLONG milli, unsigned *hours, unsigned *minutes,
|
||||
unsigned *seconds, unsigned *ms)
|
||||
{
|
||||
// LLONG milli = (LLONG) ((ccblock*1000)/29.97);
|
||||
*ms = (unsigned)(milli % 1000); // milliseconds
|
||||
milli = (milli - *ms) / 1000; // Remainder, in seconds
|
||||
*seconds = (int)(milli % 60);
|
||||
milli = (milli - *seconds) / 60; // Remainder, in minutes
|
||||
*minutes = (int)(milli % 60);
|
||||
milli = (milli - *minutes) / 60; // Remainder, in hours
|
||||
*hours = (int)milli;
|
||||
}
|
||||
/* Frees the given pointer */
|
||||
void freep(void *arg)
|
||||
{
|
||||
void **ptr = (void **)arg;
|
||||
if (*ptr)
|
||||
free(*ptr);
|
||||
*ptr = NULL;
|
||||
|
||||
}
|
||||
20
src/lib_ccx/ccx_common_common.h
Normal file
20
src/lib_ccx/ccx_common_common.h
Normal file
@@ -0,0 +1,20 @@
|
||||
#ifndef _CC_COMMON_COMMON
|
||||
#define _CC_COMMON_COMMON
|
||||
|
||||
#include "ccx_common_platform.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
|
||||
|
||||
// Declarations
|
||||
void fdprintf(int fd, const char *fmt, ...);
|
||||
void mstotime(LLONG milli, unsigned *hours, unsigned *minutes,unsigned *seconds, unsigned *ms);
|
||||
void freep(void *arg);
|
||||
void dbg_print(LLONG mask, const char *fmt, ...);
|
||||
unsigned char *debug_608toASC(unsigned char *ccdata, int channel);
|
||||
|
||||
extern int cc608_parity_table[256]; // From myth
|
||||
#endif
|
||||
@@ -1,4 +1,4 @@
|
||||
#include "ccextractor.h"
|
||||
#include "ccx_common_constants.h"
|
||||
|
||||
// RCWT header (11 bytes):
|
||||
//byte(s) value description (All values below are hex numbers, not
|
||||
@@ -8,7 +8,7 @@
|
||||
//4-5 0050 Program version number
|
||||
//6-7 0001 File format version
|
||||
//8-10 000000 Padding, required :-)
|
||||
const unsigned char rcwt_header[11]={0xCC, 0xCC, 0xED, 0xCC, 0x00, 0x50, 0, 1, 0, 0, 0};
|
||||
unsigned char rcwt_header[11]={0xCC, 0xCC, 0xED, 0xCC, 0x00, 0x50, 0, 1, 0, 0, 0};
|
||||
|
||||
const unsigned char BROADCAST_HEADER[]={0xff, 0xff, 0xff, 0xff};
|
||||
const unsigned char LITTLE_ENDIAN_BOM[]={0xff, 0xfe};
|
||||
@@ -123,3 +123,16 @@ enum
|
||||
DTVCC_PACKET_DATA = 2,
|
||||
DTVCC_PACKET_START = 3,
|
||||
};
|
||||
|
||||
/**
|
||||
* After Adding a new language here, dont forget
|
||||
* to increase NB_LANGUAGE define ccx_common_constants.h
|
||||
*/
|
||||
const char *language[NB_LANGUAGE] =
|
||||
{
|
||||
"und",
|
||||
"eng",
|
||||
"fin",
|
||||
"spa",
|
||||
NULL
|
||||
};
|
||||
@@ -1,6 +1,13 @@
|
||||
#ifndef CCX_CONSTANTS_H
|
||||
#define CCX_CONSTANTS_H
|
||||
|
||||
#include "stdio.h"
|
||||
|
||||
#ifndef __cplusplus
|
||||
#define false 0
|
||||
#define true 1
|
||||
#endif
|
||||
|
||||
extern const char *framerates_types[16];
|
||||
extern const double framerates_values[16];
|
||||
|
||||
@@ -20,7 +27,7 @@ extern const unsigned char lc4[2];
|
||||
extern const unsigned char lc5[1];
|
||||
extern const unsigned char lc6[1];
|
||||
|
||||
extern const unsigned char rcwt_header[11];
|
||||
extern unsigned char rcwt_header[11];
|
||||
|
||||
#define ONEPASS 120 /* Bytes we can always look ahead without going out of limits */
|
||||
#define BUFSIZE (2048*1024+ONEPASS) /* 2 Mb plus the safety pass */
|
||||
@@ -32,17 +39,17 @@ extern const unsigned char rcwt_header[11];
|
||||
|
||||
#define XMLRPC_CHUNK_SIZE (64*1024) // 64 Kb per chunk, to avoid too many realloc()
|
||||
|
||||
enum ccx_debug_message_types
|
||||
enum ccx_debug_message_types
|
||||
{
|
||||
/* Each debug message now belongs to one of these types. Use bitmaps in case
|
||||
we want one message to belong to more than one type. */
|
||||
/* Each debug message now belongs to one of these types. Use bitmaps in case
|
||||
we want one message to belong to more than one type. */
|
||||
CCX_DMT_PARSE=1, // Show information related to parsing the container
|
||||
CCX_DMT_VIDES=2,// Show video stream related information
|
||||
CCX_DMT_TIME=4, // Show GOP and PTS timing information
|
||||
CCX_DMT_VERBOSE=8, // Show lots of debugging output
|
||||
CCX_DMT_608=0x10, // Show CC-608 decoder debug?
|
||||
CCX_DMT_708=0x20, // Show CC-708 decoder debug?
|
||||
CCX_DMT_XDS=0x40, // Show XDS decoder debug?
|
||||
CCX_DMT_DECODER_608=0x10, // Show CC-608 decoder debug?
|
||||
CCX_DMT_708=0x20, // Show CC-708 decoder debug?
|
||||
CCX_DMT_DECODER_XDS=0x40, // Show XDS decoder debug?
|
||||
CCX_DMT_CBRAW=0x80, // Caption blocks with FTS timing
|
||||
CCX_DMT_GENERIC_NOTICES=0x100, // Generic, always displayed even if no debug is selected
|
||||
CCX_DMT_TELETEXT=0x200, // Show teletext debug?
|
||||
@@ -74,7 +81,7 @@ enum ccx_avc_nal_types
|
||||
CCX_NAL_TYPE_RESERVED_17 = 18,
|
||||
CCX_NAL_TYPE_RESERVED_18 = 18,
|
||||
CCX_NAL_TYPE_CODED_SLICE_AUXILIARY_PICTURE = 19,
|
||||
CCX_NAL_TYPE_CODED_SLICE_EXTENSION = 20,
|
||||
CCX_NAL_TYPE_CODED_SLICE_EXTENSION = 20,
|
||||
CCX_NAL_TYPE_RESERVED_21 = 21,
|
||||
CCX_NAL_TYPE_RESERVED_22 = 22,
|
||||
CCX_NAL_TYPE_RESERVED_23 = 23,
|
||||
@@ -101,7 +108,7 @@ enum ccx_stream_type
|
||||
CCX_STREAM_TYPE_PRIVATE_MPEG2 = 0x06,
|
||||
CCX_STREAM_TYPE_MHEG_PACKETS = 0x07,
|
||||
CCX_STREAM_TYPE_MPEG2_ANNEX_A_DSM_CC = 0x08,
|
||||
CCX_STREAM_TYPE_ITU_T_H222_1 = 0x09,
|
||||
CCX_STREAM_TYPE_ITU_T_H222_1 = 0x09,
|
||||
CCX_STREAM_TYPE_ISO_IEC_13818_6_TYPE_A = 0x0A,
|
||||
CCX_STREAM_TYPE_ISO_IEC_13818_6_TYPE_B = 0x0B,
|
||||
CCX_STREAM_TYPE_ISO_IEC_13818_6_TYPE_C = 0x0C,
|
||||
@@ -117,13 +124,14 @@ enum ccx_stream_type
|
||||
|
||||
enum ccx_mpeg_descriptor
|
||||
{
|
||||
CCX_MPEG_DSC_REGISTRATION = 0x05,
|
||||
CCX_MPEG_DSC_REGISTRATION = 0x05,
|
||||
CCX_MPEG_DSC_DATA_STREAM_ALIGNMENT = 0x06,
|
||||
CCX_MPEG_DSC_ISO639_LANGUAGE = 0x0A,
|
||||
CCX_MPEG_DSC_VBI_DATA_DESCRIPTOR = 0x45,
|
||||
CCX_MPEG_DSC_VBI_TELETEXT_DESCRIPTOR = 0x46,
|
||||
CCX_MPEG_DSC_TELETEXT_DESCRIPTOR = 0x56,
|
||||
CCX_MPEG_DSC_DVB_SUBTITLE = 0x59,
|
||||
CCX_MPEG_DESC_DATA_COMP = 0xfd,
|
||||
};
|
||||
|
||||
|
||||
@@ -138,7 +146,8 @@ enum ccx_datasource
|
||||
{
|
||||
CCX_DS_FILE=0,
|
||||
CCX_DS_STDIN=1,
|
||||
CCX_DS_NETWORK=2
|
||||
CCX_DS_NETWORK=2,
|
||||
CCX_DS_TCP=3
|
||||
};
|
||||
|
||||
enum ccx_output_format
|
||||
@@ -173,7 +182,9 @@ enum ccx_stream_mode_enum
|
||||
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
|
||||
};
|
||||
@@ -212,11 +223,12 @@ typedef enum {
|
||||
UNDEF = 0xff
|
||||
} bool_t;
|
||||
|
||||
enum cxx_code_type
|
||||
enum ccx_code_type
|
||||
{
|
||||
CCX_CODEC_ANY,
|
||||
CCX_CODEC_TELETEXT,
|
||||
CCX_CODEC_DVB,
|
||||
CCX_CODEC_ISDB_CC,
|
||||
CCX_CODEC_NONE,
|
||||
};
|
||||
/*
|
||||
@@ -226,13 +238,13 @@ enum cxx_code_type
|
||||
* @param desc descriptor tag given for each stream
|
||||
*
|
||||
* @return if descriptor tag is valid then it return 1 otherwise 0
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
#define IS_VALID_TELETEXT_DESC(desc) ( ((desc) == CCX_MPEG_DSC_VBI_DATA_DESCRIPTOR )|| \
|
||||
( (desc) == CCX_MPEG_DSC_VBI_TELETEXT_DESCRIPTOR ) || \
|
||||
( (desc) == CCX_MPEG_DSC_TELETEXT_DESCRIPTOR ) )
|
||||
|
||||
|
||||
/*
|
||||
* This macro to be used when you want to find out whether you
|
||||
* should parse f_sel subtitle codec type or not
|
||||
@@ -255,8 +267,6 @@ enum cxx_code_type
|
||||
#define CCX_TXT_AUTO_NOT_YET_FOUND 1
|
||||
#define CCX_TXT_IN_USE 2 // Positive autodetected, or forced, etc
|
||||
|
||||
|
||||
#define CCX_OF_TYPE_TEXT 1
|
||||
#define CCX_OF_TYPE_IMAGE 2
|
||||
|
||||
#define NB_LANGUAGE 5
|
||||
extern const char *language[NB_LANGUAGE];
|
||||
#endif
|
||||
110
src/lib_ccx/ccx_common_option.c
Normal file
110
src/lib_ccx/ccx_common_option.c
Normal file
@@ -0,0 +1,110 @@
|
||||
#include "ccx_common_option.h"
|
||||
#include "ccx_encoders_common.h"
|
||||
#include "utility.h"
|
||||
|
||||
struct ccx_s_options ccx_options;
|
||||
/* 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;
|
||||
options->settings_608.force_rollup = 0;
|
||||
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->sentence_cap_file=NULL; // Extra words file?
|
||||
options->live_stream=0; // 0 -> A regular file
|
||||
options->messages_target=1; // 1=stdout
|
||||
options->print_file_reports=0;
|
||||
/* Levenshtein's parameters, for string comparison */
|
||||
options->levdistmincnt=2; // Means 2 fails or less is "the same"...
|
||||
options->levdistmaxpct=10; // ...10% or less is also "the same"
|
||||
options->investigate_packets = 0; // Look for captions in all packets when everything else fails
|
||||
options->fullbin=0; // Disable pruning of padding cc blocks
|
||||
options->nosync=0; // Disable syncing
|
||||
options->hauppauge_mode=0; // If 1, use PID=1003, process specially and so on
|
||||
options->wtvconvertfix = 0; // Fix broken Windows 7 conversion
|
||||
options->wtvmpeg2 = 0;
|
||||
options->auto_myth = 2; // 2=auto
|
||||
/* MP4 related stuff */
|
||||
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->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.
|
||||
options->send_to_srv = 0;
|
||||
options->tcpport = NULL;
|
||||
options->tcp_password = NULL;
|
||||
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;
|
||||
|
||||
// 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);
|
||||
|
||||
}
|
||||
103
src/lib_ccx/ccx_common_option.h
Normal file
103
src/lib_ccx/ccx_common_option.h
Normal file
@@ -0,0 +1,103 @@
|
||||
#ifndef CCX_COMMON_OPTION_H
|
||||
#define CCX_COMMON_OPTION_H
|
||||
|
||||
#include "ccx_common_timing.h"
|
||||
#include "ccx_decoders_608.h"
|
||||
#include "ccx_encoders_structs.h"
|
||||
struct ccx_s_options // Options from user parameters
|
||||
{
|
||||
int extract; // Extract 1st, 2nd or both fields
|
||||
int cc_channel; // Channel we want to dump in srt mode
|
||||
int buffer_input;
|
||||
int nofontcolor;
|
||||
int notypesetting;
|
||||
struct ccx_boundary_time extraction_start, extraction_end; // Segment we actually process
|
||||
int print_file_reports;
|
||||
|
||||
int no_bom; // Set to 1 when no BOM (Byte Order Mark) should be used for files. Note, this might make files unreadable in windows!
|
||||
|
||||
ccx_decoder_608_settings settings_608; // Contains the settings for the 608 decoder.
|
||||
|
||||
/* 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;
|
||||
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
|
||||
>0 -> Live stream with a timeout of this value in seconds */
|
||||
int messages_target; // 0 = nowhere (quiet), 1=stdout, 2=stderr
|
||||
/* Levenshtein's parameters, for string comparison */
|
||||
int levdistmincnt, levdistmaxpct; // Means 2 fails or less is "the same", 10% or less is also "the same"
|
||||
int investigate_packets; // Look for captions in all packets when everything else fails
|
||||
int fullbin; // Disable pruning of padding cc blocks
|
||||
int nosync; // Disable syncing
|
||||
unsigned 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
|
||||
/* MP4 related stuff */
|
||||
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;
|
||||
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 **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
|
||||
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;
|
||||
};
|
||||
|
||||
extern struct ccx_s_options ccx_options;
|
||||
void init_options (struct ccx_s_options *options);
|
||||
#endif
|
||||
113
src/lib_ccx/ccx_common_platform.h
Normal file
113
src/lib_ccx/ccx_common_platform.h
Normal file
@@ -0,0 +1,113 @@
|
||||
#ifndef CCX_PLATFORM_H
|
||||
#define CCX_PLATFORM_H
|
||||
|
||||
// Default includes (cross-platform)
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <time.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdarg.h>
|
||||
#include <errno.h>
|
||||
|
||||
#define __STDC_FORMAT_MACROS
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <io.h>
|
||||
#include <ws2tcpip.h>
|
||||
#include <windows.h>
|
||||
#define STDIN_FILENO 0
|
||||
#define STDOUT_FILENO 1
|
||||
#define STDERR_FILENO 2
|
||||
#include "inttypes.h"
|
||||
#define UINT64_MAX _UI64_MAX
|
||||
typedef int socklen_t;
|
||||
typedef int ssize_t;
|
||||
typedef uint32_t in_addr_t;
|
||||
#ifndef IN_CLASSD
|
||||
#define IN_CLASSD(i) (((INT32)(i) & 0xf0000000) == 0xe0000000)
|
||||
#define IN_MULTICAST(i) IN_CLASSD(i)
|
||||
#endif
|
||||
#include <direct.h>
|
||||
#define mkdir(path, mode) _mkdir(path)
|
||||
#ifndef snprintf
|
||||
// Added ifndef because VS2013 warns for macro redefinition.
|
||||
#define snprintf(buf, len, fmt, ...) _snprintf(buf, len, fmt, __VA_ARGS__)
|
||||
#endif
|
||||
#define sleep(sec) Sleep((sec) * 1000)
|
||||
|
||||
#include <fcntl.h>
|
||||
#else // _WIN32
|
||||
#include <unistd.h>
|
||||
#define __STDC_LIMIT_MACROS
|
||||
#include <inttypes.h>
|
||||
#include <stdint.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <netdb.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#endif // _WIN32
|
||||
|
||||
#include "disable_warnings.h"
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#include "stdintmsc.h"
|
||||
// Don't bug me with strcpy() deprecation warnings
|
||||
#pragma warning(disable : 4996)
|
||||
#else
|
||||
#include <stdint.h>
|
||||
#endif
|
||||
|
||||
#ifdef __OpenBSD__
|
||||
#define FOPEN64 fopen
|
||||
#define OPEN open
|
||||
#define FSEEK fseek
|
||||
#define FTELL ftell
|
||||
#define LSEEK lseek
|
||||
#define FSTAT fstat
|
||||
#else
|
||||
#ifdef _WIN32
|
||||
#define OPEN _open
|
||||
// 64 bit file functions
|
||||
#if defined(MSC_VER)
|
||||
extern "C" int __cdecl _fseeki64(FILE *, __int64, int);
|
||||
extern "C" __int64 __cdecl _ftelli64(FILE *);
|
||||
#define FSEEK _fseeki64
|
||||
#define FTELL _ftelli64
|
||||
#else
|
||||
// For MinGW
|
||||
#define FSEEK fseeko64
|
||||
#define FTELL ftello64
|
||||
#endif
|
||||
#define TELL _telli64
|
||||
#define LSEEK _lseeki64
|
||||
typedef struct _stati64 FSTATSTRUCT;
|
||||
#else
|
||||
// Linux internally maps these functions to 64bit usage,
|
||||
// if _FILE_OFFSET_BITS macro is set to 64
|
||||
#define FOPEN64 fopen
|
||||
#define OPEN open
|
||||
#define LSEEK lseek
|
||||
#define FSEEK fseek
|
||||
#define FTELL ftell
|
||||
#define FSTAT fstat
|
||||
#define TELL tell
|
||||
#include <stdint.h>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef int64_t_C
|
||||
#define int64_t_C(c) (c ## LL)
|
||||
#define uint64_t_C(c) (c ## ULL)
|
||||
#endif
|
||||
|
||||
#ifndef O_BINARY
|
||||
#define O_BINARY 0 // Not present in Linux because it's always binary
|
||||
#endif
|
||||
|
||||
typedef int64_t LLONG;
|
||||
|
||||
#endif // CCX_PLATFORM_H
|
||||
53
src/lib_ccx/ccx_common_structs.h
Normal file
53
src/lib_ccx/ccx_common_structs.h
Normal file
@@ -0,0 +1,53 @@
|
||||
#ifndef _CC_COMMON_STRUCTS
|
||||
#define _CC_COMMON_STRUCTS
|
||||
|
||||
enum ccx_common_logging_gui {
|
||||
CCX_COMMON_LOGGING_GUI_XDS_PROGRAM_NAME, // Called with xds_program_name
|
||||
CCX_COMMON_LOGGING_GUI_XDS_PROGRAM_ID_NR, // Called with current_xds_min, current_xds_hour, current_xds_date, current_xds_month
|
||||
CCX_COMMON_LOGGING_GUI_XDS_PROGRAM_DESCRIPTION, // Called with line_num, xds_desc
|
||||
CCX_COMMON_LOGGING_GUI_XDS_CALL_LETTERS // Called with current_xds_call_letters
|
||||
};
|
||||
|
||||
struct ccx_common_logging_t {
|
||||
int debug_mask; // The debug mask that is used to determine if things should be printed or not.
|
||||
void(*fatal_ftn) (int exit_code, const char *fmt, ...); // Used when an unrecoverable error happens. This should log output/save the error and then exit the program.
|
||||
void(*debug_ftn) (LLONG mask, const char *fmt, ...); // Used to process debug output. Mask can be ignored (custom set by debug_mask).
|
||||
void(*log_ftn)(const char *fmt, ...); // Used to print things. Replacement of standard printf, to allow more control.
|
||||
void(*gui_ftn)(enum ccx_common_logging_gui message_type, ...); // Used to display things in a gui (if appropriate). Is called with the message_type and appropriate variables (described in enum)
|
||||
};
|
||||
extern struct ccx_common_logging_t ccx_common_logging;
|
||||
|
||||
enum subtype
|
||||
{
|
||||
CC_BITMAP,
|
||||
CC_608,
|
||||
CC_TEXT,
|
||||
};
|
||||
|
||||
/**
|
||||
* Raw Subtitle struct used as output of decoder (cc608)
|
||||
* and input for encoder (sami, srt, transcript or smptett etc)
|
||||
*/
|
||||
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;
|
||||
/* 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;
|
||||
};
|
||||
#endif
|
||||
@@ -1,64 +1,89 @@
|
||||
#include "ccextractor.h"
|
||||
#include "ccx_common_timing.h"
|
||||
#include "ccx_common_constants.h"
|
||||
#include "ccx_common_structs.h"
|
||||
#include "ccx_common_common.h"
|
||||
|
||||
/* Provide the current time since the file (or the first file) started
|
||||
* in ms using PTS time information.
|
||||
* Requires: frames_since_ref_time, current_tref
|
||||
*/
|
||||
|
||||
// int ignore_fts=1; // Don't do any kind of timing stuff. Assume it's being done externally (as happens in MP4)
|
||||
// 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;
|
||||
|
||||
struct ccx_common_timing_settings_t ccx_common_timing_settings;
|
||||
|
||||
void ccx_common_timing_init(LLONG *file_position,int no_sync)
|
||||
{
|
||||
ccx_common_timing_settings.disable_sync_check = 0;
|
||||
ccx_common_timing_settings.is_elementary_stream = 0;
|
||||
ccx_common_timing_settings.file_position = file_position;
|
||||
ccx_common_timing_settings.no_sync = no_sync;
|
||||
}
|
||||
|
||||
void set_fts(void)
|
||||
{
|
||||
int pts_jump = 0;
|
||||
|
||||
// ES don't have PTS unless GOP timing is used
|
||||
if (!pts_set && stream_mode==CCX_SM_ELEMENTARY_OR_NOT_FOUND)
|
||||
if (!pts_set && ccx_common_timing_settings.is_elementary_stream)
|
||||
return;
|
||||
|
||||
// First check for timeline jump (only when min_pts was
|
||||
// set (implies sync_pts).
|
||||
// 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;
|
||||
|
||||
// Used to distinguish gaps with missing caption information from
|
||||
// jumps in the timeline. (Currently only used for dvr-ms/NTSC
|
||||
// recordings)
|
||||
if ( CaptionGap )
|
||||
dif = 0;
|
||||
if (ccx_common_timing_settings.disable_sync_check){
|
||||
// Disables sync check. Used for several input formats.
|
||||
dif = 0;
|
||||
}
|
||||
|
||||
// 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 (stream_mode)
|
||||
{
|
||||
case CCX_SM_MCPOODLESRAW:
|
||||
case CCX_SM_RCWT:
|
||||
case CCX_SM_MP4:
|
||||
case CCX_SM_HEX_DUMP:
|
||||
dif = 0;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (dif < -0.2 || dif >=max_dif )
|
||||
if (dif < -0.2 || dif >= max_dif )
|
||||
{
|
||||
// ATSC specs: More than 3501 ms means missing component
|
||||
mprint ("\nWarning: Reference clock has changed abruptly (%d seconds filepos=%lld), attempting to synchronize\n", (int) dif, past);
|
||||
mprint ("Last sync PTS value: %lld\n",sync_pts);
|
||||
mprint ("Current PTS value: %lld\n",current_pts);
|
||||
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;
|
||||
|
||||
// Discard the gap if it is not on an I-frame or temporal reference
|
||||
// zero.
|
||||
// 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;
|
||||
mprint ("Change did not occur on first frame - probably a broken GOP\n");
|
||||
ccx_common_logging.log_ftn ("Change did not occur on first frame - probably a broken GOP\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -89,7 +114,7 @@ void set_fts(void)
|
||||
}
|
||||
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
|
||||
// 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;
|
||||
@@ -101,15 +126,15 @@ void set_fts(void)
|
||||
-frames_since_ref_time+1)
|
||||
*1000.0/current_fps);
|
||||
}
|
||||
dbg_print(CCX_DMT_TIME, "\nFirst sync time PTS: %s %+lldms (time before this PTS)\n",
|
||||
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 );
|
||||
dbg_print(CCX_DMT_TIME, "Total_frames_count %u frames_since_ref_time %u\n",
|
||||
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 diasbles syncing
|
||||
if (pts_jump && !ccx_options.nosync)
|
||||
// -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
|
||||
@@ -130,7 +155,7 @@ void set_fts(void)
|
||||
// Set min_pts = sync_pts as this is used for fts_now
|
||||
min_pts = sync_pts;
|
||||
|
||||
dbg_print(CCX_DMT_TIME, "\nNew min PTS time: %s %+lldms (time before this PTS)\n",
|
||||
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 );
|
||||
}
|
||||
@@ -158,7 +183,7 @@ void set_fts(void)
|
||||
else
|
||||
{
|
||||
// No PTS info at all!!
|
||||
fatal(EXIT_BUG_BUG,
|
||||
ccx_common_logging.fatal_ftn(CCX_COMMON_EXIT_BUG_BUG,
|
||||
"No PTS info. Please write bug report.");
|
||||
}
|
||||
}
|
||||
@@ -185,7 +210,7 @@ LLONG get_fts(void)
|
||||
fts = fts_now + fts_global + cb_708*1001/30;
|
||||
break;
|
||||
default:
|
||||
fatal(EXIT_BUG_BUG, "Cannot be reached!");
|
||||
ccx_common_logging.fatal_ftn(CCX_COMMON_EXIT_BUG_BUG, "Cannot be reached!");
|
||||
}
|
||||
|
||||
return fts;
|
||||
@@ -206,7 +231,7 @@ char *print_mstime2buf( LLONG mstime , char *buf )
|
||||
int signoffset = (mstime < 0 ? 1 : 0);
|
||||
|
||||
if (mstime<0) // Avoid loss of data warning with abs()
|
||||
mstime=-mstime;
|
||||
mstime=-mstime;
|
||||
hh = (unsigned) (mstime/1000/60/60);
|
||||
mm = (unsigned) (mstime/1000/60 - 60*hh);
|
||||
ss = (unsigned) (mstime/1000 - 60*(mm + 60*hh));
|
||||
@@ -233,9 +258,9 @@ void print_debug_timing( void )
|
||||
// for uninitialized min_pts
|
||||
LLONG tempmin_pts = (min_pts==0x01FFFFFFFFLL ? sync_pts : min_pts);
|
||||
|
||||
mprint("Sync time stamps: PTS: %s ",
|
||||
ccx_common_logging.log_ftn("Sync time stamps: PTS: %s ",
|
||||
print_mstime((sync_pts)/(MPEG_CLOCK_FREQ/1000)) );
|
||||
mprint("GOP: %s \n", print_mstime(gop_time.ms));
|
||||
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);
|
||||
@@ -243,13 +268,13 @@ void print_debug_timing( void )
|
||||
LLONG ptslenms = (unsigned)((sync_pts-tempmin_pts)/(MPEG_CLOCK_FREQ/1000)
|
||||
+ fts_offset);
|
||||
|
||||
mprint("Last FTS: %s",
|
||||
ccx_common_logging.log_ftn("Last FTS: %s",
|
||||
print_mstime(get_fts_max()));
|
||||
mprint(" GOP start FTS: %s\n",
|
||||
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
|
||||
mprint("Max FTS diff. to PTS: %6lldms GOP: %6lldms\n\n",
|
||||
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);
|
||||
}
|
||||
@@ -264,9 +289,9 @@ void calculate_ms_gop_time (struct gop_time_code *g)
|
||||
|
||||
int gop_accepted(struct gop_time_code* g )
|
||||
{
|
||||
if (! ((g->time_code_hours <= 23)
|
||||
&& (g->time_code_minutes <= 59)
|
||||
&& (g->time_code_seconds <= 59)
|
||||
if (! ((g->time_code_hours <= 23)
|
||||
&& (g->time_code_minutes <= 59)
|
||||
&& (g->time_code_seconds <= 59)
|
||||
&& (g->time_code_pictures <= 59)))
|
||||
return 0;
|
||||
|
||||
72
src/lib_ccx/ccx_common_timing.h
Normal file
72
src/lib_ccx/ccx_common_timing.h
Normal file
@@ -0,0 +1,72 @@
|
||||
#ifndef __Timing_H__
|
||||
|
||||
#include "ccx_common_platform.h"
|
||||
|
||||
struct gop_time_code
|
||||
{
|
||||
int drop_frame_flag;
|
||||
int time_code_hours;
|
||||
int time_code_minutes;
|
||||
int marker_bit;
|
||||
int time_code_seconds;
|
||||
int time_code_pictures;
|
||||
int inited;
|
||||
LLONG ms;
|
||||
};
|
||||
|
||||
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.
|
||||
LLONG *file_position; // The position of the file
|
||||
};
|
||||
extern struct ccx_common_timing_settings_t ccx_common_timing_settings;
|
||||
|
||||
struct ccx_boundary_time
|
||||
{
|
||||
int hh,mm,ss;
|
||||
LLONG time_in_ms;
|
||||
int set;
|
||||
};
|
||||
|
||||
// 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);
|
||||
char *print_mstime(LLONG mstime);
|
||||
char *print_mstime2buf(LLONG mstime, char *buf);
|
||||
void print_debug_timing(void);
|
||||
int gop_accepted(struct gop_time_code* g);
|
||||
void calculate_ms_gop_time(struct gop_time_code *g);
|
||||
|
||||
#define __Timing_H__
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
154
src/lib_ccx/ccx_decoders_608.h
Normal file
154
src/lib_ccx/ccx_decoders_608.h
Normal file
@@ -0,0 +1,154 @@
|
||||
#ifndef __608_H__
|
||||
|
||||
#include "ccx_common_platform.h"
|
||||
#include "ccx_common_structs.h"
|
||||
#include "ccx_decoders_structs.h"
|
||||
|
||||
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.
|
||||
*/
|
||||
struct ccx_decoder_608_report
|
||||
{
|
||||
unsigned xds : 1;
|
||||
unsigned cc_channels[4];
|
||||
};
|
||||
|
||||
typedef struct ccx_decoder_608_settings
|
||||
{
|
||||
int direct_rollup; // Write roll-up captions directly instead of line by line?
|
||||
int force_rollup; // 0=Disabled, 1, 2 or 3=max lines in roll-up mode
|
||||
int no_rollup; // If 1, write one line at a time
|
||||
unsigned char default_color; // Default color to use.
|
||||
int screens_to_process; // How many screenfuls we want? Use -1 for unlimited
|
||||
} ccx_decoder_608_settings;
|
||||
|
||||
typedef struct ccx_decoder_608_context
|
||||
{
|
||||
ccx_decoder_608_settings settings;
|
||||
eia608_screen buffer1;
|
||||
eia608_screen buffer2;
|
||||
int cursor_row, cursor_column;
|
||||
int visible_buffer;
|
||||
int screenfuls_counter; // Number of meaningful screenfuls written
|
||||
LLONG current_visible_start_ms; // At what time did the current visible buffer became so?
|
||||
enum cc_modes mode;
|
||||
unsigned char last_c1, last_c2;
|
||||
int channel; // Currently selected channel
|
||||
unsigned char current_color; // Color we are currently using to write
|
||||
unsigned char font; // Font we are currently using to write
|
||||
int rollup_base_row;
|
||||
LLONG ts_start_of_current_line; /* Time at which the first character for current line was received, =-1 no character received yet */
|
||||
LLONG ts_last_char_received; /* Time at which the last written character was received, =-1 no character received yet */
|
||||
int new_channel; // The new channel after a channel change
|
||||
int my_field; // Used for sanity checks
|
||||
int my_channel; // Used for sanity checks
|
||||
long bytes_processed_608; // To be written ONLY by process_608
|
||||
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;
|
||||
LLONG subs_delay; // ms to delay (or advance) subs
|
||||
enum ccx_output_format output_format; // What kind of output format should be used?
|
||||
} 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];
|
||||
|
||||
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
|
||||
} ccx_decoder_608_color_code;
|
||||
|
||||
|
||||
enum font_bits
|
||||
{
|
||||
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_RESUMETEXTDISPLAY = 13,
|
||||
COM_ALARMOFF =14,
|
||||
COM_ALARMON = 15,
|
||||
COM_DELETETOENDOFROW = 16,
|
||||
COM_RESUMEDIRECTCAPTIONING = 17,
|
||||
// Non existing commands we insert to have the decoder
|
||||
// special stuff for us.
|
||||
COM_FAKE_RULLUP1 = 18
|
||||
};
|
||||
|
||||
|
||||
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);
|
||||
|
||||
/**
|
||||
* @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
|
||||
* are stored.
|
||||
*
|
||||
* @param sub pointer to subtitle should be memset to 0 when passed first time
|
||||
* subtitle are stored when structure return
|
||||
*
|
||||
* @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);
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
int write_cc_buffer(ccx_decoder_608_context *context, struct cc_subtitle *sub);
|
||||
|
||||
#define __608_H__
|
||||
#endif
|
||||
@@ -1,10 +1,19 @@
|
||||
#include "ccextractor.h"
|
||||
#include <sys/stat.h>
|
||||
#include "ccx_decoders_708.h"
|
||||
#include "ccx_common_common.h"
|
||||
#include "ccx_common_constants.h"
|
||||
#include "ccx_common_structs.h"
|
||||
#include "ccx_common_timing.h"
|
||||
|
||||
/* Portions by Daniel Kristjansson, extracted from MythTV's source */
|
||||
|
||||
// #define DEBUG_708_PACKETS // Already working.
|
||||
// #define DEBUG_708_PACKETS // Already working.
|
||||
|
||||
int do_cea708 = 0; // Process 708 data?
|
||||
int cea708services[CCX_DECODERS_708_MAX_SERVICES]; // [] -> 1 for services to be processed
|
||||
int ccx_decoders_708_report = 0;
|
||||
int resets_708;
|
||||
|
||||
struct ccx_decoder_708_report_t ccx_decoder_708_report;
|
||||
static unsigned char current_packet[MAX_708_PACKET_LENGTH]; // Length according to EIA-708B, part 5
|
||||
static int current_packet_length=0;
|
||||
static int last_seq=-1; // -1 -> No last sequence yet
|
||||
@@ -24,27 +33,27 @@ const char *COMMANDS_C0[32]=
|
||||
"BS", // 8 = Backspace
|
||||
NULL, // 9 = Reserved
|
||||
NULL, // A = Reserved
|
||||
NULL, // B = Reserved
|
||||
NULL, // B = Reserved
|
||||
"FF", // C = FF
|
||||
"CR", // D = CR
|
||||
"HCR", // E = HCR
|
||||
NULL, // F = Reserved
|
||||
NULL, // F = Reserved
|
||||
"EXT1",// 0x10 = EXT1,
|
||||
NULL, // 0x11 = Reserved
|
||||
NULL, // 0x12 = Reserved
|
||||
NULL, // 0x13 = Reserved
|
||||
NULL, // 0x14 = Reserved
|
||||
NULL, // 0x15 = Reserved
|
||||
NULL, // 0x11 = Reserved
|
||||
NULL, // 0x12 = Reserved
|
||||
NULL, // 0x13 = Reserved
|
||||
NULL, // 0x14 = Reserved
|
||||
NULL, // 0x15 = Reserved
|
||||
NULL, // 0x16 = Reserved
|
||||
NULL, // 0x17 = Reserved
|
||||
NULL, // 0x17 = Reserved
|
||||
"P16", // 0x18 = P16
|
||||
NULL, // 0x19 = Reserved
|
||||
NULL, // 0x1A = Reserved
|
||||
NULL, // 0x1B = Reserved
|
||||
NULL, // 0x1C = Reserved
|
||||
NULL, // 0x1D = Reserved
|
||||
NULL, // 0x1E = Reserved
|
||||
NULL, // 0x1F = Reserved
|
||||
NULL, // 0x19 = Reserved
|
||||
NULL, // 0x1A = Reserved
|
||||
NULL, // 0x1B = Reserved
|
||||
NULL, // 0x1C = Reserved
|
||||
NULL, // 0x1D = Reserved
|
||||
NULL, // 0x1E = Reserved
|
||||
NULL, // 0x1F = Reserved
|
||||
};
|
||||
|
||||
struct S_COMMANDS_C1 COMMANDS_C1[32]=
|
||||
@@ -64,7 +73,7 @@ struct S_COMMANDS_C1 COMMANDS_C1[32]=
|
||||
{DLW,"DLW","DeleteWindows", 2},
|
||||
{DLY,"DLY","Delay", 2},
|
||||
{DLC,"DLC","DelayCancel", 1},
|
||||
{RST,"RST","Reset", 1},
|
||||
{RST,"RST","Reset", 1},
|
||||
{SPA,"SPA","SetPenAttributes", 3},
|
||||
{SPC,"SPC","SetPenColor", 4},
|
||||
{SPL,"SPL","SetPenLocation", 3},
|
||||
@@ -93,7 +102,7 @@ void clear_packet(void)
|
||||
}
|
||||
|
||||
void cc708_service_reset(cc708_service_decoder *decoder)
|
||||
{
|
||||
{
|
||||
// There's lots of other stuff that we need to do, such as canceling delays
|
||||
for (int j=0;j<8;j++)
|
||||
{
|
||||
@@ -101,8 +110,8 @@ void cc708_service_reset(cc708_service_decoder *decoder)
|
||||
decoder->windows[j].visible=0;
|
||||
decoder->windows[j].memory_reserved=0;
|
||||
decoder->windows[j].is_empty=1;
|
||||
memset (decoder->windows[j].commands, 0,
|
||||
sizeof (decoder->windows[j].commands));
|
||||
memset (decoder->windows[j].commands, 0,
|
||||
sizeof (decoder->windows[j].commands));
|
||||
}
|
||||
decoder->current_window=-1;
|
||||
decoder->current_visible_start_ms=0;
|
||||
@@ -112,16 +121,21 @@ void cc708_service_reset(cc708_service_decoder *decoder)
|
||||
decoder->inited=1;
|
||||
}
|
||||
|
||||
void cc708_reset()
|
||||
void cc708_reset(struct lib_cc_decode *ctx)
|
||||
{
|
||||
dbg_print(CCX_DMT_708, ">>> Entry in cc708_reset()\n");
|
||||
// Clear states of decoders
|
||||
cc708_service_reset(&decoders[0]);
|
||||
cc708_service_reset(&decoders[1]);
|
||||
// Empty packet buffer
|
||||
clear_packet();
|
||||
last_seq=-1;
|
||||
resets_708++;
|
||||
ccx_common_logging.debug_ftn(CCX_DMT_708, ">>> Entry in cc708_reset()\n");
|
||||
|
||||
decoders[0].output_format = ctx->write_format;
|
||||
decoders[1].output_format = ctx->write_format;
|
||||
decoders[0].subs_delay = ctx->subs_delay;
|
||||
decoders[1].subs_delay = ctx->subs_delay;
|
||||
// Clear states of decoders
|
||||
cc708_service_reset(&decoders[0]);
|
||||
cc708_service_reset(&decoders[1]);
|
||||
// Empty packet buffer
|
||||
clear_packet();
|
||||
last_seq=-1;
|
||||
resets_708++;
|
||||
}
|
||||
|
||||
int compWindowsPriorities (const void *a, const void *b)
|
||||
@@ -143,47 +157,50 @@ void clearTV (cc708_service_decoder *decoder, int buffer) // Buffer => 1 or 2
|
||||
|
||||
void printTVtoSRT (cc708_service_decoder *decoder, int which)
|
||||
{
|
||||
if (decoder->output_format == CCX_OF_NULL)
|
||||
return;
|
||||
|
||||
/* tvscreen *tv = (which==1)? &decoder->tv1:&decoder->tv2; */
|
||||
unsigned h1,m1,s1,ms1;
|
||||
unsigned h2,m2,s2,ms2;
|
||||
LLONG ms_start= decoder->current_visible_start_ms;
|
||||
LLONG ms_end = get_visible_end()+subs_delay;
|
||||
LLONG ms_end = get_visible_end() + decoder->subs_delay;
|
||||
int empty=1;
|
||||
ms_start+=subs_delay;
|
||||
ms_start+= decoder->subs_delay;
|
||||
if (ms_start<0) // Drop screens that because of subs_delay start too early
|
||||
return;
|
||||
|
||||
|
||||
for (int i=0;i<75;i++)
|
||||
{
|
||||
{
|
||||
for (int j=0;j<210;j++)
|
||||
if (decoder->tv->chars[i][j]!=' ')
|
||||
{
|
||||
empty=0;
|
||||
break;
|
||||
}
|
||||
if (!empty)
|
||||
if (!empty)
|
||||
break;
|
||||
}
|
||||
if (empty)
|
||||
return; // Nothing to write
|
||||
if (decoder->fh==-1) // File not yet open, do it now
|
||||
{
|
||||
mprint ("Creating %s\n", wbout1.filename);
|
||||
decoder->fh=open (decoder->filename, O_RDWR | O_CREAT | O_TRUNC | O_BINARY, S_IREAD | S_IWRITE);
|
||||
ccx_common_logging.log_ftn("Creating %s\n", decoder->filename); // originally wbout1.filename, but since line below uses decoder->filename, assume it's good to use?
|
||||
decoder->fh= open(decoder->filename, O_RDWR | O_CREAT | O_TRUNC | O_BINARY, S_IREAD | S_IWRITE);
|
||||
if (decoder->fh==-1)
|
||||
{
|
||||
fatal (EXIT_FILE_CREATION_FAILED, "Failed\n");
|
||||
ccx_common_logging.fatal_ftn(CCX_COMMON_EXIT_FILE_CREATION_FAILED, "Failed\n");
|
||||
}
|
||||
}
|
||||
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];
|
||||
char timeline[128];
|
||||
decoder->srt_counter++;
|
||||
sprintf (timeline,"%u\r\n",decoder->srt_counter);
|
||||
write (decoder->fh,timeline,strlen (timeline));
|
||||
sprintf (timeline, "%02u:%02u:%02u,%03u --> %02u:%02u:%02u,%03u\r\n",
|
||||
h1,m1,s1,ms1, h2,m2,s2,ms2);
|
||||
write (decoder->fh,timeline,strlen (timeline));
|
||||
write (decoder->fh,timeline,strlen (timeline));
|
||||
for (int i=0;i<75;i++)
|
||||
{
|
||||
int empty=1;
|
||||
@@ -200,7 +217,7 @@ void printTVtoSRT (cc708_service_decoder *decoder, int which)
|
||||
if (decoder->tv->chars[i][l]!=' ')
|
||||
break;
|
||||
for (int j=f;j<=l;j++)
|
||||
write (decoder->fh,&decoder->tv->chars[i][j],1);
|
||||
write (decoder->fh,&decoder->tv->chars[i][j],1);
|
||||
write (decoder->fh,"\r\n",2);
|
||||
}
|
||||
}
|
||||
@@ -213,7 +230,7 @@ void printTVtoConsole (cc708_service_decoder *decoder, int which)
|
||||
char tbuf1[15],tbuf2[15];
|
||||
print_mstime2buf (decoder->current_visible_start_ms,tbuf1);
|
||||
print_mstime2buf (get_visible_end(),tbuf2);
|
||||
dbg_print(CCX_DMT_GENERIC_NOTICES, "\r%s --> %s\n", tbuf1,tbuf2);
|
||||
ccx_common_logging.debug_ftn(CCX_DMT_GENERIC_NOTICES, "\r%s --> %s\n", tbuf1,tbuf2);
|
||||
for (int i=0;i<75;i++)
|
||||
{
|
||||
int empty=1;
|
||||
@@ -230,8 +247,8 @@ void printTVtoConsole (cc708_service_decoder *decoder, int which)
|
||||
if (decoder->tv->chars[i][l]!=' ')
|
||||
break;
|
||||
for (int j=f;j<=l;j++)
|
||||
dbg_print(CCX_DMT_GENERIC_NOTICES, "%c", decoder->tv->chars[i][j]);
|
||||
dbg_print(CCX_DMT_GENERIC_NOTICES, "\n");
|
||||
ccx_common_logging.debug_ftn(CCX_DMT_GENERIC_NOTICES, "%c", decoder->tv->chars[i][j]);
|
||||
ccx_common_logging.debug_ftn(CCX_DMT_GENERIC_NOTICES, "\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -267,13 +284,13 @@ void updateScreen (cc708_service_decoder *decoder)
|
||||
if (decoder->windows[i].is_defined && decoder->windows[i].visible && !decoder->windows[i].is_empty)
|
||||
wnd[visible++]=&decoder->windows[i];
|
||||
}
|
||||
qsort (wnd,visible,sizeof (int),compWindowsPriorities);
|
||||
dbg_print(CCX_DMT_708, "Visible (and populated) windows in priority order: ");
|
||||
qsort (wnd,visible,sizeof (e708Window *),compWindowsPriorities);
|
||||
ccx_common_logging.debug_ftn(CCX_DMT_708, "Visible (and populated) windows in priority order: ");
|
||||
for (int i=0;i<visible;i++)
|
||||
{
|
||||
dbg_print(CCX_DMT_708, "%d (%d) | ",wnd[i]->number, wnd[i]->priority);
|
||||
ccx_common_logging.debug_ftn(CCX_DMT_708, "%d (%d) | ",wnd[i]->number, wnd[i]->priority);
|
||||
}
|
||||
dbg_print(CCX_DMT_708, "\n");
|
||||
ccx_common_logging.debug_ftn(CCX_DMT_708, "\n");
|
||||
for (int i=0;i<visible;i++)
|
||||
{
|
||||
int top,left;
|
||||
@@ -320,7 +337,7 @@ void updateScreen (cc708_service_decoder *decoder)
|
||||
default: // Shouldn't happen, but skip the window just in case
|
||||
continue;
|
||||
}
|
||||
dbg_print(CCX_DMT_708, "For window %d: Anchor point -> %d, size %d:%d, real position %d:%d\n",
|
||||
ccx_common_logging.debug_ftn(CCX_DMT_708, "For window %d: Anchor point -> %d, size %d:%d, real position %d:%d\n",
|
||||
wnd[i]->number, wnd[i]->anchor_point, wnd[i]->row_count,wnd[i]->col_count,
|
||||
top,left);
|
||||
if (top<0)
|
||||
@@ -331,17 +348,17 @@ void updateScreen (cc708_service_decoder *decoder)
|
||||
I708_SCREENGRID_ROWS - top : wnd[i]->row_count;
|
||||
int copycols=left + wnd[i]->col_count >= I708_SCREENGRID_COLUMNS ?
|
||||
I708_SCREENGRID_COLUMNS - left : wnd[i]->col_count;
|
||||
dbg_print(CCX_DMT_708, "%d*%d will be copied to the TV.\n", copyrows, copycols);
|
||||
ccx_common_logging.debug_ftn(CCX_DMT_708, "%d*%d will be copied to the TV.\n", copyrows, copycols);
|
||||
for (int j=0;j<copyrows;j++)
|
||||
{
|
||||
memcpy (decoder->tv->chars[top+j],wnd[i]->rows[j],copycols);
|
||||
}
|
||||
}
|
||||
}
|
||||
decoder->current_visible_start_ms=get_visible_start();
|
||||
}
|
||||
|
||||
/* This function handles future codes. While by definition we can't do any work on them, we must return
|
||||
how many bytes would be consumed if these codes were supported, as defined in the specs.
|
||||
/* This function handles future codes. While by definition we can't do any work on them, we must return
|
||||
how many bytes would be consumed if these codes were supported, as defined in the specs.
|
||||
Note: EXT1 not included */
|
||||
// C2: Extended Miscellaneous Control Codes
|
||||
// TODO: This code is completely untested due to lack of samples. Just following specs!
|
||||
@@ -359,30 +376,30 @@ int handle_708_C2 (cc708_service_decoder *decoder, unsigned char *data, int data
|
||||
int handle_708_C3 (cc708_service_decoder *decoder, unsigned char *data, int data_length)
|
||||
{
|
||||
if (data[0]<0x80 || data[0]>0x9F)
|
||||
fatal (EXIT_BUG_BUG, "Entry in handle_708_C3 with an out of range value.");
|
||||
ccx_common_logging.fatal_ftn (CCX_COMMON_EXIT_BUG_BUG, "Entry in handle_708_C3 with an out of range value.");
|
||||
if (data[0]<=0x87) // 80-87...
|
||||
return 5; // ... Five-byte control bytes (4 additional bytes)
|
||||
else if (data[0]<=0x8F) // 88-8F ...
|
||||
return 6; // ..Six-byte control codes (5 additional byte)
|
||||
// If here, then 90-9F ...
|
||||
|
||||
// These are variable length commands, that can even span several segments
|
||||
// (they allow even downloading fonts or graphics).
|
||||
// These are variable length commands, that can even span several segments
|
||||
// (they allow even downloading fonts or graphics).
|
||||
// TODO: Implemen if a sample ever appears
|
||||
fatal (EXIT_UNSUPPORTED, "This sample contains unsupported 708 data. PLEASE help us improve CCExtractor by submitting it.\n");
|
||||
ccx_common_logging.fatal_ftn(CCX_COMMON_EXIT_UNSUPPORTED, "This sample contains unsupported 708 data. PLEASE help us improve CCExtractor by submitting it.\n");
|
||||
return 0; // Unreachable, but otherwise there's compilers warnings
|
||||
}
|
||||
|
||||
// This function handles extended codes (EXT1 + code), from the extended sets
|
||||
// G2 (20-7F) => Mostly unmapped, except for a few characters.
|
||||
// G2 (20-7F) => Mostly unmapped, except for a few characters.
|
||||
// G3 (A0-FF) => A0 is the CC symbol, everything else reserved for future expansion in EIA708-B
|
||||
// C2 (00-1F) => Reserved for future extended misc. control and captions command codes
|
||||
// TODO: This code is completely untested due to lack of samples. Just following specs!
|
||||
// Returns number of used bytes, usually 1 (since EXT1 is not counted).
|
||||
// Returns number of used bytes, usually 1 (since EXT1 is not counted).
|
||||
int handle_708_extended_char (cc708_service_decoder *decoder, unsigned char *data, int data_length)
|
||||
{
|
||||
int used;
|
||||
dbg_print(CCX_DMT_708, "In handle_708_extended_char, first data code: [%c], length: [%u]\n",data[0], data_length);
|
||||
{
|
||||
int used;
|
||||
ccx_common_logging.debug_ftn(CCX_DMT_708, "In handle_708_extended_char, first data code: [%c], length: [%u]\n",data[0], data_length);
|
||||
unsigned char c=0x20; // Default to space
|
||||
unsigned char code=data[0];
|
||||
if (/* data[i]>=0x00 && */ code<=0x1F) // Comment to silence warning
|
||||
@@ -444,9 +461,9 @@ int handle_708_C0 (cc708_service_decoder *decoder, unsigned char *data, int data
|
||||
const char *name=COMMANDS_C0[data[0]];
|
||||
if (name==NULL)
|
||||
name="Reserved";
|
||||
dbg_print(CCX_DMT_708, "C0: [%02X] (%d) [%s]\n",data[0],data_length, name);
|
||||
ccx_common_logging.debug_ftn(CCX_DMT_708, "C0: [%02X] (%d) [%s]\n",data[0],data_length, name);
|
||||
int len=-1;
|
||||
// These commands have a known length even if they are reserved.
|
||||
// These commands have a known length even if they are reserved.
|
||||
if (/* data[0]>=0x00 && */ data[0]<=0xF) // Comment to silence warning
|
||||
{
|
||||
switch (data[0])
|
||||
@@ -455,7 +472,7 @@ int handle_708_C0 (cc708_service_decoder *decoder, unsigned char *data, int data
|
||||
process_cr (decoder);
|
||||
break;
|
||||
case 0x0e: // HCR (Horizontal Carriage Return)
|
||||
// TODO: Process HDR
|
||||
// TODO: Process HDR
|
||||
break;
|
||||
case 0x0c: // FF (Form Feed)
|
||||
// TODO: Process FF
|
||||
@@ -480,27 +497,27 @@ int handle_708_C0 (cc708_service_decoder *decoder, unsigned char *data, int data
|
||||
}
|
||||
if (len==-1)
|
||||
{
|
||||
dbg_print(CCX_DMT_708, "In handle_708_C0. Len == -1, this is not possible!");
|
||||
ccx_common_logging.debug_ftn(CCX_DMT_708, "In handle_708_C0. Len == -1, this is not possible!");
|
||||
return -1;
|
||||
}
|
||||
if (len>data_length)
|
||||
{
|
||||
dbg_print(CCX_DMT_708, "handle_708_C0, command is %d bytes long but we only have %d\n",len, data_length);
|
||||
ccx_common_logging.debug_ftn(CCX_DMT_708, "handle_708_C0, command is %d bytes long but we only have %d\n",len, data_length);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
// TODO: Do something useful eventually
|
||||
return len;
|
||||
}
|
||||
|
||||
|
||||
void process_character (cc708_service_decoder *decoder, unsigned char internal_char)
|
||||
{
|
||||
dbg_print(CCX_DMT_708, ">>> Process_character: %c [%02X] - Window: %d %s, Pen: %d:%d\n", internal_char, internal_char,
|
||||
{
|
||||
ccx_common_logging.debug_ftn(CCX_DMT_708, ">>> Process_character: %c [%02X] - Window: %d %s, Pen: %d:%d\n", internal_char, internal_char,
|
||||
decoder->current_window,
|
||||
(decoder->windows[decoder->current_window].is_defined?"[OK]":"[undefined]"),
|
||||
decoder->current_window!=-1 ? decoder->windows[decoder->current_window].pen_row:-1,
|
||||
decoder->current_window!=-1 ? decoder->windows[decoder->current_window].pen_column:-1
|
||||
);
|
||||
);
|
||||
if (decoder->current_window==-1 ||
|
||||
!decoder->windows[decoder->current_window].is_defined) // Writing to a non existing window, skipping
|
||||
return;
|
||||
@@ -541,7 +558,7 @@ void process_character (cc708_service_decoder *decoder, unsigned char internal_c
|
||||
int handle_708_G0 (cc708_service_decoder *decoder, unsigned char *data, int data_length)
|
||||
{
|
||||
// TODO: Substitution of the music note character for the ASCII DEL character
|
||||
dbg_print(CCX_DMT_708, "G0: [%02X] (%c)\n",data[0],data[0]);
|
||||
ccx_common_logging.debug_ftn(CCX_DMT_708, "G0: [%02X] (%c)\n",data[0],data[0]);
|
||||
unsigned char c=get_internal_from_G0 (data[0]);
|
||||
process_character (decoder, c);
|
||||
return 1;
|
||||
@@ -550,7 +567,7 @@ int handle_708_G0 (cc708_service_decoder *decoder, unsigned char *data, int data
|
||||
// G1 Code Set - ISO 8859-1 LATIN-1 Character Set
|
||||
int handle_708_G1 (cc708_service_decoder *decoder, unsigned char *data, int data_length)
|
||||
{
|
||||
dbg_print(CCX_DMT_708, "G1: [%02X] (%c)\n",data[0],data[0]);
|
||||
ccx_common_logging.debug_ftn(CCX_DMT_708, "G1: [%02X] (%c)\n",data[0],data[0]);
|
||||
unsigned char c=get_internal_from_G1 (data[0]);
|
||||
process_character (decoder, c);
|
||||
return 1;
|
||||
@@ -558,10 +575,10 @@ int handle_708_G1 (cc708_service_decoder *decoder, unsigned char *data, int data
|
||||
|
||||
/*-------------------------------------------------------
|
||||
WINDOW COMMANDS
|
||||
------------------------------------------------------- */
|
||||
------------------------------------------------------- */
|
||||
void handle_708_CWx_SetCurrentWindow (cc708_service_decoder *decoder, int new_window)
|
||||
{
|
||||
dbg_print(CCX_DMT_708, " Entry in handle_708_CWx_SetCurrentWindow, new window: [%d]\n",new_window);
|
||||
ccx_common_logging.debug_ftn(CCX_DMT_708, " Entry in handle_708_CWx_SetCurrentWindow, new window: [%d]\n",new_window);
|
||||
if (decoder->windows[new_window].is_defined)
|
||||
decoder->current_window=new_window;
|
||||
}
|
||||
@@ -574,29 +591,29 @@ void clearWindow (cc708_service_decoder *decoder, int window)
|
||||
|
||||
void handle_708_CLW_ClearWindows (cc708_service_decoder *decoder, int windows_bitmap)
|
||||
{
|
||||
dbg_print(CCX_DMT_708, " Entry in handle_708_CLW_ClearWindows, windows: ");
|
||||
ccx_common_logging.debug_ftn(CCX_DMT_708, " Entry in handle_708_CLW_ClearWindows, windows: ");
|
||||
if (windows_bitmap==0)
|
||||
dbg_print(CCX_DMT_708, "None\n");
|
||||
ccx_common_logging.debug_ftn(CCX_DMT_708, "None\n");
|
||||
else
|
||||
{
|
||||
for (int i=0; i<8; i++)
|
||||
{
|
||||
if (windows_bitmap & 1)
|
||||
{
|
||||
dbg_print(CCX_DMT_708, "[Window %d] ",i );
|
||||
clearWindow (decoder, i);
|
||||
ccx_common_logging.debug_ftn(CCX_DMT_708, "[Window %d] ",i );
|
||||
clearWindow (decoder, i);
|
||||
}
|
||||
windows_bitmap>>=1;
|
||||
}
|
||||
}
|
||||
dbg_print(CCX_DMT_708, "\n");
|
||||
ccx_common_logging.debug_ftn(CCX_DMT_708, "\n");
|
||||
}
|
||||
|
||||
void handle_708_DSW_DisplayWindows (cc708_service_decoder *decoder, int windows_bitmap)
|
||||
{
|
||||
dbg_print(CCX_DMT_708, " Entry in handle_708_DSW_DisplayWindows, windows: ");
|
||||
ccx_common_logging.debug_ftn(CCX_DMT_708, " Entry in handle_708_DSW_DisplayWindows, windows: ");
|
||||
if (windows_bitmap==0)
|
||||
dbg_print(CCX_DMT_708, "None\n");
|
||||
ccx_common_logging.debug_ftn(CCX_DMT_708, "None\n");
|
||||
else
|
||||
{
|
||||
int changes=0;
|
||||
@@ -604,26 +621,26 @@ void handle_708_DSW_DisplayWindows (cc708_service_decoder *decoder, int windows_
|
||||
{
|
||||
if (windows_bitmap & 1)
|
||||
{
|
||||
dbg_print(CCX_DMT_708, "[Window %d] ",i );
|
||||
ccx_common_logging.debug_ftn(CCX_DMT_708, "[Window %d] ",i );
|
||||
if (!decoder->windows[i].visible)
|
||||
{
|
||||
changes=1;
|
||||
decoder->windows[i].visible=1;
|
||||
}
|
||||
}
|
||||
}
|
||||
windows_bitmap>>=1;
|
||||
}
|
||||
dbg_print(CCX_DMT_708, "\n");
|
||||
ccx_common_logging.debug_ftn(CCX_DMT_708, "\n");
|
||||
if (changes)
|
||||
updateScreen (decoder);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void handle_708_HDW_HideWindows (cc708_service_decoder *decoder, int windows_bitmap)
|
||||
{
|
||||
dbg_print(CCX_DMT_708, " Entry in handle_708_HDW_HideWindows, windows: ");
|
||||
ccx_common_logging.debug_ftn(CCX_DMT_708, " Entry in handle_708_HDW_HideWindows, windows: ");
|
||||
if (windows_bitmap==0)
|
||||
dbg_print(CCX_DMT_708, "None\n");
|
||||
ccx_common_logging.debug_ftn(CCX_DMT_708, "None\n");
|
||||
else
|
||||
{
|
||||
int changes=0;
|
||||
@@ -631,7 +648,7 @@ void handle_708_HDW_HideWindows (cc708_service_decoder *decoder, int windows_bit
|
||||
{
|
||||
if (windows_bitmap & 1)
|
||||
{
|
||||
dbg_print(CCX_DMT_708, "[Window %d] ",i );
|
||||
ccx_common_logging.debug_ftn(CCX_DMT_708, "[Window %d] ",i );
|
||||
if (decoder->windows[i].is_defined && decoder->windows[i].visible && !decoder->windows[i].is_empty)
|
||||
{
|
||||
changes=1;
|
||||
@@ -641,31 +658,31 @@ void handle_708_HDW_HideWindows (cc708_service_decoder *decoder, int windows_bit
|
||||
}
|
||||
windows_bitmap>>=1;
|
||||
}
|
||||
dbg_print(CCX_DMT_708, "\n");
|
||||
ccx_common_logging.debug_ftn(CCX_DMT_708, "\n");
|
||||
if (changes)
|
||||
updateScreen (decoder);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void handle_708_TGW_ToggleWindows (cc708_service_decoder *decoder, int windows_bitmap)
|
||||
{
|
||||
dbg_print(CCX_DMT_708, " Entry in handle_708_TGW_ToggleWindows, windows: ");
|
||||
ccx_common_logging.debug_ftn(CCX_DMT_708, " Entry in handle_708_TGW_ToggleWindows, windows: ");
|
||||
if (windows_bitmap==0)
|
||||
dbg_print(CCX_DMT_708, "None\n");
|
||||
ccx_common_logging.debug_ftn(CCX_DMT_708, "None\n");
|
||||
else
|
||||
{
|
||||
for (int i=0; i<8; i++)
|
||||
{
|
||||
if (windows_bitmap & 1)
|
||||
{
|
||||
dbg_print(CCX_DMT_708, "[Window %d] ",i );
|
||||
decoder->windows[i].visible=!decoder->windows[i].visible;
|
||||
ccx_common_logging.debug_ftn(CCX_DMT_708, "[Window %d] ",i );
|
||||
decoder->windows[i].visible=!decoder->windows[i].visible;
|
||||
}
|
||||
windows_bitmap>>=1;
|
||||
}
|
||||
updateScreen(decoder);
|
||||
}
|
||||
dbg_print(CCX_DMT_708, "\n");
|
||||
ccx_common_logging.debug_ftn(CCX_DMT_708, "\n");
|
||||
}
|
||||
|
||||
void clearWindowText (e708Window *window)
|
||||
@@ -682,14 +699,14 @@ void clearWindowText (e708Window *window)
|
||||
|
||||
void handle_708_DFx_DefineWindow (cc708_service_decoder *decoder, int window, unsigned char *data)
|
||||
{
|
||||
dbg_print(CCX_DMT_708, " Entry in handle_708_DFx_DefineWindow, window [%d], attributes: \n", window);
|
||||
ccx_common_logging.debug_ftn(CCX_DMT_708, " Entry in handle_708_DFx_DefineWindow, window [%d], attributes: \n", window);
|
||||
if (decoder->windows[window].is_defined &&
|
||||
memcmp (decoder->windows[window].commands, data+1, 6)==0)
|
||||
{
|
||||
// When a decoder receives a DefineWindow command for an existing window, the
|
||||
// command is to be ignored if the command parameters are unchanged from the
|
||||
// previous window definition.
|
||||
dbg_print(CCX_DMT_708, " Repeated window definition, ignored.\n");
|
||||
ccx_common_logging.debug_ftn(CCX_DMT_708, " Repeated window definition, ignored.\n");
|
||||
return;
|
||||
}
|
||||
decoder->windows[window].number=window;
|
||||
@@ -702,16 +719,16 @@ void handle_708_DFx_DefineWindow (cc708_service_decoder *decoder, int window, un
|
||||
int anchor_horizontal = data[3];
|
||||
int row_count = data[4] & 0xf;
|
||||
int anchor_point = data[4]>>4;
|
||||
int col_count = data[5] & 0x3f;
|
||||
int col_count = data[5] & 0x3f;
|
||||
int pen_style = data[6] & 0x7;
|
||||
int win_style = (data[6]>>3) & 0x7;
|
||||
dbg_print(CCX_DMT_708, " Priority: [%d] Column lock: [%3s] Row lock: [%3s]\n",
|
||||
ccx_common_logging.debug_ftn(CCX_DMT_708, " Priority: [%d] Column lock: [%3s] Row lock: [%3s]\n",
|
||||
priority, col_lock?"Yes": "No", row_lock?"Yes": "No");
|
||||
dbg_print(CCX_DMT_708, " Visible: [%3s] Anchor vertical: [%2d] Relative pos: [%s]\n",
|
||||
ccx_common_logging.debug_ftn(CCX_DMT_708, " Visible: [%3s] Anchor vertical: [%2d] Relative pos: [%s]\n",
|
||||
visible?"Yes": "No", anchor_vertical, relative_pos?"Yes": "No");
|
||||
dbg_print(CCX_DMT_708, " Anchor horizontal: [%3d] Row count: [%2d]+1 Anchor point: [%d]\n",
|
||||
ccx_common_logging.debug_ftn(CCX_DMT_708, " Anchor horizontal: [%3d] Row count: [%2d]+1 Anchor point: [%d]\n",
|
||||
anchor_horizontal, row_count, anchor_point);
|
||||
dbg_print(CCX_DMT_708, " Column count: [%2d]1 Pen style: [%d] Win style: [%d]\n",
|
||||
ccx_common_logging.debug_ftn(CCX_DMT_708, " Column count: [%2d]1 Pen style: [%d] Win style: [%d]\n",
|
||||
col_count, pen_style, win_style);
|
||||
col_count++; // These increments seems to be needed but no documentation
|
||||
row_count++; // backs it up
|
||||
@@ -732,7 +749,7 @@ void handle_708_DFx_DefineWindow (cc708_service_decoder *decoder, int window, un
|
||||
// If the window is being created, all character positions in the window
|
||||
// are set to the fill color...
|
||||
// TODO: COLORS
|
||||
// ...and the pen location is set to (0,0)
|
||||
// ...and the pen location is set to (0,0)
|
||||
decoder->windows[window].pen_column=0;
|
||||
decoder->windows[window].pen_row=0;
|
||||
if (!decoder->windows[window].memory_reserved)
|
||||
@@ -742,13 +759,13 @@ void handle_708_DFx_DefineWindow (cc708_service_decoder *decoder, int window, un
|
||||
decoder->windows[window].rows[i]=(unsigned char *) malloc (I708_MAX_COLUMNS+1);
|
||||
if (decoder->windows[window].rows[i]==NULL) // Great
|
||||
{
|
||||
decoder->windows[window].is_defined=0;
|
||||
decoder->windows[window].is_defined=0;
|
||||
decoder->current_window=-1;
|
||||
for (int j=0;j<i;j++)
|
||||
for (int j=0;j<i;j++)
|
||||
free (decoder->windows[window].rows[j]);
|
||||
return; // TODO: Warn somehow
|
||||
}
|
||||
}
|
||||
}
|
||||
decoder->windows[window].memory_reserved=1;
|
||||
}
|
||||
decoder->windows[window].is_defined=1;
|
||||
@@ -759,7 +776,7 @@ void handle_708_DFx_DefineWindow (cc708_service_decoder *decoder, int window, un
|
||||
// Specs unclear here: Do we need to delete the text in the existing window?
|
||||
// We do this because one of the sample files demands it.
|
||||
// clearWindowText (&decoder->windows[window]);
|
||||
}
|
||||
}
|
||||
// ...also makes the defined windows the current window (setCurrentWindow)
|
||||
handle_708_CWx_SetCurrentWindow (decoder, window);
|
||||
memcpy (decoder->windows[window].commands, data+1, 6);
|
||||
@@ -767,7 +784,7 @@ void handle_708_DFx_DefineWindow (cc708_service_decoder *decoder, int window, un
|
||||
|
||||
void handle_708_SWA_SetWindowAttributes (cc708_service_decoder *decoder, unsigned char *data)
|
||||
{
|
||||
dbg_print(CCX_DMT_708, " Entry in handle_708_SWA_SetWindowAttributes, attributes: \n");
|
||||
ccx_common_logging.debug_ftn(CCX_DMT_708, " Entry in handle_708_SWA_SetWindowAttributes, attributes: \n");
|
||||
int fill_color = (data[1] ) & 0x3f;
|
||||
int fill_opacity = (data[1]>>6) & 0x03;
|
||||
int border_color = (data[2] ) & 0x3f;
|
||||
@@ -780,11 +797,11 @@ void handle_708_SWA_SetWindowAttributes (cc708_service_decoder *decoder, unsigne
|
||||
int display_eff = (data[4] ) & 0x03;
|
||||
int effect_dir = (data[4]>>2) & 0x03;
|
||||
int effect_speed = (data[4]>>4) & 0x0f;
|
||||
dbg_print(CCX_DMT_708, " Fill color: [%d] Fill opacity: [%d] Border color: [%d] Border type: [%d]\n",
|
||||
ccx_common_logging.debug_ftn(CCX_DMT_708, " Fill color: [%d] Fill opacity: [%d] Border color: [%d] Border type: [%d]\n",
|
||||
fill_color, fill_opacity, border_color, border_type01);
|
||||
dbg_print(CCX_DMT_708, " Justify: [%d] Scroll dir: [%d] Print dir: [%d] Word wrap: [%d]\n",
|
||||
ccx_common_logging.debug_ftn(CCX_DMT_708, " Justify: [%d] Scroll dir: [%d] Print dir: [%d] Word wrap: [%d]\n",
|
||||
justify, scroll_dir, print_dir, word_wrap);
|
||||
dbg_print(CCX_DMT_708, " Border type: [%d] Display eff: [%d] Effect dir: [%d] Effect speed: [%d]\n",
|
||||
ccx_common_logging.debug_ftn(CCX_DMT_708, " Border type: [%d] Display eff: [%d] Effect dir: [%d] Effect speed: [%d]\n",
|
||||
border_type, display_eff, effect_dir, effect_speed);
|
||||
if (decoder->current_window==-1)
|
||||
{
|
||||
@@ -827,16 +844,16 @@ void deleteWindow (cc708_service_decoder *decoder, int window)
|
||||
void handle_708_DLW_DeleteWindows (cc708_service_decoder *decoder, int windows_bitmap)
|
||||
{
|
||||
int changes=0;
|
||||
dbg_print(CCX_DMT_708, " Entry in handle_708_DLW_DeleteWindows, windows: ");
|
||||
ccx_common_logging.debug_ftn(CCX_DMT_708, " Entry in handle_708_DLW_DeleteWindows, windows: ");
|
||||
if (windows_bitmap==0)
|
||||
dbg_print(CCX_DMT_708, "None\n");
|
||||
ccx_common_logging.debug_ftn(CCX_DMT_708, "None\n");
|
||||
else
|
||||
{
|
||||
for (int i=0; i<8; i++)
|
||||
{
|
||||
if (windows_bitmap & 1)
|
||||
{
|
||||
dbg_print(CCX_DMT_708, "[Window %d] ",i );
|
||||
ccx_common_logging.debug_ftn(CCX_DMT_708, "[Window %d] ",i );
|
||||
if (decoder->windows[i].is_defined && decoder->windows[i].visible && !decoder->windows[i].is_empty)
|
||||
changes=1;
|
||||
deleteWindow (decoder, i);
|
||||
@@ -844,7 +861,7 @@ void handle_708_DLW_DeleteWindows (cc708_service_decoder *decoder, int windows_b
|
||||
windows_bitmap>>=1;
|
||||
}
|
||||
}
|
||||
dbg_print(CCX_DMT_708, "\n");
|
||||
ccx_common_logging.debug_ftn(CCX_DMT_708, "\n");
|
||||
if (changes)
|
||||
updateScreen (decoder);
|
||||
|
||||
@@ -852,10 +869,10 @@ void handle_708_DLW_DeleteWindows (cc708_service_decoder *decoder, int windows_b
|
||||
|
||||
/*-------------------------------------------------------
|
||||
WINDOW COMMANDS
|
||||
------------------------------------------------------- */
|
||||
------------------------------------------------------- */
|
||||
void handle_708_SPA_SetPenAttributes (cc708_service_decoder *decoder, unsigned char *data)
|
||||
{
|
||||
dbg_print(CCX_DMT_708, " Entry in handle_708_SPA_SetPenAttributes, attributes: \n");
|
||||
ccx_common_logging.debug_ftn(CCX_DMT_708, " Entry in handle_708_SPA_SetPenAttributes, attributes: \n");
|
||||
int pen_size = (data[1] ) & 0x3;
|
||||
int offset = (data[1]>>2) & 0x3;
|
||||
int text_tag = (data[1]>>4) & 0xf;
|
||||
@@ -863,10 +880,10 @@ void handle_708_SPA_SetPenAttributes (cc708_service_decoder *decoder, unsigned c
|
||||
int edge_type = (data[2]>>3) & 0x7;
|
||||
int underline = (data[2]>>4) & 0x1;
|
||||
int italic = (data[2]>>5) & 0x1;
|
||||
dbg_print(CCX_DMT_708, " Pen size: [%d] Offset: [%d] Text tag: [%d] Font tag: [%d]\n",
|
||||
ccx_common_logging.debug_ftn(CCX_DMT_708, " Pen size: [%d] Offset: [%d] Text tag: [%d] Font tag: [%d]\n",
|
||||
pen_size, offset, text_tag, font_tag);
|
||||
dbg_print(CCX_DMT_708, " Edge type: [%d] Underline: [%d] Italic: [%d]\n",
|
||||
edge_type, underline, italic);
|
||||
ccx_common_logging.debug_ftn(CCX_DMT_708, " Edge type: [%d] Underline: [%d] Italic: [%d]\n",
|
||||
edge_type, underline, italic);
|
||||
if (decoder->current_window==-1)
|
||||
{
|
||||
// Can't do anything yet - we need a window to be defined first.
|
||||
@@ -883,17 +900,17 @@ void handle_708_SPA_SetPenAttributes (cc708_service_decoder *decoder, unsigned c
|
||||
|
||||
void handle_708_SPC_SetPenColor (cc708_service_decoder *decoder, unsigned char *data)
|
||||
{
|
||||
dbg_print(CCX_DMT_708, " Entry in handle_708_SPC_SetPenColor, attributes: \n");
|
||||
ccx_common_logging.debug_ftn(CCX_DMT_708, " Entry in handle_708_SPC_SetPenColor, attributes: \n");
|
||||
int fg_color = (data[1] ) & 0x3f;
|
||||
int fg_opacity = (data[1]>>6) & 0x03;
|
||||
int bg_color = (data[2] ) & 0x3f;
|
||||
int bg_opacity = (data[2]>>6) & 0x03;
|
||||
int edge_color = (data[3]>>6) & 0x3f;
|
||||
dbg_print(CCX_DMT_708, " Foreground color: [%d] Foreground opacity: [%d]\n",
|
||||
ccx_common_logging.debug_ftn(CCX_DMT_708, " Foreground color: [%d] Foreground opacity: [%d]\n",
|
||||
fg_color, fg_opacity);
|
||||
dbg_print(CCX_DMT_708, " Background color: [%d] Background opacity: [%d]\n",
|
||||
ccx_common_logging.debug_ftn(CCX_DMT_708, " Background color: [%d] Background opacity: [%d]\n",
|
||||
bg_color, bg_opacity);
|
||||
dbg_print(CCX_DMT_708, " Edge color: [%d]\n",
|
||||
ccx_common_logging.debug_ftn(CCX_DMT_708, " Edge color: [%d]\n",
|
||||
edge_color);
|
||||
if (decoder->current_window==-1)
|
||||
{
|
||||
@@ -911,10 +928,10 @@ void handle_708_SPC_SetPenColor (cc708_service_decoder *decoder, unsigned char *
|
||||
|
||||
void handle_708_SPL_SetPenLocation (cc708_service_decoder *decoder, unsigned char *data)
|
||||
{
|
||||
dbg_print(CCX_DMT_708, " Entry in handle_708_SPL_SetPenLocation, attributes: \n");
|
||||
ccx_common_logging.debug_ftn(CCX_DMT_708, " Entry in handle_708_SPL_SetPenLocation, attributes: \n");
|
||||
int row = data[1] & 0x0f;
|
||||
int col = data[2] & 0x3f;
|
||||
dbg_print(CCX_DMT_708, " row: [%d] Column: [%d]\n",
|
||||
ccx_common_logging.debug_ftn(CCX_DMT_708, " row: [%d] Column: [%d]\n",
|
||||
row, col);
|
||||
if (decoder->current_window==-1)
|
||||
{
|
||||
@@ -928,17 +945,17 @@ void handle_708_SPL_SetPenLocation (cc708_service_decoder *decoder, unsigned cha
|
||||
|
||||
/*-------------------------------------------------------
|
||||
SYNCHRONIZATION COMMANDS
|
||||
------------------------------------------------------- */
|
||||
------------------------------------------------------- */
|
||||
void handle_708_DLY_Delay (cc708_service_decoder *decoder, int tenths_of_sec)
|
||||
{
|
||||
dbg_print(CCX_DMT_708, " Entry in handle_708_DLY_Delay, delay for [%d] tenths of second.", tenths_of_sec);
|
||||
ccx_common_logging.debug_ftn(CCX_DMT_708, " Entry in handle_708_DLY_Delay, delay for [%d] tenths of second.", tenths_of_sec);
|
||||
// TODO: Probably ask for the current FTS and wait for this time before resuming -
|
||||
// not sure it's worth it though
|
||||
}
|
||||
|
||||
void handle_708_DLC_DelayCancel (cc708_service_decoder *decoder)
|
||||
{
|
||||
dbg_print(CCX_DMT_708, " Entry in handle_708_DLC_DelayCancel.");
|
||||
ccx_common_logging.debug_ftn(CCX_DMT_708, " Entry in handle_708_DLC_DelayCancel.");
|
||||
// TODO: See above
|
||||
}
|
||||
|
||||
@@ -946,12 +963,12 @@ void handle_708_DLC_DelayCancel (cc708_service_decoder *decoder)
|
||||
int handle_708_C1 (cc708_service_decoder *decoder, unsigned char *data, int data_length)
|
||||
{
|
||||
struct S_COMMANDS_C1 com=COMMANDS_C1[data[0]-0x80];
|
||||
dbg_print(CCX_DMT_708, "%s | C1: [%02X] [%s] [%s] (%d)\n",
|
||||
ccx_common_logging.debug_ftn(CCX_DMT_708, "%s | C1: [%02X] [%s] [%s] (%d)\n",
|
||||
print_mstime(get_fts()),
|
||||
data[0],com.name,com.description, com.length);
|
||||
data[0],com.name,com.description, com.length);
|
||||
if (com.length>data_length)
|
||||
{
|
||||
dbg_print(CCX_DMT_708, "C1: Warning: Not enough bytes for command.\n");
|
||||
ccx_common_logging.debug_ftn(CCX_DMT_708, "C1: Warning: Not enough bytes for command.\n");
|
||||
return -1;
|
||||
}
|
||||
switch (com.code)
|
||||
@@ -967,7 +984,7 @@ int handle_708_C1 (cc708_service_decoder *decoder, unsigned char *data, int data
|
||||
handle_708_CWx_SetCurrentWindow (decoder, com.code-CW0); /* Window 0 to 7 */
|
||||
break;
|
||||
case CLW:
|
||||
handle_708_CLW_ClearWindows (decoder, data[1]);
|
||||
handle_708_CLW_ClearWindows (decoder, data[1]);
|
||||
break;
|
||||
case DSW:
|
||||
handle_708_DSW_DisplayWindows (decoder, data[1]);
|
||||
@@ -998,16 +1015,16 @@ int handle_708_C1 (cc708_service_decoder *decoder, unsigned char *data, int data
|
||||
break;
|
||||
case SPL:
|
||||
handle_708_SPL_SetPenLocation (decoder, data);
|
||||
break;
|
||||
break;
|
||||
case RSV93:
|
||||
case RSV94:
|
||||
case RSV95:
|
||||
case RSV96:
|
||||
dbg_print(CCX_DMT_708, "Warning, found Reserved codes, ignored.\n");
|
||||
ccx_common_logging.debug_ftn(CCX_DMT_708, "Warning, found Reserved codes, ignored.\n");
|
||||
break;
|
||||
case SWA:
|
||||
handle_708_SWA_SetWindowAttributes (decoder, data);
|
||||
break;
|
||||
break;
|
||||
case DF0:
|
||||
case DF1:
|
||||
case DF2:
|
||||
@@ -1017,23 +1034,23 @@ int handle_708_C1 (cc708_service_decoder *decoder, unsigned char *data, int data
|
||||
case DF6:
|
||||
case DF7:
|
||||
handle_708_DFx_DefineWindow (decoder, com.code-DF0, data); /* Window 0 to 7 */
|
||||
break;
|
||||
break;
|
||||
default:
|
||||
mprint ("BUG: Unhandled code in handle_708_C1.\n");
|
||||
break;
|
||||
ccx_common_logging.log_ftn ("BUG: Unhandled code in handle_708_C1.\n");
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
return com.length;
|
||||
}
|
||||
|
||||
|
||||
void process_service_block (cc708_service_decoder *decoder, unsigned char *data, int data_length)
|
||||
{
|
||||
int i=0;
|
||||
int i=0;
|
||||
while (i<data_length)
|
||||
{
|
||||
int used=-1;
|
||||
if (data[i]!=EXT1)
|
||||
if (data[i]!=EXT1)
|
||||
{
|
||||
// Group C0
|
||||
if (/* data[i]>=0x00 && */ data[i]<=0x1F) // Comment to silence warning
|
||||
@@ -1055,142 +1072,143 @@ void process_service_block (cc708_service_decoder *decoder, unsigned char *data,
|
||||
used=handle_708_G1 (decoder,data+i,data_length-i);
|
||||
if (used==-1)
|
||||
{
|
||||
dbg_print(CCX_DMT_708, "There was a problem handling the data. Reseting service decoder\n");
|
||||
ccx_common_logging.debug_ftn(CCX_DMT_708, "There was a problem handling the data. Reseting service decoder\n");
|
||||
// TODO: Not sure if a local reset is going to be helpful here.
|
||||
cc708_service_reset (decoder);
|
||||
return;
|
||||
return;
|
||||
}
|
||||
}
|
||||
else // Use extended set
|
||||
{
|
||||
{
|
||||
used=handle_708_extended_char (decoder, data+i+1,data_length-1);
|
||||
used++; // Since we had EXT1
|
||||
used++; // Since we had EXT1
|
||||
}
|
||||
i+=used;
|
||||
i+=used;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void process_current_packet (void)
|
||||
void process_current_packet (struct lib_cc_decode* ctx)
|
||||
{
|
||||
int seq=(current_packet[0] & 0xC0) >> 6; // Two most significants bits
|
||||
int len=current_packet[0] & 0x3F; // 6 least significants bits
|
||||
#ifdef DEBUG_708_PACKETS
|
||||
mprint ("Processing EIA-708 packet, length=%d, seq=%d\n",current_packet_length, seq);
|
||||
ccx_common_logging.log_ftn ("Processing EIA-708 packet, length=%d, seq=%d\n",current_packet_length, seq);
|
||||
#endif
|
||||
if (current_packet_length==0)
|
||||
return;
|
||||
if (len==0) // This is well defined in EIA-708; no magic.
|
||||
len=128;
|
||||
len=128;
|
||||
else
|
||||
len=len*2;
|
||||
// Note that len here is the length including the header
|
||||
#ifdef DEBUG_708_PACKETS
|
||||
mprint ("Sequence: %d, packet length: %d\n",seq,len);
|
||||
#ifdef DEBUG_708_PACKETS
|
||||
ccx_common_logging.log_ftn ("Sequence: %d, packet length: %d\n",seq,len);
|
||||
#endif
|
||||
if (current_packet_length!=len) // Is this possible?
|
||||
{
|
||||
dbg_print(CCX_DMT_708, "Packet length mismatch (%s%d), first two data bytes %02X %02X, current picture:%s\n",
|
||||
current_packet_length-len>0?"+":"", current_packet_length-len,
|
||||
ccx_common_logging.debug_ftn(CCX_DMT_708, "Packet length mismatch (%s%d), first two data bytes %02X %02X, current picture:%s\n",
|
||||
current_packet_length-len>0?"+":"", current_packet_length-len,
|
||||
current_packet[0], current_packet[1], pict_types[current_picture_coding_type]);
|
||||
cc708_reset();
|
||||
cc708_reset(ctx);
|
||||
return;
|
||||
}
|
||||
if (last_seq!=-1 && (last_seq+1)%4!=seq)
|
||||
{
|
||||
dbg_print(CCX_DMT_708, "Unexpected sequence number, it was [%d] but should have been [%d]\n",
|
||||
ccx_common_logging.debug_ftn(CCX_DMT_708, "Unexpected sequence number, it was [%d] but should have been [%d]\n",
|
||||
seq,(last_seq+1)%4);
|
||||
cc708_reset();
|
||||
cc708_reset(ctx);
|
||||
return;
|
||||
}
|
||||
last_seq=seq;
|
||||
|
||||
unsigned char *pos=current_packet+1;
|
||||
|
||||
unsigned char *pos=current_packet+1;
|
||||
|
||||
while (pos<current_packet+len)
|
||||
{
|
||||
int service_number=(pos[0] & 0xE0)>>5; // 3 more significant bits
|
||||
int block_length = (pos[0] & 0x1F); // 5 less significant bits
|
||||
|
||||
dbg_print (CCX_DMT_708, "Standard header: Service number: [%d] Block length: [%d]\n",service_number,
|
||||
block_length);
|
||||
ccx_common_logging.debug_ftn (CCX_DMT_708, "Standard header: Service number: [%d] Block length: [%d]\n",service_number,
|
||||
block_length);
|
||||
|
||||
if (service_number==7) // There is an extended header
|
||||
{
|
||||
pos++;
|
||||
pos++;
|
||||
service_number=(pos[0] & 0x3F); // 6 more significant bits
|
||||
// printf ("Extended header: Service number: [%d]\n",service_number);
|
||||
if (service_number<7)
|
||||
if (service_number<7)
|
||||
{
|
||||
dbg_print(CCX_DMT_708, "Illegal service number in extended header: [%d]\n",service_number);
|
||||
ccx_common_logging.debug_ftn(CCX_DMT_708, "Illegal service number in extended header: [%d]\n",service_number);
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
if (service_number==0 && block_length==0) // Null header already?
|
||||
{
|
||||
if (pos!=(current_packet+len-1)) // i.e. last byte in packet
|
||||
{
|
||||
// Not sure if this is correct
|
||||
printf ("Null header before it was expected.\n");
|
||||
printf ("Null header before it was expected.\n");
|
||||
// break;
|
||||
}
|
||||
} */
|
||||
pos++; // Move to service data
|
||||
if (service_number==0 && block_length!=0) // Illegal, but specs say what to do...
|
||||
{
|
||||
dbg_print(CCX_DMT_708, "Data received for service 0, skipping rest of packet.");
|
||||
ccx_common_logging.debug_ftn(CCX_DMT_708, "Data received for service 0, skipping rest of packet.");
|
||||
pos = current_packet+len; // Move to end
|
||||
break;
|
||||
}
|
||||
|
||||
if (block_length != 0)
|
||||
file_report.services708[service_number] = 1;
|
||||
if (block_length != 0){
|
||||
ccx_decoder_708_report.services[service_number] = 1;
|
||||
}
|
||||
|
||||
if (service_number>0 && decoders[service_number-1].inited)
|
||||
process_service_block (&decoders[service_number-1], pos, block_length);
|
||||
|
||||
pos+=block_length; // Skip data
|
||||
|
||||
pos+=block_length; // Skip data
|
||||
}
|
||||
|
||||
clear_packet();
|
||||
|
||||
if (pos!=current_packet+len) // For some reason we didn't parse the whole packet
|
||||
{
|
||||
dbg_print(CCX_DMT_708, "There was a problem with this packet, reseting\n");
|
||||
cc708_reset();
|
||||
ccx_common_logging.debug_ftn(CCX_DMT_708, "There was a problem with this packet, reseting\n");
|
||||
cc708_reset(ctx);
|
||||
}
|
||||
|
||||
if (len<128 && *pos) // Null header is mandatory if there is room
|
||||
{
|
||||
dbg_print(CCX_DMT_708, "Warning: Null header expected but not found.\n");
|
||||
}
|
||||
ccx_common_logging.debug_ftn(CCX_DMT_708, "Warning: Null header expected but not found.\n");
|
||||
}
|
||||
}
|
||||
|
||||
void do_708 (const unsigned char *data, int datalength)
|
||||
void do_708 (struct lib_cc_decode* ctx, const unsigned char *data, int datalength)
|
||||
{
|
||||
/* Note: The data has this format:
|
||||
1 byte for cc_valid
|
||||
/* Note: The data has this format:
|
||||
1 byte for cc_valid
|
||||
1 byte for cc_type
|
||||
2 bytes for the actual data */
|
||||
if (!do_cea708 && !ccx_options.print_file_reports)
|
||||
if (!do_cea708 && !ccx_decoders_708_report)
|
||||
return;
|
||||
|
||||
|
||||
for (int i=0;i<datalength;i+=4)
|
||||
{
|
||||
unsigned char cc_valid=data[i];
|
||||
unsigned char cc_type=data[i+1];
|
||||
unsigned char cc_type=data[i+1];
|
||||
|
||||
switch (cc_type)
|
||||
{
|
||||
case 2:
|
||||
dbg_print (CCX_DMT_708, "708: DTVCC Channel Packet Data\n");
|
||||
case 2:
|
||||
ccx_common_logging.debug_ftn (CCX_DMT_708, "708: DTVCC Channel Packet Data\n");
|
||||
if (cc_valid==0) // This ends the previous packet
|
||||
process_current_packet();
|
||||
process_current_packet(ctx);
|
||||
else
|
||||
{
|
||||
if (current_packet_length>253)
|
||||
if (current_packet_length>253)
|
||||
{
|
||||
dbg_print(CCX_DMT_708, "Warning: Legal packet size exceeded, data not added.\n");
|
||||
ccx_common_logging.debug_ftn(CCX_DMT_708, "Warning: Legal packet size exceeded, data not added.\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1199,14 +1217,14 @@ void do_708 (const unsigned char *data, int datalength)
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
dbg_print (CCX_DMT_708, "708: DTVCC Channel Packet Start\n");
|
||||
process_current_packet();
|
||||
case 3:
|
||||
ccx_common_logging.debug_ftn (CCX_DMT_708, "708: DTVCC Channel Packet Start\n");
|
||||
process_current_packet(ctx);
|
||||
if (cc_valid)
|
||||
{
|
||||
if (current_packet_length>253)
|
||||
if (current_packet_length>127)
|
||||
{
|
||||
dbg_print(CCX_DMT_708, "Warning: Legal packet size exceeded, data not added.\n");
|
||||
ccx_common_logging.debug_ftn(CCX_DMT_708, "Warning: Legal packet size exceeded, data not added.\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1215,25 +1233,26 @@ void do_708 (const unsigned char *data, int datalength)
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
fatal (EXIT_BUG_BUG, "708: shouldn't be here - cc_type: %d\n", cc_type);
|
||||
default:
|
||||
ccx_common_logging.fatal_ftn (CCX_COMMON_EXIT_BUG_BUG, "708: shouldn't be here - cc_type: %d\n", cc_type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void init_708(void)
|
||||
void ccx_decoders_708_init_library(char *basefilename,const char *extension,int report)
|
||||
{
|
||||
for (int i=0;i<63;i++)
|
||||
for (int i = 0; i<CCX_DECODERS_708_MAX_SERVICES; i++)
|
||||
{
|
||||
if (!cea708services[i])
|
||||
continue;
|
||||
cc708_service_reset (&decoders[i]);
|
||||
if (decoders[i].filename==NULL)
|
||||
{
|
||||
decoders[i].filename = (char *) malloc (strlen (basefilename)+4+strlen (extension));
|
||||
sprintf (decoders[i].filename, "%s_%d%s", basefilename,i+1,extension);
|
||||
}
|
||||
decoders[i].filename = (char *) malloc (strlen (basefilename)+4+strlen (extension));
|
||||
sprintf (decoders[i].filename, "%s_%d%s", basefilename,i+1,extension);
|
||||
}
|
||||
decoders[i].fh=-1;
|
||||
decoders[i].srt_counter=0;
|
||||
}
|
||||
}
|
||||
ccx_decoders_708_report = report;
|
||||
}
|
||||
@@ -1,15 +1,31 @@
|
||||
#ifndef _INCLUDE_708_
|
||||
#define _INCLUDE_708_
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include "ccx_decoders_common.h"
|
||||
|
||||
#define MAX_708_PACKET_LENGTH 128
|
||||
#define CCX_DECODERS_708_MAX_SERVICES 63
|
||||
|
||||
#define I708_MAX_ROWS 15
|
||||
#define I708_MAX_COLUMNS 42
|
||||
#define I708_MAX_COLUMNS 42
|
||||
|
||||
#define I708_SCREENGRID_ROWS 75
|
||||
#define I708_SCREENGRID_COLUMNS 210
|
||||
|
||||
#define I708_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
|
||||
{
|
||||
unsigned services[CCX_DECODERS_708_MAX_SERVICES];
|
||||
};
|
||||
extern struct ccx_decoder_708_report_t ccx_decoder_708_report;
|
||||
|
||||
enum COMMANDS_C0_CODES
|
||||
{
|
||||
NUL=0,
|
||||
@@ -40,7 +56,7 @@ enum COMMANDS_C1_CODES
|
||||
DLY=0x8D,
|
||||
DLC=0x8E,
|
||||
RST=0x8F,
|
||||
SPA=0x90,
|
||||
SPA=0x90,
|
||||
SPC=0x91,
|
||||
SPL=0x92,
|
||||
RSV93=0x93,
|
||||
@@ -259,7 +275,7 @@ typedef struct e708Window
|
||||
typedef struct tvscreen
|
||||
{
|
||||
unsigned char chars[I708_SCREENGRID_ROWS][I708_SCREENGRID_COLUMNS+1];
|
||||
}
|
||||
}
|
||||
tvscreen;
|
||||
|
||||
typedef struct cc708_service_decoder
|
||||
@@ -275,13 +291,17 @@ typedef struct cc708_service_decoder
|
||||
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;
|
||||
|
||||
extern int do_cea708; // Process 708 data?
|
||||
extern int cea708services[]; // [] -> 1 for services to be processed
|
||||
|
||||
extern int resets_708;
|
||||
|
||||
void do_708 (const unsigned char *data, int datalength);
|
||||
void init_708(void);
|
||||
void do_708 (struct lib_cc_decode* ctx, const unsigned char *data, int datalength);
|
||||
|
||||
unsigned char get_internal_from_G0 (unsigned char g0_char);
|
||||
unsigned char get_internal_from_G1 (unsigned char g1_char);
|
||||
@@ -289,4 +309,5 @@ 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);
|
||||
|
||||
void ccx_decoders_708_init_library(char *basefilename,const char *extension, int report);
|
||||
#endif
|
||||
319
src/lib_ccx/ccx_decoders_common.c
Normal file
319
src/lib_ccx/ccx_decoders_common.c
Normal file
@@ -0,0 +1,319 @@
|
||||
/* Functions used by both the 608 and 708 decoders. An effort should be
|
||||
made to reuse, not duplicate, as many functions as possible */
|
||||
|
||||
#include "ccx_decoders_common.h"
|
||||
#include "ccx_common_structs.h"
|
||||
#include "ccx_common_char_encoding.h"
|
||||
#include "ccx_common_constants.h"
|
||||
#include "ccx_common_timing.h"
|
||||
#include "ccx_common_common.h"
|
||||
#include "lib_ccx.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 fts = get_fts();
|
||||
if (fts <= minimum_fts)
|
||||
fts = 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)
|
||||
{
|
||||
LLONG fts = get_fts();
|
||||
if (fts>minimum_fts)
|
||||
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;
|
||||
for (int j = 0; j < cc_count * 3; j = j + 3)
|
||||
{
|
||||
if (validate_cc_data_pair( cc_data + j ) )
|
||||
continue;
|
||||
ret = do_cb(ctx, cc_data + j, sub);
|
||||
if (ret == 1) //1 means success here
|
||||
ret = 0;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
int validate_cc_data_pair (unsigned char *cc_data_pair)
|
||||
{
|
||||
unsigned char cc_valid = (*cc_data_pair & 4) >>2;
|
||||
unsigned char cc_type = *cc_data_pair & 3;
|
||||
|
||||
if (!cc_valid)
|
||||
return -1;
|
||||
|
||||
if (cc_type==0 || cc_type==1)
|
||||
{
|
||||
// For EIA-608 data we verify parity.
|
||||
if (!cc608_parity_table[cc_data_pair[2]])
|
||||
{
|
||||
// If the second byte doesn't pass parity, ignore pair
|
||||
return -1;
|
||||
}
|
||||
if (!cc608_parity_table[cc_data_pair[1]])
|
||||
{
|
||||
// The first byte doesn't pass parity, we replace it with a solid blank
|
||||
// and process the pair.
|
||||
cc_data_pair[1]=0x7F;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
|
||||
}
|
||||
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;
|
||||
|
||||
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->write_format!=CCX_OF_RAW && // In raw we cannot skip padding because timing depends on it
|
||||
ctx->write_format!=CCX_OF_DVDRAW &&
|
||||
(cc_block[0]==0xFA || cc_block[0]==0xFC || cc_block[0]==0xFD )
|
||||
&& (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]);
|
||||
|
||||
/* 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. */
|
||||
|
||||
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));
|
||||
|
||||
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));
|
||||
|
||||
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");
|
||||
|
||||
// DTVCC packet start
|
||||
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");
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
struct lib_cc_decode* init_cc_decode (struct ccx_decoders_common_settings_t *setting)
|
||||
{
|
||||
struct lib_cc_decode *ctx = NULL;
|
||||
|
||||
ctx = malloc(sizeof(struct lib_cc_decode));
|
||||
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->fix_padding = setting->fix_padding;
|
||||
ctx->write_format = setting->output_format;
|
||||
ctx->subs_delay = setting->subs_delay;
|
||||
ctx->wbout1 = setting->wbout1;
|
||||
memcpy(&ctx->extraction_start, &setting->extraction_start,sizeof(struct ccx_boundary_time));
|
||||
memcpy(&ctx->extraction_end, &setting->extraction_end,sizeof(struct ccx_boundary_time));
|
||||
return ctx;
|
||||
}
|
||||
void dinit_cc_decode(struct lib_cc_decode **ctx)
|
||||
{
|
||||
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);
|
||||
}
|
||||
34
src/lib_ccx/ccx_decoders_common.h
Normal file
34
src/lib_ccx/ccx_decoders_common.h
Normal file
@@ -0,0 +1,34 @@
|
||||
#ifndef _CC_DECODER_COMMON
|
||||
#define _CC_DECODER_COMMON
|
||||
|
||||
#include "ccx_common_platform.h"
|
||||
#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
|
||||
|
||||
extern uint64_t utc_refvalue; // UTC referential value
|
||||
|
||||
// Declarations
|
||||
LLONG get_visible_start(void);
|
||||
LLONG get_visible_end(void);
|
||||
|
||||
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);
|
||||
|
||||
void ccx_decoders_common_settings_init(LLONG subs_delay, enum ccx_output_format output_format);
|
||||
|
||||
int validate_cc_data_pair (unsigned char *cc_data_pair);
|
||||
int process_cc_data (struct lib_cc_decode *ctx, unsigned char *cc_data, int cc_count, struct cc_subtitle *sub);
|
||||
int do_cb (struct lib_cc_decode *ctx, unsigned char *cc_block, struct cc_subtitle *sub);
|
||||
void printdata (struct lib_cc_decode *ctx, const unsigned char *data1, int length1,
|
||||
const unsigned char *data2, int length2, struct cc_subtitle *sub);
|
||||
struct lib_cc_decode* init_cc_decode (struct ccx_decoders_common_settings_t *setting);
|
||||
void dinit_cc_decode(struct lib_cc_decode **ctx);
|
||||
#endif
|
||||
106
src/lib_ccx/ccx_decoders_structs.h
Normal file
106
src/lib_ccx/ccx_decoders_structs.h
Normal file
@@ -0,0 +1,106 @@
|
||||
#ifndef CCX_DECODERS_STRUCTS_H
|
||||
#define CCX_DECODERS_STRUCTS_H
|
||||
|
||||
#include "ccx_common_platform.h"
|
||||
#include "ccx_common_constants.h"
|
||||
#include "ccx_common_timing.h"
|
||||
|
||||
// Define max width in characters/columns on the screen
|
||||
#define CCX_DECODER_608_SCREEN_WIDTH 32
|
||||
|
||||
/* flag raised when end of display marker arrives in Dvb Subtitle */
|
||||
#define SUB_EOD_MARKER (1 << 0 )
|
||||
struct cc_bitmap
|
||||
{
|
||||
int x;
|
||||
int y;
|
||||
int w;
|
||||
int h;
|
||||
int nb_colors;
|
||||
unsigned char *data[2];
|
||||
int linesize[2];
|
||||
#ifdef ENABLE_OCR
|
||||
char *ocr_text;
|
||||
#endif
|
||||
};
|
||||
|
||||
enum ccx_eia608_format
|
||||
{
|
||||
SFORMAT_CC_SCREEN,
|
||||
SFORMAT_CC_LINE,
|
||||
SFORMAT_XDS
|
||||
};
|
||||
|
||||
enum cc_modes
|
||||
{
|
||||
MODE_POPON = 0,
|
||||
MODE_ROLLUP_2 = 1,
|
||||
MODE_ROLLUP_3 = 2,
|
||||
MODE_ROLLUP_4 = 3,
|
||||
MODE_TEXT = 4,
|
||||
MODE_PAINTON = 5,
|
||||
// Fake modes to emulate stuff
|
||||
MODE_FAKE_ROLLUP_1 = 100
|
||||
};
|
||||
|
||||
/**
|
||||
* This structure have fields which need to be ignored according to format,
|
||||
* for example if format is SFORMAT_XDS then all fields other then
|
||||
* xds related (xds_str, xds_len and cur_xds_packet_class) should be
|
||||
* ignored and not to be derefrenced.
|
||||
*
|
||||
* TODO use union inside struct for each kind of fields
|
||||
*/
|
||||
typedef struct eia608_screen // A CC buffer
|
||||
{
|
||||
/** format of data inside this structure */
|
||||
enum ccx_eia608_format format;
|
||||
unsigned char characters[15][33];
|
||||
unsigned char colors[15][33];
|
||||
unsigned char fonts[15][33]; // Extra char at the end for a 0
|
||||
int row_used[15]; // Any data in row?
|
||||
int empty; // Buffer completely empty?
|
||||
/** start time of this CC buffer */
|
||||
LLONG start_time;
|
||||
/** end time of this CC buffer */
|
||||
LLONG end_time;
|
||||
enum cc_modes mode;
|
||||
int channel; // Currently selected channel
|
||||
int my_field; // Used for sanity checks
|
||||
/** XDS string */
|
||||
char *xds_str;
|
||||
/** length of XDS string */
|
||||
size_t xds_len;
|
||||
/** Class of XDS string */
|
||||
int cur_xds_packet_class;
|
||||
} eia608_screen;
|
||||
|
||||
struct ccx_decoders_common_settings_t
|
||||
{
|
||||
LLONG subs_delay; // ms to delay (or advance) subs
|
||||
enum ccx_output_format output_format; // What kind of output format should be used?
|
||||
int fix_padding; // Replace 0000 with 8080 in HDTV (needed for some cards)
|
||||
struct ccx_boundary_time extraction_start, extraction_end; // Segment we actually process
|
||||
void *wbout1;
|
||||
int cc_to_stdout;
|
||||
};
|
||||
struct lib_cc_decode
|
||||
{
|
||||
int cc_stats[4];
|
||||
int saw_caption_block;
|
||||
int processed_enough; // If 1, we have enough lines, time, etc.
|
||||
|
||||
/* 608 contexts - note that this shouldn't be global, they should be
|
||||
per program */
|
||||
void *context_cc608_field_1;
|
||||
void *context_cc608_field_2;
|
||||
|
||||
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
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,4 +1,14 @@
|
||||
#include "ccextractor.h"
|
||||
#include "ccx_decoders_xds.h"
|
||||
#include "ccx_common_constants.h"
|
||||
#include "ccx_common_timing.h"
|
||||
|
||||
LLONG ts_start_of_xds = -1; // Time at which we switched to XDS mode, =-1 hasn't happened yet
|
||||
|
||||
struct ccx_decoders_xds_context_t {
|
||||
ccx_encoders_transcript_format transcriptFormat;
|
||||
LLONG subsDelay;
|
||||
char millisSeparator;
|
||||
} ccx_decoders_xds_context;
|
||||
|
||||
// Program Identification Number (Start Time) for current program
|
||||
static int current_xds_min=-1;
|
||||
@@ -10,8 +20,8 @@ static int xds_start_time_shown=0;
|
||||
static int xds_program_length_shown=0;
|
||||
static char xds_program_description[8][33];
|
||||
|
||||
static char current_xds_network_name[33];
|
||||
static char current_xds_program_name[33];
|
||||
static char current_xds_network_name[33];
|
||||
static char current_xds_program_name[33];
|
||||
static char current_xds_call_letters[7];
|
||||
static char current_xds_program_type[33];
|
||||
|
||||
@@ -71,6 +81,7 @@ static const char *XDSProgramTypes[]=
|
||||
#define XDS_CLASS_RESERVED 5
|
||||
#define XDS_CLASS_PRIVATE 6
|
||||
#define XDS_CLASS_END 7
|
||||
#define XDS_CLASS_OUT_OF_BAND 0x40 // Not a real class, a marker for packets for out-of-band data.
|
||||
|
||||
// Types for the classes current and future
|
||||
#define XDS_TYPE_PIN_START_TIME 1
|
||||
@@ -90,13 +101,14 @@ static const char *XDSProgramTypes[]=
|
||||
#define XDS_TYPE_PROGRAM_DESC_8 0x17
|
||||
|
||||
// Types for the class channel
|
||||
#define XDS_TYPE_NETWORK_NAME 1
|
||||
#define XDS_TYPE_CALL_LETTERS_AND_CHANNEL 2
|
||||
#define XDS_TYPE_NETWORK_NAME 1
|
||||
#define XDS_TYPE_CALL_LETTERS_AND_CHANNEL 2
|
||||
#define XDS_TYPE_TSID 4 // Transmission Signal Identifier
|
||||
|
||||
// Types for miscellaneous packets
|
||||
#define XDS_TYPE_TIME_OF_DAY 1
|
||||
#define XDS_TYPE_TIME_OF_DAY 1
|
||||
#define XDS_TYPE_LOCAL_TIME_ZONE 4
|
||||
#define XDS_TYPE_OUT_OF_BAND_CHANNEL_NUMBER 0x40
|
||||
|
||||
#define NUM_XDS_BUFFERS 9 // CEA recommends no more than one level of interleaving. Play it safe
|
||||
#define NUM_BYTES_PER_PACKET 35 // Class + type (repeated for convenience) + data + zero
|
||||
@@ -108,7 +120,7 @@ struct xds_buffer
|
||||
int xds_type;
|
||||
unsigned char bytes[NUM_BYTES_PER_PACKET]; // Class + type (repeated for convenience) + data + zero
|
||||
unsigned char used_bytes;
|
||||
} xds_buffers[NUM_XDS_BUFFERS];
|
||||
} xds_buffers[NUM_XDS_BUFFERS];
|
||||
|
||||
static int cur_xds_buffer_idx=-1;
|
||||
static int cur_xds_packet_class=-1;
|
||||
@@ -116,26 +128,60 @@ static unsigned char *cur_xds_payload;
|
||||
static int cur_xds_payload_length;
|
||||
static int cur_xds_packet_type;
|
||||
|
||||
|
||||
void xds_init()
|
||||
void ccx_decoders_xds_init_library(ccx_encoders_transcript_format *transcriptSettings, LLONG subs_delay, char millis_separator)
|
||||
{
|
||||
for (int i=0;i<NUM_XDS_BUFFERS;i++)
|
||||
ccx_decoders_xds_context.transcriptFormat = *transcriptSettings;
|
||||
ccx_decoders_xds_context.subsDelay = subs_delay;
|
||||
ccx_decoders_xds_context.millisSeparator = millis_separator;
|
||||
|
||||
for (int i = 0; i<NUM_XDS_BUFFERS; i++)
|
||||
{
|
||||
xds_buffers[i].in_use=0;
|
||||
xds_buffers[i].xds_class=-1;
|
||||
xds_buffers[i].xds_type=-1;
|
||||
xds_buffers[i].used_bytes=0;
|
||||
memset (xds_buffers[i].bytes , 0, NUM_BYTES_PER_PACKET);
|
||||
xds_buffers[i].in_use = 0;
|
||||
xds_buffers[i].xds_class = -1;
|
||||
xds_buffers[i].xds_type = -1;
|
||||
xds_buffers[i].used_bytes = 0;
|
||||
memset(xds_buffers[i].bytes, 0, NUM_BYTES_PER_PACKET);
|
||||
}
|
||||
for (int i=0; i<9; i++)
|
||||
memset (xds_program_description,0,32);
|
||||
|
||||
memset (current_xds_network_name,0,33);
|
||||
memset (current_xds_program_name,0,33);
|
||||
memset (current_xds_call_letters,0,7);
|
||||
memset (current_xds_program_type,0,33);
|
||||
for (int i = 0; i<9; i++)
|
||||
memset(xds_program_description, 0, 32);
|
||||
|
||||
memset(current_xds_network_name, 0, 33);
|
||||
memset(current_xds_program_name, 0, 33);
|
||||
memset(current_xds_call_letters, 0, 7);
|
||||
memset(current_xds_program_type, 0, 33);
|
||||
}
|
||||
|
||||
int write_xds_string(struct cc_subtitle *sub,char *p,size_t len)
|
||||
{
|
||||
struct eia608_screen *data = NULL;
|
||||
data = (struct eia608_screen *) realloc(sub->data,( sub->nb_data + 1 ) * sizeof(*data));
|
||||
if (!data)
|
||||
{
|
||||
freep(&sub->data);
|
||||
sub->nb_data = 0;
|
||||
ccx_common_logging.log_ftn("No Memory left");
|
||||
return -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
sub->data = data;
|
||||
data = (struct eia608_screen *)sub->data + sub->nb_data;
|
||||
data->format = SFORMAT_XDS;
|
||||
data->start_time = ts_start_of_xds;
|
||||
data->end_time = get_fts();
|
||||
data->xds_str = p;
|
||||
data->xds_len = len;
|
||||
data->cur_xds_packet_class = cur_xds_packet_class;
|
||||
sub->nb_data++;
|
||||
sub->type = CC_608;
|
||||
sub->got_output = 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
|
||||
void xds_write_transcript_line_suffix (struct ccx_s_write *wb)
|
||||
{
|
||||
if (!wb || wb->fh==-1)
|
||||
@@ -143,81 +189,75 @@ void xds_write_transcript_line_suffix (struct ccx_s_write *wb)
|
||||
write (wb->fh, encoded_crlf, encoded_crlf_length);
|
||||
}
|
||||
|
||||
void xds_write_transcript_line_prefix (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)
|
||||
{
|
||||
unsigned h1,m1,s1,ms1;
|
||||
unsigned h2,m2,s2,ms2;
|
||||
if (!wb || wb->fh==-1)
|
||||
return;
|
||||
|
||||
if (ts_start_of_xds == -1)
|
||||
if (start_time == -1)
|
||||
{
|
||||
// Means we entered XDS mode without making a note of the XDS start time. This is a bug.
|
||||
fatal (EXIT_BUG_BUG, "Bug in timedtranscript (XDS). Please report.");
|
||||
;
|
||||
ccx_common_logging.fatal_ftn(CCX_COMMON_EXIT_BUG_BUG, "Bug in timedtranscript (XDS). Please report.");
|
||||
}
|
||||
|
||||
if (ccx_options.transcript_settings.showStartTime){
|
||||
if (ccx_decoders_xds_context.transcriptFormat.showStartTime){
|
||||
char buffer[80];
|
||||
if (ccx_options.transcript_settings.relativeTimestamp){
|
||||
if (ccx_decoders_xds_context.transcriptFormat.relativeTimestamp){
|
||||
if (utc_refvalue == UINT64_MAX)
|
||||
{
|
||||
mstotime(ts_start_of_xds + subs_delay, &h1, &m1, &s1, &ms1);
|
||||
fdprintf(wb->fh, "%02u:%02u:%02u%c%03u|", h1, m1, s1, ccx_options.millis_separator, ms1);
|
||||
mstotime(start_time + ccx_decoders_xds_context.subsDelay, &h1, &m1, &s1, &ms1);
|
||||
fdprintf(wb->fh, "%02u:%02u:%02u%c%03u|", h1, m1, s1, ccx_decoders_xds_context.millisSeparator, ms1);
|
||||
}
|
||||
else {
|
||||
fdprintf(wb->fh, "%lld%c%03d|", (ts_start_of_xds + subs_delay) / 1000, ccx_options.millis_separator, (ts_start_of_xds + subs_delay) % 1000);
|
||||
fdprintf(wb->fh, "%lld%c%03d|", (start_time + ccx_decoders_xds_context.subsDelay) / 1000, ccx_decoders_xds_context.millisSeparator, (start_time + ccx_decoders_xds_context.subsDelay) % 1000);
|
||||
}
|
||||
}
|
||||
else {
|
||||
mstotime(ts_start_of_xds + subs_delay, &h1, &m1, &s1, &ms1);
|
||||
time_t start_time_int = (ts_start_of_xds + subs_delay) / 1000;
|
||||
int start_time_dec = (ts_start_of_xds + subs_delay) % 1000;
|
||||
mstotime(start_time + ccx_decoders_xds_context.subsDelay, &h1, &m1, &s1, &ms1);
|
||||
time_t start_time_int = (start_time + ccx_decoders_xds_context.subsDelay) / 1000;
|
||||
int start_time_dec = (start_time + ccx_decoders_xds_context.subsDelay) % 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,ccx_options.millis_separator,start_time_dec);
|
||||
fdprintf(wb->fh, "%s%c%03d|", buffer, ccx_decoders_xds_context.millisSeparator, start_time_dec);
|
||||
}
|
||||
}
|
||||
|
||||
if (ccx_options.transcript_settings.showEndTime){
|
||||
if (ccx_decoders_xds_context.transcriptFormat.showEndTime){
|
||||
char buffer[80];
|
||||
if (ccx_options.transcript_settings.relativeTimestamp){
|
||||
if (ccx_decoders_xds_context.transcriptFormat.relativeTimestamp){
|
||||
if (utc_refvalue == UINT64_MAX)
|
||||
{
|
||||
mstotime(get_fts() + subs_delay, &h2, &m2, &s2, &ms2);
|
||||
fdprintf(wb->fh, "%02u:%02u:%02u%c%03u|", h2, m2, s2, ccx_options.millis_separator, ms2);
|
||||
mstotime(end_time + ccx_decoders_xds_context.subsDelay, &h2, &m2, &s2, &ms2);
|
||||
fdprintf(wb->fh, "%02u:%02u:%02u%c%03u|", h2, m2, s2, ccx_decoders_xds_context.millisSeparator, ms2);
|
||||
}
|
||||
else
|
||||
{
|
||||
fdprintf(wb->fh, "%lld%s%03d|", (get_fts() + subs_delay) / 1000, ccx_options.millis_separator, (get_fts() + subs_delay) % 1000);
|
||||
fdprintf(wb->fh, "%lld%s%03d|", (end_time + ccx_decoders_xds_context.subsDelay) / 1000, ccx_decoders_xds_context.millisSeparator, (end_time + ccx_decoders_xds_context.subsDelay) % 1000);
|
||||
}
|
||||
}
|
||||
else {
|
||||
mstotime(get_fts() + subs_delay, &h2, &m2, &s2, &ms2);
|
||||
time_t end_time_int = (get_fts() + subs_delay) / 1000;
|
||||
int end_time_dec = (get_fts() + subs_delay) % 1000;
|
||||
mstotime(end_time + ccx_decoders_xds_context.subsDelay, &h2, &m2, &s2, &ms2);
|
||||
time_t end_time_int = (end_time + ccx_decoders_xds_context.subsDelay) / 1000;
|
||||
int end_time_dec = (end_time + ccx_decoders_xds_context.subsDelay) % 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, ccx_options.millis_separator, end_time_dec);
|
||||
fdprintf(wb->fh, "%s%c%03d|", buffer, ccx_decoders_xds_context.millisSeparator, end_time_dec);
|
||||
}
|
||||
}
|
||||
|
||||
if (ccx_options.transcript_settings.showMode){
|
||||
if (ccx_decoders_xds_context.transcriptFormat.showMode){
|
||||
const char *mode = "XDS";
|
||||
fdprintf(wb->fh, "%s|", mode);
|
||||
}
|
||||
|
||||
if (ccx_options.transcript_settings.showCC){
|
||||
if (ccx_decoders_xds_context.transcriptFormat.showCC){
|
||||
fdprintf(wb->fh, "%s|", XDSclasses_short[cur_xds_packet_class]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void xdsprint (const char *fmt,...)
|
||||
void xdsprint (struct cc_subtitle *sub,const char *fmt,...)
|
||||
{
|
||||
if (wbxdsout==NULL || wbxdsout->fh==-1)
|
||||
return;
|
||||
xds_write_transcript_line_prefix (wbxdsout);
|
||||
|
||||
/* Guess we need no more than 100 bytes. */
|
||||
int n, size = 100;
|
||||
char *p, *np;
|
||||
@@ -226,8 +266,8 @@ void xdsprint (const char *fmt,...)
|
||||
if ((p = (char *) malloc (size)) == NULL)
|
||||
return;
|
||||
|
||||
while (1)
|
||||
{
|
||||
while (1)
|
||||
{
|
||||
/* Try to print in the allocated space. */
|
||||
va_start(ap, fmt);
|
||||
n = vsnprintf (p, size, fmt, ap);
|
||||
@@ -235,36 +275,33 @@ void xdsprint (const char *fmt,...)
|
||||
/* If that worked, return the string. */
|
||||
if (n > -1 && n < size)
|
||||
{
|
||||
write (wbxdsout->fh, p, n);
|
||||
free (p);
|
||||
xds_write_transcript_line_suffix (wbxdsout);
|
||||
return;
|
||||
write_xds_string(sub, p, n);
|
||||
return;
|
||||
}
|
||||
/* Else try again with more space. */
|
||||
if (n > -1) /* glibc 2.1 */
|
||||
size = n+1; /* precisely what is needed */
|
||||
else /* glibc 2.0 */
|
||||
size *= 2; /* twice the old size */
|
||||
if ((np = (char *) realloc (p, size)) == NULL)
|
||||
if ((np = (char *) realloc (p, size)) == NULL)
|
||||
{
|
||||
free(p);
|
||||
xds_write_transcript_line_suffix (wbxdsout);
|
||||
return ;
|
||||
return ;
|
||||
} else {
|
||||
p = np;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void xds_debug_test()
|
||||
void xds_debug_test(struct cc_subtitle *sub)
|
||||
{
|
||||
process_xds_bytes (0x05,0x02);
|
||||
process_xds_bytes (0x20,0x20);
|
||||
do_end_of_xds (0x2a);
|
||||
do_end_of_xds (sub, 0x2a);
|
||||
|
||||
}
|
||||
|
||||
void xds_cea608_test()
|
||||
void xds_cea608_test(struct cc_subtitle *sub)
|
||||
{
|
||||
/* This test is the sample data that comes in CEA-608. It sets the program name
|
||||
to be "Star Trek". The checksum is 0x1d and the validation must succeed. */
|
||||
@@ -276,7 +313,7 @@ void xds_cea608_test()
|
||||
process_xds_bytes (0x02,0x03);
|
||||
process_xds_bytes (0x02,0x03);
|
||||
process_xds_bytes (0x6b,0x00);
|
||||
do_end_of_xds (0x1d);
|
||||
do_end_of_xds (sub, 0x1d);
|
||||
}
|
||||
|
||||
int how_many_used()
|
||||
@@ -299,18 +336,18 @@ void clear_xds_buffer (int num)
|
||||
}
|
||||
|
||||
void process_xds_bytes (const unsigned char hi, int lo)
|
||||
{
|
||||
{
|
||||
int is_new;
|
||||
if (hi>=0x01 && hi<=0x0f)
|
||||
{
|
||||
int xds_class=(hi-1)/2; // Start codes 1 and 2 are "class type" 0, 3-4 are 2, and so on.
|
||||
is_new=hi%2; // Start codes are even
|
||||
dbg_print(CCX_DMT_XDS, "XDS Start: %u.%u Is new: %d | Class: %d (%s), Used buffers: %d\n",hi,lo, is_new,xds_class, XDSclasses[xds_class], how_many_used());
|
||||
ccx_common_logging.debug_ftn(CCX_DMT_DECODER_XDS, "XDS Start: %u.%u Is new: %d | Class: %d (%s), Used buffers: %d\n",hi,lo, is_new,xds_class, XDSclasses[xds_class], how_many_used());
|
||||
int first_free_buf=-1;
|
||||
int matching_buf=-1;
|
||||
for (int i=0;i<NUM_XDS_BUFFERS;i++)
|
||||
{
|
||||
if (xds_buffers[i].in_use &&
|
||||
if (xds_buffers[i].in_use &&
|
||||
xds_buffers[i].xds_class==xds_class &&
|
||||
xds_buffers[i].xds_type==lo)
|
||||
{
|
||||
@@ -320,14 +357,14 @@ void process_xds_bytes (const unsigned char hi, int lo)
|
||||
if (first_free_buf==-1 && !xds_buffers[i].in_use)
|
||||
first_free_buf=i;
|
||||
}
|
||||
/* Here, 3 possibilities:
|
||||
/* Here, 3 possibilities:
|
||||
1) We already had a buffer for this class/type and matching_buf points to it
|
||||
2) We didn't have a buffer for this class/type and first_free_buf points to an unused one
|
||||
3) All buffers are full and we will have to skip this packet.
|
||||
*/
|
||||
if (matching_buf==-1 && first_free_buf==-1)
|
||||
{
|
||||
mprint ("Note: All XDS buffers full (bug or suicidal stream). Ignoring this one (%d,%d).\n",xds_class,lo);
|
||||
ccx_common_logging.log_ftn ("Note: All XDS buffers full (bug or suicidal stream). Ignoring this one (%d,%d).\n",xds_class,lo);
|
||||
cur_xds_buffer_idx=-1;
|
||||
return;
|
||||
|
||||
@@ -341,7 +378,7 @@ void process_xds_bytes (const unsigned char hi, int lo)
|
||||
xds_buffers[cur_xds_buffer_idx].xds_type=lo;
|
||||
xds_buffers[cur_xds_buffer_idx].used_bytes=0;
|
||||
xds_buffers[cur_xds_buffer_idx].in_use=1;
|
||||
memset (xds_buffers[cur_xds_buffer_idx].bytes,0,NUM_BYTES_PER_PACKET);
|
||||
memset (xds_buffers[cur_xds_buffer_idx].bytes,0,NUM_BYTES_PER_PACKET);
|
||||
}
|
||||
if (!is_new)
|
||||
{
|
||||
@@ -352,10 +389,10 @@ void process_xds_bytes (const unsigned char hi, int lo)
|
||||
else
|
||||
{
|
||||
// Informational: 00, or 0x20-0x7F, so 01-0x1f forbidden
|
||||
dbg_print(CCX_DMT_XDS, "XDS: %02X.%02X (%c, %c)\n",hi,lo,hi,lo);
|
||||
ccx_common_logging.debug_ftn(CCX_DMT_DECODER_XDS, "XDS: %02X.%02X (%c, %c)\n",hi,lo,hi,lo);
|
||||
if ((hi>0 && hi<=0x1f) || (lo>0 && lo<=0x1f))
|
||||
{
|
||||
mprint ("\rNote: Illegal XDS data");
|
||||
ccx_common_logging.log_ftn ("\rNote: Illegal XDS data");
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -364,17 +401,17 @@ void process_xds_bytes (const unsigned char hi, int lo)
|
||||
// Should always happen
|
||||
xds_buffers[cur_xds_buffer_idx].bytes[xds_buffers[cur_xds_buffer_idx].used_bytes++]=hi;
|
||||
xds_buffers[cur_xds_buffer_idx].bytes[xds_buffers[cur_xds_buffer_idx].used_bytes++]=lo;
|
||||
xds_buffers[cur_xds_buffer_idx].bytes[xds_buffers[cur_xds_buffer_idx].used_bytes]=0;
|
||||
xds_buffers[cur_xds_buffer_idx].bytes[xds_buffers[cur_xds_buffer_idx].used_bytes]=0;
|
||||
}
|
||||
}
|
||||
|
||||
void xds_do_copy_generation_management_system (unsigned c1, unsigned c2)
|
||||
void xds_do_copy_generation_management_system (struct cc_subtitle *sub, unsigned c1, unsigned c2)
|
||||
{
|
||||
static unsigned last_c1=-1, last_c2=-1;
|
||||
static char copy_permited[256];
|
||||
static char copy_permited[256];
|
||||
static char aps[256];
|
||||
static char rcd[256];
|
||||
int changed=0;
|
||||
int changed=0;
|
||||
unsigned c1_6=(c1&0x40)>>6;
|
||||
/* unsigned unused1=(c1&0x20)>>5; */
|
||||
unsigned cgms_a_b4=(c1&0x10)>>4;
|
||||
@@ -394,11 +431,11 @@ void xds_do_copy_generation_management_system (unsigned c1, unsigned c2)
|
||||
if (last_c1!=c1 || last_c2!=c2)
|
||||
{
|
||||
changed=1;
|
||||
last_c1=c1;
|
||||
last_c2=c2;
|
||||
last_c1=c1;
|
||||
last_c2=c2;
|
||||
// Changed since last time, decode
|
||||
|
||||
const char *copytext[4]={"Copy permited (no restrictions)", "No more copies (one generation copy has been made)",
|
||||
const char *copytext[4]={"Copy permited (no restrictions)", "No more copies (one generation copy has been made)",
|
||||
"One generation of copies can be made", "No copying is permited"};
|
||||
const char *apstext[4]={"No APS", "PSP On; Split Burst Off", "PSP On; 2 line Split Burst On", "PSP On; 4 line Split Burst On"};
|
||||
sprintf (copy_permited,"CGMS: %s", copytext[cgms_a_b4*2+cgms_a_b3]);
|
||||
@@ -407,21 +444,24 @@ void xds_do_copy_generation_management_system (unsigned c1, unsigned c2)
|
||||
|
||||
}
|
||||
|
||||
xdsprint(copy_permited);
|
||||
xdsprint(aps);
|
||||
xdsprint(rcd);
|
||||
if (changed)
|
||||
if (ccx_decoders_xds_context.transcriptFormat.xds)
|
||||
{
|
||||
mprint ("\rXDS: %s\n",copy_permited);
|
||||
mprint ("\rXDS: %s\n",aps);
|
||||
mprint ("\rXDS: %s\n",rcd);
|
||||
xdsprint(sub, copy_permited);
|
||||
xdsprint(sub, aps);
|
||||
xdsprint(sub, rcd);
|
||||
}
|
||||
dbg_print(CCX_DMT_XDS, "\rXDS: %s\n",copy_permited);
|
||||
dbg_print(CCX_DMT_XDS, "\rXDS: %s\n",aps);
|
||||
dbg_print(CCX_DMT_XDS, "\rXDS: %s\n",rcd);
|
||||
if (changed)
|
||||
{
|
||||
ccx_common_logging.log_ftn ("\rXDS: %s\n",copy_permited);
|
||||
ccx_common_logging.log_ftn ("\rXDS: %s\n",aps);
|
||||
ccx_common_logging.log_ftn ("\rXDS: %s\n",rcd);
|
||||
}
|
||||
ccx_common_logging.debug_ftn(CCX_DMT_DECODER_XDS, "\rXDS: %s\n",copy_permited);
|
||||
ccx_common_logging.debug_ftn(CCX_DMT_DECODER_XDS, "\rXDS: %s\n",aps);
|
||||
ccx_common_logging.debug_ftn(CCX_DMT_DECODER_XDS, "\rXDS: %s\n",rcd);
|
||||
}
|
||||
|
||||
void xds_do_content_advisory (unsigned c1, unsigned c2)
|
||||
void xds_do_content_advisory (struct cc_subtitle *sub, unsigned c1, unsigned c2)
|
||||
{
|
||||
static unsigned last_c1=-1, last_c2=-1;
|
||||
static char age[256];
|
||||
@@ -449,14 +489,14 @@ void xds_do_content_advisory (unsigned c1, unsigned c2)
|
||||
if (last_c1!=c1 || last_c2!=c2)
|
||||
{
|
||||
changed=1;
|
||||
last_c1=c1;
|
||||
last_c2=c2;
|
||||
last_c1=c1;
|
||||
last_c2=c2;
|
||||
// Changed since last time, decode
|
||||
// Bits a1 and a0 determine the encoding. I'll add parsing as more samples become available
|
||||
if (!a1 && a0) // US TV parental guidelines
|
||||
{
|
||||
const char *agetext[8]={"None", "TV-Y (All Children)", "TV-Y7 (Older Children)",
|
||||
"TV-G (General Audience)", "TV-PG (Parental Guidance Suggested)",
|
||||
const char *agetext[8]={"None", "TV-Y (All Children)", "TV-Y7 (Older Children)",
|
||||
"TV-G (General Audience)", "TV-PG (Parental Guidance Suggested)",
|
||||
"TV-14 (Parents Strongly Cautioned)", "TV-MA (Mature Audience Only)", "None"};
|
||||
sprintf (age,"ContentAdvisory: US TV Parental Guidelines. Age Rating: %s", agetext[g2*4+g1*2+g0]);
|
||||
content[0]=0;
|
||||
@@ -486,8 +526,8 @@ void xds_do_content_advisory (unsigned c1, unsigned c2)
|
||||
}
|
||||
if (a0 && a1 && !Da2 && !La3) // Canadian English Language Rating
|
||||
{
|
||||
const char *ratingtext[8]={"Exempt", "Children", "Children eight years and older",
|
||||
"General programming suitable for all audiences", "Parental Guidance",
|
||||
const char *ratingtext[8]={"Exempt", "Children", "Children eight years and older",
|
||||
"General programming suitable for all audiences", "Parental Guidance",
|
||||
"Viewers 14 years and older", "Adult Programming", "[undefined]"};
|
||||
sprintf (rating,"ContentAdvisory: Canadian English Rating: %s", ratingtext[g2*4+g1*2+g0]);
|
||||
supported=1;
|
||||
@@ -496,36 +536,45 @@ void xds_do_content_advisory (unsigned c1, unsigned c2)
|
||||
}
|
||||
// Bits a1 and a0 determine the encoding. I'll add parsing as more samples become available
|
||||
if (!a1 && a0) // US TV parental guidelines
|
||||
{
|
||||
xdsprint(age);
|
||||
xdsprint(content);
|
||||
if (changed)
|
||||
{
|
||||
if (ccx_decoders_xds_context.transcriptFormat.xds)
|
||||
{
|
||||
mprint ("\rXDS: %s\n ",age);
|
||||
mprint ("\rXDS: %s\n ",content);
|
||||
xdsprint(sub, age);
|
||||
xdsprint(sub, content);
|
||||
}
|
||||
dbg_print(CCX_DMT_XDS, "\rXDS: %s\n",age);
|
||||
dbg_print(CCX_DMT_XDS, "\rXDS: %s\n",content);
|
||||
if (changed)
|
||||
{
|
||||
ccx_common_logging.log_ftn ("\rXDS: %s\n ",age);
|
||||
ccx_common_logging.log_ftn ("\rXDS: %s\n ",content);
|
||||
}
|
||||
ccx_common_logging.debug_ftn(CCX_DMT_DECODER_XDS, "\rXDS: %s\n",age);
|
||||
ccx_common_logging.debug_ftn(CCX_DMT_DECODER_XDS, "\rXDS: %s\n",content);
|
||||
}
|
||||
if (!a0 || // MPA
|
||||
(a0 && a1 && !Da2 && !La3) // Canadian English Language Rating
|
||||
)
|
||||
)
|
||||
{
|
||||
xdsprint(rating);
|
||||
if (changed)
|
||||
mprint ("\rXDS: %s\n ",rating);
|
||||
dbg_print(CCX_DMT_XDS, "\rXDS: %s\n",rating);
|
||||
if (ccx_decoders_xds_context.transcriptFormat.xds)
|
||||
xdsprint(sub, rating);
|
||||
if (changed)
|
||||
ccx_common_logging.log_ftn ("\rXDS: %s\n ",rating);
|
||||
ccx_common_logging.debug_ftn(CCX_DMT_DECODER_XDS, "\rXDS: %s\n",rating);
|
||||
}
|
||||
|
||||
if (changed && !supported)
|
||||
mprint ("XDS: Unsupported ContentAdvisory encoding, please submit sample.\n");
|
||||
|
||||
if (changed && !supported)
|
||||
ccx_common_logging.log_ftn ("XDS: Unsupported ContentAdvisory encoding, please submit sample.\n");
|
||||
|
||||
|
||||
}
|
||||
|
||||
int xds_do_current_and_future ()
|
||||
int xds_do_current_and_future (struct cc_subtitle *sub)
|
||||
{
|
||||
int was_proc=0;
|
||||
|
||||
char *str = malloc(1024);
|
||||
char *tstr = NULL;
|
||||
int str_len = 1024;
|
||||
|
||||
switch (cur_xds_packet_type)
|
||||
{
|
||||
case XDS_TYPE_PIN_START_TIME:
|
||||
@@ -551,21 +600,21 @@ int xds_do_current_and_future ()
|
||||
current_xds_month=month;
|
||||
}
|
||||
|
||||
dbg_print(CCX_DMT_XDS, "PIN (Start Time): %s %02d-%02d %02d:%02d\n",
|
||||
ccx_common_logging.debug_ftn(CCX_DMT_DECODER_XDS, "PIN (Start Time): %s %02d-%02d %02d:%02d\n",
|
||||
(cur_xds_packet_class==XDS_CLASS_CURRENT?"Current":"Future"),
|
||||
date,month,hour,min);
|
||||
xdsprint ( "PIN (Start Time): %s %02d-%02d %02d:%02d\n",
|
||||
if (ccx_decoders_xds_context.transcriptFormat.xds)
|
||||
xdsprint (sub, "PIN (Start Time): %s %02d-%02d %02d:%02d\n",
|
||||
(cur_xds_packet_class==XDS_CLASS_CURRENT?"Current":"Future"),
|
||||
date,month,hour,min);
|
||||
|
||||
if (!xds_start_time_shown && cur_xds_packet_class==XDS_CLASS_CURRENT)
|
||||
{
|
||||
mprint ("\rXDS: Program changed.\n");
|
||||
mprint ("XDS program start time (DD/MM HH:MM) %02d-%02d %02d:%02d\n",date,month,hour,min);
|
||||
activity_xds_program_identification_number (current_xds_min,
|
||||
current_xds_hour, current_xds_date, current_xds_month);
|
||||
ccx_common_logging.log_ftn("\rXDS: Program changed.\n");
|
||||
ccx_common_logging.log_ftn ("XDS program start time (DD/MM HH:MM) %02d-%02d %02d:%02d\n",date,month,hour,min);
|
||||
ccx_common_logging.gui_ftn(CCX_COMMON_LOGGING_GUI_XDS_PROGRAM_ID_NR, current_xds_min, current_xds_hour, current_xds_date, current_xds_month);
|
||||
xds_start_time_shown=1;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case XDS_TYPE_LENGH_AND_CURRENT_TIME:
|
||||
@@ -575,35 +624,38 @@ int xds_do_current_and_future ()
|
||||
break;
|
||||
int min=cur_xds_payload[2] & 0x3f; // 6 bits
|
||||
int hour = cur_xds_payload[3] & 0x1f; // 5 bits
|
||||
if (!xds_program_length_shown)
|
||||
mprint ("\rXDS: Program length (HH:MM): %02d:%02d ",hour,min);
|
||||
if (!xds_program_length_shown)
|
||||
ccx_common_logging.log_ftn ("\rXDS: Program length (HH:MM): %02d:%02d ",hour,min);
|
||||
else
|
||||
dbg_print(CCX_DMT_XDS, "\rXDS: Program length (HH:MM): %02d:%02d ",hour,min);
|
||||
ccx_common_logging.debug_ftn(CCX_DMT_DECODER_XDS, "\rXDS: Program length (HH:MM): %02d:%02d ",hour,min);
|
||||
|
||||
xdsprint("Program length (HH:MM): %02d:%02d ",hour,min);
|
||||
if (ccx_decoders_xds_context.transcriptFormat.xds)
|
||||
xdsprint(sub, "Program length (HH:MM): %02d:%02d ",hour,min);
|
||||
|
||||
if (cur_xds_payload_length>6) // Next two bytes (optional) available
|
||||
{
|
||||
int el_min=cur_xds_payload[4] & 0x3f; // 6 bits
|
||||
int el_hour = cur_xds_payload[5] & 0x1f; // 5 bits
|
||||
if (!xds_program_length_shown)
|
||||
mprint ("Elapsed (HH:MM): %02d:%02d",el_hour,el_min);
|
||||
ccx_common_logging.log_ftn ("Elapsed (HH:MM): %02d:%02d",el_hour,el_min);
|
||||
else
|
||||
dbg_print(CCX_DMT_XDS, "Elapsed (HH:MM): %02d:%02d",el_hour,el_min);
|
||||
xdsprint("Elapsed (HH:MM): %02d:%02d",el_hour,el_min);
|
||||
ccx_common_logging.debug_ftn(CCX_DMT_DECODER_XDS, "Elapsed (HH:MM): %02d:%02d",el_hour,el_min);
|
||||
if (ccx_decoders_xds_context.transcriptFormat.xds)
|
||||
xdsprint(sub, "Elapsed (HH:MM): %02d:%02d",el_hour,el_min);
|
||||
|
||||
}
|
||||
if (cur_xds_payload_length>8) // Next two bytes (optional) available
|
||||
{
|
||||
int el_sec=cur_xds_payload[6] & 0x3f; // 6 bits
|
||||
int el_sec=cur_xds_payload[6] & 0x3f; // 6 bits
|
||||
if (!xds_program_length_shown)
|
||||
dbg_print(CCX_DMT_XDS, ":%02d",el_sec);
|
||||
xdsprint("Elapsed (SS) :%02d",el_sec);
|
||||
ccx_common_logging.debug_ftn(CCX_DMT_DECODER_XDS, ":%02d",el_sec);
|
||||
if (ccx_decoders_xds_context.transcriptFormat.xds)
|
||||
xdsprint(sub, "Elapsed (SS) :%02d",el_sec);
|
||||
}
|
||||
if (!xds_program_length_shown)
|
||||
printf ("\n");
|
||||
ccx_common_logging.log_ftn("\n");
|
||||
else
|
||||
dbg_print(CCX_DMT_XDS, "\n");
|
||||
ccx_common_logging.debug_ftn(CCX_DMT_DECODER_XDS, "\n");
|
||||
xds_program_length_shown=1;
|
||||
}
|
||||
break;
|
||||
@@ -615,21 +667,22 @@ int xds_do_current_and_future ()
|
||||
for (i=2;i<cur_xds_payload_length-1;i++)
|
||||
xds_program_name[i-2]=cur_xds_payload[i];
|
||||
xds_program_name[i-2]=0;
|
||||
dbg_print(CCX_DMT_XDS, "\rXDS Program name: %s\n",xds_program_name);
|
||||
xdsprint("Program name: %s",xds_program_name);
|
||||
if (cur_xds_packet_class==XDS_CLASS_CURRENT &&
|
||||
ccx_common_logging.debug_ftn(CCX_DMT_DECODER_XDS, "\rXDS Program name: %s\n",xds_program_name);
|
||||
if (ccx_decoders_xds_context.transcriptFormat.xds)
|
||||
xdsprint(sub, "Program name: %s",xds_program_name);
|
||||
if (cur_xds_packet_class==XDS_CLASS_CURRENT &&
|
||||
strcmp (xds_program_name, current_xds_program_name)) // Change of program
|
||||
{
|
||||
if (!ccx_options.gui_mode_reports)
|
||||
mprint ("\rXDS Notice: Program is now %s\n", xds_program_name);
|
||||
strcpy (current_xds_program_name,xds_program_name);
|
||||
activity_xds_program_name (xds_program_name);
|
||||
//if (!ccx_options.gui_mode_reports)
|
||||
ccx_common_logging.log_ftn ("\rXDS Notice: Program is now %s\n", xds_program_name);
|
||||
strncpy (current_xds_program_name,xds_program_name, 33);
|
||||
ccx_common_logging.gui_ftn(CCX_COMMON_LOGGING_GUI_XDS_PROGRAM_NAME, xds_program_name);
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case XDS_TYPE_PROGRAM_TYPE:
|
||||
was_proc=1;
|
||||
was_proc=1;
|
||||
if (cur_xds_payload_length<5) // We need 2 data bytes
|
||||
break;
|
||||
if (current_program_type_reported)
|
||||
@@ -644,55 +697,58 @@ int xds_do_current_and_future ()
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!(ccx_options.debug_mask & CCX_DMT_XDS) && current_program_type_reported &&
|
||||
ccx_options.transcript_settings.xds == 0)
|
||||
//ccx_options.export_xds==0)
|
||||
if (!(ccx_common_logging.debug_mask & CCX_DMT_DECODER_XDS) && current_program_type_reported &&
|
||||
ccx_decoders_xds_context.transcriptFormat.xds == 0){
|
||||
break;
|
||||
}
|
||||
memcpy (current_xds_program_type,cur_xds_payload,cur_xds_payload_length);
|
||||
current_xds_program_type[cur_xds_payload_length]=0;
|
||||
if (!current_program_type_reported)
|
||||
mprint ("\rXDS Program Type: ");
|
||||
xds_write_transcript_line_prefix(wbxdsout);
|
||||
if (wbxdsout && wbxdsout->fh!=-1)
|
||||
fdprintf (wbxdsout->fh,"Program type ");
|
||||
ccx_common_logging.log_ftn ("\rXDS Program Type: ");
|
||||
|
||||
*str = '\0';
|
||||
tstr = str;
|
||||
for (int i=2;i<cur_xds_payload_length - 1; i++)
|
||||
{
|
||||
{
|
||||
if (cur_xds_payload[i]==0) // Padding
|
||||
continue;
|
||||
continue;
|
||||
if (!current_program_type_reported)
|
||||
mprint ("[%02X-", cur_xds_payload[i]);
|
||||
if (wbxdsout && wbxdsout->fh!=-1)
|
||||
ccx_common_logging.log_ftn ("[%02X-", cur_xds_payload[i]);
|
||||
if (ccx_decoders_xds_context.transcriptFormat.xds)
|
||||
{
|
||||
if (cur_xds_payload[i]>=0x20 && cur_xds_payload[i]<0x7F)
|
||||
fdprintf (wbxdsout->fh,"[%s] ",XDSProgramTypes[cur_xds_payload[i]-0x20]);
|
||||
{
|
||||
snprintf(tstr,str_len - (tstr - str),"[%s] ",XDSProgramTypes[cur_xds_payload[i]-0x20]);
|
||||
tstr += strlen(tstr);
|
||||
}
|
||||
}
|
||||
if (!current_program_type_reported)
|
||||
{
|
||||
if (cur_xds_payload[i]>=0x20 && cur_xds_payload[i]<0x7F)
|
||||
mprint ("%s",XDSProgramTypes[cur_xds_payload[i]-0x20]);
|
||||
ccx_common_logging.log_ftn ("%s",XDSProgramTypes[cur_xds_payload[i]-0x20]);
|
||||
else
|
||||
mprint ("ILLEGAL VALUE");
|
||||
mprint ("] ");
|
||||
ccx_common_logging.log_ftn ("ILLEGAL VALUE");
|
||||
ccx_common_logging.log_ftn ("] ");
|
||||
}
|
||||
}
|
||||
xds_write_transcript_line_suffix(wbxdsout);
|
||||
}
|
||||
if (ccx_decoders_xds_context.transcriptFormat.xds)
|
||||
xdsprint(sub,"Program type %s",str);
|
||||
if (!current_program_type_reported)
|
||||
mprint ("\n");
|
||||
ccx_common_logging.log_ftn ("\n");
|
||||
current_program_type_reported=1;
|
||||
break;
|
||||
case XDS_TYPE_CONTENT_ADVISORY:
|
||||
was_proc=1;
|
||||
break;
|
||||
case XDS_TYPE_CONTENT_ADVISORY:
|
||||
was_proc=1;
|
||||
if (cur_xds_payload_length<5) // We need 2 data bytes
|
||||
break;
|
||||
xds_do_content_advisory (cur_xds_payload[2],cur_xds_payload[3]);
|
||||
xds_do_content_advisory (sub, cur_xds_payload[2],cur_xds_payload[3]);
|
||||
break;
|
||||
case XDS_TYPE_AUDIO_SERVICES:
|
||||
case XDS_TYPE_AUDIO_SERVICES:
|
||||
was_proc=1; // I don't have any sample with this.
|
||||
break;
|
||||
case XDS_TYPE_CGMS:
|
||||
was_proc=1;
|
||||
xds_do_copy_generation_management_system (cur_xds_payload[2],cur_xds_payload[3]);
|
||||
was_proc=1;
|
||||
xds_do_copy_generation_management_system (sub, cur_xds_payload[2],cur_xds_payload[3]);
|
||||
break;
|
||||
case XDS_TYPE_PROGRAM_DESC_1:
|
||||
case XDS_TYPE_PROGRAM_DESC_2:
|
||||
@@ -710,31 +766,34 @@ int xds_do_current_and_future ()
|
||||
for (i=2;i<cur_xds_payload_length-1;i++)
|
||||
xds_desc[i-2]=cur_xds_payload[i];
|
||||
xds_desc[i-2]=0;
|
||||
|
||||
|
||||
if (xds_desc[0])
|
||||
{
|
||||
{
|
||||
int line_num=cur_xds_packet_type-XDS_TYPE_PROGRAM_DESC_1;
|
||||
if (strcmp (xds_desc, xds_program_description[line_num]))
|
||||
changed=1;
|
||||
if (changed)
|
||||
{
|
||||
mprint ("\rXDS description line %d: %s\n",line_num,xds_desc);
|
||||
ccx_common_logging.log_ftn ("\rXDS description line %d: %s\n",line_num,xds_desc);
|
||||
strcpy (xds_program_description[line_num], xds_desc);
|
||||
}
|
||||
else
|
||||
{
|
||||
dbg_print(CCX_DMT_XDS, "\rXDS description line %d: %s\n",line_num,xds_desc);
|
||||
{
|
||||
ccx_common_logging.debug_ftn(CCX_DMT_DECODER_XDS, "\rXDS description line %d: %s\n",line_num,xds_desc);
|
||||
}
|
||||
xdsprint("XDS description line %d: %s",line_num,xds_desc);
|
||||
activity_xds_program_description (line_num, xds_desc);
|
||||
if (ccx_decoders_xds_context.transcriptFormat.xds)
|
||||
xdsprint(sub, "XDS description line %d: %s",line_num,xds_desc);
|
||||
ccx_common_logging.gui_ftn(CCX_COMMON_LOGGING_GUI_XDS_PROGRAM_DESCRIPTION, line_num, xds_desc);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
free(str);
|
||||
return was_proc;
|
||||
}
|
||||
|
||||
int xds_do_channel ()
|
||||
int xds_do_channel (struct cc_subtitle *sub)
|
||||
{
|
||||
int was_proc=0;
|
||||
switch (cur_xds_packet_type)
|
||||
@@ -746,49 +805,51 @@ int xds_do_channel ()
|
||||
for (i=2;i<cur_xds_payload_length-1;i++)
|
||||
xds_network_name[i-2]=cur_xds_payload[i];
|
||||
xds_network_name[i-2]=0;
|
||||
dbg_print(CCX_DMT_XDS, "XDS Network name: %s\n",xds_network_name);
|
||||
xdsprint ("Network: %s",xds_network_name);
|
||||
ccx_common_logging.debug_ftn(CCX_DMT_DECODER_XDS, "XDS Network name: %s\n",xds_network_name);
|
||||
if (ccx_decoders_xds_context.transcriptFormat.xds)
|
||||
xdsprint (sub, "Network: %s",xds_network_name);
|
||||
if (strcmp (xds_network_name, current_xds_network_name)) // Change of station
|
||||
{
|
||||
mprint ("XDS Notice: Network is now %s\n", xds_network_name);
|
||||
ccx_common_logging.log_ftn ("XDS Notice: Network is now %s\n", xds_network_name);
|
||||
strcpy (current_xds_network_name,xds_network_name);
|
||||
}
|
||||
break;
|
||||
case XDS_TYPE_CALL_LETTERS_AND_CHANNEL:
|
||||
{
|
||||
was_proc=1;
|
||||
char xds_call_letters[33];
|
||||
if (cur_xds_payload_length<7) // We need 4-6 data bytes
|
||||
break;
|
||||
char xds_call_letters[7];
|
||||
if (cur_xds_payload_length != 7 && cur_xds_payload_length != 9) // We need 4-6 data bytes
|
||||
break;
|
||||
for (i=2;i<cur_xds_payload_length-1;i++)
|
||||
{
|
||||
if (cur_xds_payload)
|
||||
xds_call_letters[i-2]=cur_xds_payload[i];
|
||||
}
|
||||
xds_call_letters[i-2]=0;
|
||||
dbg_print(CCX_DMT_XDS, "XDS Network call letters: %s\n",xds_call_letters);
|
||||
xdsprint ("Call Letters: %s",xds_call_letters);
|
||||
if (strcmp (xds_call_letters, current_xds_call_letters)) // Change of station
|
||||
xds_call_letters[i-2]=0;
|
||||
ccx_common_logging.debug_ftn(CCX_DMT_DECODER_XDS, "XDS Network call letters: %s\n",xds_call_letters);
|
||||
if (ccx_decoders_xds_context.transcriptFormat.xds)
|
||||
xdsprint (sub, "Call Letters: %s",xds_call_letters);
|
||||
if (strncmp (xds_call_letters, current_xds_call_letters, 7)) // Change of station
|
||||
{
|
||||
mprint ("XDS Notice: Network call letters now %s\n", xds_call_letters);
|
||||
strcpy (current_xds_call_letters,xds_call_letters);
|
||||
activity_xds_network_call_letters (current_xds_call_letters);
|
||||
ccx_common_logging.log_ftn ("XDS Notice: Network call letters now %s\n", xds_call_letters);
|
||||
strncpy (current_xds_call_letters, xds_call_letters, 7);
|
||||
ccx_common_logging.gui_ftn(CCX_COMMON_LOGGING_GUI_XDS_CALL_LETTERS, current_xds_call_letters);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case XDS_TYPE_TSID:
|
||||
// According to CEA-608, data here (4 bytes) are used to identify the
|
||||
// According to CEA-608, data here (4 bytes) are used to identify the
|
||||
// "originating analog licensee". No interesting data for us.
|
||||
was_proc=1;
|
||||
if (cur_xds_payload_length<7) // We need 4 data bytes
|
||||
break;
|
||||
break;
|
||||
unsigned b1=(cur_xds_payload[2])&0x10; // Only low 4 bits from each byte
|
||||
unsigned b2=(cur_xds_payload[3])&0x10;
|
||||
unsigned b3=(cur_xds_payload[4])&0x10;
|
||||
unsigned b4=(cur_xds_payload[5])&0x10;
|
||||
unsigned tsid=(b4<<12) | (b3<<8) | (b2<<4) | b1;
|
||||
if (tsid)
|
||||
xdsprint ("TSID: %u",tsid);
|
||||
if (tsid && ccx_decoders_xds_context.transcriptFormat.xds)
|
||||
xdsprint (sub, "TSID: %u",tsid);
|
||||
break;
|
||||
}
|
||||
return was_proc;
|
||||
@@ -796,15 +857,16 @@ int xds_do_channel ()
|
||||
|
||||
|
||||
|
||||
int xds_do_private_data ()
|
||||
int xds_do_private_data (struct cc_subtitle *sub)
|
||||
{
|
||||
if (wbxdsout==NULL) // Only thing we can do with private data is dump it.
|
||||
char *str = malloc((cur_xds_payload_length *3) + 1);
|
||||
if (str==NULL) // Only thing we can do with private data is dump it.
|
||||
return 1;
|
||||
xds_write_transcript_line_prefix (wbxdsout);
|
||||
for (int i=2;i<cur_xds_payload_length-1;i++)
|
||||
fdprintf(wbxdsout->fh, "%02X ",cur_xds_payload[i]);
|
||||
sprintf(str, "%02X ",cur_xds_payload[i]);
|
||||
|
||||
xds_write_transcript_line_suffix (wbxdsout);
|
||||
xdsprint(sub,str);
|
||||
free(str);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -812,7 +874,7 @@ int xds_do_misc ()
|
||||
{
|
||||
int was_proc=0;
|
||||
switch (cur_xds_packet_type)
|
||||
{
|
||||
{
|
||||
case XDS_TYPE_TIME_OF_DAY:
|
||||
{
|
||||
was_proc=1;
|
||||
@@ -822,11 +884,11 @@ int xds_do_misc ()
|
||||
int hour = cur_xds_payload[3] & 0x1f; // 5 bits
|
||||
int date = cur_xds_payload[4] & 0x1f; // 5 bits
|
||||
int month = cur_xds_payload[5] & 0xf; // 4 bits
|
||||
int reset_seconds = (cur_xds_payload[5] & 0x20);
|
||||
int reset_seconds = (cur_xds_payload[5] & 0x20);
|
||||
int day_of_week = cur_xds_payload[6] & 0x7;
|
||||
int year = (cur_xds_payload[7] & 0x3f) + 1990;
|
||||
dbg_print(CCX_DMT_XDS, "Time of day: (YYYY/MM/DD) %04d/%02d/%02d (HH:SS) %02d:%02d DoW: %d Reset seconds: %d\n",
|
||||
year,month,date,hour,min, day_of_week, reset_seconds);
|
||||
ccx_common_logging.debug_ftn(CCX_DMT_DECODER_XDS, "Time of day: (YYYY/MM/DD) %04d/%02d/%02d (HH:SS) %02d:%02d DoW: %d Reset seconds: %d\n",
|
||||
year,month,date,hour,min, day_of_week, reset_seconds);
|
||||
break;
|
||||
}
|
||||
case XDS_TYPE_LOCAL_TIME_ZONE:
|
||||
@@ -837,81 +899,90 @@ int xds_do_misc ()
|
||||
// int b6 = (cur_xds_payload[2] & 0x40) >>6; // Bit 6 should always be 1
|
||||
int dst = (cur_xds_payload[2] & 0x20) >>5; // Daylight Saving Time
|
||||
int hour = cur_xds_payload[2] & 0x1f; // 5 bits
|
||||
dbg_print(CCX_DMT_XDS, "Local Time Zone: %02d DST: %d\n",
|
||||
hour, dst);
|
||||
ccx_common_logging.debug_ftn(CCX_DMT_DECODER_XDS, "Local Time Zone: %02d DST: %d\n",
|
||||
hour, dst);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
was_proc=0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return was_proc;
|
||||
}
|
||||
|
||||
void do_end_of_xds (unsigned char expected_checksum)
|
||||
void do_end_of_xds (struct cc_subtitle *sub, unsigned char expected_checksum)
|
||||
{
|
||||
if (cur_xds_buffer_idx== -1 || /* Unknown buffer, or not in use (bug) */
|
||||
!xds_buffers[cur_xds_buffer_idx].in_use)
|
||||
return;
|
||||
cur_xds_packet_class=xds_buffers[cur_xds_buffer_idx].xds_class;
|
||||
cur_xds_packet_class=xds_buffers[cur_xds_buffer_idx].xds_class;
|
||||
cur_xds_payload=xds_buffers[cur_xds_buffer_idx].bytes;
|
||||
cur_xds_payload_length=xds_buffers[cur_xds_buffer_idx].used_bytes;
|
||||
cur_xds_packet_type=cur_xds_payload[1];
|
||||
cur_xds_payload[cur_xds_payload_length++]=0x0F; // The end byte itself, added to the packet
|
||||
|
||||
|
||||
int cs=0;
|
||||
for (int i=0; i<cur_xds_payload_length;i++)
|
||||
{
|
||||
cs=cs+cur_xds_payload[i];
|
||||
cs=cs & 0x7f; // Keep 7 bits only
|
||||
int c=cur_xds_payload[i]&0x7F;
|
||||
dbg_print(CCX_DMT_XDS, "%02X - %c cs: %02X\n",
|
||||
ccx_common_logging.debug_ftn(CCX_DMT_DECODER_XDS, "%02X - %c cs: %02X\n",
|
||||
c,(c>=0x20)?c:'?', cs);
|
||||
}
|
||||
cs=(128-cs) & 0x7F; // Convert to 2's complement & discard high-order bit
|
||||
|
||||
dbg_print(CCX_DMT_XDS, "End of XDS. Class=%d (%s), size=%d Checksum OK: %d Used buffers: %d\n",
|
||||
ccx_common_logging.debug_ftn(CCX_DMT_DECODER_XDS, "End of XDS. Class=%d (%s), size=%d Checksum OK: %d Used buffers: %d\n",
|
||||
cur_xds_packet_class,XDSclasses[cur_xds_packet_class],
|
||||
cur_xds_payload_length,
|
||||
cs==expected_checksum, how_many_used());
|
||||
cs==expected_checksum, how_many_used());
|
||||
|
||||
if (cs!=expected_checksum || cur_xds_payload_length<3)
|
||||
{
|
||||
dbg_print(CCX_DMT_XDS, "Expected checksum: %02X Calculated: %02X\n", expected_checksum, cs);
|
||||
clear_xds_buffer (cur_xds_buffer_idx);
|
||||
ccx_common_logging.debug_ftn(CCX_DMT_DECODER_XDS, "Expected checksum: %02X Calculated: %02X\n", expected_checksum, cs);
|
||||
clear_xds_buffer (cur_xds_buffer_idx);
|
||||
return; // Bad packets ignored as per specs
|
||||
}
|
||||
|
||||
|
||||
int was_proc=0; /* Indicated if the packet was processed. Not processed means "code to do it doesn't exist yet", not an error. */
|
||||
|
||||
if (cur_xds_packet_type & 0x40) // Bit 6 set
|
||||
{
|
||||
cur_xds_packet_class = XDS_CLASS_OUT_OF_BAND;
|
||||
}
|
||||
|
||||
switch (cur_xds_packet_class)
|
||||
{
|
||||
case XDS_CLASS_FUTURE: // Info on future program
|
||||
if (!(ccx_options.debug_mask & CCX_DMT_XDS)) // Don't bother processing something we don't need
|
||||
if (!(ccx_common_logging.debug_mask & CCX_DMT_DECODER_XDS)) // Don't bother processing something we don't need
|
||||
{
|
||||
was_proc=1;
|
||||
break;
|
||||
break;
|
||||
}
|
||||
case XDS_CLASS_CURRENT: // Info on current program
|
||||
was_proc = xds_do_current_and_future();
|
||||
case XDS_CLASS_CURRENT: // Info on current program
|
||||
was_proc = xds_do_current_and_future(sub);
|
||||
break;
|
||||
case XDS_CLASS_CHANNEL:
|
||||
was_proc = xds_do_channel();
|
||||
was_proc = xds_do_channel(sub);
|
||||
break;
|
||||
|
||||
|
||||
case XDS_CLASS_MISC:
|
||||
was_proc = xds_do_misc();
|
||||
break;
|
||||
case XDS_CLASS_PRIVATE: // CEA-608:
|
||||
// The Private Data Class is for use in any closed system for whatever that
|
||||
// system wishes. It shall not be defined by this standard now or in the future.
|
||||
was_proc=xds_do_private_data();
|
||||
// The Private Data Class is for use in any closed system for whatever that
|
||||
// system wishes. It shall not be defined by this standard now or in the future.
|
||||
if (ccx_decoders_xds_context.transcriptFormat.xds)
|
||||
was_proc=xds_do_private_data(sub);
|
||||
break;
|
||||
case XDS_CLASS_OUT_OF_BAND:
|
||||
ccx_common_logging.debug_ftn(CCX_DMT_DECODER_XDS, "Out-of-band data, ignored.");
|
||||
was_proc = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
if (!was_proc)
|
||||
{
|
||||
mprint ("Note: We found an currently unsupported XDS packet.\n");
|
||||
ccx_common_logging.log_ftn ("Note: We found an currently unsupported XDS packet.\n");
|
||||
}
|
||||
clear_xds_buffer (cur_xds_buffer_idx);
|
||||
|
||||
15
src/lib_ccx/ccx_decoders_xds.h
Normal file
15
src/lib_ccx/ccx_decoders_xds.h
Normal file
@@ -0,0 +1,15 @@
|
||||
#ifndef _XDS_H
|
||||
#define _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);
|
||||
|
||||
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);
|
||||
|
||||
#endif
|
||||
601
src/lib_ccx/ccx_encoders_common.c
Normal file
601
src/lib_ccx/ccx_encoders_common.c
Normal file
@@ -0,0 +1,601 @@
|
||||
#include "ccx_decoders_common.h"
|
||||
#include "ccx_encoders_common.h"
|
||||
#include "spupng_encoder.h"
|
||||
#include "608_spupng.h"
|
||||
#include "utility.h"
|
||||
#include "ocr.h"
|
||||
#include "ccx_decoders_608.h"
|
||||
#include "ccx_decoders_xds.h"
|
||||
#include "ccx_common_option.h"
|
||||
|
||||
// These are the default settings for plain transcripts. No times, no CC or caption mode, and no XDS.
|
||||
ccx_encoders_transcript_format ccx_encoders_default_transcript_settings =
|
||||
{
|
||||
.showStartTime = 0,
|
||||
.showEndTime = 0,
|
||||
.showMode = 0,
|
||||
.showCC = 0,
|
||||
.relativeTimestamp = 1,
|
||||
.xds = 0,
|
||||
.useColors = 1,
|
||||
.isFinal = 0
|
||||
};
|
||||
|
||||
static const char *sami_header= // TODO: Revise the <!-- comments
|
||||
"<SAMI>\n\
|
||||
<HEAD>\n\
|
||||
<STYLE TYPE=\"text/css\">\n\
|
||||
<!--\n\
|
||||
P {margin-left: 16pt; margin-right: 16pt; margin-bottom: 16pt; margin-top: 16pt;\n\
|
||||
text-align: center; font-size: 18pt; font-family: arial; font-weight: bold; color: #f0f0f0;}\n\
|
||||
.UNKNOWNCC {Name:Unknown; lang:en-US; SAMIType:CC;}\n\
|
||||
-->\n\
|
||||
</STYLE>\n\
|
||||
</HEAD>\n\n\
|
||||
<BODY>\n";
|
||||
|
||||
static const char *smptett_header = "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n"
|
||||
"<tt xmlns:ttm=\"http://www.w3.org/ns/ttml#metadata\" xmlns:tts=\"http://www.w3.org/ns/ttml#styling\" xmlns=\"http://www.w3.org/ns/ttml\" xml:lang=\"en\">\n"
|
||||
" <head>\n"
|
||||
" <styling>\n"
|
||||
" <style xml:id=\"speakerStyle\" tts:fontFamily=\"proportionalSansSerif\" tts:fontSize=\"150%\" tts:textAlign=\"center\" tts:displayAlign=\"center\" tts:color=\"white\" tts:textOutline=\"black 1px\"/>\n"
|
||||
" </styling>\n"
|
||||
" <layout>\n"
|
||||
" <region xml:id=\"speaker\" tts:origin=\"10% 80%\" tts:extent=\"80% 10%\" style=\"speakerStyle\"/>\n"
|
||||
" </layout>\n"
|
||||
" </head>\n"
|
||||
" <body>\n"
|
||||
" <div>\n";
|
||||
void write_subtitle_file_footer(struct encoder_ctx *ctx,struct ccx_s_write *out)
|
||||
{
|
||||
int used;
|
||||
switch (ccx_options.write_format)
|
||||
{
|
||||
case CCX_OF_SAMI:
|
||||
sprintf ((char *) str,"</BODY></SAMI>\n");
|
||||
if (ccx_options.encoding!=CCX_ENC_UNICODE)
|
||||
{
|
||||
dbg_print(CCX_DMT_DECODER_608, "\r%s\n", str);
|
||||
}
|
||||
used=encode_line (ctx->buffer,(unsigned char *) str);
|
||||
write(out->fh, ctx->buffer, used);
|
||||
break;
|
||||
case CCX_OF_SMPTETT:
|
||||
sprintf ((char *) str," </div>\n </body>\n</tt>\n");
|
||||
if (ccx_options.encoding!=CCX_ENC_UNICODE)
|
||||
{
|
||||
dbg_print(CCX_DMT_DECODER_608, "\r%s\n", str);
|
||||
}
|
||||
used=encode_line (ctx->buffer,(unsigned char *) str);
|
||||
write (out->fh, ctx->buffer,used);
|
||||
break;
|
||||
case CCX_OF_SPUPNG:
|
||||
write_spumux_footer(out);
|
||||
break;
|
||||
default: // Nothing to do, no footer on this format
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void write_subtitle_file_header(struct encoder_ctx *ctx,struct ccx_s_write *out)
|
||||
{
|
||||
int used;
|
||||
switch (ccx_options.write_format)
|
||||
{
|
||||
case CCX_OF_SRT: // Subrip subtitles have no header
|
||||
break;
|
||||
case CCX_OF_SAMI: // This header brought to you by McPoodle's CCASDI
|
||||
//fprintf_encoded (wb->fh, sami_header);
|
||||
REQUEST_BUFFER_CAPACITY(ctx,strlen (sami_header)*3);
|
||||
used=encode_line (ctx->buffer,(unsigned char *) sami_header);
|
||||
write (out->fh, ctx->buffer,used);
|
||||
break;
|
||||
case CCX_OF_SMPTETT: // This header brought to you by McPoodle's CCASDI
|
||||
//fprintf_encoded (wb->fh, sami_header);
|
||||
REQUEST_BUFFER_CAPACITY(ctx,strlen (smptett_header)*3);
|
||||
used=encode_line (ctx->buffer,(unsigned char *) smptett_header);
|
||||
write(out->fh, ctx->buffer, used);
|
||||
break;
|
||||
case CCX_OF_RCWT: // Write header
|
||||
if (ccx_options.teletext_mode == CCX_TXT_IN_USE)
|
||||
rcwt_header[7] = 2; // sets file format version
|
||||
|
||||
if (ccx_options.send_to_srv)
|
||||
net_send_header(rcwt_header, sizeof(rcwt_header));
|
||||
else
|
||||
write(out->fh, rcwt_header, sizeof(rcwt_header));
|
||||
|
||||
break;
|
||||
case CCX_OF_SPUPNG:
|
||||
write_spumux_header(out);
|
||||
break;
|
||||
case CCX_OF_TRANSCRIPT: // No header. Fall thru
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void write_cc_line_as_transcript2(struct eia608_screen *data, struct encoder_ctx *context, int line_number)
|
||||
{
|
||||
unsigned h1,m1,s1,ms1;
|
||||
unsigned h2,m2,s2,ms2;
|
||||
LLONG start_time = data->start_time;
|
||||
LLONG end_time = data->end_time;
|
||||
if (ccx_options.sentence_cap)
|
||||
{
|
||||
capitalize (line_number,data);
|
||||
correct_case(line_number,data);
|
||||
}
|
||||
int length = get_decoder_line_basic (subline, line_number, data,ccx_options.trim_subs,ccx_options.encoding);
|
||||
if (ccx_options.encoding!=CCX_ENC_UNICODE)
|
||||
{
|
||||
dbg_print(CCX_DMT_DECODER_608, "\r");
|
||||
dbg_print(CCX_DMT_DECODER_608, "%s\n",subline);
|
||||
}
|
||||
if (length>0)
|
||||
{
|
||||
if (data->start_time == -1)
|
||||
{
|
||||
// CFS: Means that the line has characters but we don't have a timestamp for the first one. Since the timestamp
|
||||
// is set for example by the write_char function, it possible that we don't have one in empty lines (unclear)
|
||||
// For now, let's not consider this a bug as before and just return.
|
||||
// fatal (EXIT_BUG_BUG, "Bug in timedtranscript (ts_start_of_current_line==-1). Please report.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (ccx_options.transcript_settings.showStartTime){
|
||||
char buf1[80];
|
||||
if (ccx_options.transcript_settings.relativeTimestamp){
|
||||
millis_to_date(start_time + context->subs_delay, buf1);
|
||||
fdprintf(context->out->fh, "%s|", buf1);
|
||||
}
|
||||
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(buf1, sizeof(buf1), "%Y%m%d%H%M%S", start_time_struct);
|
||||
fdprintf(context->out->fh, "%s%c%03d|", buf1,ccx_options.millis_separator,start_time_dec);
|
||||
}
|
||||
}
|
||||
|
||||
if (ccx_options.transcript_settings.showEndTime){
|
||||
char buf2[80];
|
||||
if (ccx_options.transcript_settings.relativeTimestamp){
|
||||
millis_to_date(end_time, buf2);
|
||||
fdprintf(context->out->fh, "%s|", buf2);
|
||||
}
|
||||
else {
|
||||
mstotime(get_fts() + context->subs_delay, &h2, &m2, &s2, &ms2);
|
||||
time_t end_time_int = (end_time + context->subs_delay) / 1000;
|
||||
int end_time_dec = (end_time + context->subs_delay) % 1000;
|
||||
struct tm *end_time_struct = gmtime(&end_time_int);
|
||||
strftime(buf2, sizeof(buf2), "%Y%m%d%H%M%S", end_time_struct);
|
||||
fdprintf(context->out->fh, "%s%c%03d|", buf2,ccx_options.millis_separator,end_time_dec);
|
||||
}
|
||||
}
|
||||
|
||||
if (ccx_options.transcript_settings.showCC){
|
||||
fdprintf(context->out->fh, "CC%d|", data->my_field == 1 ? data->channel : data->channel + 2); // Data from field 2 is CC3 or 4
|
||||
}
|
||||
if (ccx_options.transcript_settings.showMode){
|
||||
const char *mode = "???";
|
||||
switch (data->mode)
|
||||
{
|
||||
case MODE_POPON:
|
||||
mode = "POP";
|
||||
break;
|
||||
case MODE_FAKE_ROLLUP_1:
|
||||
mode = "RU1";
|
||||
break;
|
||||
case MODE_ROLLUP_2:
|
||||
mode = "RU2";
|
||||
break;
|
||||
case MODE_ROLLUP_3:
|
||||
mode = "RU3";
|
||||
break;
|
||||
case MODE_ROLLUP_4:
|
||||
mode = "RU4";
|
||||
break;
|
||||
case MODE_TEXT:
|
||||
mode = "TXT";
|
||||
break;
|
||||
case MODE_PAINTON:
|
||||
mode = "PAI";
|
||||
break;
|
||||
}
|
||||
fdprintf(context->out->fh, "%s|", mode);
|
||||
}
|
||||
|
||||
write(context->out->fh, subline, length);
|
||||
write(context->out->fh, encoded_crlf, encoded_crlf_length);
|
||||
}
|
||||
// fprintf (wb->fh,encoded_crlf);
|
||||
}
|
||||
|
||||
int write_cc_buffer_as_transcript2(struct eia608_screen *data, struct encoder_ctx *context)
|
||||
{
|
||||
int wrote_something = 0;
|
||||
dbg_print(CCX_DMT_DECODER_608, "\n- - - TRANSCRIPT caption - - -\n");
|
||||
|
||||
for (int i=0;i<15;i++)
|
||||
{
|
||||
if (data->row_used[i])
|
||||
{
|
||||
write_cc_line_as_transcript2 (data, context, i);
|
||||
}
|
||||
wrote_something=1;
|
||||
}
|
||||
dbg_print(CCX_DMT_DECODER_608, "- - - - - - - - - - - -\r\n");
|
||||
return wrote_something;
|
||||
}
|
||||
int write_cc_bitmap_as_transcript(struct cc_subtitle *sub, struct encoder_ctx *context)
|
||||
{
|
||||
int ret = 0;
|
||||
struct cc_bitmap* rect;
|
||||
|
||||
#ifdef ENABLE_OCR
|
||||
unsigned h1,m1,s1,ms1;
|
||||
unsigned h2,m2,s2,ms2;
|
||||
#endif
|
||||
LLONG start_time, end_time;
|
||||
|
||||
if (context->prev_start != -1 && (sub->flags & SUB_EOD_MARKER))
|
||||
{
|
||||
start_time = context->prev_start + context->subs_delay;
|
||||
end_time = sub->start_time - 1;
|
||||
}
|
||||
else if ( !(sub->flags & SUB_EOD_MARKER))
|
||||
{
|
||||
start_time = sub->start_time + context->subs_delay;
|
||||
end_time = sub->end_time - 1;
|
||||
}
|
||||
|
||||
if(sub->nb_data == 0 )
|
||||
return ret;
|
||||
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;
|
||||
token = strtok(rect[0].ocr_text ,"\r\n");
|
||||
while (token)
|
||||
{
|
||||
|
||||
if (ccx_options.transcript_settings.showStartTime)
|
||||
{
|
||||
char buf1[80];
|
||||
if (ccx_options.transcript_settings.relativeTimestamp)
|
||||
{
|
||||
millis_to_date(start_time + context->subs_delay, buf1);
|
||||
fdprintf(context->out->fh, "%s|", buf1);
|
||||
}
|
||||
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(buf1, sizeof(buf1), "%Y%m%d%H%M%S", start_time_struct);
|
||||
fdprintf(context->out->fh, "%s%c%03d|", buf1,ccx_options.millis_separator,start_time_dec);
|
||||
}
|
||||
}
|
||||
|
||||
if (ccx_options.transcript_settings.showEndTime)
|
||||
{
|
||||
char buf2[80];
|
||||
if (ccx_options.transcript_settings.relativeTimestamp)
|
||||
{
|
||||
millis_to_date(end_time, buf2);
|
||||
fdprintf(context->out->fh, "%s|", buf2);
|
||||
}
|
||||
else
|
||||
{
|
||||
mstotime(get_fts() + context->subs_delay, &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(buf2, sizeof(buf2), "%Y%m%d%H%M%S", end_time_struct);
|
||||
fdprintf(context->out->fh, "%s%c%03d|", buf2,ccx_options.millis_separator,end_time_dec);
|
||||
}
|
||||
}
|
||||
if (ccx_options.transcript_settings.showCC)
|
||||
{
|
||||
fdprintf(context->out->fh,"%s|",language[sub->lang_index]);
|
||||
}
|
||||
if (ccx_options.transcript_settings.showMode)
|
||||
{
|
||||
fdprintf(context->out->fh,"DVB|");
|
||||
}
|
||||
fdprintf(context->out->fh,"%s\n",token);
|
||||
token = strtok(NULL,"\r\n");
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
sub->nb_data = 0;
|
||||
freep(&sub->data);
|
||||
return ret;
|
||||
|
||||
}
|
||||
void try_to_add_end_credits(struct encoder_ctx *context, struct ccx_s_write *out)
|
||||
{
|
||||
LLONG window, length, st, end;
|
||||
if (out->fh == -1)
|
||||
return;
|
||||
window=get_fts()-context->last_displayed_subs_ms-1;
|
||||
if (window<ccx_options.endcreditsforatleast.time_in_ms) // Won't happen, window is too short
|
||||
return;
|
||||
length=ccx_options.endcreditsforatmost.time_in_ms > window ?
|
||||
window : ccx_options.endcreditsforatmost.time_in_ms;
|
||||
|
||||
st=get_fts()-length-1;
|
||||
end=get_fts();
|
||||
|
||||
switch (ccx_options.write_format)
|
||||
{
|
||||
case CCX_OF_SRT:
|
||||
write_stringz_as_srt(ccx_options.end_credits_text, context, st, end);
|
||||
break;
|
||||
case CCX_OF_SAMI:
|
||||
write_stringz_as_sami(ccx_options.end_credits_text, context, st, end);
|
||||
break;
|
||||
case CCX_OF_SMPTETT:
|
||||
write_stringz_as_smptett(ccx_options.end_credits_text, context, st, end);
|
||||
break ;
|
||||
default:
|
||||
// Do nothing for the rest
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void try_to_add_start_credits(struct encoder_ctx *context,LLONG start_ms)
|
||||
{
|
||||
LLONG st, end, window, length;
|
||||
LLONG l = start_ms + context->subs_delay;
|
||||
// We have a windows from last_displayed_subs_ms to l - we need to see if it fits
|
||||
|
||||
if (l<ccx_options.startcreditsnotbefore.time_in_ms) // Too early
|
||||
return;
|
||||
|
||||
if (context->last_displayed_subs_ms+1 > ccx_options.startcreditsnotafter.time_in_ms) // Too late
|
||||
return;
|
||||
|
||||
st = ccx_options.startcreditsnotbefore.time_in_ms>(context->last_displayed_subs_ms+1) ?
|
||||
ccx_options.startcreditsnotbefore.time_in_ms : (context->last_displayed_subs_ms+1); // When would credits actually start
|
||||
|
||||
end = ccx_options.startcreditsnotafter.time_in_ms<(l-1) ?
|
||||
ccx_options.startcreditsnotafter.time_in_ms : (l-1);
|
||||
|
||||
window = end-st; // Allowable time in MS
|
||||
|
||||
if (ccx_options.startcreditsforatleast.time_in_ms>window) // Window is too short
|
||||
return;
|
||||
|
||||
length=ccx_options.startcreditsforatmost.time_in_ms > window ?
|
||||
window : ccx_options.startcreditsforatmost.time_in_ms;
|
||||
|
||||
dbg_print(CCX_DMT_VERBOSE, "Last subs: %lld Current position: %lld\n",
|
||||
context->last_displayed_subs_ms, l);
|
||||
dbg_print(CCX_DMT_VERBOSE, "Not before: %lld Not after: %lld\n",
|
||||
ccx_options.startcreditsnotbefore.time_in_ms,
|
||||
ccx_options.startcreditsnotafter.time_in_ms);
|
||||
dbg_print(CCX_DMT_VERBOSE, "Start of window: %lld End of window: %lld\n",st,end);
|
||||
|
||||
if (window>length+2)
|
||||
{
|
||||
// Center in time window
|
||||
LLONG pad=window-length;
|
||||
st+=(pad/2);
|
||||
}
|
||||
end=st+length;
|
||||
switch (ccx_options.write_format)
|
||||
{
|
||||
case CCX_OF_SRT:
|
||||
write_stringz_as_srt(ccx_options.start_credits_text,context,st,end);
|
||||
break;
|
||||
case CCX_OF_SAMI:
|
||||
write_stringz_as_sami(ccx_options.start_credits_text, context, st, end);
|
||||
break;
|
||||
case CCX_OF_SMPTETT:
|
||||
write_stringz_as_smptett(ccx_options.start_credits_text, context, st, end);
|
||||
break;
|
||||
default:
|
||||
// Do nothing for the rest
|
||||
break;
|
||||
}
|
||||
context->startcredits_displayed=1;
|
||||
return;
|
||||
|
||||
|
||||
}
|
||||
int init_encoder(struct encoder_ctx *ctx,struct ccx_s_write *out)
|
||||
{
|
||||
ctx->buffer = (unsigned char *) malloc (INITIAL_ENC_BUFFER_CAPACITY);
|
||||
if (ctx->buffer==NULL)
|
||||
return -1;
|
||||
ctx->capacity=INITIAL_ENC_BUFFER_CAPACITY;
|
||||
ctx->srt_counter = 0;
|
||||
ctx->out = out;
|
||||
/** used in case of SUB_EOD_MARKER */
|
||||
ctx->prev_start = -1;
|
||||
write_subtitle_file_header(ctx,out);
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
void set_encoder_last_displayed_subs_ms(struct encoder_ctx *ctx, LLONG last_displayed_subs_ms)
|
||||
{
|
||||
ctx->last_displayed_subs_ms = last_displayed_subs_ms;
|
||||
}
|
||||
void set_encoder_subs_delay(struct encoder_ctx *ctx, LLONG subs_delay)
|
||||
{
|
||||
ctx->subs_delay = subs_delay;
|
||||
}
|
||||
void set_encoder_startcredits_displayed(struct encoder_ctx *ctx, int startcredits_displayed)
|
||||
{
|
||||
ctx->startcredits_displayed = startcredits_displayed;
|
||||
}
|
||||
void dinit_encoder(struct encoder_ctx *ctx)
|
||||
{
|
||||
|
||||
if (ccx_options.end_credits_text!=NULL)
|
||||
try_to_add_end_credits(ctx,ctx->out);
|
||||
write_subtitle_file_footer(ctx,ctx->out);
|
||||
freep(&ctx->buffer);
|
||||
ctx->capacity = 0;
|
||||
}
|
||||
|
||||
int encode_sub(struct encoder_ctx *context, struct cc_subtitle *sub)
|
||||
{
|
||||
int wrote_something = 0;
|
||||
|
||||
if (sub->type == CC_608)
|
||||
{
|
||||
struct eia608_screen *data = NULL;
|
||||
for(data = sub->data; sub->nb_data ; sub->nb_data--,data++)
|
||||
{
|
||||
// Determine context based on channel. This replaces the code that was above, as this was incomplete (for cases where -12 was used for example)
|
||||
//if (ccx_options.extract!=1)
|
||||
//context++;
|
||||
if (data->my_field == 2)
|
||||
context++;
|
||||
|
||||
new_sentence=1;
|
||||
|
||||
if(data->format == SFORMAT_XDS)
|
||||
{
|
||||
xds_write_transcript_line_prefix (context->out, data->start_time, data->end_time,data->cur_xds_packet_class);
|
||||
if(data->xds_len > 0)
|
||||
write (context->out->fh, data->xds_str,data->xds_len);
|
||||
freep (&data->xds_str);
|
||||
xds_write_transcript_line_suffix (context->out);
|
||||
continue;
|
||||
}
|
||||
|
||||
if(!data->start_time)
|
||||
break;
|
||||
switch (ccx_options.write_format)
|
||||
{
|
||||
case CCX_OF_SRT:
|
||||
if (!context->startcredits_displayed && ccx_options.start_credits_text!=NULL)
|
||||
try_to_add_start_credits(context, data->start_time);
|
||||
wrote_something = write_cc_buffer_as_srt(data, context);
|
||||
break;
|
||||
case CCX_OF_SAMI:
|
||||
if (!context->startcredits_displayed && ccx_options.start_credits_text!=NULL)
|
||||
try_to_add_start_credits(context, data->start_time);
|
||||
wrote_something = write_cc_buffer_as_sami(data, context);
|
||||
break;
|
||||
case CCX_OF_SMPTETT:
|
||||
if (!context->startcredits_displayed && ccx_options.start_credits_text!=NULL)
|
||||
try_to_add_start_credits(context, data->start_time);
|
||||
wrote_something = write_cc_buffer_as_smptett(data, context);
|
||||
break;
|
||||
case CCX_OF_TRANSCRIPT:
|
||||
wrote_something = write_cc_buffer_as_transcript2(data, context);
|
||||
break;
|
||||
case CCX_OF_SPUPNG:
|
||||
wrote_something = write_cc_buffer_as_spupng(data, context);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (wrote_something)
|
||||
context->last_displayed_subs_ms=get_fts() + context->subs_delay;
|
||||
|
||||
if (ccx_options.gui_mode_reports)
|
||||
write_cc_buffer_to_gui(sub->data, context);
|
||||
}
|
||||
freep(&sub->data);
|
||||
}
|
||||
if(sub->type == CC_BITMAP)
|
||||
{
|
||||
switch (ccx_options.write_format)
|
||||
{
|
||||
case CCX_OF_SRT:
|
||||
if (!context->startcredits_displayed && ccx_options.start_credits_text!=NULL)
|
||||
try_to_add_start_credits(context, sub->start_time);
|
||||
wrote_something = write_cc_bitmap_as_srt(sub, context);
|
||||
case CCX_OF_SAMI:
|
||||
if (!context->startcredits_displayed && ccx_options.start_credits_text!=NULL)
|
||||
try_to_add_start_credits(context, sub->start_time);
|
||||
wrote_something = write_cc_bitmap_as_sami(sub, context);
|
||||
case CCX_OF_SMPTETT:
|
||||
if (!context->startcredits_displayed && ccx_options.start_credits_text!=NULL)
|
||||
try_to_add_start_credits(context, sub->start_time);
|
||||
wrote_something = write_cc_bitmap_as_smptett(sub, context);
|
||||
case CCX_OF_TRANSCRIPT:
|
||||
wrote_something = write_cc_bitmap_as_transcript(sub, context);
|
||||
break;
|
||||
case CCX_OF_SPUPNG:
|
||||
wrote_something = write_cc_bitmap_as_spupng(sub, context);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
if (!sub->nb_data)
|
||||
freep(&sub->data);
|
||||
return wrote_something;
|
||||
}
|
||||
|
||||
void write_cc_buffer_to_gui(struct eia608_screen *data, struct encoder_ctx *context)
|
||||
{
|
||||
unsigned h1, m1, s1, ms1;
|
||||
unsigned h2, m2, s2, ms2;
|
||||
LLONG ms_start;
|
||||
int with_data = 0;
|
||||
|
||||
for (int i = 0; i<15; i++)
|
||||
{
|
||||
if (data->row_used[i])
|
||||
with_data = 1;
|
||||
}
|
||||
if (!with_data)
|
||||
return;
|
||||
|
||||
ms_start = data->start_time;
|
||||
|
||||
ms_start += context->subs_delay;
|
||||
if (ms_start<0) // Drop screens that because of subs_delay start too early
|
||||
return;
|
||||
int time_reported = 0;
|
||||
for (int i = 0; i<15; i++)
|
||||
{
|
||||
if (data->row_used[i])
|
||||
{
|
||||
fprintf(stderr, "###SUBTITLE#");
|
||||
if (!time_reported)
|
||||
{
|
||||
LLONG 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.
|
||||
// Note, only MM:SS here as we need to save space in the preview window
|
||||
fprintf(stderr, "%02u:%02u#%02u:%02u#",
|
||||
h1 * 60 + m1, s1, h2 * 60 + m2, s2);
|
||||
time_reported = 1;
|
||||
}
|
||||
else
|
||||
fprintf(stderr, "##");
|
||||
|
||||
// We don't capitalize here because whatever function that was used
|
||||
// before to write to file already took care of it.
|
||||
int length = get_decoder_line_encoded_for_gui(subline, i, data);
|
||||
fwrite(subline, 1, length, stderr);
|
||||
fwrite("\n", 1, 1, stderr);
|
||||
}
|
||||
}
|
||||
fflush(stderr);
|
||||
}
|
||||
88
src/lib_ccx/ccx_encoders_common.h
Normal file
88
src/lib_ccx/ccx_encoders_common.h
Normal file
@@ -0,0 +1,88 @@
|
||||
#ifndef _CC_ENCODER_COMMON_H
|
||||
#define _CC_ENCODER_COMMON_H
|
||||
|
||||
#include "ccx_common_structs.h"
|
||||
#include "ccx_decoders_structs.h"
|
||||
#include "ccx_encoders_structs.h"
|
||||
#include "ccx_encoders_helpers.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;
|
||||
|
||||
/**
|
||||
* Context of encoder, This structure gives single interface
|
||||
* to all encoder
|
||||
*/
|
||||
struct encoder_ctx
|
||||
{
|
||||
/* common buffer used by all encoder */
|
||||
unsigned char *buffer;
|
||||
/* capacity of buffer */
|
||||
unsigned int capacity;
|
||||
/* keep count of srt subtitle*/
|
||||
unsigned int srt_counter;
|
||||
/* output context */
|
||||
struct ccx_s_write *out;
|
||||
/* start time of previous sub */
|
||||
LLONG prev_start;
|
||||
|
||||
LLONG subs_delay;
|
||||
LLONG last_displayed_subs_ms;
|
||||
int startcredits_displayed;
|
||||
};
|
||||
|
||||
#define INITIAL_ENC_BUFFER_CAPACITY 2048
|
||||
/**
|
||||
* Inialize encoder context with output context
|
||||
* allocate initial memory to buffer of context
|
||||
* write subtitle header to file refrenced by
|
||||
* output context
|
||||
*
|
||||
* @param ctx preallocated encoder ctx
|
||||
* @param out output context
|
||||
*
|
||||
* @return 0 on SUCESS, -1 on failure
|
||||
*/
|
||||
int init_encoder(struct encoder_ctx *ctx,struct ccx_s_write *out);
|
||||
|
||||
/**
|
||||
* try to add end credits in subtitle file and then write subtitle
|
||||
* footer
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
void dinit_encoder(struct encoder_ctx *ctx);
|
||||
|
||||
/**
|
||||
* @param ctx encoder context
|
||||
* @param sub subtitle context returned by decoder
|
||||
*/
|
||||
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_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_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);
|
||||
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_bitmap_as_srt(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);
|
||||
|
||||
|
||||
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);
|
||||
#endif
|
||||
392
src/lib_ccx/ccx_encoders_helpers.c
Normal file
392
src/lib_ccx/ccx_encoders_helpers.c
Normal file
@@ -0,0 +1,392 @@
|
||||
#include "ccx_encoders_helpers.h"
|
||||
#include "ccx_common_char_encoding.h"
|
||||
#include "ccx_common_constants.h"
|
||||
#include "ccx_common_structs.h"
|
||||
#include "ccx_decoders_common.h"
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#define strcasecmp stricmp
|
||||
#endif
|
||||
|
||||
// userdefined rgb color
|
||||
unsigned char usercolor_rgb[8] = "";
|
||||
|
||||
static int spell_builtin_added = 0; // so we don't do it twice
|
||||
// Case arrays
|
||||
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[] =
|
||||
{
|
||||
"I", "I'd", "I've", "I'd", "I'll",
|
||||
"January", "February", "March", "April", // May skipped intentionally
|
||||
"June", "July", "August", "September", "October", "November",
|
||||
"December", "Monday", "Tuesday", "Wednesday", "Thursday",
|
||||
"Friday", "Saturday", "Sunday", "Halloween", "United States",
|
||||
"Spain", "France", "Italy", "England",
|
||||
NULL
|
||||
};
|
||||
|
||||
int string_cmp2(const void *p1, const void *p2, void *arg)
|
||||
{
|
||||
return strcasecmp(*(char**)p1, *(char**)p2);
|
||||
}
|
||||
int string_cmp(const void *p1, const void *p2)
|
||||
{
|
||||
return string_cmp2(p1, p2, NULL);
|
||||
}
|
||||
|
||||
void correct_case(int line_num, struct eia608_screen *data)
|
||||
{
|
||||
char delim[64] = {
|
||||
' ', '\n', '\r', 0x89, 0x99,
|
||||
'!', '"', '#', '%', '&',
|
||||
'\'', '(', ')', ';', '<',
|
||||
'=', '>', '?', '[', '\\',
|
||||
']', '*', '+', ',', '-',
|
||||
'.', '/', ':', '^', '_',
|
||||
'{', '|', '}', '~', '\0' };
|
||||
|
||||
char *line = strdup(((char*)data->characters[line_num]));
|
||||
char *oline = (char*)data->characters[line_num];
|
||||
char *c = strtok(line, delim);
|
||||
do
|
||||
{
|
||||
char **index = bsearch(&c, spell_lower, spell_words, sizeof(*spell_lower), string_cmp);
|
||||
|
||||
if (index)
|
||||
{
|
||||
char *correct_c = *(spell_correct + (index - spell_lower));
|
||||
size_t len = strlen(correct_c);
|
||||
memcpy(oline + (c - line), correct_c, len);
|
||||
}
|
||||
} while ((c = strtok(NULL, delim)) != NULL);
|
||||
free(line);
|
||||
}
|
||||
|
||||
void capitalize(int line_num, struct eia608_screen *data)
|
||||
{
|
||||
for (int i = 0; i<CCX_DECODER_608_SCREEN_WIDTH; i++)
|
||||
{
|
||||
switch (data->characters[line_num][i])
|
||||
{
|
||||
case ' ':
|
||||
case 0x89: // This is a transparent space
|
||||
case '-':
|
||||
break;
|
||||
case '.': // Fallthrough
|
||||
case '?': // Fallthrough
|
||||
case '!':
|
||||
case ':':
|
||||
new_sentence = 1;
|
||||
break;
|
||||
default:
|
||||
if (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;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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 bytes = 0;
|
||||
while (*text)
|
||||
{
|
||||
switch (ccx_encoders_helpers_settings.encoding)
|
||||
{
|
||||
case CCX_ENC_UTF_8:
|
||||
case CCX_ENC_LATIN_1:
|
||||
*buffer = *text;
|
||||
bytes++;
|
||||
buffer++;
|
||||
break;
|
||||
case CCX_ENC_UNICODE:
|
||||
*buffer = *text;
|
||||
*(buffer + 1) = 0;
|
||||
bytes += 2;
|
||||
buffer += 2;
|
||||
break;
|
||||
}
|
||||
text++;
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
|
||||
unsigned get_decoder_line_encoded_for_gui(unsigned char *buffer, int line_num, struct eia608_screen *data)
|
||||
{
|
||||
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);
|
||||
for (int i = first; i <= last; i++)
|
||||
{
|
||||
get_char_in_latin_1(buffer, line[i]);
|
||||
buffer++;
|
||||
}
|
||||
*buffer = 0;
|
||||
return (unsigned)(buffer - orig); // Return length
|
||||
|
||||
}
|
||||
|
||||
unsigned char *close_tag(unsigned char *buffer, char *tagstack, char tagtype, int *punderlined, int *pitalics, int *pchanged_font)
|
||||
{
|
||||
for (int l = strlen(tagstack) - 1; l >= 0; l--)
|
||||
{
|
||||
char cur = tagstack[l];
|
||||
switch (cur)
|
||||
{
|
||||
case 'F':
|
||||
buffer += encode_line(buffer, (unsigned char *) "</font>");
|
||||
(*pchanged_font)--;
|
||||
break;
|
||||
case 'U':
|
||||
buffer += encode_line(buffer, (unsigned char *) "</u>");
|
||||
(*punderlined)--;
|
||||
break;
|
||||
case 'I':
|
||||
buffer += encode_line(buffer, (unsigned char *) "</i>");
|
||||
(*pitalics)--;
|
||||
break;
|
||||
}
|
||||
tagstack[l] = 0; // Remove from stack
|
||||
if (cur == tagtype) // We closed up to the required tag, done
|
||||
return buffer;
|
||||
}
|
||||
if (tagtype != 'A') // All
|
||||
ccx_common_logging.fatal_ftn(CCX_COMMON_EXIT_BUG_BUG, "Mismatched tags in encoding, this is a bug, please report");
|
||||
return buffer;
|
||||
}
|
||||
|
||||
unsigned get_decoder_line_encoded(unsigned char *buffer, int line_num, struct eia608_screen *data)
|
||||
{
|
||||
int col = COL_WHITE;
|
||||
int underlined = 0;
|
||||
int italics = 0;
|
||||
int changed_font = 0;
|
||||
char tagstack[128] = ""; // Keep track of opening/closing tags
|
||||
|
||||
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);
|
||||
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 &&
|
||||
!(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);
|
||||
// Add new font tag
|
||||
buffer += encode_line(buffer, (unsigned char*)color_text[its_col][1]);
|
||||
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*) "\">");
|
||||
}
|
||||
if (color_text[its_col][1][0]) // That means a <font> was added to the buffer
|
||||
{
|
||||
strcat(tagstack, "F");
|
||||
changed_font++;
|
||||
}
|
||||
col = its_col;
|
||||
}
|
||||
// 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
|
||||
{
|
||||
buffer += encode_line(buffer, (unsigned char *) "<u>");
|
||||
strcat(tagstack, "U");
|
||||
underlined++;
|
||||
}
|
||||
if (is_underlined == 0 && underlined && !ccx_encoders_helpers_settings.no_type_setting) // Close underline
|
||||
{
|
||||
buffer = close_tag(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
|
||||
{
|
||||
buffer += encode_line(buffer, (unsigned char *) "<i>");
|
||||
strcat(tagstack, "I");
|
||||
italics++;
|
||||
}
|
||||
if (has_ita == 0 && italics && !ccx_encoders_helpers_settings.no_type_setting) // Close italics
|
||||
{
|
||||
buffer = close_tag(buffer, tagstack, 'I', &underlined, &italics, &changed_font);
|
||||
}
|
||||
int bytes = 0;
|
||||
switch (ccx_encoders_helpers_settings.encoding)
|
||||
{
|
||||
case CCX_ENC_UTF_8:
|
||||
bytes = get_char_in_utf_8(buffer, line[i]);
|
||||
break;
|
||||
case CCX_ENC_LATIN_1:
|
||||
get_char_in_latin_1(buffer, line[i]);
|
||||
bytes = 1;
|
||||
break;
|
||||
case CCX_ENC_UNICODE:
|
||||
get_char_in_unicode(buffer, line[i]);
|
||||
bytes = 2;
|
||||
break;
|
||||
}
|
||||
buffer += bytes;
|
||||
}
|
||||
buffer = close_tag(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;
|
||||
return (unsigned)(buffer - orig); // Return length
|
||||
}
|
||||
|
||||
/*void delete_all_lines_but_current(ccx_decoder_608_context *context, struct eia608_screen *data, int row)
|
||||
{
|
||||
for (int i=0;i<15;i++)
|
||||
{
|
||||
if (i!=row)
|
||||
{
|
||||
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->fonts[i], FONT_REGULAR, CCX_DECODER_608_SCREEN_WIDTH + 1);
|
||||
data->row_used[i]=0;
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
/*void fprintf_encoded (struct encoder_ctx *ctx,FILE *fh, const char *string)
|
||||
{
|
||||
int used;
|
||||
REQUEST_BUFFER_CAPACITY(ctx,strlen (string)*3);
|
||||
used=encode_line (ctx->buffer,(unsigned char *) string);
|
||||
fwrite (ctx->buffer,used,1,fh);
|
||||
}*/
|
||||
|
||||
int add_word(const char *word)
|
||||
{
|
||||
char *new_lower;
|
||||
char *new_correct;
|
||||
char **ptr_lower;
|
||||
char **ptr_correct;
|
||||
int i;
|
||||
if (spell_words == spell_capacity)
|
||||
{
|
||||
// Time to grow
|
||||
spell_capacity += 50;
|
||||
ptr_lower = (char **)realloc(spell_lower, sizeof (char *)*
|
||||
spell_capacity);
|
||||
ptr_correct = (char **)realloc(spell_correct, sizeof (char *)*
|
||||
spell_capacity);
|
||||
}
|
||||
size_t len = strlen(word);
|
||||
new_lower = (char *)malloc(len + 1);
|
||||
new_correct = (char *)malloc(len + 1);
|
||||
if (ptr_lower == NULL || ptr_correct == NULL ||
|
||||
new_lower == NULL || new_correct == NULL)
|
||||
{
|
||||
spell_capacity = 0;
|
||||
for (i = 0; i < spell_words; i++)
|
||||
{
|
||||
freep(&spell_lower[spell_words]);
|
||||
freep(&spell_correct[spell_words]);
|
||||
}
|
||||
freep(&spell_lower);
|
||||
freep(&spell_correct);
|
||||
freep(&ptr_lower);
|
||||
freep(&ptr_correct);
|
||||
freep(&new_lower);
|
||||
freep(&new_correct);
|
||||
spell_words = 0;
|
||||
return -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
spell_lower = ptr_lower;
|
||||
spell_correct = ptr_correct;
|
||||
}
|
||||
strcpy(new_correct, word);
|
||||
for (size_t i = 0; i<len; i++)
|
||||
{
|
||||
char c = new_correct[i];
|
||||
c = tolower(c); // TO-DO: Add Spanish characters
|
||||
new_lower[i] = c;
|
||||
}
|
||||
new_lower[len] = 0;
|
||||
spell_lower[spell_words] = new_lower;
|
||||
spell_correct[spell_words] = new_correct;
|
||||
spell_words++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int add_built_in_words(void)
|
||||
{
|
||||
if (!spell_builtin_added)
|
||||
{
|
||||
int i = 0;
|
||||
while (spell_builtin[i] != NULL)
|
||||
{
|
||||
if (add_word(spell_builtin[i]))
|
||||
return -1;
|
||||
i++;
|
||||
}
|
||||
spell_builtin_added = 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param base points to the start of the array
|
||||
* @param nb number of element in array
|
||||
* @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.
|
||||
* @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)
|
||||
{
|
||||
unsigned char *lbase = (unsigned char*)base;
|
||||
unsigned char *tmp = (unsigned char*)malloc(size);
|
||||
for (int gap = nb / 2; gap > 0; gap = gap / 2)
|
||||
{
|
||||
int p, j;
|
||||
for (p = gap; p < nb; p++)
|
||||
{
|
||||
memcpy(tmp, lbase + (p *size), size);
|
||||
for (j = p; j >= gap && (compar(tmp, lbase + ((j - gap) * size), arg) < 0); j -= gap)
|
||||
{
|
||||
memcpy(lbase + (j*size), lbase + ((j - gap) * size), size);
|
||||
}
|
||||
memcpy(lbase + (j *size), tmp, size);
|
||||
}
|
||||
}
|
||||
free(tmp);
|
||||
}
|
||||
|
||||
void ccx_encoders_helpers_perform_shellsort_words(void)
|
||||
{
|
||||
shell_sort(spell_lower, spell_words, sizeof(*spell_lower), string_cmp2, NULL);
|
||||
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;
|
||||
}
|
||||
39
src/lib_ccx/ccx_encoders_helpers.h
Normal file
39
src/lib_ccx/ccx_encoders_helpers.h
Normal file
@@ -0,0 +1,39 @@
|
||||
#ifndef _CCX_ENCODERS_HELPERS_H
|
||||
#define _CCX_ENCODERS_HELPERS_H
|
||||
|
||||
#include "ccx_common_common.h"
|
||||
#include "ccx_common_constants.h"
|
||||
#include "ccx_decoders_structs.h"
|
||||
#include "ccx_decoders_608.h"
|
||||
|
||||
extern char **spell_lower;
|
||||
extern char **spell_correct;
|
||||
extern int spell_words;
|
||||
extern int spell_capacity;
|
||||
|
||||
extern unsigned char usercolor_rgb[8];
|
||||
|
||||
struct ccx_encoders_helpers_settings_t {
|
||||
int trim_subs;
|
||||
int no_font_color;
|
||||
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);
|
||||
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);
|
||||
|
||||
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);
|
||||
|
||||
void shell_sort(void *base, int nb, size_t size, int(*compar)(const void*p1, const void *p2, void*arg), void *arg);
|
||||
|
||||
void ccx_encoders_helpers_perform_shellsort_words(void);
|
||||
void ccx_encoders_helpers_setup(enum ccx_encoding_type encoding, int no_font_color, int no_type_setting, int trim_subs);
|
||||
#endif
|
||||
25
src/lib_ccx/ccx_encoders_structs.h
Normal file
25
src/lib_ccx/ccx_encoders_structs.h
Normal file
@@ -0,0 +1,25 @@
|
||||
#ifndef CCX_ENCODERS_STRUCTS_H
|
||||
|
||||
typedef struct ccx_encoders_transcript_format {
|
||||
// TODO: add more options, and (perhaps) reduce other ccextractor options?
|
||||
int showStartTime, showEndTime; // Show start and/or end time.
|
||||
int showMode; // Show which mode if available (E.G.: POP, RU1, ...)
|
||||
int showCC; // Show which CC channel has been captured.
|
||||
int relativeTimestamp; // Timestamps relative to start of sample or in UTC?
|
||||
int xds; // Show XDS or not
|
||||
int useColors; // Add colors or no colors
|
||||
int isFinal; // Used to determine if these parameters should be changed afterwards.
|
||||
|
||||
} 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
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "ccextractor.h"
|
||||
#include "lib_ccx.h"
|
||||
#include "ccx_common_option.h"
|
||||
#include "configuration.h"
|
||||
#include <stddef.h>
|
||||
#define CNF_FILE "ccextractor.cnf"
|
||||
@@ -44,7 +45,6 @@ static int set_int(void *var, char*val)
|
||||
struct conf_map configuration_map[] = {
|
||||
{"INPUT_SOURCE",offsetof(struct ccx_s_options,input_source),set_int},
|
||||
{"BUFFER_INPUT",offsetof(struct ccx_s_options,buffer_input),set_int},
|
||||
{"DIRECT_ROLLUP",offsetof(struct ccx_s_options,direct_rollup),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},
|
||||
@@ -61,8 +61,6 @@ struct conf_map configuration_map[] = {
|
||||
{"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},
|
||||
{"NO_ROLL_UP",offsetof(struct ccx_s_options,norollup),set_int},
|
||||
{"FORCED_RU",offsetof(struct ccx_s_options,forced_ru),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},
|
||||
@@ -71,7 +69,6 @@ struct conf_map configuration_map[] = {
|
||||
{"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},
|
||||
{"SCREEN_TO_PROCESS",offsetof(struct ccx_s_options,screens_to_process),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},
|
||||
@@ -89,6 +86,12 @@ struct conf_map configuration_map[] = {
|
||||
{"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 },
|
||||
{ "FORCED_RU", offsetof(struct ccx_s_options, settings_608.force_rollup), set_int },
|
||||
{ "DIRECT_ROLLUP", offsetof(struct ccx_s_options, settings_608.direct_rollup), set_int },
|
||||
{ "SCREEN_TO_PROCESS", offsetof(struct ccx_s_options, settings_608.screens_to_process), set_int },
|
||||
|
||||
{NULL}
|
||||
};
|
||||
static int parse_opts(char *str, struct ccx_s_options *opt)
|
||||
@@ -115,14 +118,17 @@ static void parse_file(FILE *f,struct ccx_s_options *opt)
|
||||
int comments = 0;
|
||||
int i = 0;
|
||||
int ret = 0;
|
||||
while ((c = fgetc(f)) != EOF )
|
||||
*str = '\0';
|
||||
while ((c = (char)fgetc(f)) != EOF )
|
||||
{
|
||||
if( c == '\n')
|
||||
{
|
||||
if( str[0] != '\0')
|
||||
{
|
||||
ret = parse_opts(str,opt);
|
||||
if(ret < 0)
|
||||
mprint("invalid configuration file\n");
|
||||
}
|
||||
comments = 0;
|
||||
i = 0;
|
||||
str[0] = '\0';
|
||||
@@ -140,10 +146,10 @@ static void parse_file(FILE *f,struct ccx_s_options *opt)
|
||||
}
|
||||
void parse_configuration(struct ccx_s_options *opt)
|
||||
{
|
||||
FILE *f = NULL;
|
||||
if( (f = fopen(CNF_FILE,"r") ) != NULL)
|
||||
{
|
||||
FILE *f = NULL;
|
||||
if( (f = fopen(CNF_FILE,"r") ) != NULL)
|
||||
{
|
||||
parse_file(f,opt);
|
||||
}
|
||||
|
||||
fclose(f);
|
||||
}
|
||||
}
|
||||
@@ -21,20 +21,11 @@
|
||||
#include <limits.h>
|
||||
#include <errno.h>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#define snprintf(str,size,format,...) _snprintf(str,size-1,format,__VA_ARGS__)
|
||||
#endif
|
||||
|
||||
#include "dvb_subtitle_decoder.h"
|
||||
#include "spupng_encoder.h"
|
||||
#include "ocr.h"
|
||||
#include "utility.h"
|
||||
#define DEBUG
|
||||
|
||||
#ifdef DEBUG
|
||||
#define PNG_DEBUG 3
|
||||
#include "png.h"
|
||||
#endif
|
||||
#include "ccx_decoders_common.h"
|
||||
#include "ocr.h"
|
||||
|
||||
#define DVBSUB_PAGE_SEGMENT 0x10
|
||||
#define DVBSUB_REGION_SEGMENT 0x11
|
||||
@@ -95,7 +86,6 @@ const uint8_t crop_tab[256 + 2 * MAX_NEG_CROP] = { times256(0x00), 0x00, 0x01,
|
||||
|
||||
#define cm (crop_tab + MAX_NEG_CROP)
|
||||
|
||||
const char *dvb_language[] = { "und", "eng", "fin", NULL };
|
||||
|
||||
static __inline unsigned int bytestream_get_byte(const uint8_t **b)
|
||||
{
|
||||
@@ -161,7 +151,7 @@ static __inline unsigned int get_bits(GetBitContext *s, int n)
|
||||
unsigned int re_cache = 0;
|
||||
unsigned int re_size_plus8 = s->size_in_bits_plus8;
|
||||
|
||||
if (n <= 0 && n > 25)
|
||||
if (n <= 0 || n > 25)
|
||||
return -1;
|
||||
re_cache = RB32( s->buffer + (re_index >> 3 )) << (re_index & 7);
|
||||
|
||||
@@ -189,308 +179,6 @@ static __inline unsigned int get_bits1(GetBitContext *s)
|
||||
return result;
|
||||
}
|
||||
|
||||
static void freep(void *arg)
|
||||
{
|
||||
void **ptr = (void **) arg;
|
||||
if (*ptr)
|
||||
free(*ptr);
|
||||
*ptr = NULL;
|
||||
|
||||
}
|
||||
#ifdef DEBUG
|
||||
|
||||
struct transIntensity
|
||||
{
|
||||
uint8_t *t;
|
||||
uint8_t *i;
|
||||
};
|
||||
int check_trans_tn_intensity(const void *p1, const void *p2, void *arg)
|
||||
{
|
||||
struct transIntensity *ti = arg;
|
||||
unsigned char* tmp = (unsigned char*)p1;
|
||||
unsigned char* act = (unsigned char*)p2;
|
||||
|
||||
if (ti->t[*tmp] < ti->t[*act] || (ti->t[*tmp] == ti->t[*act] && ti->i[*tmp] < ti->i[*act]))
|
||||
return -1;
|
||||
else if (ti->t[*tmp] == ti->t[*act] && ti->i[*tmp] == ti->i[*act])
|
||||
return 0;
|
||||
|
||||
|
||||
return 1;
|
||||
}
|
||||
int mapclut_paletee(png_color *palette, png_byte *alpha, uint32_t *clut,
|
||||
uint8_t depth)
|
||||
{
|
||||
for (int i = 0; i < depth; i++)
|
||||
{
|
||||
palette[i].red = ((clut[i] >> 16) & 0xff);
|
||||
palette[i].green = ((clut[i] >> 8) & 0xff);
|
||||
palette[i].blue = (clut[i] & 0xff);
|
||||
alpha[i] = ((clut[i] >> 24) & 0xff);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
/*
|
||||
* @param alpha out
|
||||
* @param intensity in
|
||||
* @param palette out should be already initialized
|
||||
* @param bitmap in
|
||||
* @param size in size of bitmap
|
||||
* @param max_color in
|
||||
* @param nb_color in
|
||||
*/
|
||||
int quantize_map(png_byte *alpha, uint8_t *intensity, png_color *palette,
|
||||
uint8_t *bitmap, int size, int max_color, int nb_color)
|
||||
{
|
||||
/*
|
||||
* occurrence of color in image
|
||||
*/
|
||||
uint32_t *histogram = NULL;
|
||||
/* intensity ordered table */
|
||||
uint8_t *iot = NULL;
|
||||
/* array of color with most occurrence according to histogram
|
||||
* save index of intensity order table
|
||||
*/
|
||||
uint32_t *mcit = NULL;
|
||||
struct transIntensity ti = { alpha,intensity};
|
||||
|
||||
int ret = 0;
|
||||
|
||||
histogram = (uint32_t*) malloc(nb_color * sizeof(uint32_t));
|
||||
if (!histogram)
|
||||
{
|
||||
ret = -1;
|
||||
goto end;
|
||||
}
|
||||
|
||||
iot = (uint8_t*) malloc(nb_color * sizeof(uint8_t));
|
||||
if (!iot)
|
||||
{
|
||||
ret = -1;
|
||||
goto end;
|
||||
}
|
||||
|
||||
mcit = (uint32_t*) malloc(nb_color * sizeof(uint32_t));
|
||||
if (!mcit)
|
||||
{
|
||||
ret = -1;
|
||||
goto end;
|
||||
}
|
||||
|
||||
memset(histogram, 0, nb_color * sizeof(uint32_t));
|
||||
for (int i = 0; i < nb_color; i++)
|
||||
{
|
||||
iot[i] = i;
|
||||
}
|
||||
memset(mcit, 0, nb_color * sizeof(uint32_t));
|
||||
|
||||
/* calculate histogram of image */
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
histogram[bitmap[i]]++;
|
||||
}
|
||||
shell_sort((void*)iot, nb_color, sizeof(*iot), check_trans_tn_intensity, (void*)&ti);
|
||||
|
||||
/* using selection sort since need to find only max_color */
|
||||
for (int i = 0; i < max_color; i++)
|
||||
{
|
||||
uint32_t max_val = 0;
|
||||
uint32_t max_ind = 0;
|
||||
int j;
|
||||
for (j = 0; j < nb_color; j++)
|
||||
{
|
||||
if (max_val < histogram[iot[j]])
|
||||
{
|
||||
max_val = histogram[iot[j]];
|
||||
max_ind = j;
|
||||
}
|
||||
}
|
||||
for (j = i; j > 0 && max_ind < mcit[j - 1]; j--)
|
||||
{
|
||||
mcit[j] = mcit[j - 1];
|
||||
}
|
||||
mcit[j] = max_ind;
|
||||
histogram[iot[max_ind]] = 0;
|
||||
}
|
||||
|
||||
for (int i = 0, mxi = 0; i < nb_color; i++)
|
||||
{
|
||||
int step, inc;
|
||||
if (i == mcit[mxi])
|
||||
{
|
||||
mxi = (mxi < max_color) ? mxi + 1 : mxi;
|
||||
continue;
|
||||
}
|
||||
inc = (mxi) ? -1 : 0;
|
||||
step = mcit[mxi + inc] + ((mcit[mxi] - mcit[mxi + inc]) / 3);
|
||||
if (i <= step)
|
||||
{
|
||||
int index = iot[mcit[mxi + inc]];
|
||||
alpha[i] = alpha[index];
|
||||
palette[i].red = palette[index].red;
|
||||
palette[i].blue = palette[index].blue;
|
||||
palette[i].green = palette[index].green;
|
||||
}
|
||||
else
|
||||
{
|
||||
int index = iot[mcit[mxi]];
|
||||
alpha[i] = alpha[index];
|
||||
palette[i].red = palette[index].red;
|
||||
palette[i].blue = palette[index].blue;
|
||||
palette[i].green = palette[index].green;
|
||||
}
|
||||
|
||||
}
|
||||
end: freep(&histogram);
|
||||
freep(&mcit);
|
||||
freep(&iot);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static int pre_process_bitmap(png_color **palette, png_byte **alpha, int size,
|
||||
uint32_t *clut, uint8_t *luit, uint8_t *bitmap, uint8_t depth)
|
||||
{
|
||||
/*local pointer to palette */
|
||||
png_color *lpalette = NULL;
|
||||
/* local pointer to alpha */
|
||||
png_byte *lalpha = NULL;
|
||||
int nb_color = (1<< depth);
|
||||
int ret = 0;
|
||||
|
||||
|
||||
lpalette = (png_color*) malloc(nb_color * sizeof(png_color));
|
||||
if(!lpalette)
|
||||
{
|
||||
ret = -1;
|
||||
goto end;
|
||||
}
|
||||
lalpha = (png_byte*) malloc(nb_color * sizeof(png_byte));
|
||||
if(!lalpha)
|
||||
{
|
||||
ret = -1;
|
||||
goto end;
|
||||
}
|
||||
if(clut)
|
||||
mapclut_paletee(lpalette, lalpha, clut, nb_color);
|
||||
else
|
||||
{
|
||||
/* initialize colors with white */
|
||||
memset(palette,0xff,sizeof(nb_color * sizeof(*lpalette)));
|
||||
|
||||
/* initialize transparency as complete transparent */
|
||||
memset(lalpha,0,sizeof(nb_color * sizeof(*lalpha)));
|
||||
}
|
||||
|
||||
if(bitmap)
|
||||
{
|
||||
quantize_map(lalpha, luit, lpalette, bitmap, size, 3, nb_color);
|
||||
}
|
||||
*palette = lpalette;
|
||||
*alpha = lalpha;
|
||||
end:
|
||||
return ret;
|
||||
}
|
||||
static int save_spupng(const char *filename, uint8_t *bitmap, int w, int h,
|
||||
png_color *palette, png_byte *alpha, int nb_color)
|
||||
{
|
||||
FILE *f = NULL;
|
||||
png_structp png_ptr = NULL;
|
||||
png_infop info_ptr = NULL;
|
||||
png_bytep* row_pointer = NULL;
|
||||
int i, j, ret = 0;
|
||||
int k = 0;
|
||||
if(!h)
|
||||
h = 1;
|
||||
if(!w)
|
||||
w = 1;
|
||||
|
||||
|
||||
f = fopen(filename, "wb");
|
||||
if (!f)
|
||||
{
|
||||
mprint("DVB:unable to open %s in write mode \n", filename);
|
||||
ret = -1;
|
||||
goto end;
|
||||
}
|
||||
|
||||
png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL,NULL);
|
||||
if (!png_ptr)
|
||||
{
|
||||
mprint("DVB:unable to create png write struct\n");
|
||||
goto end;
|
||||
}
|
||||
if (!(info_ptr = png_create_info_struct(png_ptr)))
|
||||
{
|
||||
mprint("DVB:unable to create png info struct\n");
|
||||
ret = -1;
|
||||
goto end;
|
||||
}
|
||||
|
||||
row_pointer = (png_bytep*) malloc(sizeof(png_bytep) * h);
|
||||
if (!row_pointer)
|
||||
{
|
||||
mprint("DVB: unable to allocate row_pointer\n");
|
||||
ret = -1;
|
||||
goto end;
|
||||
}
|
||||
memset(row_pointer, 0, sizeof(png_bytep) * h);
|
||||
png_init_io(png_ptr, f);
|
||||
|
||||
png_set_IHDR(png_ptr, info_ptr, w, h,
|
||||
/* 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, nb_color);
|
||||
png_set_tRNS(png_ptr, info_ptr, alpha, nb_color, NULL);
|
||||
|
||||
for (i = 0; i < h; i++)
|
||||
{
|
||||
row_pointer[i] = (png_byte*) malloc(
|
||||
png_get_rowbytes(png_ptr, info_ptr));
|
||||
if (row_pointer[i] == NULL)
|
||||
break;
|
||||
}
|
||||
if (i != h)
|
||||
{
|
||||
mprint("DVB: unable to allocate row_pointer internals\n");
|
||||
ret = -1;
|
||||
goto end;
|
||||
}
|
||||
png_write_info(png_ptr, info_ptr);
|
||||
for (i = 0; i < h; i++)
|
||||
{
|
||||
for (j = 0; j < png_get_rowbytes(png_ptr, info_ptr); j++)
|
||||
{
|
||||
if(bitmap)
|
||||
k = bitmap[i * w + (j)];
|
||||
else
|
||||
k = 0;
|
||||
row_pointer[i][j] = k;
|
||||
}
|
||||
}
|
||||
|
||||
png_write_image(png_ptr, row_pointer);
|
||||
|
||||
png_write_end(png_ptr, info_ptr);
|
||||
end: if (row_pointer)
|
||||
{
|
||||
for (i = 0; i < h; i++)
|
||||
freep(&row_pointer[i]);
|
||||
freep(&row_pointer);
|
||||
}
|
||||
png_destroy_write_struct(&png_ptr, &info_ptr);
|
||||
if (f)
|
||||
fclose(f);
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#define RGBA(r,g,b,a) (((unsigned)(a) << 24) | ((r) << 16) | ((g) << 8) | (b))
|
||||
|
||||
typedef struct DVBSubCLUT
|
||||
@@ -582,26 +270,20 @@ typedef struct DVBSubContext
|
||||
{
|
||||
int composition_id;
|
||||
int ancillary_id;
|
||||
|
||||
int lang_index;
|
||||
int version;
|
||||
int time_out;
|
||||
#ifdef ENABLE_OCR
|
||||
void *ocr_ctx;
|
||||
#endif
|
||||
DVBSubRegion *region_list;
|
||||
DVBSubCLUT *clut_list;
|
||||
DVBSubObject *object_list;
|
||||
|
||||
DVBSubRegionDisplay *display_list;
|
||||
DVBSubDisplayDefinition *display_definition;
|
||||
struct ccx_s_write *out;
|
||||
long long prev_start;
|
||||
} DVBSubContext;
|
||||
|
||||
typedef struct DVBOutContext
|
||||
{
|
||||
long long start_time;
|
||||
long long end_time;
|
||||
|
||||
}DVBOutContext;
|
||||
|
||||
static DVBSubObject* get_object(DVBSubContext *ctx, int object_id)
|
||||
{
|
||||
DVBSubObject *ptr = ctx->object_list;
|
||||
@@ -729,124 +411,6 @@ static void delete_regions(DVBSubContext *ctx)
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
static void save_display_set(DVBSubContext *ctx)
|
||||
{
|
||||
DVBSubRegion *region;
|
||||
DVBSubRegionDisplay *display;
|
||||
DVBSubCLUT *clut;
|
||||
int x_pos, y_pos, width, height;
|
||||
int x, y, y_off, x_off;
|
||||
uint8_t *pbuf;
|
||||
char *filename;
|
||||
void *sp = ctx->out->spupng_data;
|
||||
x_pos = -1;
|
||||
y_pos = -1;
|
||||
width = 0;
|
||||
height = 0;
|
||||
|
||||
|
||||
for (display = ctx->display_list; display; display = display->next)
|
||||
{
|
||||
region = get_region(ctx, display->region_id);
|
||||
|
||||
if (x_pos == -1)
|
||||
{
|
||||
x_pos = display->x_pos;
|
||||
y_pos = display->y_pos;
|
||||
width = region->width;
|
||||
height = region->height;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (display->x_pos < x_pos)
|
||||
{
|
||||
width += (x_pos - display->x_pos);
|
||||
x_pos = display->x_pos;
|
||||
}
|
||||
|
||||
if (display->y_pos < y_pos)
|
||||
{
|
||||
height += (y_pos - display->y_pos);
|
||||
y_pos = display->y_pos;
|
||||
}
|
||||
|
||||
if (display->x_pos + region->width > x_pos + width)
|
||||
{
|
||||
width = display->x_pos + region->width - x_pos;
|
||||
}
|
||||
|
||||
if (display->y_pos + region->height > y_pos + height)
|
||||
{
|
||||
height = display->y_pos + region->height - y_pos;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (x_pos >= 0)
|
||||
{
|
||||
png_color *palette = NULL;
|
||||
png_byte *alpha = NULL;
|
||||
#ifdef ENABLE_OCR
|
||||
char*str = NULL;
|
||||
#endif
|
||||
|
||||
filename = get_spupng_filename(sp);
|
||||
inc_spupng_fileindex(sp);
|
||||
set_spupng_offset(sp,y_pos,x_pos);
|
||||
|
||||
pbuf = (uint8_t*) malloc(width * height);
|
||||
memset(pbuf, 0x0, width * height);
|
||||
|
||||
for (display = ctx->display_list; display; display = display->next)
|
||||
{
|
||||
region = get_region(ctx, display->region_id);
|
||||
|
||||
x_off = display->x_pos - x_pos;
|
||||
y_off = display->y_pos - y_pos;
|
||||
|
||||
clut = get_clut(ctx, region->clut);
|
||||
|
||||
if (clut == 0)
|
||||
clut = &default_clut;
|
||||
|
||||
for (y = 0; y < region->height; y++)
|
||||
{
|
||||
for (x = 0; x < region->width; x++)
|
||||
{
|
||||
pbuf[((y + y_off) * width) + x_off + x] =
|
||||
region->pbuf[y * region->width + x];
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
pre_process_bitmap(&palette,&alpha,width*height,clut->clut16, clut->ilut16,pbuf,region->depth);
|
||||
#ifdef ENABLE_OCR
|
||||
str = ocr_bitmap(palette,alpha,pbuf,width,height);
|
||||
if(str)
|
||||
{
|
||||
write_spucomment(sp,str);
|
||||
}
|
||||
#endif
|
||||
save_spupng(filename, pbuf, width, height, palette, alpha,(1 << region->depth));
|
||||
|
||||
free(pbuf);
|
||||
freep(&palette);
|
||||
freep(&alpha);
|
||||
}
|
||||
else if(!ctx->prev_start)
|
||||
{
|
||||
png_color palette = {0,0,0};
|
||||
png_byte alpha = 0;
|
||||
filename = get_spupng_filename(sp);
|
||||
inc_spupng_fileindex(sp);
|
||||
/* save dummy frame */
|
||||
save_spupng(filename,NULL,1,1,&palette,&alpha,1);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @param composition_id composition-page_id found in Subtitle descriptors
|
||||
@@ -859,15 +423,24 @@ static void save_display_set(DVBSubContext *ctx)
|
||||
* @return DVB context kept as void* for abstraction
|
||||
*
|
||||
*/
|
||||
void* dvbsub_init_decoder(int composition_id, int ancillary_id)
|
||||
void* dvbsub_init_decoder(struct dvb_config* cfg)
|
||||
{
|
||||
int i, r, g, b, a = 0;
|
||||
DVBSubContext *ctx = (DVBSubContext*) malloc(sizeof(DVBSubContext));
|
||||
memset(ctx, 0, sizeof(DVBSubContext));
|
||||
|
||||
ctx->composition_id = composition_id;
|
||||
ctx->ancillary_id = ancillary_id;
|
||||
ctx->composition_id = cfg->composition_id[0];
|
||||
ctx->ancillary_id = cfg->ancillary_id[0];
|
||||
ctx->lang_index = cfg->lang_index[0];
|
||||
|
||||
#ifdef ENABLE_OCR
|
||||
ctx->ocr_ctx = init_ocr(ctx->lang_index);
|
||||
if(!ctx->ocr_ctx)
|
||||
{
|
||||
freep(&ctx);
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
ctx->version = -1;
|
||||
|
||||
default_clut.id = -1;
|
||||
@@ -1738,9 +1311,6 @@ static void dvbsub_parse_page_segment(void *dvb_ctx, const uint8_t *buf,
|
||||
int page_state;
|
||||
int timeout;
|
||||
int version;
|
||||
long long start = get_visible_start();
|
||||
void *sp = ctx->out->spupng_data;
|
||||
|
||||
|
||||
if (buf_size < 1)
|
||||
return;
|
||||
@@ -1758,19 +1328,6 @@ static void dvbsub_parse_page_segment(void *dvb_ctx, const uint8_t *buf,
|
||||
ctx->time_out = timeout;
|
||||
ctx->version = version;
|
||||
|
||||
if(ctx->prev_start == 0)
|
||||
{
|
||||
write_sputag(sp, ctx->prev_start, start);
|
||||
save_display_set(ctx);
|
||||
}
|
||||
else if(ctx->display_list)
|
||||
{
|
||||
write_sputag(sp, ctx->prev_start, start);
|
||||
save_display_set(ctx);
|
||||
}
|
||||
|
||||
ctx->prev_start = start;
|
||||
|
||||
if (page_state == 1 || page_state == 2)
|
||||
{
|
||||
delete_regions(ctx);
|
||||
@@ -1870,25 +1427,53 @@ static void dvbsub_parse_display_definition_segment(void *dvb_ctx,
|
||||
}
|
||||
}
|
||||
|
||||
static int dvbsub_display_end_segment(void *dvb_ctx, const uint8_t *buf,
|
||||
int buf_size)
|
||||
static int write_dvb_sub(void *dvb_ctx, struct cc_subtitle *sub)
|
||||
{
|
||||
DVBSubContext *ctx = (DVBSubContext *) dvb_ctx;
|
||||
|
||||
DVBSubRegion *region;
|
||||
DVBSubRegionDisplay *display;
|
||||
DVBSubCLUT *clut;
|
||||
int i;
|
||||
DVBSubDisplayDefinition *display_def = ctx->display_definition;
|
||||
struct cc_bitmap *rect = NULL;
|
||||
uint32_t *clut_table;
|
||||
int offset_x=0, offset_y=0;
|
||||
int ret = 0;
|
||||
|
||||
sub->type = CC_BITMAP;
|
||||
sub->lang_index = ctx->lang_index;
|
||||
if (display_def)
|
||||
{
|
||||
offset_x = display_def->x;
|
||||
offset_y = display_def->y;
|
||||
}
|
||||
|
||||
for (display = ctx->display_list; display; display = display->next)
|
||||
{
|
||||
region = get_region(ctx, display->region_id);
|
||||
if (region && region->dirty)
|
||||
sub->nb_data++;
|
||||
}
|
||||
if( sub->nb_data <= 0 )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
i = 0;
|
||||
rect = malloc( sizeof(struct cc_bitmap) * sub->nb_data);
|
||||
if(!rect)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
sub->start_time = get_visible_start();
|
||||
sub->end_time = sub->start_time + ( ctx->time_out * 1000 );
|
||||
sub->flags |= SUB_EOD_MARKER;
|
||||
sub->got_output = 1;
|
||||
sub->data = rect;
|
||||
for (display = ctx->display_list; display; display = display->next)
|
||||
{
|
||||
#ifdef ENABLE_OCR
|
||||
char *ocr_str = NULL;
|
||||
#endif
|
||||
region = get_region(ctx, display->region_id);
|
||||
|
||||
if (!region)
|
||||
@@ -1896,52 +1481,84 @@ static int dvbsub_display_end_segment(void *dvb_ctx, const uint8_t *buf,
|
||||
|
||||
if (!region->dirty)
|
||||
continue;
|
||||
|
||||
rect->x = display->x_pos + offset_x;
|
||||
rect->y = display->y_pos + offset_y;
|
||||
rect->w = region->width;
|
||||
rect->h = region->height;
|
||||
rect->nb_colors = (1 << region->depth);
|
||||
rect->linesize[0] = region->width;
|
||||
|
||||
clut = get_clut(ctx, region->clut);
|
||||
|
||||
if (!clut)
|
||||
clut = &default_clut;
|
||||
|
||||
i++;
|
||||
switch (region->depth)
|
||||
{
|
||||
case 2:
|
||||
clut_table = clut->clut4;
|
||||
case 8:
|
||||
clut_table = clut->clut256;
|
||||
break;
|
||||
case 4:
|
||||
default:
|
||||
clut_table = clut->clut16;
|
||||
break;
|
||||
}
|
||||
|
||||
rect->data[1] = malloc(1024);
|
||||
memset(rect->data[1], 0, 1024);
|
||||
memcpy(rect->data[1], clut_table, (1 << region->depth) * sizeof(uint32_t));
|
||||
|
||||
rect->data[0] = malloc(region->buf_size);
|
||||
memcpy(rect->data[0], region->pbuf, region->buf_size);
|
||||
#ifdef ENABLE_OCR
|
||||
ret = ocr_rect(ctx->ocr_ctx, rect, &ocr_str);
|
||||
if(ret >= 0)
|
||||
rect->ocr_text = ocr_str;
|
||||
#endif
|
||||
rect++;
|
||||
|
||||
}
|
||||
#ifdef DEBUG
|
||||
#ifdef DeBUG
|
||||
if (ctx->object_list)
|
||||
{
|
||||
//save_display_set(ctx);
|
||||
}
|
||||
#endif
|
||||
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
/**
|
||||
* @param dvb_ctx PreInitialized DVB context using DVB
|
||||
* @param data output subtitle data, to be implemented
|
||||
* @param data_size Output subtitle data size. pass the pointer to an integer, NOT to be NULL.
|
||||
* @param buf buffer containing segment data, first sync byte need to 0x0f.
|
||||
* does not include data_identifier and subtitle_stream_id.
|
||||
* @param buf_size size of buf buffer
|
||||
* @param sub output subtitle data
|
||||
*
|
||||
* @return -1 on error
|
||||
*/
|
||||
int dvbsub_decode(void *dvb_ctx, void *data, int *data_size,
|
||||
const unsigned char *buf, int buf_size)
|
||||
int dvbsub_decode(void *dvb_ctx, const unsigned char *buf, int buf_size, struct cc_subtitle *sub)
|
||||
{
|
||||
DVBSubContext *ctx = (DVBSubContext *) dvb_ctx;
|
||||
const uint8_t *p, *p_end;
|
||||
int segment_type;
|
||||
int page_id;
|
||||
int segment_length;
|
||||
int ret;
|
||||
int ret = 0;
|
||||
int got_segment = 0;
|
||||
|
||||
if (buf_size <= 6 || *buf != 0x0f)
|
||||
{
|
||||
mprint("incomplete or broken packet");
|
||||
mprint("incomplete or broken packet\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
p = buf;
|
||||
p_end = buf + buf_size;
|
||||
|
||||
set_fts();
|
||||
while (p_end - p >= 6 && *p == 0x0f)
|
||||
{
|
||||
p += 1;
|
||||
@@ -1953,7 +1570,7 @@ int dvbsub_decode(void *dvb_ctx, void *data, int *data_size,
|
||||
|
||||
if (p_end - p < segment_length)
|
||||
{
|
||||
mprint("incomplete or broken packet");
|
||||
mprint("incomplete or broken packet\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -1973,7 +1590,7 @@ int dvbsub_decode(void *dvb_ctx, void *data, int *data_size,
|
||||
case DVBSUB_CLUT_SEGMENT:
|
||||
ret = dvbsub_parse_clut_segment(dvb_ctx, p, segment_length);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
goto end;
|
||||
got_segment |= 4;
|
||||
break;
|
||||
case DVBSUB_OBJECT_SEGMENT:
|
||||
@@ -1985,8 +1602,7 @@ int dvbsub_decode(void *dvb_ctx, void *data, int *data_size,
|
||||
segment_length);
|
||||
break;
|
||||
case DVBSUB_DISPLAY_SEGMENT:
|
||||
*data_size = dvbsub_display_end_segment(dvb_ctx, p,
|
||||
segment_length);
|
||||
write_dvb_sub(dvb_ctx,sub);
|
||||
got_segment |= 16;
|
||||
break;
|
||||
default:
|
||||
@@ -2002,11 +1618,15 @@ int dvbsub_decode(void *dvb_ctx, void *data, int *data_size,
|
||||
// segments then we need no further data.
|
||||
if (got_segment == 15)
|
||||
{
|
||||
*data_size = dvbsub_display_end_segment(dvb_ctx, p, 0);
|
||||
write_dvb_sub(dvb_ctx,sub);
|
||||
|
||||
|
||||
}
|
||||
end:
|
||||
if ( ret >= 0 )
|
||||
ret = p - buf;
|
||||
|
||||
return p - buf;
|
||||
return ret;
|
||||
}
|
||||
/**
|
||||
* @func parse_dvb_description
|
||||
@@ -2051,9 +1671,9 @@ int parse_dvb_description(struct dvb_config* cfg, unsigned char*data,
|
||||
for (i = 0; i < cfg->n_language; i++, data += i * 8)
|
||||
{
|
||||
/* setting language to undefined if not found in language lkup table */
|
||||
for (j = 0, cfg->lang_index[i] = 0; dvb_language[j] != NULL; j++)
|
||||
for (j = 0, cfg->lang_index[i] = 0; language[j] != NULL; j++)
|
||||
{
|
||||
if (!strncmp((const char*) (data), dvb_language[j], 3))
|
||||
if (!strncmp((const char*) (data), language[j], 3))
|
||||
cfg->lang_index[i] = j;
|
||||
}
|
||||
cfg->sub_type[i] = data[3];
|
||||
@@ -2064,17 +1684,3 @@ int parse_dvb_description(struct dvb_config* cfg, unsigned char*data,
|
||||
|
||||
return 0;
|
||||
}
|
||||
/*
|
||||
* @func dvbsub_set_write the output structure in dvb
|
||||
* set ccx_s_write structure in dvb_ctx
|
||||
*
|
||||
* @param dvb_ctx context of dvb which was returned by dvbsub_init_decoder
|
||||
*
|
||||
* @param out output context returned by init_write
|
||||
*
|
||||
*/
|
||||
void dvbsub_set_write(void *dvb_ctx, struct ccx_s_write *out)
|
||||
{
|
||||
DVBSubContext *ctx = (DVBSubContext *) dvb_ctx;
|
||||
ctx->out = out;
|
||||
}
|
||||
@@ -18,7 +18,7 @@
|
||||
|
||||
#define MAX_LANGUAGE_PER_DESC 5
|
||||
|
||||
#include "ccextractor.h"
|
||||
#include "lib_ccx.h"
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
@@ -37,32 +37,26 @@ struct dvb_config
|
||||
};
|
||||
|
||||
/**
|
||||
* @param composition_id composition-page_id found in Subtitle descriptors
|
||||
* associated with subtitle stream in the PMT
|
||||
* it could be -1 if not found in PMT.
|
||||
* @param ancillary_id ancillary-page_id found in Subtitle descriptors
|
||||
* associated with subtitle stream in the PMT.
|
||||
* it could be -1 if not found in PMT.
|
||||
* @param cfg Structure containg configuration
|
||||
*
|
||||
* @return DVB context kept as void* for abstraction
|
||||
*
|
||||
*/
|
||||
void* dvbsub_init_decoder(int composition_id, int ancillary_id);
|
||||
void* dvbsub_init_decoder(struct dvb_config* cfg);
|
||||
|
||||
int dvbsub_close_decoder(void *dvb_ctx);
|
||||
|
||||
/**
|
||||
* @param dvb_ctx PreInitialized DVB context using DVB
|
||||
* @param data output subtitle data, to be implemented
|
||||
* @param data_size Output subtitle data size. pass the pointer to an intiger, NOT to be NULL.
|
||||
* @param buf buffer containg segment data, first sync byte needto 0x0f.
|
||||
* @param buf buffer containing segment data, first sync byte need to 0x0f.
|
||||
* does not include data_identifier and subtitle_stream_id.
|
||||
* @param buf_size size of buf buffer
|
||||
* @param sub output subtitle data
|
||||
*
|
||||
* @return -1 on error
|
||||
*/
|
||||
int dvbsub_decode(void *dvb_ctx, void *data, int *data_size,
|
||||
const unsigned char *buf, int buf_size);
|
||||
int dvbsub_decode(void *dvb_ctx, const unsigned char *buf, int buf_size, struct cc_subtitle *sub);
|
||||
|
||||
/**
|
||||
* @func parse_dvb_description
|
||||
*
|
||||
@@ -82,8 +76,8 @@ int parse_dvb_description(struct dvb_config* cfg, unsigned char*data,
|
||||
*
|
||||
* @param dvb_ctx context of dvb which was returned by dvbsub_init_decoder
|
||||
*
|
||||
* @param out output context returned by init_write
|
||||
*
|
||||
* @param out output context returned by init_write
|
||||
*
|
||||
*/
|
||||
void dvbsub_set_write(void *dvb_ctx, struct ccx_s_write *out);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#include "ccextractor.h"
|
||||
|
||||
#include "lib_ccx.h"
|
||||
#include "ccx_common_option.h"
|
||||
// Functions to parse a mpeg-2 data stream, see ISO/IEC 13818-2 6.2
|
||||
|
||||
static int no_bitstream_error = 0;
|
||||
@@ -20,24 +20,24 @@ static unsigned pulldownfields = 0;
|
||||
|
||||
static uint8_t search_start_code(struct bitstream *esstream);
|
||||
static uint8_t next_start_code(struct bitstream *esstream);
|
||||
static int es_video_sequence(struct bitstream *esstream);
|
||||
static int read_seq_info(struct bitstream *esstream);
|
||||
static int sequence_header(struct bitstream *esstream);
|
||||
static int es_video_sequence(struct lib_ccx_ctx *ctx, struct bitstream *esstream, struct cc_subtitle *sub);
|
||||
static int read_seq_info(struct lib_ccx_ctx *ctx, struct bitstream *esstream);
|
||||
static int sequence_header(struct lib_ccx_ctx *ctx, struct bitstream *esstream);
|
||||
static int sequence_ext(struct bitstream *esstream);
|
||||
static int read_gop_info(struct bitstream *esstream);
|
||||
static int gop_header(struct bitstream *esstream);
|
||||
static int read_pic_info(struct bitstream *esstream);
|
||||
static int read_gop_info(struct lib_ccx_ctx *ctx, struct bitstream *esstream, struct cc_subtitle *sub);
|
||||
static int gop_header(struct lib_ccx_ctx *ctx, struct bitstream *esstream, struct cc_subtitle *sub);
|
||||
static int read_pic_info(struct lib_ccx_ctx *ctx, struct bitstream *esstream, struct cc_subtitle *sub);
|
||||
static int pic_header(struct bitstream *esstream);
|
||||
static int pic_coding_ext(struct bitstream *esstream);
|
||||
static int read_eau_info(struct bitstream *esstream, int udtype);
|
||||
static int extension_and_user_data(struct bitstream *esstream, int udtype);
|
||||
static int read_eau_info(struct lib_ccx_ctx* ctx, struct bitstream *esstream, int udtype, struct cc_subtitle *sub);
|
||||
static int extension_and_user_data(struct lib_ccx_ctx *ctx, struct bitstream *esstream, int udtype, struct cc_subtitle *sub);
|
||||
static int read_pic_data(struct bitstream *esstream);
|
||||
|
||||
|
||||
/* Process a mpeg-2 data stream with "lenght" bytes in buffer "data".
|
||||
* The number of processed bytes is returned.
|
||||
* Defined in ISO/IEC 13818-2 6.2 */
|
||||
LLONG process_m2v (unsigned char *data, LLONG length)
|
||||
LLONG process_m2v (struct lib_ccx_ctx *ctx, unsigned char *data, LLONG length,struct cc_subtitle *sub)
|
||||
{
|
||||
if (length<8) // Need to look ahead 8 bytes
|
||||
return length;
|
||||
@@ -48,7 +48,7 @@ LLONG process_m2v (unsigned char *data, LLONG length)
|
||||
|
||||
// Process data. The return value is ignored as esstream.pos holds
|
||||
// the information how far the parsing progressed.
|
||||
es_video_sequence(&esstream);
|
||||
es_video_sequence(ctx, &esstream, sub);
|
||||
|
||||
// This returns how many bytes were processed and can therefore
|
||||
// be discarded from "buffer". "esstream.pos" points to the next byte
|
||||
@@ -185,7 +185,7 @@ static uint8_t next_start_code(struct bitstream *esstream)
|
||||
// Otherwise. estream->pos shall point to the position where
|
||||
// the next call will continue, i.e. the possible begin of an
|
||||
// unfinished video sequence or after the finished sequence.
|
||||
static int es_video_sequence(struct bitstream *esstream)
|
||||
static int es_video_sequence(struct lib_ccx_ctx *ctx, struct bitstream *esstream, struct cc_subtitle *sub)
|
||||
{
|
||||
// Avoid "Skip forward" message on first call and later only
|
||||
// once per search.
|
||||
@@ -260,7 +260,7 @@ static int es_video_sequence(struct bitstream *esstream)
|
||||
|
||||
if (!in_pic_data && startcode == 0xB3)
|
||||
{
|
||||
if (!read_seq_info(esstream))
|
||||
if (!read_seq_info(ctx, esstream))
|
||||
{
|
||||
if (esstream->error)
|
||||
no_bitstream_error = 0;
|
||||
@@ -272,7 +272,7 @@ static int es_video_sequence(struct bitstream *esstream)
|
||||
|
||||
if (!in_pic_data && startcode == 0xB8)
|
||||
{
|
||||
if (!read_gop_info(esstream))
|
||||
if (!read_gop_info(ctx, esstream,sub))
|
||||
{
|
||||
if (esstream->error)
|
||||
no_bitstream_error = 0;
|
||||
@@ -284,7 +284,7 @@ static int es_video_sequence(struct bitstream *esstream)
|
||||
|
||||
if (!in_pic_data && startcode == 0x00)
|
||||
{
|
||||
if (!read_pic_info(esstream))
|
||||
if (!read_pic_info(ctx, esstream, sub))
|
||||
{
|
||||
if (esstream->error)
|
||||
no_bitstream_error = 0;
|
||||
@@ -300,7 +300,7 @@ static int es_video_sequence(struct bitstream *esstream)
|
||||
// This check needs to be before the "in_pic_data" part.
|
||||
if ( saw_seqgoppic && (startcode == 0xB2 || startcode == 0xB5))
|
||||
{
|
||||
if (!read_eau_info(esstream, saw_seqgoppic-1))
|
||||
if (!read_eau_info(ctx, esstream, saw_seqgoppic-1, sub))
|
||||
{
|
||||
if (esstream->error)
|
||||
no_bitstream_error = 0;
|
||||
@@ -345,20 +345,20 @@ static int es_video_sequence(struct bitstream *esstream)
|
||||
// If a bitstream syntax problem occured the bitstream will
|
||||
// point to after the problem, in case we run out of data the bitstream
|
||||
// will point to where we want to restart after getting more.
|
||||
static int read_seq_info(struct bitstream *esstream)
|
||||
static int read_seq_info(struct lib_ccx_ctx *ctx, struct bitstream *esstream)
|
||||
{
|
||||
dbg_print(CCX_DMT_VERBOSE, "Read Sequence Info\n");
|
||||
|
||||
// We only get here after seeing that start code
|
||||
if (next_u32(esstream) != 0xB3010000) // LSB first (0x000001B3)
|
||||
fatal(EXIT_BUG_BUG, "read_seq_info: Impossible!");
|
||||
fatal(CCX_COMMON_EXIT_BUG_BUG, "read_seq_info: Impossible!");
|
||||
|
||||
// If we get here esstream points to the start of a sequence_header_code
|
||||
// should we run out of data in esstream this is where we want to restart
|
||||
// after getting more.
|
||||
unsigned char *video_seq_start = esstream->pos;
|
||||
|
||||
sequence_header(esstream);
|
||||
sequence_header(ctx, esstream);
|
||||
sequence_ext(esstream);
|
||||
// FIXME: if sequence extension is missing this is not MPEG-2,
|
||||
// or broken. Set bitstream error.
|
||||
@@ -382,7 +382,7 @@ static int read_seq_info(struct bitstream *esstream)
|
||||
// Return TRUE if the data parsing finished, FALSE otherwise.
|
||||
// estream->pos is advanced. Data is only processed if esstream->error
|
||||
// is FALSE, parsing can set esstream->error to TRUE.
|
||||
static int sequence_header(struct bitstream *esstream)
|
||||
static int sequence_header(struct lib_ccx_ctx *ctx, struct bitstream *esstream)
|
||||
{
|
||||
dbg_print(CCX_DMT_VERBOSE, "Sequence header\n");
|
||||
|
||||
@@ -391,17 +391,17 @@ static int sequence_header(struct bitstream *esstream)
|
||||
|
||||
// We only get here after seeing that start code
|
||||
if (read_u32(esstream) != 0xB3010000) // LSB first (0x000001B3)
|
||||
fatal(EXIT_BUG_BUG, "Impossible!");
|
||||
fatal(CCX_COMMON_EXIT_BUG_BUG, "Impossible!");
|
||||
|
||||
unsigned hor_size = (unsigned) read_bits(esstream,12);
|
||||
unsigned vert_size = (unsigned) read_bits(esstream,12);
|
||||
unsigned aspect_ratio = (unsigned) read_bits(esstream,4);
|
||||
unsigned frame_rate = (unsigned) read_bits(esstream,4);
|
||||
|
||||
file_report.width = hor_size;
|
||||
file_report.height = vert_size;
|
||||
file_report.aspect_ratio = aspect_ratio;
|
||||
file_report.frame_rate = frame_rate;
|
||||
ctx->freport.width = hor_size;
|
||||
ctx->freport.height = vert_size;
|
||||
ctx->freport.aspect_ratio = aspect_ratio;
|
||||
ctx->freport.frame_rate = frame_rate;
|
||||
|
||||
// Discard some information
|
||||
read_bits(esstream, 18+1+10+1);
|
||||
@@ -425,9 +425,10 @@ static int sequence_header(struct bitstream *esstream)
|
||||
// If horizontal/vertical size, framerate and/or aspect
|
||||
// ratio are ilegal, we discard the
|
||||
// whole sequence info.
|
||||
if (vert_size >= 288 && vert_size <= 1088 &&
|
||||
if (vert_size >= 288 && vert_size <= 1088 &&
|
||||
hor_size >= 352 && hor_size <= 1920 &&
|
||||
hor_size / vert_size >= 352/576 && hor_size / vert_size <= 2 &&
|
||||
(hor_size*100) / vert_size >= (352*100)/576 && // The weird *100 is to avoid using floats
|
||||
hor_size / vert_size <= 2 &&
|
||||
frame_rate>0 && frame_rate<9 &&
|
||||
aspect_ratio>0 && aspect_ratio<5)
|
||||
{
|
||||
@@ -438,7 +439,7 @@ static int sequence_header(struct bitstream *esstream)
|
||||
/ MPEG_CLOCK_FREQ);
|
||||
mprint (" at %02u:%02u",cur_sec/60, cur_sec % 60);
|
||||
}
|
||||
mprint ("\n");
|
||||
mprint ("\n");
|
||||
mprint ("[%u * %u] [AR: %s] [FR: %s]",
|
||||
hor_size,vert_size,
|
||||
aspect_ratio_types[aspect_ratio],
|
||||
@@ -455,8 +456,8 @@ static int sequence_header(struct bitstream *esstream)
|
||||
activity_video_info (hor_size,vert_size,
|
||||
aspect_ratio_types[aspect_ratio],
|
||||
framerates_types[frame_rate]);
|
||||
}
|
||||
else
|
||||
}
|
||||
else
|
||||
{
|
||||
dbg_print(CCX_DMT_VERBOSE, "\nInvalid sequence header:\n");
|
||||
dbg_print(CCX_DMT_VERBOSE, "V: %u H: %u FR: %u AS: %u\n",
|
||||
@@ -527,20 +528,20 @@ static int sequence_ext(struct bitstream *esstream)
|
||||
// If a bitstream syntax problem occured the bitstream will
|
||||
// point to after the problem, in case we run out of data the bitstream
|
||||
// will point to where we want to restart after getting more.
|
||||
static int read_gop_info(struct bitstream *esstream)
|
||||
static int read_gop_info(struct lib_ccx_ctx *ctx, struct bitstream *esstream, struct cc_subtitle *sub)
|
||||
{
|
||||
dbg_print(CCX_DMT_VERBOSE, "Read GOP Info\n");
|
||||
|
||||
// We only get here after seeing that start code
|
||||
if (next_u32(esstream) != 0xB8010000) // LSB first (0x000001B8)
|
||||
fatal(EXIT_BUG_BUG, "Impossible!");
|
||||
fatal(CCX_COMMON_EXIT_BUG_BUG, "Impossible!");
|
||||
|
||||
// If we get here esstream points to the start of a group_start_code
|
||||
// should we run out of data in esstream this is where we want to restart
|
||||
// after getting more.
|
||||
unsigned char *gop_info_start = esstream->pos;
|
||||
|
||||
gop_header(esstream);
|
||||
gop_header(ctx, esstream, sub);
|
||||
//extension_and_user_data(esstream);
|
||||
|
||||
if (esstream->error)
|
||||
@@ -561,7 +562,7 @@ static int read_gop_info(struct bitstream *esstream)
|
||||
// Return TRUE if the data parsing finished, FALSE otherwise.
|
||||
// estream->pos is advanced. Data is only processed if esstream->error
|
||||
// is FALSE, parsing can set esstream->error to TRUE.
|
||||
static int gop_header(struct bitstream *esstream)
|
||||
static int gop_header(struct lib_ccx_ctx *ctx, struct bitstream *esstream, struct cc_subtitle *sub)
|
||||
{
|
||||
dbg_print(CCX_DMT_VERBOSE, "GOP header\n");
|
||||
|
||||
@@ -570,7 +571,7 @@ static int gop_header(struct bitstream *esstream)
|
||||
|
||||
// We only get here after seeing that start code
|
||||
if (read_u32(esstream) != 0xB8010000) // LSB first (0x000001B8)
|
||||
fatal(EXIT_BUG_BUG, "Impossible!");
|
||||
fatal(CCX_COMMON_EXIT_BUG_BUG, "Impossible!");
|
||||
|
||||
unsigned drop_frame_flag = (unsigned) read_bits(esstream,1);
|
||||
struct gop_time_code gtc;
|
||||
@@ -594,7 +595,7 @@ static int gop_header(struct bitstream *esstream)
|
||||
// Flush buffered cc blocks before doing the housekeeping
|
||||
if (has_ccdata_buffered)
|
||||
{
|
||||
process_hdcc();
|
||||
process_hdcc(ctx, sub);
|
||||
}
|
||||
|
||||
// Last GOPs pulldown frames
|
||||
@@ -612,19 +613,19 @@ static int gop_header(struct bitstream *esstream)
|
||||
// are 20% or more deviation.
|
||||
if ( (ccx_options.debug_mask & CCX_DMT_TIME)
|
||||
&& ((gtc.ms - gop_time.ms // more than 20% longer
|
||||
> frames_since_last_gop*1000.0/current_fps*1.2)
|
||||
> ctx->frames_since_last_gop*1000.0/current_fps*1.2)
|
||||
||
|
||||
(gtc.ms - gop_time.ms // or 20% shorter
|
||||
< frames_since_last_gop*1000.0/current_fps*0.8))
|
||||
< ctx->frames_since_last_gop*1000.0/current_fps*0.8))
|
||||
&& first_gop_time.inited )
|
||||
{
|
||||
mprint("\rWarning: Jump in GOP timing.\n");
|
||||
mprint(" (old) %s",
|
||||
print_mstime(gop_time.ms));
|
||||
mprint(" + %s (%uF)",
|
||||
print_mstime((LLONG) (frames_since_last_gop
|
||||
print_mstime((LLONG) (ctx->frames_since_last_gop
|
||||
*1000.0/current_fps)),
|
||||
frames_since_last_gop);
|
||||
ctx->frames_since_last_gop);
|
||||
mprint(" != (new) %s\n",
|
||||
print_mstime(gtc.ms));
|
||||
}
|
||||
@@ -657,9 +658,9 @@ static int gop_header(struct bitstream *esstream)
|
||||
|
||||
gop_time = gtc;
|
||||
|
||||
frames_since_last_gop=0;
|
||||
ctx->frames_since_last_gop=0;
|
||||
// Indicate that we read a gop header (since last frame number 0)
|
||||
saw_gop_header = 1;
|
||||
ctx->saw_gop_header = 1;
|
||||
|
||||
// If we use GOP timing, reconstruct the PTS from the GOP
|
||||
if (ccx_options.use_gop_as_pts==1)
|
||||
@@ -698,13 +699,13 @@ static int gop_header(struct bitstream *esstream)
|
||||
// If a bitstream syntax problem occured the bitstream will
|
||||
// point to after the problem, in case we run out of data the bitstream
|
||||
// will point to where we want to restart after getting more.
|
||||
static int read_pic_info(struct bitstream *esstream)
|
||||
static int read_pic_info(struct lib_ccx_ctx *ctx, struct bitstream *esstream, struct cc_subtitle *sub)
|
||||
{
|
||||
dbg_print(CCX_DMT_VERBOSE, "Read PIC Info\n");
|
||||
|
||||
// We only get here after seeing that start code
|
||||
if (next_u32(esstream) != 0x00010000) // LSB first (0x00000100)
|
||||
fatal(EXIT_BUG_BUG, "Impossible!");
|
||||
fatal(CCX_COMMON_EXIT_BUG_BUG, "Impossible!");
|
||||
|
||||
// If we get here esstream points to the start of a group_start_code
|
||||
// should we run out of data in esstream this is where we want to restart
|
||||
@@ -729,18 +730,18 @@ static int read_pic_info(struct bitstream *esstream)
|
||||
// A new anchor frame - flush buffered caption data. Might be flushed
|
||||
// in GOP header already.
|
||||
if (picture_coding_type==CCX_FRAME_TYPE_I_FRAME || picture_coding_type==CCX_FRAME_TYPE_P_FRAME)
|
||||
{
|
||||
// if (((picture_structure != 0x1) && (picture_structure != 0x2)) ||
|
||||
// (temporal_reference != current_tref))
|
||||
// {
|
||||
{
|
||||
if (((picture_structure != 0x1) && (picture_structure != 0x2)) ||
|
||||
(temporal_reference != current_tref))
|
||||
{
|
||||
// NOTE: process_hdcc() needs to be called before set_fts() as it
|
||||
// uses fts_now to re-create the timeline !!!!!
|
||||
if (has_ccdata_buffered)
|
||||
{
|
||||
process_hdcc();
|
||||
process_hdcc(ctx, sub);
|
||||
}
|
||||
anchor_hdcc(temporal_reference);
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
current_tref = temporal_reference;
|
||||
@@ -758,7 +759,7 @@ static int read_pic_info(struct bitstream *esstream)
|
||||
(unsigned) (current_pts), temporal_reference,
|
||||
pict_types[picture_coding_type],
|
||||
(unsigned) (frames_since_ref_time),
|
||||
(unsigned) (frames_since_last_gop));
|
||||
(unsigned) (ctx->frames_since_last_gop));
|
||||
dbg_print(CCX_DMT_VIDES, " t:%d r:%d p:%d", top_field_first,
|
||||
repeat_first_field, progressive_frame);
|
||||
dbg_print(CCX_DMT_VIDES, " FTS: %s\n", print_mstime(get_fts()));
|
||||
@@ -772,16 +773,16 @@ static int read_pic_info(struct bitstream *esstream)
|
||||
// the beginning of the GOP, otherwise it is now.
|
||||
if(temporal_reference == 0)
|
||||
{
|
||||
last_gop_length = maxtref + 1;
|
||||
ctx->last_gop_length = maxtref + 1;
|
||||
maxtref = temporal_reference;
|
||||
|
||||
// frames_since_ref_time is used in set_fts()
|
||||
|
||||
if( saw_gop_header )
|
||||
if( ctx->saw_gop_header )
|
||||
{
|
||||
// This time (fts_at_gop_start) that was set in the
|
||||
// GOP header and it might be off by one GOP. See the comment there.
|
||||
frames_since_ref_time = frames_since_last_gop; // Should this be 0?
|
||||
frames_since_ref_time = ctx->frames_since_last_gop; // Should this be 0?
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -796,39 +797,39 @@ static int read_pic_info(struct bitstream *esstream)
|
||||
print_debug_timing();
|
||||
}
|
||||
|
||||
saw_gop_header = 0; // Reset the value
|
||||
ctx->saw_gop_header = 0; // Reset the value
|
||||
}
|
||||
|
||||
if ( !saw_gop_header && picture_coding_type==CCX_FRAME_TYPE_I_FRAME )
|
||||
if ( !ctx->saw_gop_header && picture_coding_type==CCX_FRAME_TYPE_I_FRAME )
|
||||
{
|
||||
// A new GOP beginns with an I-frame. Lets hope there are
|
||||
// never more than one per GOP
|
||||
frames_since_last_gop = 0;
|
||||
ctx->frames_since_last_gop = 0;
|
||||
}
|
||||
|
||||
// Set maxtref
|
||||
if( temporal_reference > maxtref ) {
|
||||
maxtref = temporal_reference;
|
||||
if (maxtref+1 > max_gop_length)
|
||||
max_gop_length = maxtref+1;
|
||||
if (maxtref+1 > ctx->max_gop_length)
|
||||
ctx->max_gop_length = maxtref+1;
|
||||
}
|
||||
|
||||
unsigned extraframe = 0;
|
||||
if (repeat_first_field)
|
||||
{
|
||||
pulldownfields++;
|
||||
total_pulldownfields++;
|
||||
if ( current_progressive_sequence || !(total_pulldownfields%2) )
|
||||
ctx->total_pulldownfields++;
|
||||
if ( current_progressive_sequence || !(ctx->total_pulldownfields%2) )
|
||||
extraframe = 1;
|
||||
if ( current_progressive_sequence && top_field_first )
|
||||
extraframe = 2;
|
||||
dbg_print(CCX_DMT_VIDES, "Pulldown: total pd fields: %d - %d extra frames\n",
|
||||
total_pulldownfields, extraframe);
|
||||
ctx->total_pulldownfields, extraframe);
|
||||
}
|
||||
|
||||
total_pulldownframes += extraframe;
|
||||
ctx->total_pulldownframes += extraframe;
|
||||
total_frames_count += 1+extraframe;
|
||||
frames_since_last_gop += 1+extraframe;
|
||||
ctx->frames_since_last_gop += 1+extraframe;
|
||||
frames_since_ref_time += 1+extraframe;
|
||||
|
||||
dbg_print(CCX_DMT_VERBOSE, "Read PIC Info - processed\n\n");
|
||||
@@ -849,7 +850,7 @@ static int pic_header(struct bitstream *esstream)
|
||||
|
||||
// We only get here after seeing that start code
|
||||
if (read_u32(esstream) != 0x00010000) // LSB first (0x00000100)
|
||||
fatal(EXIT_BUG_BUG, "Impossible!");
|
||||
fatal(CCX_COMMON_EXIT_BUG_BUG, "Impossible!");
|
||||
|
||||
temporal_reference = (int) read_bits(esstream,10);
|
||||
picture_coding_type = (enum ccx_frame_type) read_bits(esstream,3);
|
||||
@@ -945,7 +946,7 @@ static int pic_coding_ext(struct bitstream *esstream)
|
||||
// If a bitstream syntax problem occured the bitstream will
|
||||
// point to after the problem, in case we run out of data the bitstream
|
||||
// will point to where we want to restart after getting more.
|
||||
static int read_eau_info(struct bitstream *esstream, int udtype)
|
||||
static int read_eau_info(struct lib_ccx_ctx* ctx, struct bitstream *esstream, int udtype, struct cc_subtitle *sub)
|
||||
{
|
||||
dbg_print(CCX_DMT_VERBOSE, "Read Extension and User Info\n");
|
||||
|
||||
@@ -953,13 +954,13 @@ static int read_eau_info(struct bitstream *esstream, int udtype)
|
||||
unsigned char *tst = next_bytes(esstream, 4);
|
||||
if (!tst || tst[0]!=0x00 || tst[1]!=0x00 || tst[2]!=0x01
|
||||
|| (tst[3]!=0xB2 && tst[3]!=0xB5) ) // (0x000001 B2||B5)
|
||||
fatal(EXIT_BUG_BUG, "Impossible!");
|
||||
fatal(CCX_COMMON_EXIT_BUG_BUG, "Impossible!");
|
||||
|
||||
// The following extension_and_user_data() function makes sure that
|
||||
// user data is not evaluated twice. Should the function run out of
|
||||
// data it will make sure that esstream points to where we want to
|
||||
// continue after getting more.
|
||||
if( !extension_and_user_data(esstream, udtype) )
|
||||
if( !extension_and_user_data(ctx, esstream, udtype, sub) )
|
||||
{
|
||||
if (esstream->error)
|
||||
dbg_print(CCX_DMT_VERBOSE, "\nWarning: Retry while reading Extension and User Data!\n");
|
||||
@@ -978,7 +979,7 @@ static int read_eau_info(struct bitstream *esstream, int udtype)
|
||||
// Return TRUE if the data parsing finished, FALSE otherwise.
|
||||
// estream->pos is advanced. Data is only processed if esstream->error
|
||||
// is FALSE, parsing can set esstream->error to TRUE.
|
||||
static int extension_and_user_data(struct bitstream *esstream, int udtype)
|
||||
static int extension_and_user_data(struct lib_ccx_ctx *ctx, struct bitstream *esstream, int udtype, struct cc_subtitle *sub)
|
||||
{
|
||||
dbg_print(CCX_DMT_VERBOSE, "Extension and user data(%d)\n", udtype);
|
||||
|
||||
@@ -1002,7 +1003,7 @@ static int extension_and_user_data(struct bitstream *esstream, int udtype)
|
||||
// Advanve esstream to the next startcode. Verify that
|
||||
// the whole extension was available and discard blocks
|
||||
// followed by PACK headers. The latter usually indicates
|
||||
// a PS treated as an ES.
|
||||
// a PS treated as an ES.
|
||||
uint8_t nextstartcode = search_start_code(esstream);
|
||||
if (nextstartcode == 0xBA)
|
||||
{
|
||||
@@ -1030,7 +1031,7 @@ static int extension_and_user_data(struct bitstream *esstream, int udtype)
|
||||
{
|
||||
struct bitstream ustream;
|
||||
init_bitstream(&ustream, dstart, esstream->pos);
|
||||
user_data(&ustream, udtype);
|
||||
user_data(ctx, &ustream, udtype, sub);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1,4 +1,4 @@
|
||||
#include "ccextractor.h"
|
||||
#include "lib_ccx.h"
|
||||
|
||||
|
||||
// Parse the user data for captions. The udtype variable denotes
|
||||
@@ -9,8 +9,10 @@
|
||||
// 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 bitstream *ustream, int udtype)
|
||||
int user_data(struct lib_ccx_ctx *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);
|
||||
|
||||
// Shall not happen
|
||||
@@ -19,26 +21,26 @@ int user_data(struct bitstream *ustream, int udtype)
|
||||
// ustream->error=1;
|
||||
return 0; // Actually discarded on call.
|
||||
// CFS: Seen in a Wobble edited file.
|
||||
// fatal(EXIT_BUG_BUG, "user_data: Impossible!");
|
||||
// fatal(CCX_COMMON_EXIT_BUG_BUG, "user_data: Impossible!");
|
||||
}
|
||||
|
||||
// Do something
|
||||
stat_numuserheaders++;
|
||||
ctx->stat_numuserheaders++;
|
||||
//header+=4;
|
||||
|
||||
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(EXIT_BUG_BUG, "user_data: Impossible!");
|
||||
// CFS: Seen in Stick_VHS.mpg.
|
||||
// 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 ) )
|
||||
{
|
||||
stat_dvdccheaders++;
|
||||
ctx->stat_dvdccheaders++;
|
||||
|
||||
// Probably unneeded, but keep looking for extra caption blocks
|
||||
int maybeextracb = 1;
|
||||
@@ -51,7 +53,7 @@ int user_data(struct bitstream *ustream, int udtype)
|
||||
int truncate_flag = (int) read_bits(ustream,1); // truncate_flag - one CB extra
|
||||
|
||||
int field1packet = 0; // expect Field 1 first
|
||||
if (pattern_flag == 0x00)
|
||||
if (pattern_flag == 0x00)
|
||||
field1packet=1; // expect Field 1 second
|
||||
|
||||
dbg_print(CCX_DMT_VERBOSE, "Reading %d%s DVD CC segments\n",
|
||||
@@ -87,12 +89,12 @@ int user_data(struct bitstream *ustream, int udtype)
|
||||
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(data);
|
||||
do_cb(dec_ctx, data, sub);
|
||||
rcbcount++;
|
||||
}
|
||||
else
|
||||
@@ -118,12 +120,12 @@ int user_data(struct bitstream *ustream, int udtype)
|
||||
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(data);
|
||||
do_cb(dec_ctx, data, sub);
|
||||
ecbcount++;
|
||||
}
|
||||
else
|
||||
@@ -144,7 +146,7 @@ int user_data(struct bitstream *ustream, int udtype)
|
||||
{
|
||||
unsigned char cc_data[3*31+1]; // Maximum cc_count is 31
|
||||
|
||||
stat_scte20ccheaders++;
|
||||
ctx->stat_scte20ccheaders++;
|
||||
read_bytes(ustream, 2); // "03 01"
|
||||
|
||||
unsigned cc_count = (unsigned int) read_bits(ustream,5);
|
||||
@@ -164,7 +166,7 @@ int user_data(struct bitstream *ustream, int udtype)
|
||||
/* marker = (unsigned int)read_bits(ustream,1); // TODO: Add syntax check */
|
||||
|
||||
if (ustream->bitsleft < 0)
|
||||
fatal(EXIT_BUG_BUG, "Oops!");
|
||||
fatal(CCX_COMMON_EXIT_BUG_BUG, "Oops!");
|
||||
|
||||
// Field_number is either
|
||||
// 0 .. forbiden
|
||||
@@ -192,7 +194,7 @@ int user_data(struct bitstream *ustream, int udtype)
|
||||
}
|
||||
}
|
||||
cc_data[cc_count*3]=0xFF;
|
||||
store_hdcc(cc_data, cc_count, current_tref, fts_now);
|
||||
store_hdcc(ctx, cc_data, cc_count, current_tref, fts_now, sub);
|
||||
|
||||
dbg_print(CCX_DMT_VERBOSE, "Reading SCTE 20 CC blocks - done\n");
|
||||
}
|
||||
@@ -200,31 +202,31 @@ int user_data(struct bitstream *ustream, int udtype)
|
||||
}
|
||||
// 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
|
||||
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)
|
||||
stat_replay4000headers++;
|
||||
ctx->stat_replay4000headers++;
|
||||
else
|
||||
stat_replay5000headers++;
|
||||
ctx->stat_replay5000headers++;
|
||||
|
||||
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(data);
|
||||
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(data);
|
||||
do_cb(dec_ctx, data, sub);
|
||||
}
|
||||
// HDTV - see A/53 Part 4 (Video)
|
||||
else if ( !memcmp(ud_header,"\x47\x41\x39\x34", 4 ) )
|
||||
{
|
||||
stat_hdtv++;
|
||||
ctx->stat_hdtv++;
|
||||
|
||||
read_bytes(ustream, 4); // "47 41 39 34"
|
||||
|
||||
@@ -243,7 +245,7 @@ int user_data(struct bitstream *ustream, int udtype)
|
||||
int proceed = 1;
|
||||
unsigned char *cc_data = read_bytes(ustream, cc_count*3);
|
||||
if (ustream->bitsleft < 0)
|
||||
fatal(EXIT_BUG_BUG, "Not enough for CC captions!");
|
||||
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.
|
||||
@@ -264,7 +266,7 @@ int user_data(struct bitstream *ustream, int udtype)
|
||||
// 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(cc_data, cc_count, current_tref, fts_now);
|
||||
store_hdcc(ctx, cc_data, cc_count, current_tref, fts_now, sub);
|
||||
|
||||
dbg_print(CCX_DMT_VERBOSE, "Reading HDTV blocks - done\n");
|
||||
}
|
||||
@@ -285,7 +287,7 @@ int user_data(struct bitstream *ustream, int udtype)
|
||||
|
||||
dbg_print(CCX_DMT_VERBOSE, "Reading Dish Network user data\n");
|
||||
|
||||
stat_dishheaders++;
|
||||
ctx->stat_dishheaders++;
|
||||
|
||||
read_bytes(ustream, 2); // "05 02"
|
||||
|
||||
@@ -344,7 +346,7 @@ int user_data(struct bitstream *ustream, int udtype)
|
||||
|
||||
dishdata[cc_count*3] = 0xFF; // Set end marker
|
||||
|
||||
store_hdcc(dishdata, cc_count, current_tref, fts_now);
|
||||
store_hdcc(ctx, dishdata, cc_count, current_tref, fts_now, sub);
|
||||
|
||||
// Ignore 3 (0x0A, followed by two unknown) bytes.
|
||||
break;
|
||||
@@ -360,7 +362,7 @@ int user_data(struct bitstream *ustream, int udtype)
|
||||
cc_count = 2;
|
||||
dishdata[1]=dcd[1];
|
||||
dishdata[2]=dcd[2];
|
||||
|
||||
|
||||
dishdata[3]=0x04; // Field 1
|
||||
dishdata[4]=dcd[3];
|
||||
dishdata[5]=dcd[4];
|
||||
@@ -368,8 +370,8 @@ int user_data(struct bitstream *ustream, int udtype)
|
||||
|
||||
dbg_print(CCX_DMT_PARSE, "%s", debug_608toASC( dishdata, 0) );
|
||||
dbg_print(CCX_DMT_PARSE, "%s:\n", debug_608toASC( dishdata+3, 0) );
|
||||
|
||||
store_hdcc(dishdata, cc_count, current_tref, fts_now);
|
||||
|
||||
store_hdcc(ctx, dishdata, cc_count, current_tref, fts_now, sub);
|
||||
|
||||
// Ignore 4 (0x020A, followed by two unknown) bytes.
|
||||
break;
|
||||
@@ -379,7 +381,7 @@ int user_data(struct bitstream *ustream, int udtype)
|
||||
// 0 : 0x04
|
||||
// - the following are from previous 0x05 caption header -
|
||||
// 1 : prev dcd[2]
|
||||
// 2-3: prev dcd[3-4]
|
||||
// 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],
|
||||
@@ -429,13 +431,13 @@ int user_data(struct bitstream *ustream, int udtype)
|
||||
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:\n", debug_608toASC( dishdata+3, 0) );
|
||||
}
|
||||
|
||||
store_hdcc(dishdata, cc_count, current_tref, fts_now);
|
||||
|
||||
|
||||
store_hdcc(ctx, dishdata, cc_count, current_tref, fts_now, sub);
|
||||
|
||||
// Ignore 3 (0x0A, followed by 2 unknown) bytes.
|
||||
break;
|
||||
default:
|
||||
@@ -450,7 +452,7 @@ int user_data(struct bitstream *ustream, int udtype)
|
||||
else if ( !memcmp(ud_header,"\x02\x09", 2 ) )
|
||||
{
|
||||
// Either a documentation or more examples are needed.
|
||||
stat_divicom++;
|
||||
ctx->stat_divicom++;
|
||||
|
||||
unsigned char data[3];
|
||||
|
||||
@@ -460,7 +462,7 @@ int user_data(struct bitstream *ustream, int udtype)
|
||||
data[0]=0x04; // Field 1
|
||||
data[1]=read_u8(ustream);
|
||||
data[2]=read_u8(ustream);
|
||||
do_cb(data);
|
||||
do_cb(dec_ctx, data, sub);
|
||||
// This is probably incomplete!
|
||||
}
|
||||
else
|
||||
187
src/lib_ccx/ffmpeg_intgr.c
Normal file
187
src/lib_ccx/ffmpeg_intgr.c
Normal file
@@ -0,0 +1,187 @@
|
||||
#include "ffmpeg_intgr.h"
|
||||
|
||||
#ifdef ENABLE_FFMPEG
|
||||
#include <libavformat/avformat.h>
|
||||
#include <libavfilter/avfiltergraph.h>
|
||||
#include <libavfilter/avcodec.h>
|
||||
#include <libavfilter/buffersink.h>
|
||||
#include <libavfilter/buffersrc.h>
|
||||
#include <libavutil/avstring.h>
|
||||
#include <libswresample/swresample.h>
|
||||
#include <libavutil/opt.h>
|
||||
#include <libavutil/audio_fifo.h>
|
||||
#include <libavutil/log.h>
|
||||
#include <libavutil/error.h>
|
||||
|
||||
#include "lib_ccx.h"
|
||||
struct ffmpeg_ctx
|
||||
{
|
||||
AVFormatContext *ifmt;
|
||||
AVCodecContext *dec_ctx;
|
||||
AVFrame *frame;
|
||||
int stream_index;
|
||||
};
|
||||
/**
|
||||
* call back function to be registered for avlog
|
||||
*/
|
||||
static void log_cb(void* ptr, int level, const char* fmt, va_list vl)
|
||||
{
|
||||
if (level > av_log_get_level())
|
||||
return;
|
||||
|
||||
const char*name = NULL;
|
||||
FILE *flog;
|
||||
|
||||
if (ccx_options.messages_target==CCX_MESSAGES_STDOUT)
|
||||
flog = stdout;
|
||||
else
|
||||
flog = stderr;
|
||||
|
||||
if (level == AV_LOG_PANIC)
|
||||
fprintf(flog, "[panic][%s] ", name);
|
||||
else if (level == AV_LOG_FATAL)
|
||||
fprintf(flog, "[fatal][%s] ", name);
|
||||
else if (level == AV_LOG_ERROR)
|
||||
fprintf(flog, "[error][%s] ", name);
|
||||
else if (level == AV_LOG_WARNING)
|
||||
fprintf(flog, "[warning][%s] ", name);
|
||||
else if (level == AV_LOG_INFO)
|
||||
fprintf(flog, "[info][%s] ", name);
|
||||
else if (level == AV_LOG_DEBUG)
|
||||
fprintf(flog, "[debug][%s] ", name);
|
||||
|
||||
vfprintf(flog, fmt, vl);
|
||||
}
|
||||
/**
|
||||
* @path this path could be relative or absolute path of static file
|
||||
* this path could be path of device
|
||||
*
|
||||
* @return ctx Context of ffmpeg
|
||||
*/
|
||||
void *init_ffmpeg(char *path)
|
||||
{
|
||||
int ret = 0;
|
||||
int stream_index = 0;
|
||||
struct ffmpeg_ctx *ctx;
|
||||
AVCodec *dec = NULL;
|
||||
avcodec_register_all();
|
||||
av_register_all();
|
||||
|
||||
if(ccx_options.debug_mask & CCX_DMT_VERBOSE)
|
||||
av_log_set_level(AV_LOG_INFO);
|
||||
else if (ccx_options.messages_target == 0)
|
||||
av_log_set_level(AV_LOG_FATAL);
|
||||
|
||||
av_log_set_callback(log_cb);
|
||||
|
||||
ctx = av_mallocz(sizeof(*ctx));
|
||||
if(!ctx)
|
||||
{
|
||||
av_log(NULL,AV_LOG_ERROR,"Not enough memory\n");
|
||||
return NULL;
|
||||
}
|
||||
/**
|
||||
* Initialize decoder according to the name of input
|
||||
*/
|
||||
ret = avformat_open_input(&ctx->ifmt, path, NULL, NULL);
|
||||
if(ret < 0)
|
||||
{
|
||||
av_log(NULL,AV_LOG_ERROR,"could not open input(%s) format\n",path);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ret = avformat_find_stream_info(ctx->ifmt,NULL);
|
||||
if(ret < 0)
|
||||
{
|
||||
av_log(NULL,AV_LOG_ERROR,"could not find any stream\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* first search in strean if not found search the video stream */
|
||||
ret = av_find_best_stream(ctx->ifmt, AVMEDIA_TYPE_VIDEO, -1, -1, &dec, 0);
|
||||
if (ret < 0)
|
||||
{
|
||||
av_log(NULL, AV_LOG_ERROR, "no suitable subtitle or caption\n");
|
||||
goto fail;
|
||||
}
|
||||
stream_index = ret;
|
||||
ctx->dec_ctx = ctx->ifmt->streams[stream_index]->codec;
|
||||
ctx->stream_index = stream_index;
|
||||
ret = avcodec_open2(ctx->dec_ctx, dec, NULL);
|
||||
if(ret < 0)
|
||||
{
|
||||
av_log(NULL,AV_LOG_ERROR,"unable to open codec\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
//Initialize frame where input frame will be stored
|
||||
ctx->frame = av_frame_alloc();
|
||||
|
||||
fail:
|
||||
return ctx;
|
||||
}
|
||||
/**
|
||||
* @param ctx context of ffmpeg
|
||||
* @param data preallocated buffer where data will be recieved
|
||||
* @param maxlen length of buffer, where data will be copied
|
||||
* @return number of bytes recieved as data
|
||||
*/
|
||||
int ff_get_ccframe(void *arg, unsigned char*data, int maxlen)
|
||||
{
|
||||
struct ffmpeg_ctx *ctx = arg;
|
||||
int len = 0;
|
||||
int ret = 0;
|
||||
int got_frame;
|
||||
AVPacket packet;
|
||||
|
||||
ret = av_read_frame(ctx->ifmt, &packet);
|
||||
if(ret == AVERROR_EOF)
|
||||
{
|
||||
return ret;
|
||||
}
|
||||
else if(ret < 0)
|
||||
{
|
||||
av_log(NULL, AV_LOG_ERROR, "not able to read the packet\n");
|
||||
return ret;
|
||||
}
|
||||
else if(packet.stream_index != ctx->stream_index)
|
||||
{
|
||||
return AVERROR(EAGAIN);
|
||||
}
|
||||
|
||||
ret = avcodec_decode_video2(ctx->dec_ctx,ctx->frame, &got_frame,&packet);
|
||||
if(ret < 0)
|
||||
{
|
||||
av_log(NULL,AV_LOG_ERROR,"unable to decode packet\n");
|
||||
}
|
||||
else if (!got_frame)
|
||||
{
|
||||
return AVERROR(EAGAIN);
|
||||
}
|
||||
current_pts = av_frame_get_best_effort_timestamp(ctx->frame);
|
||||
if(!pts_set)
|
||||
pts_set = 1;
|
||||
set_fts();
|
||||
for(int i = 0;i< ctx->frame->nb_side_data;i++)
|
||||
{
|
||||
if(ctx->frame->side_data[i]->type == AV_FRAME_DATA_A53_CC)
|
||||
{
|
||||
ctx->frame->pts = av_frame_get_best_effort_timestamp(ctx->frame);
|
||||
if(ctx->frame->side_data[i]->size > maxlen)
|
||||
av_log(NULL,AV_LOG_ERROR,"Please consider increasing length of data\n");
|
||||
else
|
||||
{
|
||||
memcpy(data,ctx->frame->side_data[i]->data,ctx->frame->side_data[i]->size);
|
||||
len = ctx->frame->side_data[i]->size;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if(ret < 0)
|
||||
return ret;
|
||||
|
||||
return len;
|
||||
}
|
||||
#endif
|
||||
23
src/lib_ccx/ffmpeg_intgr.h
Normal file
23
src/lib_ccx/ffmpeg_intgr.h
Normal file
@@ -0,0 +1,23 @@
|
||||
#ifndef _FFMPEG_INTIGRATION
|
||||
#define _FFMPEG_INTIGRATION
|
||||
|
||||
#ifdef ENABLE_FFMPEG
|
||||
#include "libavutil/common.h"
|
||||
#include "libavutil/error.h"
|
||||
#endif
|
||||
/**
|
||||
* @path this path could be relative or absolute path of static file
|
||||
* this path could be path of device
|
||||
*
|
||||
* @return ctx Context of ffmpeg
|
||||
*/
|
||||
void *init_ffmpeg(char *path);
|
||||
|
||||
/**
|
||||
* @param ctx context of ffmpeg
|
||||
* @param data preallocated buffer where data will be recieved
|
||||
* @param maxlen length of buffer, where data will be copied
|
||||
* @return number of bytes recieved as data
|
||||
*/
|
||||
int ff_get_ccframe(void *arg, unsigned char*data, int maxlen);
|
||||
#endif
|
||||
@@ -1,5 +1,5 @@
|
||||
#include "ccextractor.h"
|
||||
|
||||
#include "lib_ccx.h"
|
||||
#include "ccx_common_option.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);
|
||||
|
||||
@@ -10,28 +10,35 @@ LLONG buffered_read_opt_file (unsigned char *buffer, unsigned int bytes);
|
||||
|
||||
LLONG getfilesize (int in)
|
||||
{
|
||||
LLONG current=LSEEK (in, 0, SEEK_CUR);
|
||||
LLONG length = LSEEK (in,0,SEEK_END);
|
||||
LSEEK (in,current,SEEK_SET);
|
||||
return length;
|
||||
int ret = 0;
|
||||
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;
|
||||
}
|
||||
|
||||
LLONG gettotalfilessize (void) // -1 if one or more files failed to open
|
||||
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<num_input_files;i++)
|
||||
for (int i=0;i<ctx->num_input_files;i++)
|
||||
{
|
||||
if (0 == strcmp(inputfile[i],"-")) // Skip stdin
|
||||
continue;
|
||||
if (0 == strcmp(ctx->inputfile[i],"-")) // Skip stdin
|
||||
continue;
|
||||
#ifdef _WIN32
|
||||
h=OPEN (inputfile[i],O_RDONLY | O_BINARY);
|
||||
h=OPEN (ctx->inputfile[i],O_RDONLY | O_BINARY);
|
||||
#else
|
||||
h=OPEN (inputfile[i],O_RDONLY);
|
||||
h=OPEN (ctx->inputfile[i],O_RDONLY);
|
||||
#endif
|
||||
if (h==-1)
|
||||
{
|
||||
mprint ("\rUnable to open %s\r\n",inputfile[i]);
|
||||
mprint ("\rUnable to open %s\r\n",ctx->inputfile[i]);
|
||||
return -1;
|
||||
}
|
||||
if (!ccx_options.live_stream)
|
||||
@@ -41,208 +48,185 @@ LLONG gettotalfilessize (void) // -1 if one or more files failed to open
|
||||
return ts;
|
||||
}
|
||||
|
||||
void prepare_for_new_file (void)
|
||||
void prepare_for_new_file (struct lib_ccx_ctx *ctx)
|
||||
{
|
||||
// Init per file variables
|
||||
min_pts=0x01FFFFFFFFLL; // 33 bit
|
||||
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()
|
||||
last_reported_progress=-1;
|
||||
stat_numuserheaders = 0;
|
||||
stat_dvdccheaders = 0;
|
||||
stat_scte20ccheaders = 0;
|
||||
stat_replay5000headers = 0;
|
||||
stat_replay4000headers = 0;
|
||||
stat_dishheaders = 0;
|
||||
stat_hdtv = 0;
|
||||
stat_divicom = 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;
|
||||
total_pulldownfields = 0;
|
||||
total_pulldownframes = 0;
|
||||
cc_stats[0]=0; cc_stats[1]=0; cc_stats[2]=0; cc_stats[3]=0;
|
||||
false_pict_header=0;
|
||||
frames_since_last_gop=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;
|
||||
saw_caption_block=0;
|
||||
past=0;
|
||||
dec_ctx->saw_caption_block=0;
|
||||
ctx->past=0;
|
||||
pts_big_change=0;
|
||||
startbytes_pos=0;
|
||||
startbytes_avail=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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
/* Close input file if there is one and let the GUI know */
|
||||
void close_input_file (void)
|
||||
void close_input_file (struct lib_ccx_ctx *ctx)
|
||||
{
|
||||
if (infd!=-1 && ccx_options.input_source==CCX_DS_FILE)
|
||||
if (ctx->infd!=-1 && ccx_options.input_source==CCX_DS_FILE)
|
||||
{
|
||||
close (infd);
|
||||
infd=-1;
|
||||
activity_input_file_closed();
|
||||
close (ctx->infd);
|
||||
ctx->infd=-1;
|
||||
activity_input_file_closed();
|
||||
}
|
||||
}
|
||||
|
||||
int init_sockets (void)
|
||||
{
|
||||
static int socket_inited=0;
|
||||
if (!socket_inited)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
WSADATA wsaData = {0};
|
||||
// Initialize Winsock
|
||||
iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
|
||||
if (iResult != 0) {
|
||||
wprintf(L"WSAStartup failed: %d\n", iResult);
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
socket_inited=1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Close current file and open next one in list -if any- */
|
||||
/* bytesinbuffer is the number of bytes read (in some buffer) that haven't been added
|
||||
to 'past' yet. We provide this number to switch_to_next_file() so a final sanity check
|
||||
can be done */
|
||||
|
||||
int switch_to_next_file (LLONG bytesinbuffer)
|
||||
int switch_to_next_file (struct lib_ccx_ctx *ctx, LLONG bytesinbuffer)
|
||||
{
|
||||
if (current_file==-1 || !ccx_options.binary_concat)
|
||||
struct lib_cc_decode *dec_ctx = NULL;
|
||||
dec_ctx = ctx->dec_ctx;
|
||||
if (ctx->current_file==-1 || !ccx_options.binary_concat)
|
||||
{
|
||||
memset (PIDs_seen,0,65536*sizeof (int));
|
||||
memset (PIDs_programs,0,65536*sizeof (struct PMT_entry *));
|
||||
memset (ctx->PIDs_seen,0,65536*sizeof (int));
|
||||
memset (ctx->PIDs_programs,0,65536*sizeof (struct PMT_entry *));
|
||||
}
|
||||
|
||||
if (ccx_options.input_source==CCX_DS_STDIN)
|
||||
{
|
||||
if (infd!=-1) // Means we had already processed stdin. So we're done.
|
||||
if (ctx->infd!=-1) // Means we had already processed stdin. So we're done.
|
||||
{
|
||||
if (ccx_options.print_file_reports)
|
||||
print_file_report();
|
||||
print_file_report(ctx);
|
||||
return 0;
|
||||
}
|
||||
infd=0;
|
||||
ctx->infd=0;
|
||||
mprint ("\n\r-----------------------------------------------------------------\n");
|
||||
mprint ("\rReading from standard input\n");
|
||||
return 1;
|
||||
}
|
||||
if (ccx_options.input_source==CCX_DS_NETWORK)
|
||||
{
|
||||
if (infd!=-1) // Means we have already bound a socket.
|
||||
if (ctx->infd!=-1) // Means we have already bound a socket.
|
||||
{
|
||||
if (ccx_options.print_file_reports)
|
||||
print_file_report();
|
||||
print_file_report(ctx);
|
||||
|
||||
return 0;
|
||||
}
|
||||
if (init_sockets())
|
||||
return 1;
|
||||
infd=socket(AF_INET,SOCK_DGRAM,0);
|
||||
if (IN_MULTICAST(ccx_options.udpaddr))
|
||||
|
||||
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)
|
||||
{
|
||||
int on = 1;
|
||||
(void)setsockopt(infd, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on));
|
||||
}
|
||||
servaddr.sin_family = AF_INET;
|
||||
servaddr.sin_addr.s_addr=htonl(IN_MULTICAST(ccx_options.udpaddr) ? ccx_options.udpaddr : INADDR_ANY);
|
||||
servaddr.sin_port=htons(ccx_options.udpport);
|
||||
if (bind(infd,(struct sockaddr *)&servaddr,sizeof(servaddr)))
|
||||
{
|
||||
fatal (EXIT_BUG_BUG, "bind() failed.");
|
||||
}
|
||||
if (IN_MULTICAST(ccx_options.udpaddr)) {
|
||||
struct ip_mreq group;
|
||||
group.imr_multiaddr.s_addr = htonl(ccx_options.udpaddr);
|
||||
group.imr_interface.s_addr = htonl(INADDR_ANY);
|
||||
if (setsockopt(infd, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *)&group, sizeof(group)) < 0)
|
||||
{
|
||||
fatal (EXIT_BUG_BUG, "cannot join multicast group.");
|
||||
}
|
||||
if (ccx_options.print_file_reports)
|
||||
print_file_report(ctx);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
mprint ("\n\r-----------------------------------------------------------------\n");
|
||||
if (ccx_options.udpaddr == INADDR_ANY)
|
||||
{
|
||||
mprint ("\rReading from UDP socket %u\n",ccx_options.udpport);
|
||||
}
|
||||
else
|
||||
{
|
||||
struct in_addr in;
|
||||
in.s_addr = htonl(ccx_options.udpaddr);
|
||||
mprint ("\rReading from UDP socket %s:%u\n", inet_ntoa(in), ccx_options.udpport);
|
||||
}
|
||||
ctx->infd = start_tcp_srv(ccx_options.tcpport, ccx_options.tcp_password);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Close current and make sure things are still sane */
|
||||
if (infd!=-1)
|
||||
if (ctx->infd!=-1)
|
||||
{
|
||||
if (ccx_options.print_file_reports)
|
||||
print_file_report();
|
||||
close_input_file ();
|
||||
if (inputsize>0 && ((past+bytesinbuffer) < inputsize) && !processed_enough)
|
||||
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",
|
||||
inputfile[current_file], current_file, past, inputsize);
|
||||
ctx->inputfile[ctx->current_file], ctx->current_file, ctx->past, ctx->inputsize);
|
||||
}
|
||||
if (ccx_options.binary_concat)
|
||||
{
|
||||
total_past+=inputsize;
|
||||
past=0; // Reset always or at the end we'll have double the size
|
||||
ctx->total_past+=ctx->inputsize;
|
||||
ctx->past=0; // Reset always or at the end we'll have double the size
|
||||
}
|
||||
}
|
||||
for (;;)
|
||||
{
|
||||
current_file++;
|
||||
if (current_file>=num_input_files)
|
||||
{
|
||||
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", inputfile[current_file]);
|
||||
mprint ("\rOpening file: %s\n", ctx->inputfile[ctx->current_file]);
|
||||
#ifdef _WIN32
|
||||
infd=OPEN (inputfile[current_file],O_RDONLY | O_BINARY);
|
||||
ctx->infd=OPEN (ctx->inputfile[ctx->current_file],O_RDONLY | O_BINARY);
|
||||
#else
|
||||
infd=OPEN (inputfile[current_file],O_RDONLY);
|
||||
ctx->infd=OPEN (ctx->inputfile[ctx->current_file],O_RDONLY);
|
||||
#endif
|
||||
if (infd == -1)
|
||||
mprint ("\rWarning: Unable to open input file [%s]\n", inputfile[current_file]);
|
||||
if (ctx->infd == -1)
|
||||
mprint ("\rWarning: Unable to open input file [%s]\n", ctx->inputfile[ctx->current_file]);
|
||||
else
|
||||
{
|
||||
activity_input_file_open (inputfile[current_file]);
|
||||
activity_input_file_open (ctx->inputfile[ctx->current_file]);
|
||||
if (!ccx_options.live_stream)
|
||||
{
|
||||
inputsize = getfilesize (infd);
|
||||
ctx->inputsize = getfilesize (ctx->infd);
|
||||
if (!ccx_options.binary_concat)
|
||||
total_inputsize=inputsize;
|
||||
ctx->total_inputsize=ctx->inputsize;
|
||||
}
|
||||
return 1; // Succeeded
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void position_sanity_check ()
|
||||
void position_sanity_check (void)
|
||||
{
|
||||
#ifdef SANITY_CHECK
|
||||
if (in!=-1)
|
||||
{
|
||||
LLONG realpos=LSEEK (in,0,SEEK_CUR);
|
||||
if (realpos!=past-filebuffer_pos+bytesinbuffer)
|
||||
if (realpos!=ctx->past-filebuffer_pos+bytesinbuffer)
|
||||
{
|
||||
fatal (EXIT_BUG_BUG, "Position desync, THIS IS A BUG. Real pos =%lld, past=%lld.\n",realpos,past);
|
||||
fatal (CCX_COMMON_EXIT_BUG_BUG, "Position desync, THIS IS A BUG. Real pos =%lld, past=%lld.\n",realpos,ctx->past);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -250,20 +234,20 @@ void position_sanity_check ()
|
||||
int init_file_buffer(void)
|
||||
{
|
||||
filebuffer_start=0;
|
||||
filebuffer_pos=0;
|
||||
filebuffer_pos=0;
|
||||
if (filebuffer==NULL)
|
||||
{
|
||||
filebuffer=(unsigned char *) malloc (FILEBUFFERSIZE);
|
||||
bytesinbuffer=0;
|
||||
}
|
||||
if (filebuffer==NULL)
|
||||
if (filebuffer==NULL)
|
||||
{
|
||||
fatal (EXIT_NOT_ENOUGH_MEMORY, "Not enough memory\n");
|
||||
fatal (EXIT_NOT_ENOUGH_MEMORY, "Not enough memory\n");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void buffered_seek (int offset)
|
||||
void buffered_seek (struct lib_ccx_ctx *ctx, int offset)
|
||||
{
|
||||
position_sanity_check();
|
||||
if (offset<0)
|
||||
@@ -272,17 +256,17 @@ void buffered_seek (int offset)
|
||||
if (filebuffer_pos<0)
|
||||
{
|
||||
// We got into the start buffer (hopefully)
|
||||
if (startbytes_pos+filebuffer_pos < 0)
|
||||
if ((filebuffer_pos+ctx->startbytes_pos) < 0)
|
||||
{
|
||||
fatal (EXIT_BUG_BUG, "PANIC: Attempt to seek before buffer start, this is a bug!");
|
||||
fatal (CCX_COMMON_EXIT_BUG_BUG, "PANIC: Attempt to seek before buffer start, this is a bug!");
|
||||
}
|
||||
startbytes_pos+=filebuffer_pos;
|
||||
ctx->startbytes_pos+=filebuffer_pos;
|
||||
filebuffer_pos=0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
buffered_read_opt (NULL, offset);
|
||||
buffered_read_opt (ctx, NULL, offset);
|
||||
position_sanity_check();
|
||||
}
|
||||
}
|
||||
@@ -291,7 +275,7 @@ void sleepandchecktimeout (time_t start)
|
||||
{
|
||||
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
|
||||
// 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;
|
||||
@@ -304,7 +288,7 @@ void sleepandchecktimeout (time_t start)
|
||||
return;
|
||||
}
|
||||
if (time(NULL)>start+ccx_options.live_stream) // More than live_stream seconds elapsed. No more live
|
||||
ccx_options.live_stream=0;
|
||||
ccx_options.live_stream=0;
|
||||
else
|
||||
sleep_secs(1);
|
||||
}
|
||||
@@ -313,7 +297,7 @@ void return_to_buffer (unsigned char *buffer, unsigned int bytes)
|
||||
{
|
||||
if (bytes == filebuffer_pos)
|
||||
{
|
||||
// Usually we're just going back in the buffer and memcpy would be
|
||||
// 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);
|
||||
@@ -326,44 +310,44 @@ void return_to_buffer (unsigned char *buffer, unsigned int bytes)
|
||||
// we're never here in ccextractor.
|
||||
memmove (filebuffer,filebuffer+filebuffer_pos,bytesinbuffer-filebuffer_pos);
|
||||
bytesinbuffer-=filebuffer_pos;
|
||||
bytesinbuffer=0;
|
||||
bytesinbuffer=0;
|
||||
filebuffer_pos=0;
|
||||
}
|
||||
|
||||
if (bytesinbuffer + bytes > FILEBUFFERSIZE)
|
||||
fatal (EXIT_BUG_BUG, "Invalid return_to_buffer() - please submit a bug report.");
|
||||
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;
|
||||
}
|
||||
|
||||
LLONG buffered_read_opt (unsigned char *buffer, unsigned int bytes)
|
||||
LLONG buffered_read_opt (struct lib_ccx_ctx *ctx, unsigned char *buffer, unsigned int bytes)
|
||||
{
|
||||
LLONG copied=0;
|
||||
position_sanity_check();
|
||||
time_t seconds=0;
|
||||
if (ccx_options.live_stream>0)
|
||||
time (&seconds);
|
||||
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 = (infd==-1);
|
||||
{
|
||||
// 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
|
||||
// for the data to come up
|
||||
sleepandchecktimeout (seconds);
|
||||
}
|
||||
size_t ready = bytesinbuffer-filebuffer_pos;
|
||||
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
|
||||
// detection stuff. However we don't want more buffering so
|
||||
// we do the rest directly on the final buffer.
|
||||
int i;
|
||||
do
|
||||
@@ -372,7 +356,7 @@ LLONG buffered_read_opt (unsigned char *buffer, unsigned int bytes)
|
||||
// buffered - if here, then it must be files.
|
||||
if (buffer!=NULL) // Read
|
||||
{
|
||||
i=read (infd,buffer,bytes);
|
||||
i=read (ctx->infd,buffer,bytes);
|
||||
if( i == -1)
|
||||
fatal (EXIT_READ_ERROR, "Error reading input file!\n");
|
||||
buffer+=i;
|
||||
@@ -380,10 +364,10 @@ LLONG buffered_read_opt (unsigned char *buffer, unsigned int bytes)
|
||||
else // Seek
|
||||
{
|
||||
LLONG op, np;
|
||||
op =LSEEK (infd,0,SEEK_CUR); // Get current pos
|
||||
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 (infd,bytes,SEEK_CUR); // Pos after moving
|
||||
np =LSEEK (ctx->infd,bytes,SEEK_CUR); // Pos after moving
|
||||
i=(int) (np-op);
|
||||
}
|
||||
if (i==0 && ccx_options.live_stream)
|
||||
@@ -403,31 +387,28 @@ LLONG buffered_read_opt (unsigned char *buffer, unsigned int bytes)
|
||||
copied+=i;
|
||||
bytes-=i;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
while ((i || ccx_options.live_stream ||
|
||||
(ccx_options.binary_concat && switch_to_next_file(copied))) && bytes);
|
||||
return copied;
|
||||
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
|
||||
// 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);
|
||||
int i;
|
||||
if (ccx_options.input_source==CCX_DS_FILE || ccx_options.input_source==CCX_DS_STDIN)
|
||||
i=read (infd, filebuffer+keep,FILEBUFFERSIZE-keep);
|
||||
i=read (ctx->infd, filebuffer+keep,FILEBUFFERSIZE-keep);
|
||||
else
|
||||
{
|
||||
socklen_t len = sizeof(cliaddr);
|
||||
i = recvfrom(infd,(char *) filebuffer+keep,FILEBUFFERSIZE-keep,0,(struct sockaddr *)&cliaddr,&len);
|
||||
}
|
||||
if( i == -1)
|
||||
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(copied)))
|
||||
if (ccx_options.live_stream || !(ccx_options.binary_concat && switch_to_next_file(ctx, copied)))
|
||||
eof=1;
|
||||
}
|
||||
filebuffer_pos=keep;
|
||||
@@ -437,27 +418,27 @@ LLONG buffered_read_opt (unsigned char *buffer, unsigned int bytes)
|
||||
int copy = (int) (ready>=bytes ? bytes:ready);
|
||||
if (copy)
|
||||
{
|
||||
if (buffer!=NULL)
|
||||
if (buffer!=NULL)
|
||||
{
|
||||
memcpy (buffer, filebuffer+filebuffer_pos, copy);
|
||||
memcpy (buffer, filebuffer+filebuffer_pos, copy);
|
||||
buffer+=copy;
|
||||
}
|
||||
filebuffer_pos+=copy;
|
||||
filebuffer_pos+=copy;
|
||||
bytes-=copy;
|
||||
copied+=copy;
|
||||
}
|
||||
}
|
||||
return copied;
|
||||
}
|
||||
else // Read without buffering
|
||||
else // Read without buffering
|
||||
{
|
||||
|
||||
|
||||
if (buffer!=NULL)
|
||||
{
|
||||
int i;
|
||||
while (bytes>0 && infd!=-1 &&
|
||||
((i=read(infd,buffer,bytes))!=0 || ccx_options.live_stream ||
|
||||
(ccx_options.binary_concat && switch_to_next_file(copied))))
|
||||
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");
|
||||
@@ -474,13 +455,13 @@ LLONG buffered_read_opt (unsigned char *buffer, unsigned int bytes)
|
||||
}
|
||||
// return fread(buffer,1,bytes,in);
|
||||
//return FSEEK (in,bytes,SEEK_CUR);
|
||||
while (bytes!=0 && infd!=-1)
|
||||
while (bytes!=0 && ctx->infd!=-1)
|
||||
{
|
||||
LLONG op, np;
|
||||
op =LSEEK (infd,0,SEEK_CUR); // Get current pos
|
||||
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 (infd,bytes,SEEK_CUR); // Pos after moving
|
||||
np =LSEEK (ctx->infd,bytes,SEEK_CUR); // Pos after moving
|
||||
copied=copied+(np-op);
|
||||
bytes=bytes-(unsigned int) copied;
|
||||
if (copied==0)
|
||||
@@ -490,7 +471,7 @@ LLONG buffered_read_opt (unsigned char *buffer, unsigned int bytes)
|
||||
else
|
||||
{
|
||||
if (ccx_options.binary_concat)
|
||||
switch_to_next_file(0);
|
||||
switch_to_next_file(ctx, 0);
|
||||
else
|
||||
break;
|
||||
}
|
||||
@@ -1,13 +1,13 @@
|
||||
#include "ccextractor.h"
|
||||
#include "lib_ccx.h"
|
||||
#include "ccx_common_option.h"
|
||||
#ifdef _WIN32
|
||||
#include <io.h>
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include "708.h"
|
||||
#include "dvb_subtitle_decoder.h"
|
||||
|
||||
#include "ccx_encoders_common.h"
|
||||
// IMPORTED TRASH INFO, REMOVE
|
||||
extern long num_nal_unit_type_7;
|
||||
extern long num_vcl_hrd;
|
||||
@@ -20,20 +20,15 @@ extern long num_unexpected_sei_length;
|
||||
unsigned current_vert_size = 0;
|
||||
unsigned current_aspect_ratio = 0;
|
||||
unsigned current_frame_rate = 4; // Assume standard fps, 29.97
|
||||
double current_fps = (double) 30000.0 / 1001; /* 29.97 */ // TODO: Get from framerates_values[] instead
|
||||
LLONG current_pts = 0;
|
||||
unsigned rollover_bits = 0; // The PTS rolls over every 26 hours and that can happen in the middle of a stream.
|
||||
LLONG result; // Number of bytes read/skipped in last read operation
|
||||
int end_of_file=0; // End of file?
|
||||
|
||||
|
||||
const static unsigned char DO_NOTHING[] = {0x80, 0x80};
|
||||
LLONG inbuf = 0; // Number of bytes loaded in buffer
|
||||
LLONG inbuf = 0; // Number of bytes loaded in buffer
|
||||
int ccx_bufferdatatype = CCX_PES; // Can be RAW, PES, H264 or Hauppage
|
||||
|
||||
int current_tref = 0; // Store temporal reference of current frame
|
||||
enum ccx_frame_type current_picture_coding_type = CCX_FRAME_TYPE_RESET_OR_UNKNOWN;
|
||||
|
||||
// Remember if the last header was valid. Used to suppress too much output
|
||||
// and the expected unrecognized first header for TiVo files.
|
||||
int strangeheader=0;
|
||||
@@ -42,12 +37,10 @@ unsigned char *filebuffer;
|
||||
LLONG filebuffer_start; // Position of buffer start relative to file
|
||||
int filebuffer_pos; // Position of pointer relative to buffer start
|
||||
int bytesinbuffer; // Number of bytes we actually have on buffer
|
||||
extern void *cxx_dvb_context;
|
||||
|
||||
LLONG process_raw_with_field (void);
|
||||
extern void *ccx_dvb_context;
|
||||
|
||||
// Program stream specific data grabber
|
||||
LLONG ps_getmoredata(void)
|
||||
LLONG ps_getmoredata(struct lib_ccx_ctx *ctx)
|
||||
{
|
||||
int enough = 0;
|
||||
int payload_read = 0;
|
||||
@@ -58,18 +51,18 @@ LLONG ps_getmoredata(void)
|
||||
int falsepack=0;
|
||||
|
||||
// Read and return the next video PES payload
|
||||
do
|
||||
do
|
||||
{
|
||||
if (BUFSIZE-inbuf<500)
|
||||
if (BUFSIZE-inbuf<500)
|
||||
{
|
||||
mprint("Less than 500 left\n");
|
||||
enough=1; // Stop when less than 500 bytes are left in buffer
|
||||
}
|
||||
else
|
||||
else
|
||||
{
|
||||
buffered_read(nextheader,6);
|
||||
past+=result;
|
||||
if (result!=6)
|
||||
buffered_read(ctx, nextheader, 6);
|
||||
ctx->past+=result;
|
||||
if (result!=6)
|
||||
{
|
||||
// Consider this the end of the show.
|
||||
end_of_file=1;
|
||||
@@ -78,7 +71,7 @@ LLONG ps_getmoredata(void)
|
||||
|
||||
// Search for a header that is not a picture header (nextheader[3]!=0x00)
|
||||
while ( !(nextheader[0]==0x00 && nextheader[1]==0x00
|
||||
&& nextheader[2]==0x01 && nextheader[3]!=0x00) )
|
||||
&& nextheader[2]==0x01 && nextheader[3]!=0x00) )
|
||||
{
|
||||
if( !strangeheader )
|
||||
{
|
||||
@@ -99,10 +92,10 @@ LLONG ps_getmoredata(void)
|
||||
{
|
||||
int atpos = newheader-nextheader;
|
||||
|
||||
memmove (nextheader,newheader,(size_t)(hlen-atpos));
|
||||
buffered_read(nextheader+(hlen-atpos),atpos);
|
||||
past+=result;
|
||||
if (result!=atpos)
|
||||
memmove (nextheader,newheader,(size_t)(hlen-atpos));
|
||||
buffered_read(ctx, nextheader+(hlen-atpos),atpos);
|
||||
ctx->past+=result;
|
||||
if (result!=atpos)
|
||||
{
|
||||
end_of_file=1;
|
||||
break;
|
||||
@@ -110,9 +103,9 @@ LLONG ps_getmoredata(void)
|
||||
}
|
||||
else
|
||||
{
|
||||
buffered_read(nextheader,hlen);
|
||||
past+=result;
|
||||
if (result!=hlen)
|
||||
buffered_read(ctx, nextheader, hlen);
|
||||
ctx->past+=result;
|
||||
if (result!=hlen)
|
||||
{
|
||||
end_of_file=1;
|
||||
break;
|
||||
@@ -128,12 +121,12 @@ LLONG ps_getmoredata(void)
|
||||
strangeheader=0;
|
||||
|
||||
// PACK header
|
||||
if ( nextheader[3]==0xBA)
|
||||
if ( nextheader[3]==0xBA)
|
||||
{
|
||||
dbg_print(CCX_DMT_VERBOSE, "PACK header\n");
|
||||
buffered_read(nextheader+6,8);
|
||||
past+=result;
|
||||
if (result!=8)
|
||||
buffered_read(ctx, nextheader+6,8);
|
||||
ctx->past+=result;
|
||||
if (result!=8)
|
||||
{
|
||||
// Consider this the end of the show.
|
||||
end_of_file=1;
|
||||
@@ -142,7 +135,7 @@ LLONG ps_getmoredata(void)
|
||||
|
||||
if ( (nextheader[4]&0xC4)!=0x44 || !(nextheader[6]&0x04)
|
||||
|| !(nextheader[8]&0x04) || !(nextheader[9]&0x01)
|
||||
|| (nextheader[12]&0x03)!=0x03 )
|
||||
|| (nextheader[12]&0x03)!=0x03 )
|
||||
{
|
||||
// broken pack header
|
||||
falsepack=1;
|
||||
@@ -150,20 +143,20 @@ LLONG ps_getmoredata(void)
|
||||
// We don't need SCR/SCR_ext
|
||||
int stufflen=nextheader[13]&0x07;
|
||||
|
||||
if (falsepack)
|
||||
if (falsepack)
|
||||
{
|
||||
mprint ("Warning: Defective Pack header\n");
|
||||
}
|
||||
|
||||
// If not defect, load stuffing
|
||||
buffered_skip ((int) stufflen);
|
||||
past+=stufflen;
|
||||
buffered_skip (ctx, (int) stufflen);
|
||||
ctx->past+=stufflen;
|
||||
// fake a result value as something was skipped
|
||||
result=1;
|
||||
continue;
|
||||
}
|
||||
// Some PES stream
|
||||
else if (nextheader[3]>=0xBB && nextheader[3]<=0xDF)
|
||||
else if (nextheader[3]>=0xBB && nextheader[3]<=0xDF)
|
||||
{
|
||||
// System header
|
||||
// nextheader[3]==0xBB
|
||||
@@ -188,18 +181,18 @@ LLONG ps_getmoredata(void)
|
||||
}
|
||||
|
||||
// Skip over it
|
||||
buffered_skip ((int) headerlen);
|
||||
past+=headerlen;
|
||||
buffered_skip (ctx, (int) headerlen);
|
||||
ctx->past+=headerlen;
|
||||
// fake a result value as something was skipped
|
||||
result=1;
|
||||
|
||||
continue;
|
||||
}
|
||||
// Read the next video PES
|
||||
else if ((nextheader[3]&0xf0)==0xe0)
|
||||
else if ((nextheader[3]&0xf0)==0xe0)
|
||||
{
|
||||
int hlen; // Dummy variable, unused
|
||||
int peslen = read_video_pes_header(nextheader, &hlen, 0);
|
||||
int peslen = read_video_pes_header(ctx, nextheader, &hlen, 0);
|
||||
if (peslen < 0)
|
||||
{
|
||||
end_of_file=1;
|
||||
@@ -220,8 +213,8 @@ LLONG ps_getmoredata(void)
|
||||
continue;
|
||||
}
|
||||
|
||||
buffered_read (buffer+inbuf,want);
|
||||
past=past+result;
|
||||
buffered_read (ctx, ctx->buffer+inbuf, want);
|
||||
ctx->past=ctx->past+result;
|
||||
if (result>0) {
|
||||
payload_read+=(int) result;
|
||||
}
|
||||
@@ -239,7 +232,7 @@ LLONG ps_getmoredata(void)
|
||||
strangeheader=1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
while (result!=0 && !enough && BUFSIZE!=inbuf);
|
||||
|
||||
dbg_print(CCX_DMT_VERBOSE, "PES data read: %d\n", payload_read);
|
||||
@@ -249,30 +242,31 @@ LLONG ps_getmoredata(void)
|
||||
|
||||
|
||||
// Returns number of bytes read, or zero for EOF
|
||||
LLONG general_getmoredata(void)
|
||||
LLONG general_getmoredata(struct lib_ccx_ctx *ctx)
|
||||
{
|
||||
int bytesread = 0;
|
||||
int want;
|
||||
|
||||
do
|
||||
{
|
||||
do
|
||||
{
|
||||
want = (int) (BUFSIZE-inbuf);
|
||||
buffered_read (buffer+inbuf,want); // This is a macro.
|
||||
buffered_read (ctx, ctx->buffer+inbuf,want); // This is a macro.
|
||||
// 'result' HAS the number of bytes read
|
||||
past=past+result;
|
||||
ctx->past=ctx->past+result;
|
||||
inbuf+=result;
|
||||
bytesread+=(int) result;
|
||||
} while (result!=0 && result!=want);
|
||||
return bytesread;
|
||||
}
|
||||
|
||||
#ifdef WTV_DEBUG
|
||||
// Hexadecimal dump process
|
||||
void processhex (char *filename)
|
||||
void processhex (struct lib_ccx_ctx *ctx, char *filename)
|
||||
{
|
||||
size_t max=(size_t) inputsize+1; // Enough for the whole thing. Hex dumps are small so we can be lazy here
|
||||
size_t max=(size_t) ctx->inputsize+1; // Enough for the whole thing. Hex dumps are small so we can be lazy here
|
||||
char *line=(char *) malloc (max);
|
||||
/* const char *mpeg_header="00 00 01 b2 43 43 01 f8 "; // Always present */
|
||||
FILE *fr = fopen (filename, "rt");
|
||||
FILE *fr = fopen (filename, "rt");
|
||||
unsigned char *bytes=NULL;
|
||||
unsigned byte_count=0;
|
||||
int warning_shown=0;
|
||||
@@ -280,7 +274,7 @@ void processhex (char *filename)
|
||||
{
|
||||
char *c1, *c2=NULL; // Positions for first and second colons
|
||||
/* int len; */
|
||||
long timing;
|
||||
long timing;
|
||||
if (line[0]==';') // Skip comments
|
||||
continue;
|
||||
c1=strchr (line,':');
|
||||
@@ -291,7 +285,7 @@ void processhex (char *filename)
|
||||
*c2=0;
|
||||
/* len=atoi (line); */
|
||||
timing=atol (c1+2)*(MPEG_CLOCK_FREQ/1000);
|
||||
current_pts=timing;
|
||||
current_pts=timing;
|
||||
if (pts_set==0)
|
||||
pts_set=1;
|
||||
set_fts();
|
||||
@@ -300,10 +294,10 @@ void processhex (char *filename)
|
||||
if (strlen (c2)==8)
|
||||
{
|
||||
unsigned char high1=c2[1];
|
||||
unsigned char low1=c2[2];
|
||||
unsigned char low1=c2[2];
|
||||
int value1=hex2int (high1,low1);
|
||||
unsigned char high2=c2[4];
|
||||
unsigned char low2=c2[5];
|
||||
unsigned char low2=c2[5];
|
||||
int value2=hex2int (high2,low2);
|
||||
buffer[0]=value1;
|
||||
buffer[1]=value2;
|
||||
@@ -327,7 +321,7 @@ void processhex (char *filename)
|
||||
// OK, seems like a decent chunk of CCdata.
|
||||
c2+=strlen (mpeg_header);
|
||||
*/
|
||||
byte_count=strlen (c2)/3;
|
||||
byte_count=strlen (c2)/3;
|
||||
/*
|
||||
if (atoi (line)!=byte_count+strlen (mpeg_header)/3) // Number of bytes reported don't match actual contents
|
||||
continue;
|
||||
@@ -336,42 +330,42 @@ void processhex (char *filename)
|
||||
continue;
|
||||
if (!byte_count) // Nothing to get from this line except timing info, already done.
|
||||
continue;
|
||||
bytes=(unsigned char *) malloc (byte_count);
|
||||
bytes=(unsigned char *) malloc (byte_count);
|
||||
if (!bytes)
|
||||
fatal (EXIT_NOT_ENOUGH_MEMORY, "Out of memory.\n");
|
||||
unsigned char *bytes=(unsigned char *) malloc (byte_count);
|
||||
for (unsigned i=0;i<byte_count;i++)
|
||||
{
|
||||
unsigned char high=c2[0];
|
||||
unsigned char low=c2[1];
|
||||
unsigned char low=c2[1];
|
||||
int value=hex2int (high,low);
|
||||
if (value==-1)
|
||||
fatal (EXIT_FAILURE, "Incorrect format, unexpected non-hex string.");
|
||||
bytes[i]=value;
|
||||
c2+=3;
|
||||
}
|
||||
memcpy (buffer, bytes, byte_count);
|
||||
memcpy (ctx->buffer, bytes, byte_count);
|
||||
inbuf=byte_count;
|
||||
process_raw();
|
||||
continue;
|
||||
// New wtv format, everything else hopefully obsolete
|
||||
|
||||
int ok=0; // Were we able to process the line?
|
||||
// Attempt to detect how the data is encoded.
|
||||
// Attempt to detect how the data is encoded.
|
||||
// Case 1 (seen in all elderman's samples):
|
||||
// 18 : 467 : 00 00 01 b2 43 43 01 f8 03 42 ff fd 54 80 fc 94 2c ff
|
||||
// Always 03 after header, then something unknown (seen 42, 43, c2, c3...),
|
||||
// 18 : 467 : 00 00 01 b2 43 43 01 f8 03 42 ff fd 54 80 fc 94 2c ff
|
||||
// Always 03 after header, then something unknown (seen 42, 43, c2, c3...),
|
||||
// then ff, then data with field info, and terminated with ff.
|
||||
if (byte_count>3 && bytes[0]==0x03 &&
|
||||
bytes[2]==0xff && bytes[byte_count-1]==0xff)
|
||||
{
|
||||
{
|
||||
ok=1;
|
||||
for (unsigned i=3; i<byte_count-2; i+=3)
|
||||
{
|
||||
inbuf=3;
|
||||
buffer[0]=bytes[i];
|
||||
buffer[1]=bytes[i+1];
|
||||
buffer[2]=bytes[i+2];
|
||||
ctx->buffer[0]=bytes[i];
|
||||
ctx->buffer[1]=bytes[i+1];
|
||||
ctx->buffer[2]=bytes[i+2];
|
||||
process_raw_with_field();
|
||||
}
|
||||
}
|
||||
@@ -383,18 +377,18 @@ void processhex (char *filename)
|
||||
/* unsigned extra_field_flag=magic&1; */
|
||||
unsigned caption_count=((magic>>1)&0x1F);
|
||||
unsigned filler=((magic>>6)&1);
|
||||
/* unsigned pattern=((magic>>7)&1); */
|
||||
/* unsigned pattern=((magic>>7)&1); */
|
||||
int always_ff=1;
|
||||
int current_field=0;
|
||||
int current_field=0;
|
||||
if (filler==0 && caption_count*6==byte_count-1) // Note that we are ignoring the extra field for now...
|
||||
{
|
||||
ok=1;
|
||||
ok=1;
|
||||
for (unsigned i=1; i<byte_count-2; i+=3)
|
||||
if (bytes[i]!=0xff)
|
||||
{
|
||||
// If we only find FF in the first byte then either there's only field 1 data, OR
|
||||
// there's alternating field 1 and field 2 data. Don't know how to tell apart. For now
|
||||
// let's assume that always FF means alternating.
|
||||
// let's assume that always FF means alternating.
|
||||
always_ff=0;
|
||||
break;
|
||||
}
|
||||
@@ -404,14 +398,14 @@ void processhex (char *filename)
|
||||
inbuf=3;
|
||||
if (always_ff) // Try to tell apart the fields based on the pattern field.
|
||||
{
|
||||
buffer[0]=current_field | 4; // | 4 to enable the 'valid' bit
|
||||
current_field = !current_field;
|
||||
ctx->buffer[0]=current_field | 4; // | 4 to enable the 'valid' bit
|
||||
current_field = !current_field;
|
||||
}
|
||||
else
|
||||
buffer[0]=bytes[i];
|
||||
ctx->buffer[0]=bytes[i];
|
||||
|
||||
buffer[1]=bytes[i+1];
|
||||
buffer[2]=bytes[i+2];
|
||||
ctx->buffer[1]=bytes[i+1];
|
||||
ctx->buffer[2]=bytes[i+2];
|
||||
process_raw_with_field();
|
||||
}
|
||||
}
|
||||
@@ -423,18 +417,20 @@ void processhex (char *filename)
|
||||
}
|
||||
free (bytes);
|
||||
}
|
||||
fclose(fr);
|
||||
fclose(fr);
|
||||
}
|
||||
|
||||
#endif
|
||||
// Raw file process
|
||||
void raw_loop ()
|
||||
void raw_loop (struct lib_ccx_ctx *ctx, void *enc_ctx)
|
||||
{
|
||||
LLONG got;
|
||||
LLONG processed;
|
||||
|
||||
struct cc_subtitle dec_sub;
|
||||
|
||||
current_pts = 90; // Pick a valid PTS time
|
||||
pts_set = 1;
|
||||
set_fts(); // Now set the FTS related variables
|
||||
memset(&dec_sub, 0, sizeof(dec_sub));
|
||||
dbg_print(CCX_DMT_VIDES, "PTS: %s (%8u)",
|
||||
print_mstime(current_pts/(MPEG_CLOCK_FREQ/1000)),
|
||||
(unsigned) (current_pts));
|
||||
@@ -444,12 +440,17 @@ void raw_loop ()
|
||||
{
|
||||
inbuf=0;
|
||||
|
||||
got=general_getmoredata();
|
||||
got = general_getmoredata(ctx);
|
||||
|
||||
if (got == 0) // Shortcircuit if we got nothing to process
|
||||
break;
|
||||
|
||||
processed=process_raw();
|
||||
processed=process_raw(ctx, &dec_sub);
|
||||
if (dec_sub.got_output)
|
||||
{
|
||||
encode_sub(enc_ctx,&dec_sub);
|
||||
dec_sub.got_output = 0;
|
||||
}
|
||||
|
||||
int ccblocks = cb_field1;
|
||||
current_pts += cb_field1*1001/30*(MPEG_CLOCK_FREQ/1000);
|
||||
@@ -460,10 +461,10 @@ void raw_loop ()
|
||||
(unsigned) (current_pts));
|
||||
dbg_print(CCX_DMT_VIDES, " FTS: %s incl. %d CB\n",
|
||||
print_mstime(get_fts()), ccblocks);
|
||||
|
||||
|
||||
if (processed<got)
|
||||
{
|
||||
mprint ("BUG BUG\n");
|
||||
mprint ("BUG BUG\n");
|
||||
}
|
||||
}
|
||||
while (inbuf);
|
||||
@@ -471,78 +472,87 @@ void raw_loop ()
|
||||
|
||||
/* Process inbuf bytes in buffer holding raw caption data (three byte packets, the first being the field).
|
||||
* The number of processed bytes is returned. */
|
||||
LLONG process_raw_with_field (void)
|
||||
LLONG process_raw_with_field (struct lib_ccx_ctx *ctx, struct cc_subtitle *sub)
|
||||
{
|
||||
unsigned char data[3];
|
||||
data[0]=0x04; // Field 1
|
||||
current_field=1;
|
||||
unsigned char data[3];
|
||||
struct lib_cc_decode *dec_ctx = NULL;
|
||||
dec_ctx = ctx->dec_ctx;
|
||||
data[0]=0x04; // Field 1
|
||||
current_field=1;
|
||||
|
||||
for (unsigned long i=0; i<inbuf; i=i+3)
|
||||
{
|
||||
if ( !saw_caption_block && *(buffer+i)==0xff && *(buffer+i+1)==0xff)
|
||||
{
|
||||
// Skip broadcast header
|
||||
}
|
||||
else
|
||||
{
|
||||
data[0]=buffer[i];
|
||||
data[1]=buffer[i+1];
|
||||
data[2]=buffer[i+2];
|
||||
for (unsigned long i=0; i<inbuf; i=i+3)
|
||||
{
|
||||
if ( !dec_ctx->saw_caption_block && *(ctx->buffer+i)==0xff && *(ctx->buffer+i+1)==0xff)
|
||||
{
|
||||
// Skip broadcast header
|
||||
}
|
||||
else
|
||||
{
|
||||
data[0]=ctx->buffer[i];
|
||||
data[1]=ctx->buffer[i+1];
|
||||
data[2]=ctx->buffer[i+2];
|
||||
|
||||
// do_cb increases the cb_field1 counter so that get_fts()
|
||||
// is correct.
|
||||
do_cb(data);
|
||||
}
|
||||
}
|
||||
return inbuf;
|
||||
// do_cb increases the cb_field1 counter so that get_fts()
|
||||
// is correct.
|
||||
do_cb(dec_ctx, data, sub);
|
||||
}
|
||||
}
|
||||
return inbuf;
|
||||
}
|
||||
|
||||
|
||||
/* Process inbuf bytes in buffer holding raw caption data (two byte packets).
|
||||
* The number of processed bytes is returned. */
|
||||
LLONG process_raw (void)
|
||||
LLONG process_raw (struct lib_ccx_ctx *ctx, struct cc_subtitle *sub)
|
||||
{
|
||||
unsigned char data[3];
|
||||
unsigned char data[3];
|
||||
struct lib_cc_decode *dec_ctx = NULL;
|
||||
dec_ctx = ctx->dec_ctx;
|
||||
data[0]=0x04; // Field 1
|
||||
current_field=1;
|
||||
|
||||
for (unsigned long i=0; i<inbuf; i=i+2)
|
||||
{
|
||||
if ( !saw_caption_block && *(buffer+i)==0xff && *(buffer+i+1)==0xff)
|
||||
if ( !dec_ctx->saw_caption_block && *(ctx->buffer+i)==0xff && *(ctx->buffer+i+1)==0xff)
|
||||
{
|
||||
// Skip broadcast header
|
||||
// Skip broadcast header
|
||||
}
|
||||
else
|
||||
{
|
||||
data[1]=buffer[i];
|
||||
data[2]=buffer[i+1];
|
||||
data[1]=ctx->buffer[i];
|
||||
data[2]=ctx->buffer[i+1];
|
||||
|
||||
// do_cb increases the cb_field1 counter so that get_fts()
|
||||
// is correct.
|
||||
do_cb(data);
|
||||
do_cb(dec_ctx, data, sub);
|
||||
}
|
||||
}
|
||||
return inbuf;
|
||||
}
|
||||
|
||||
|
||||
void general_loop(void)
|
||||
void general_loop(struct lib_ccx_ctx *ctx, void *enc_ctx)
|
||||
{
|
||||
LLONG overlap=0;
|
||||
LLONG pos = 0; /* Current position in buffer */
|
||||
LLONG overlap=0;
|
||||
LLONG pos = 0; /* Current position in buffer */
|
||||
struct cc_subtitle dec_sub;
|
||||
struct lib_cc_decode *dec_ctx = NULL;
|
||||
dec_ctx = ctx->dec_ctx;
|
||||
dec_ctx->wbout1 = (struct ccx_s_write*)&ctx->wbout1 ;
|
||||
dec_ctx->wbout2 = (struct ccx_s_write*)&ctx->wbout2 ;
|
||||
inbuf = 0; // No data yet
|
||||
|
||||
end_of_file = 0;
|
||||
current_picture_coding_type = CCX_FRAME_TYPE_RESET_OR_UNKNOWN;
|
||||
|
||||
while (!end_of_file && !processed_enough)
|
||||
memset(&dec_sub, 0,sizeof(dec_sub));
|
||||
while (!end_of_file && !dec_ctx->processed_enough)
|
||||
{
|
||||
/* Get rid of the bytes we already processed */
|
||||
overlap=inbuf-pos;
|
||||
/* Get rid of the bytes we already processed */
|
||||
overlap=inbuf-pos;
|
||||
if ( pos != 0 ) {
|
||||
// Only when needed as memmove has been seen crashing
|
||||
// for dest==source and n >0
|
||||
memmove (buffer,buffer+pos,(size_t) (inbuf-pos));
|
||||
memmove (ctx->buffer,ctx->buffer+pos,(size_t) (inbuf-pos));
|
||||
inbuf-=pos;
|
||||
}
|
||||
pos = 0;
|
||||
@@ -550,35 +560,35 @@ void general_loop(void)
|
||||
// GET MORE DATA IN BUFFER
|
||||
LLONG i;
|
||||
position_sanity_check();
|
||||
switch (stream_mode)
|
||||
switch (ctx->stream_mode)
|
||||
{
|
||||
case CCX_SM_ELEMENTARY_OR_NOT_FOUND:
|
||||
i = general_getmoredata();
|
||||
i = general_getmoredata(ctx);
|
||||
break;
|
||||
case CCX_SM_TRANSPORT:
|
||||
i = ts_getmoredata();
|
||||
i = ts_getmoredata(ctx);
|
||||
break;
|
||||
case CCX_SM_PROGRAM:
|
||||
i = ps_getmoredata();
|
||||
i = ps_getmoredata(ctx);
|
||||
break;
|
||||
case CCX_SM_ASF:
|
||||
i = asf_getmoredata();
|
||||
i = asf_getmoredata(ctx);
|
||||
break;
|
||||
case CCX_SM_WTV:
|
||||
i = wtv_getmoredata();
|
||||
i = wtv_getmoredata(ctx);
|
||||
break;
|
||||
default:
|
||||
fatal(EXIT_BUG_BUG, "Impossible stream_mode");
|
||||
fatal(CCX_COMMON_EXIT_BUG_BUG, "Impossible stream_mode");
|
||||
}
|
||||
|
||||
position_sanity_check();
|
||||
if (fh_out_elementarystream!=NULL)
|
||||
fwrite (buffer+overlap,1,(size_t) (inbuf-overlap),fh_out_elementarystream);
|
||||
if (ctx->fh_out_elementarystream!=NULL)
|
||||
fwrite (ctx->buffer+overlap,1,(size_t) (inbuf-overlap),ctx->fh_out_elementarystream);
|
||||
|
||||
if (i==0)
|
||||
{
|
||||
end_of_file = 1;
|
||||
memset (buffer+inbuf, 0, (size_t) (BUFSIZE-inbuf)); /* Clear buffer at the end */
|
||||
memset (ctx->buffer+inbuf, 0, (size_t) (BUFSIZE-inbuf)); /* Clear buffer at the end */
|
||||
}
|
||||
|
||||
if (inbuf == 0)
|
||||
@@ -593,26 +603,25 @@ void general_loop(void)
|
||||
|
||||
if (ccx_options.hauppauge_mode)
|
||||
{
|
||||
got = process_raw_with_field();
|
||||
got = process_raw_with_field(ctx, &dec_sub);
|
||||
if (pts_set)
|
||||
set_fts(); // Try to fix timing from TS data
|
||||
}
|
||||
else if(ccx_bufferdatatype == CCX_DVB_SUBTITLE)
|
||||
{
|
||||
int out_size = 0;
|
||||
dvbsub_decode(cxx_dvb_context,NULL,&out_size,buffer + 2,inbuf);
|
||||
dvbsub_decode(ccx_dvb_context, ctx->buffer + 2, inbuf, &dec_sub);
|
||||
set_fts();
|
||||
got = inbuf;
|
||||
}
|
||||
else if (ccx_bufferdatatype == CCX_PES)
|
||||
{
|
||||
got = process_m2v (buffer, inbuf);
|
||||
}
|
||||
else if (ccx_bufferdatatype == CCX_PES)
|
||||
{
|
||||
got = process_m2v (ctx, ctx->buffer, inbuf,&dec_sub);
|
||||
}
|
||||
else if (ccx_bufferdatatype == CCX_TELETEXT)
|
||||
{
|
||||
// Dispatch to Petr Kutalek 's telxcc.
|
||||
tlt_process_pes_packet (buffer, (uint16_t) inbuf);
|
||||
got = inbuf;
|
||||
tlt_process_pes_packet (ctx, ctx->buffer, (uint16_t) inbuf);
|
||||
got = inbuf;
|
||||
}
|
||||
else if (ccx_bufferdatatype == CCX_PRIVATE_MPEG2_CC)
|
||||
{
|
||||
@@ -654,18 +663,18 @@ void general_loop(void)
|
||||
(unsigned) (current_pts));
|
||||
dbg_print(CCX_DMT_VIDES, " FTS: %s\n", print_mstime(get_fts()));
|
||||
|
||||
got = process_raw();
|
||||
got = process_raw(ctx, &dec_sub);
|
||||
}
|
||||
else if (ccx_bufferdatatype == CCX_H264) // H.264 data from TS file
|
||||
{
|
||||
got = process_avc(buffer, inbuf);
|
||||
got = process_avc(ctx, ctx->buffer, inbuf,&dec_sub);
|
||||
}
|
||||
else
|
||||
fatal(EXIT_BUG_BUG, "Unknown data type!");
|
||||
fatal(CCX_COMMON_EXIT_BUG_BUG, "Unknown data type!");
|
||||
|
||||
if (got>inbuf)
|
||||
{
|
||||
mprint ("BUG BUG\n");
|
||||
mprint ("BUG BUG\n");
|
||||
}
|
||||
pos+=got;
|
||||
|
||||
@@ -673,39 +682,44 @@ void general_loop(void)
|
||||
{
|
||||
int cur_sec = (int) (get_fts() / 1000);
|
||||
int th=cur_sec/10;
|
||||
if (last_reported_progress!=th)
|
||||
if (ctx->last_reported_progress!=th)
|
||||
{
|
||||
activity_progress (-1,cur_sec/60, cur_sec%60);
|
||||
last_reported_progress = th;
|
||||
ctx->last_reported_progress = th;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (total_inputsize>255) // Less than 255 leads to division by zero below.
|
||||
if (ctx->total_inputsize>255) // Less than 255 leads to division by zero below.
|
||||
{
|
||||
int progress = (int) ((((total_past+past)>>8)*100)/(total_inputsize>>8));
|
||||
if (last_reported_progress != progress)
|
||||
int progress = (int) ((((ctx->total_past+ctx->past)>>8)*100)/(ctx->total_inputsize>>8));
|
||||
if (ctx->last_reported_progress != progress)
|
||||
{
|
||||
LLONG t=get_fts();
|
||||
if (!t && global_timestamp_inited)
|
||||
t=global_timestamp-min_global_timestamp;
|
||||
if (!t && ctx->global_timestamp_inited)
|
||||
t=ctx->global_timestamp-ctx->min_global_timestamp;
|
||||
int cur_sec = (int) (t / 1000);
|
||||
activity_progress(progress, cur_sec/60, cur_sec%60);
|
||||
last_reported_progress = progress;
|
||||
activity_progress(progress, cur_sec/60, cur_sec%60);
|
||||
ctx->last_reported_progress = progress;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (dec_sub.got_output)
|
||||
{
|
||||
encode_sub(enc_ctx,&dec_sub);
|
||||
dec_sub.got_output = 0;
|
||||
}
|
||||
position_sanity_check();
|
||||
}
|
||||
// Flush remaining HD captions
|
||||
if (has_ccdata_buffered)
|
||||
process_hdcc();
|
||||
process_hdcc(ctx, &dec_sub);
|
||||
|
||||
if (total_past!=total_inputsize && ccx_options.binary_concat && !processed_enough)
|
||||
if (ctx->total_past!=ctx->total_inputsize && ccx_options.binary_concat && !dec_ctx->processed_enough)
|
||||
{
|
||||
mprint("\n\n\n\nATTENTION!!!!!!\n");
|
||||
mprint("Processing of %s %d ended prematurely %lld < %lld, please send bug report.\n\n",
|
||||
inputfile[current_file], current_file, past, inputsize);
|
||||
ctx->inputfile[ctx->current_file], ctx->current_file, ctx->past, ctx->inputsize);
|
||||
}
|
||||
mprint ("\nNumber of NAL_type_7: %ld\n",num_nal_unit_type_7);
|
||||
mprint ("Number of VCL_HRD: %ld\n",num_vcl_hrd);
|
||||
@@ -715,26 +729,30 @@ void general_loop(void)
|
||||
}
|
||||
|
||||
// Raw caption with FTS file process
|
||||
void rcwt_loop( void )
|
||||
void rcwt_loop(struct lib_ccx_ctx *ctx, void *enc_ctx)
|
||||
{
|
||||
static unsigned char *parsebuf;
|
||||
static long parsebufsize = 1024;
|
||||
struct cc_subtitle dec_sub;
|
||||
struct lib_cc_decode *dec_ctx = NULL;
|
||||
dec_ctx = ctx->dec_ctx;
|
||||
|
||||
memset(&dec_sub, 0,sizeof(dec_sub));
|
||||
// As BUFSIZE is a macro this is just a reminder
|
||||
if (BUFSIZE < (3*0xFFFF + 10))
|
||||
fatal (EXIT_BUG_BUG, "BUFSIZE too small for RCWT caption block.\n");
|
||||
fatal (CCX_COMMON_EXIT_BUG_BUG, "BUFSIZE too small for RCWT caption block.\n");
|
||||
|
||||
// Generic buffer to hold some data
|
||||
parsebuf = (unsigned char*)malloc(1024);
|
||||
|
||||
|
||||
|
||||
LLONG currfts;
|
||||
uint16_t cbcount = 0;
|
||||
|
||||
int bread = 0; // Bytes read
|
||||
|
||||
buffered_read(parsebuf,11);
|
||||
past+=result;
|
||||
buffered_read(ctx, parsebuf, 11);
|
||||
ctx->past+=result;
|
||||
bread+=(int) result;
|
||||
if (result!=11)
|
||||
{
|
||||
@@ -750,13 +768,19 @@ void rcwt_loop( void )
|
||||
dbg_print(CCX_DMT_PARSE, "File created by %02X version %02X%02X\nFile format revision: %02X%02X\n",
|
||||
parsebuf[3], parsebuf[4], parsebuf[5],
|
||||
parsebuf[6], parsebuf[7]);
|
||||
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
fatal(EXIT_MISSING_RCWT_HEADER, "Missing RCWT header. Abort.\n");
|
||||
}
|
||||
|
||||
if (parsebuf[6] == 0 && parsebuf[7] == 2)
|
||||
{
|
||||
tlt_read_rcwt(ctx);
|
||||
return;
|
||||
}
|
||||
|
||||
// Initialize first time. As RCWT files come with the correct FTS the
|
||||
// initial (minimal) time needs to be set to 0.
|
||||
current_pts = 0;
|
||||
@@ -767,8 +791,8 @@ void rcwt_loop( void )
|
||||
while(1)
|
||||
{
|
||||
// Read the data header
|
||||
buffered_read(parsebuf,10);
|
||||
past+=result;
|
||||
buffered_read(ctx, parsebuf, 10);
|
||||
ctx->past+=result;
|
||||
bread+=(int) result;
|
||||
|
||||
if (result!=10)
|
||||
@@ -794,8 +818,8 @@ void rcwt_loop( void )
|
||||
fatal(EXIT_NOT_ENOUGH_MEMORY, "Out of memory");
|
||||
parsebufsize = cbcount*3;
|
||||
}
|
||||
buffered_read(parsebuf,cbcount*3);
|
||||
past+=result;
|
||||
buffered_read(ctx, parsebuf, cbcount*3);
|
||||
ctx->past+=result;
|
||||
bread+=(int) result;
|
||||
if (result!=cbcount*3)
|
||||
{
|
||||
@@ -817,9 +841,14 @@ void rcwt_loop( void )
|
||||
|
||||
for (int j=0; j<cbcount*3; j=j+3)
|
||||
{
|
||||
do_cb(parsebuf+j);
|
||||
do_cb(dec_ctx, parsebuf+j, &dec_sub);
|
||||
}
|
||||
}
|
||||
if (dec_sub.got_output)
|
||||
{
|
||||
encode_sub(enc_ctx,&dec_sub);
|
||||
dec_sub.got_output = 0;
|
||||
}
|
||||
} // end while(1)
|
||||
|
||||
dbg_print(CCX_DMT_PARSE, "Processed %d bytes\n", bread);
|
||||
128
src/lib_ccx/lib_ccx.c
Normal file
128
src/lib_ccx/lib_ccx.c
Normal file
@@ -0,0 +1,128 @@
|
||||
#include "lib_ccx.h"
|
||||
#include "ccx_common_option.h"
|
||||
|
||||
struct ccx_common_logging_t ccx_common_logging;
|
||||
static struct ccx_decoders_common_settings_t *init_decoder_setting(
|
||||
struct ccx_s_options *opt)
|
||||
{
|
||||
struct ccx_decoders_common_settings_t *setting;
|
||||
|
||||
setting = malloc(sizeof(struct ccx_decoders_common_settings_t));
|
||||
if(!setting)
|
||||
return NULL;
|
||||
|
||||
setting->subs_delay = opt->subs_delay;
|
||||
setting->output_format = opt->write_format;
|
||||
setting->fix_padding = opt->fix_padding;
|
||||
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;
|
||||
return setting;
|
||||
}
|
||||
static void dinit_decoder_setting (struct ccx_decoders_common_settings_t **setting)
|
||||
{
|
||||
freep(setting);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
ctx = malloc(sizeof(struct lib_ccx_ctx));
|
||||
if(!ctx)
|
||||
return NULL;
|
||||
memset(ctx,0,sizeof(struct lib_ccx_ctx));
|
||||
|
||||
report_608 = malloc(sizeof(struct ccx_decoder_608_report));
|
||||
if (!report_608)
|
||||
return NULL;
|
||||
memset(report_608,0,sizeof(struct ccx_decoder_608_report));
|
||||
|
||||
ctx->capbufsize = 20000;
|
||||
ctx->capbuf = NULL;
|
||||
ctx->capbuflen = 0; // Bytes read in capbuf
|
||||
|
||||
// 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;
|
||||
ccx_common_logging.debug_mask = opt->debug_mask;
|
||||
ccx_common_logging.fatal_ftn = &fatal;
|
||||
ccx_common_logging.log_ftn = &mprint;
|
||||
ccx_common_logging.gui_ftn = &activity_library_process;
|
||||
|
||||
// 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();
|
||||
|
||||
//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);
|
||||
|
||||
ctx->cc_to_stdout = opt->cc_to_stdout;
|
||||
|
||||
build_parity_table();
|
||||
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++)
|
||||
{
|
||||
if( lctx->PIDs_programs[i])
|
||||
freep(lctx->PIDs_programs + i);
|
||||
}
|
||||
// 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(ctx);
|
||||
}
|
||||
476
src/lib_ccx/lib_ccx.h
Normal file
476
src/lib_ccx/lib_ccx.h
Normal file
@@ -0,0 +1,476 @@
|
||||
#ifndef CCX_CCEXTRACTOR_H
|
||||
#define CCX_CCEXTRACTOR_H
|
||||
|
||||
#define VERSION "0.76"
|
||||
|
||||
// Load common includes and constants for library usage
|
||||
#include "ccx_common_platform.h"
|
||||
#include "ccx_common_constants.h"
|
||||
#include "ccx_common_common.h"
|
||||
#include "ccx_common_char_encoding.h"
|
||||
#include "ccx_common_structs.h"
|
||||
#include "ccx_common_timing.h"
|
||||
#include "ccx_common_option.h"
|
||||
|
||||
#include "ccx_encoders_common.h"
|
||||
#include "ccx_decoders_608.h"
|
||||
#include "ccx_decoders_xds.h"
|
||||
#include "ccx_decoders_708.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];
|
||||
};
|
||||
|
||||
/* Report information */
|
||||
#define SUB_STREAMS_CNT 10
|
||||
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];
|
||||
unsigned mp4_cc_track_cnt;
|
||||
};
|
||||
|
||||
// Stuff for telcc.c
|
||||
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
|
||||
double offset; // time offset in seconds
|
||||
uint8_t bom : 1; // print UTF-8 BOM characters at the beginning of output
|
||||
uint8_t nonempty : 1; // produce at least one (dummy) frame
|
||||
// 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
|
||||
};
|
||||
#define MAX_PID 65536
|
||||
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;
|
||||
LLONG total_past; // Only in binary concat mode
|
||||
|
||||
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;
|
||||
int stat_dvdccheaders;
|
||||
int stat_scte20ccheaders;
|
||||
int stat_replay5000headers;
|
||||
int stat_replay4000headers;
|
||||
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;
|
||||
|
||||
|
||||
int rawmode; // Broadcast or DVD
|
||||
// See -d from
|
||||
|
||||
int cc_to_stdout; // If 1, captions go to stdout instead of file
|
||||
|
||||
|
||||
LLONG subs_delay; // ms to delay (or advance) subs
|
||||
|
||||
int startcredits_displayed;
|
||||
int end_credits_displayed;
|
||||
LLONG last_displayed_subs_ms; // When did the last subs end?
|
||||
LLONG screens_to_process; // How many screenfuls we want?
|
||||
char *basefilename; // Input filename without the extension
|
||||
|
||||
const char *extension; // Output extension
|
||||
int current_file; // If current_file!=1, we are processing *inputfile[current_file]
|
||||
|
||||
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 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];
|
||||
int epg_last_output;
|
||||
int epg_last_live_output;
|
||||
struct file_report freport;
|
||||
|
||||
long capbufsize;
|
||||
unsigned char *capbuf;
|
||||
long capbuflen; // Bytes read in capbuf
|
||||
};
|
||||
#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[]);
|
||||
void usage (void);
|
||||
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 processhex (char *filename);
|
||||
void rcwt_loop(struct lib_ccx_ctx *ctx, void *enc_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);
|
||||
|
||||
// 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);
|
||||
|
||||
// es_functions.c
|
||||
LLONG process_m2v (struct lib_ccx_ctx *ctx, unsigned char *data, LLONG 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);
|
||||
|
||||
// bitstream.c - see bitstream.h
|
||||
|
||||
// file_functions.c
|
||||
LLONG getfilesize (int in);
|
||||
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);
|
||||
|
||||
// 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);
|
||||
|
||||
// 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 flushbuffer (struct lib_ccx_ctx *ctx, struct ccx_s_write *wb, int closefile);
|
||||
void writercwtdata (struct lib_cc_decode *ctx, const unsigned char *data);
|
||||
|
||||
// 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);
|
||||
|
||||
// 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 parse_EPG_packet (struct lib_ccx_ctx *ctx);
|
||||
void EPG_free();
|
||||
|
||||
// myth.c
|
||||
void myth_loop(struct lib_ccx_ctx *ctx, void *enc_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);
|
||||
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);
|
||||
#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);
|
||||
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);
|
||||
|
||||
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 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;
|
||||
|
||||
#endif
|
||||
921
src/lib_ccx/myth.c
Normal file
921
src/lib_ccx/myth.c
Normal file
@@ -0,0 +1,921 @@
|
||||
/* This code comes from MythTV.
|
||||
For now, integration with ccextractor is a quick hack. It could get better with time. */
|
||||
|
||||
#include "lib_ccx.h"
|
||||
#include "ccx_common_option.h"
|
||||
#include "ccx_decoders_608.h"
|
||||
#ifdef _WIN32
|
||||
#include <io.h>
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <fcntl.h>
|
||||
#include "ccx_encoders_common.h"
|
||||
|
||||
static unsigned int header_state;
|
||||
static unsigned char psm_es_type[256];
|
||||
int cc608_parity_table[256];
|
||||
|
||||
// LLONG processed_ccblocks = 0;
|
||||
|
||||
#define AVERROR_IO (-2)
|
||||
|
||||
#define VBI_TYPE_TELETEXT 0x1 // Teletext (uses lines 6-22 for PAL, 10-21 for NTSC)
|
||||
#define VBI_TYPE_CC 0x4 // Closed Captions (line 21 NTSC, line 22 PAL)
|
||||
#define VBI_TYPE_WSS 0x5 // Wide Screen Signal (line 20 NTSC, line 23 PAL)
|
||||
#define VBI_TYPE_VPS 0x7 // Video Programming System (PAL) (line 16)
|
||||
|
||||
#define MAX_SYNC_SIZE 100000
|
||||
#define PACK_START_CODE ((unsigned int)0x000001ba)
|
||||
#define SYSTEM_HEADER_START_CODE ((unsigned int)0x000001bb)
|
||||
#define SEQUENCE_END_CODE ((unsigned int)0x000001b7)
|
||||
#define PACKET_START_CODE_MASK ((unsigned int)0xffffff00)
|
||||
#define PACKET_START_CODE_PREFIX ((unsigned int)0x00000100)
|
||||
#define ISO_11172_END_CODE ((unsigned int)0x000001b9)
|
||||
|
||||
#define AV_NOPTS_VALUE ((LLONG)0x8000000000000000LL)
|
||||
|
||||
/* mpeg2 */
|
||||
#define PROGRAM_STREAM_MAP 0x1bc
|
||||
#define PRIVATE_STREAM_1 0x1bd
|
||||
#define PADDING_STREAM 0x1be
|
||||
#define PRIVATE_STREAM_2 0x1bf
|
||||
|
||||
extern struct ccx_s_write wbout1, wbout2; // Output structures
|
||||
#define AUDIO_ID 0xc0
|
||||
#define VIDEO_ID 0xe0
|
||||
#define AC3_ID 0x80
|
||||
#define DTS_ID 0x8a
|
||||
#define LPCM_ID 0xa0
|
||||
#define SUB_ID 0x20
|
||||
|
||||
#define STREAM_TYPE_VIDEO_MPEG1 0x01
|
||||
#define STREAM_TYPE_VIDEO_MPEG2 0x02
|
||||
#define STREAM_TYPE_AUDIO_MPEG1 0x03
|
||||
#define STREAM_TYPE_AUDIO_MPEG2 0x04
|
||||
#define STREAM_TYPE_PRIVATE_SECTION 0x05
|
||||
#define STREAM_TYPE_PRIVATE_DATA 0x06
|
||||
#define STREAM_TYPE_AUDIO_AAC 0x0f
|
||||
#define STREAM_TYPE_VIDEO_MPEG4 0x10
|
||||
#define STREAM_TYPE_VIDEO_H264 0x1b
|
||||
|
||||
#define STREAM_TYPE_AUDIO_AC3 0x81
|
||||
#define STREAM_TYPE_AUDIO_DTS 0x8a
|
||||
|
||||
enum CodecType
|
||||
{
|
||||
CODEC_TYPE_UNKNOWN = -1,
|
||||
CODEC_TYPE_VIDEO,
|
||||
CODEC_TYPE_AUDIO,
|
||||
CODEC_TYPE_DATA,
|
||||
CODEC_TYPE_SUBTITLE,
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
enum CodecID
|
||||
{
|
||||
CODEC_ID_NONE,
|
||||
CODEC_ID_MPEG1VIDEO,
|
||||
CODEC_ID_MPEG2VIDEO, /* prefered ID for MPEG Video 1 or 2 decoding */
|
||||
CODEC_ID_MPEG2VIDEO_XVMC,
|
||||
CODEC_ID_MPEG2VIDEO_XVMC_VLD,
|
||||
CODEC_ID_H261,
|
||||
CODEC_ID_H263,
|
||||
CODEC_ID_RV10,
|
||||
CODEC_ID_RV20,
|
||||
CODEC_ID_MJPEG,
|
||||
CODEC_ID_MJPEGB,
|
||||
CODEC_ID_LJPEG,
|
||||
CODEC_ID_SP5X,
|
||||
CODEC_ID_JPEGLS,
|
||||
CODEC_ID_MPEG4,
|
||||
CODEC_ID_RAWVIDEO,
|
||||
CODEC_ID_MSMPEG4V1,
|
||||
CODEC_ID_MSMPEG4V2,
|
||||
CODEC_ID_MSMPEG4V3,
|
||||
CODEC_ID_WMV1,
|
||||
CODEC_ID_WMV2,
|
||||
CODEC_ID_H263P,
|
||||
CODEC_ID_H263I,
|
||||
CODEC_ID_FLV1,
|
||||
CODEC_ID_SVQ1,
|
||||
CODEC_ID_SVQ3,
|
||||
CODEC_ID_DVVIDEO,
|
||||
CODEC_ID_HUFFYUV,
|
||||
CODEC_ID_CYUV,
|
||||
CODEC_ID_H264,
|
||||
CODEC_ID_INDEO3,
|
||||
CODEC_ID_VP3,
|
||||
CODEC_ID_THEORA,
|
||||
CODEC_ID_ASV1,
|
||||
CODEC_ID_ASV2,
|
||||
CODEC_ID_FFV1,
|
||||
CODEC_ID_4XM,
|
||||
CODEC_ID_VCR1,
|
||||
CODEC_ID_CLJR,
|
||||
CODEC_ID_MDEC,
|
||||
CODEC_ID_ROQ,
|
||||
CODEC_ID_INTERPLAY_VIDEO,
|
||||
CODEC_ID_XAN_WC3,
|
||||
CODEC_ID_XAN_WC4,
|
||||
CODEC_ID_RPZA,
|
||||
CODEC_ID_CINEPAK,
|
||||
CODEC_ID_WS_VQA,
|
||||
CODEC_ID_MSRLE,
|
||||
CODEC_ID_MSVIDEO1,
|
||||
CODEC_ID_IDCIN,
|
||||
CODEC_ID_8BPS,
|
||||
CODEC_ID_SMC,
|
||||
CODEC_ID_FLIC,
|
||||
CODEC_ID_TRUEMOTION1,
|
||||
CODEC_ID_VMDVIDEO,
|
||||
CODEC_ID_MSZH,
|
||||
CODEC_ID_ZLIB,
|
||||
CODEC_ID_QTRLE,
|
||||
CODEC_ID_SNOW,
|
||||
CODEC_ID_TSCC,
|
||||
CODEC_ID_ULTI,
|
||||
CODEC_ID_QDRAW,
|
||||
CODEC_ID_VIXL,
|
||||
CODEC_ID_QPEG,
|
||||
CODEC_ID_XVID,
|
||||
CODEC_ID_PNG,
|
||||
CODEC_ID_PPM,
|
||||
CODEC_ID_PBM,
|
||||
CODEC_ID_PGM,
|
||||
CODEC_ID_PGMYUV,
|
||||
CODEC_ID_PAM,
|
||||
CODEC_ID_FFVHUFF,
|
||||
CODEC_ID_RV30,
|
||||
CODEC_ID_RV40,
|
||||
CODEC_ID_VC1,
|
||||
CODEC_ID_WMV3,
|
||||
CODEC_ID_LOCO,
|
||||
CODEC_ID_WNV1,
|
||||
CODEC_ID_AASC,
|
||||
CODEC_ID_INDEO2,
|
||||
CODEC_ID_FRAPS,
|
||||
CODEC_ID_TRUEMOTION2,
|
||||
CODEC_ID_BMP,
|
||||
CODEC_ID_CSCD,
|
||||
CODEC_ID_MMVIDEO,
|
||||
CODEC_ID_ZMBV,
|
||||
CODEC_ID_AVS,
|
||||
CODEC_ID_SMACKVIDEO,
|
||||
CODEC_ID_NUV,
|
||||
CODEC_ID_KMVC,
|
||||
CODEC_ID_FLASHSV,
|
||||
CODEC_ID_CAVS,
|
||||
|
||||
/* various pcm "codecs" */
|
||||
CODEC_ID_PCM_S16LE= 0x10000,
|
||||
CODEC_ID_PCM_S16BE,
|
||||
CODEC_ID_PCM_U16LE,
|
||||
CODEC_ID_PCM_U16BE,
|
||||
CODEC_ID_PCM_S8,
|
||||
CODEC_ID_PCM_U8,
|
||||
CODEC_ID_PCM_MULAW,
|
||||
CODEC_ID_PCM_ALAW,
|
||||
CODEC_ID_PCM_S32LE,
|
||||
CODEC_ID_PCM_S32BE,
|
||||
CODEC_ID_PCM_U32LE,
|
||||
CODEC_ID_PCM_U32BE,
|
||||
CODEC_ID_PCM_S24LE,
|
||||
CODEC_ID_PCM_S24BE,
|
||||
CODEC_ID_PCM_U24LE,
|
||||
CODEC_ID_PCM_U24BE,
|
||||
CODEC_ID_PCM_S24DAUD,
|
||||
|
||||
/* various adpcm codecs */
|
||||
CODEC_ID_ADPCM_IMA_QT= 0x11000,
|
||||
CODEC_ID_ADPCM_IMA_WAV,
|
||||
CODEC_ID_ADPCM_IMA_DK3,
|
||||
CODEC_ID_ADPCM_IMA_DK4,
|
||||
CODEC_ID_ADPCM_IMA_WS,
|
||||
CODEC_ID_ADPCM_IMA_SMJPEG,
|
||||
CODEC_ID_ADPCM_MS,
|
||||
CODEC_ID_ADPCM_4XM,
|
||||
CODEC_ID_ADPCM_XA,
|
||||
CODEC_ID_ADPCM_ADX,
|
||||
CODEC_ID_ADPCM_EA,
|
||||
CODEC_ID_ADPCM_G726,
|
||||
CODEC_ID_ADPCM_CT,
|
||||
CODEC_ID_ADPCM_SWF,
|
||||
CODEC_ID_ADPCM_YAMAHA,
|
||||
CODEC_ID_ADPCM_SBPRO_4,
|
||||
CODEC_ID_ADPCM_SBPRO_3,
|
||||
CODEC_ID_ADPCM_SBPRO_2,
|
||||
|
||||
/* AMR */
|
||||
CODEC_ID_AMR_NB= 0x12000,
|
||||
CODEC_ID_AMR_WB,
|
||||
|
||||
/* RealAudio codecs*/
|
||||
CODEC_ID_RA_144= 0x13000,
|
||||
CODEC_ID_RA_288,
|
||||
|
||||
/* various DPCM codecs */
|
||||
CODEC_ID_ROQ_DPCM= 0x14000,
|
||||
CODEC_ID_INTERPLAY_DPCM,
|
||||
CODEC_ID_XAN_DPCM,
|
||||
CODEC_ID_SOL_DPCM,
|
||||
|
||||
CODEC_ID_MP2= 0x15000,
|
||||
CODEC_ID_MP3, /* prefered ID for MPEG Audio layer 1, 2 or3 decoding */
|
||||
CODEC_ID_AAC,
|
||||
CODEC_ID_MPEG4AAC,
|
||||
CODEC_ID_AC3,
|
||||
CODEC_ID_DTS,
|
||||
CODEC_ID_VORBIS,
|
||||
CODEC_ID_DVAUDIO,
|
||||
CODEC_ID_WMAV1,
|
||||
CODEC_ID_WMAV2,
|
||||
CODEC_ID_MACE3,
|
||||
CODEC_ID_MACE6,
|
||||
CODEC_ID_VMDAUDIO,
|
||||
CODEC_ID_SONIC,
|
||||
CODEC_ID_SONIC_LS,
|
||||
CODEC_ID_FLAC,
|
||||
CODEC_ID_MP3ADU,
|
||||
CODEC_ID_MP3ON4,
|
||||
CODEC_ID_SHORTEN,
|
||||
CODEC_ID_ALAC,
|
||||
CODEC_ID_WESTWOOD_SND1,
|
||||
CODEC_ID_GSM,
|
||||
CODEC_ID_QDM2,
|
||||
CODEC_ID_COOK,
|
||||
CODEC_ID_TRUESPEECH,
|
||||
CODEC_ID_TTA,
|
||||
CODEC_ID_SMACKAUDIO,
|
||||
CODEC_ID_QCELP,
|
||||
|
||||
/* subtitle codecs */
|
||||
CODEC_ID_DVD_SUBTITLE= 0x17000,
|
||||
CODEC_ID_DVB_SUBTITLE,
|
||||
|
||||
/* teletext codecs */
|
||||
CODEC_ID_MPEG2VBI,
|
||||
CODEC_ID_DVB_VBI,
|
||||
|
||||
/* DSMCC codec */
|
||||
CODEC_ID_DSMCC_B,
|
||||
|
||||
CODEC_ID_MPEG2TS= 0x20000, /* _FAKE_ codec to indicate a raw MPEG2 transport
|
||||
stream (only used by libavformat) */
|
||||
};
|
||||
|
||||
|
||||
typedef struct AVPacket
|
||||
{
|
||||
LLONG pts; ///< presentation time stamp in time_base units
|
||||
LLONG dts; ///< decompression time stamp in time_base units
|
||||
unsigned char *data;
|
||||
int size;
|
||||
int stream_index;
|
||||
int flags;
|
||||
int duration; ///< presentation duration in time_base units (0 if not available)
|
||||
void (*destruct)(struct AVPacket *);
|
||||
void *priv;
|
||||
LLONG pos; ///< byte position in stream, -1 if unknown
|
||||
int codec_id;
|
||||
int type;
|
||||
} 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)
|
||||
{
|
||||
LLONG pts;
|
||||
int val;
|
||||
|
||||
if (c < 0)
|
||||
c = get_byte(ctx);
|
||||
pts = (LLONG) ((c >> 1) & 0x07) << 30;
|
||||
val = get_be16(ctx);
|
||||
pts |= (LLONG) (val >> 1) << 15;
|
||||
val = get_be16(ctx);
|
||||
pts |= (LLONG) (val >> 1);
|
||||
return pts;
|
||||
}
|
||||
|
||||
static int find_next_start_code(struct lib_ccx_ctx *ctx, int *size_ptr,
|
||||
unsigned int *header_state)
|
||||
{
|
||||
unsigned int state, v;
|
||||
int val, n;
|
||||
|
||||
state = *header_state;
|
||||
n = *size_ptr;
|
||||
while (n > 0)
|
||||
{
|
||||
unsigned char cx;
|
||||
unsigned char *cx_p = &cx;
|
||||
buffered_read_byte (ctx, cx_p);
|
||||
if (result!=1)
|
||||
break;
|
||||
ctx->past++;
|
||||
v = cx;
|
||||
n--;
|
||||
if (state == 0x000001) {
|
||||
state = ((state << 8) | v) & 0xffffff;
|
||||
val = state;
|
||||
goto found;
|
||||
}
|
||||
state = ((state << 8) | v) & 0xffffff;
|
||||
}
|
||||
val = -1;
|
||||
found:
|
||||
*header_state = state;
|
||||
*size_ptr = n;
|
||||
return val;
|
||||
}
|
||||
|
||||
void url_fskip (struct lib_ccx_ctx *ctx, int length)
|
||||
{
|
||||
buffered_seek (ctx, length);
|
||||
ctx->past+=length;
|
||||
}
|
||||
|
||||
static long mpegps_psm_parse(struct lib_ccx_ctx *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);
|
||||
|
||||
/* skip program_stream_info */
|
||||
url_fskip(ctx, ps_info_length);
|
||||
es_map_length = 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);
|
||||
/* 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 */
|
||||
return 2 + psm_length;
|
||||
}
|
||||
|
||||
|
||||
static int mpegps_read_pes_header(struct lib_ccx_ctx *ctx, int *pstart_code,
|
||||
LLONG *ppts, LLONG *pdts)
|
||||
{
|
||||
int len, size, startcode, c, flags, header_len;
|
||||
LLONG pts, dts;
|
||||
|
||||
redo:
|
||||
/* next start code (should be immediately after) */
|
||||
header_state = 0xff;
|
||||
size = MAX_SYNC_SIZE;
|
||||
startcode = find_next_start_code(ctx, &size, &header_state);
|
||||
//printf("startcode=%x pos=0x%Lx\n", startcode, url_ftell(&s->pb));
|
||||
if (startcode < 0)
|
||||
return AVERROR_IO;
|
||||
if (startcode == PACK_START_CODE)
|
||||
goto redo;
|
||||
if (startcode == SYSTEM_HEADER_START_CODE)
|
||||
goto redo;
|
||||
if (startcode == PADDING_STREAM ||
|
||||
startcode == PRIVATE_STREAM_2)
|
||||
{
|
||||
/* skip them */
|
||||
len = get_be16(ctx);
|
||||
// url_fskip(ctx, len);
|
||||
goto redo;
|
||||
}
|
||||
position_sanity_check();
|
||||
if (startcode == PROGRAM_STREAM_MAP)
|
||||
{
|
||||
mpegps_psm_parse(ctx);
|
||||
goto redo;
|
||||
}
|
||||
|
||||
/* find matching stream */
|
||||
if (!((startcode >= 0x1c0 && startcode <= 0x1df) ||
|
||||
(startcode >= 0x1e0 && startcode <= 0x1ef) ||
|
||||
(startcode == 0x1bd)))
|
||||
goto redo;
|
||||
|
||||
len = get_be16(ctx);
|
||||
pts = AV_NOPTS_VALUE;
|
||||
dts = AV_NOPTS_VALUE;
|
||||
position_sanity_check();
|
||||
/* stuffing */
|
||||
for(;;) {
|
||||
if (len < 1)
|
||||
goto redo;
|
||||
c = get_byte(ctx);
|
||||
len--;
|
||||
/* XXX: for mpeg1, should test only bit 7 */
|
||||
if (c != 0xff)
|
||||
break;
|
||||
}
|
||||
position_sanity_check();
|
||||
if ((c & 0xc0) == 0x40) {
|
||||
/* buffer scale & size */
|
||||
if (len < 2)
|
||||
goto redo;
|
||||
get_byte(ctx);
|
||||
c = get_byte(ctx);
|
||||
len -= 2;
|
||||
}
|
||||
position_sanity_check();
|
||||
if ((c & 0xf0) == 0x20) {
|
||||
if (len < 4)
|
||||
goto redo;
|
||||
dts = pts = get_pts(ctx, c);
|
||||
len -= 4;
|
||||
} else if ((c & 0xf0) == 0x30) {
|
||||
if (len < 9)
|
||||
goto redo;
|
||||
pts = get_pts(ctx, c);
|
||||
dts = get_pts(ctx, -1);
|
||||
len -= 9;
|
||||
} else if ((c & 0xc0) == 0x80) {
|
||||
/* mpeg 2 PES */
|
||||
#if 0 /* some streams have this field set for no apparent reason */
|
||||
if ((c & 0x30) != 0) {
|
||||
/* Encrypted multiplex not handled */
|
||||
goto redo;
|
||||
}
|
||||
#endif
|
||||
flags = get_byte(ctx);
|
||||
header_len = get_byte(ctx);
|
||||
len -= 2;
|
||||
if (header_len > len)
|
||||
goto redo;
|
||||
if ((flags & 0xc0) == 0x80) {
|
||||
dts = pts = get_pts(ctx, -1);
|
||||
if (header_len < 5)
|
||||
goto redo;
|
||||
header_len -= 5;
|
||||
len -= 5;
|
||||
} if ((flags & 0xc0) == 0xc0) {
|
||||
pts = get_pts(ctx, -1);
|
||||
dts = get_pts(ctx, -1);
|
||||
if (header_len < 10)
|
||||
goto redo;
|
||||
header_len -= 10;
|
||||
len -= 10;
|
||||
}
|
||||
len -= header_len;
|
||||
while (header_len > 0) {
|
||||
get_byte(ctx);
|
||||
header_len--;
|
||||
}
|
||||
}
|
||||
else if( c!= 0xf )
|
||||
goto redo;
|
||||
position_sanity_check();
|
||||
if (startcode == PRIVATE_STREAM_1 /* && psm_es_type[startcode & 0xff] */)
|
||||
{
|
||||
if (len < 1)
|
||||
goto redo;
|
||||
startcode = 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);
|
||||
len -= 3;
|
||||
}
|
||||
}
|
||||
|
||||
*pstart_code = startcode;
|
||||
*ppts = pts;
|
||||
*pdts = dts;
|
||||
return len;
|
||||
}
|
||||
|
||||
static int cc608_good_parity(const int *parity_table, unsigned int data)
|
||||
{
|
||||
int ret = parity_table[data & 0xff] && parity_table[(data & 0xff00) >> 8];
|
||||
if (!ret)
|
||||
{
|
||||
/* VERBOSE(VB_VBI, QString("VBI: Bad parity in EIA-608 data (%1)")
|
||||
.arg(data,0,16)); */
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
void ProcessVBIDataPacket(struct lib_ccx_ctx *ctx, struct cc_subtitle *sub)
|
||||
{
|
||||
|
||||
struct lib_cc_decode *dec_ctx = NULL;
|
||||
const unsigned char *meat = av.data;
|
||||
if (meat==NULL)
|
||||
{
|
||||
mprint ("Warning: ProcessVBIDataPacket called with NULL data, ignoring.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
LLONG linemask = 0;
|
||||
dec_ctx = ctx->dec_ctx;
|
||||
// unsigned long long utc = lastccptsu;
|
||||
|
||||
// [i]tv0 means there is a linemask
|
||||
// [I]TV0 means there is no linemask and all lines are present
|
||||
if ((meat[0]=='t') && (meat[1]=='v') && (meat[2] == '0'))
|
||||
{
|
||||
/// TODO this is almost certainly not endian safe....
|
||||
memcpy(&linemask, meat + 3, 8);
|
||||
meat += 11;
|
||||
}
|
||||
else if ((meat[0]=='T') && (meat[1]=='V') && (meat[2] == '0'))
|
||||
{
|
||||
linemask = 0xffffffffffffffffLL;
|
||||
meat += 3;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* VERBOSE(VB_VBI, LOC + QString("Unknown VBI data stream '%1%2%3'")
|
||||
.arg(QChar(buf[0])).arg(QChar(buf[1])).arg(QChar(buf[2]))); */
|
||||
mprint (" - Unknown VBI data stream\n");
|
||||
return;
|
||||
}
|
||||
static const unsigned int min_blank = 6;
|
||||
for (unsigned int i = 0; i < 36; i++)
|
||||
{
|
||||
if (!((linemask >> i) & 0x1))
|
||||
continue;
|
||||
|
||||
const unsigned int line = ((i < 18) ? i : i-18) + min_blank;
|
||||
const unsigned int field = (i<18) ? 0 : 1;
|
||||
const unsigned int id2 = *meat & 0xf;
|
||||
switch (id2)
|
||||
{
|
||||
case VBI_TYPE_TELETEXT:
|
||||
// SECAM lines 6-23
|
||||
// PAL lines 6-22
|
||||
// NTSC lines 10-21 (rare)
|
||||
// ttd->Decode(buf+1, VBI_IVTV);
|
||||
break;
|
||||
case VBI_TYPE_CC:
|
||||
// PAL line 22 (rare)
|
||||
// NTSC line 21
|
||||
if (21 == line)
|
||||
{
|
||||
int data = (meat[2] << 8) | meat[1];
|
||||
if (cc608_good_parity(cc608_parity_table, data))
|
||||
{
|
||||
unsigned char ccdata[3];
|
||||
if (field==0)
|
||||
{
|
||||
ccdata[0]=0x04; // Field 1
|
||||
ccdata[1]=meat[1];
|
||||
ccdata[2]=meat[2];
|
||||
do_cb(dec_ctx, ccdata, sub);
|
||||
// processed_ccblocks++; // Not sure this is accurate
|
||||
}
|
||||
else
|
||||
{
|
||||
ccdata[0]=0x05; // Field 1
|
||||
ccdata[1]=meat[1];
|
||||
ccdata[2]=meat[2];
|
||||
do_cb(dec_ctx, ccdata, sub);
|
||||
}
|
||||
}
|
||||
// utc += 33367;
|
||||
}
|
||||
break;
|
||||
case VBI_TYPE_VPS: // Video Programming System
|
||||
// PAL line 16
|
||||
// ccd608->DecodeVPS(buf+1); // a.k.a. PDC
|
||||
break;
|
||||
case VBI_TYPE_WSS: // Wide Screen Signal
|
||||
// PAL line 23
|
||||
// NTSC line 20
|
||||
// ccd608->DecodeWSS(buf+1);
|
||||
break;
|
||||
}
|
||||
meat += 43;
|
||||
}
|
||||
// lastccptsu = utc;
|
||||
}
|
||||
|
||||
static int mpegps_read_packet(struct lib_ccx_ctx *ctx)
|
||||
{
|
||||
LLONG pts, dts;
|
||||
|
||||
int len, startcode, type, codec_id = 0, es_type;
|
||||
redo:
|
||||
len = mpegps_read_pes_header(ctx, &startcode, &pts, &dts);
|
||||
if (len < 0)
|
||||
return len;
|
||||
position_sanity_check();
|
||||
/* now find stream */
|
||||
/*
|
||||
for(i=0;i<s->nb_streams;i++) {
|
||||
st = s->streams[i];
|
||||
if (st->id == startcode)
|
||||
goto found;
|
||||
}
|
||||
*/
|
||||
es_type = psm_es_type[startcode & 0xff];
|
||||
if(es_type > 0){
|
||||
if(es_type == STREAM_TYPE_VIDEO_MPEG1){
|
||||
codec_id = CODEC_ID_MPEG2VIDEO;
|
||||
type = CODEC_TYPE_VIDEO;
|
||||
} else if(es_type == STREAM_TYPE_VIDEO_MPEG2){
|
||||
codec_id = CODEC_ID_MPEG2VIDEO;
|
||||
type = CODEC_TYPE_VIDEO;
|
||||
} else if(es_type == STREAM_TYPE_AUDIO_MPEG1 ||
|
||||
es_type == STREAM_TYPE_AUDIO_MPEG2){
|
||||
codec_id = CODEC_ID_MP3;
|
||||
type = CODEC_TYPE_AUDIO;
|
||||
} else if(es_type == STREAM_TYPE_AUDIO_AAC){
|
||||
codec_id = CODEC_ID_AAC;
|
||||
type = CODEC_TYPE_AUDIO;
|
||||
} else if(es_type == STREAM_TYPE_VIDEO_MPEG4){
|
||||
codec_id = CODEC_ID_MPEG4;
|
||||
type = CODEC_TYPE_VIDEO;
|
||||
} else if(es_type == STREAM_TYPE_VIDEO_H264){
|
||||
codec_id = CODEC_ID_H264;
|
||||
type = CODEC_TYPE_VIDEO;
|
||||
} else if(es_type == STREAM_TYPE_AUDIO_AC3){
|
||||
codec_id = CODEC_ID_AC3;
|
||||
type = CODEC_TYPE_AUDIO;
|
||||
} else {
|
||||
goto skip;
|
||||
}
|
||||
}
|
||||
else
|
||||
if (startcode >= 0x1e0 && startcode <= 0x1ef)
|
||||
{
|
||||
static const unsigned char avs_seqh[4] = { 0, 0, 1, 0xb0 };
|
||||
unsigned char buf[8];
|
||||
buffered_read (ctx, buf,8);
|
||||
ctx->past+=8;
|
||||
// get_buffer(&s->pb, buf, 8);
|
||||
buffered_seek(ctx, -8);
|
||||
ctx->past-=8;
|
||||
if(!memcmp(buf, avs_seqh, 4) && (buf[6] != 0 || buf[7] != 1))
|
||||
codec_id = CODEC_ID_CAVS;
|
||||
else
|
||||
codec_id = CODEC_ID_MPEG2VIDEO;
|
||||
type = CODEC_TYPE_VIDEO;
|
||||
} else if (startcode >= 0x1c0 && startcode <= 0x1df) {
|
||||
type = CODEC_TYPE_AUDIO;
|
||||
codec_id = CODEC_ID_MP2;
|
||||
} else if (startcode >= 0x80 && startcode <= 0x87) {
|
||||
type = CODEC_TYPE_AUDIO;
|
||||
codec_id = CODEC_ID_AC3;
|
||||
} else if (startcode >= 0x88 && startcode <= 0x9f) {
|
||||
type = CODEC_TYPE_AUDIO;
|
||||
codec_id = CODEC_ID_DTS;
|
||||
} else if (startcode >= 0xa0 && startcode <= 0xbf) {
|
||||
type = CODEC_TYPE_AUDIO;
|
||||
codec_id = CODEC_ID_PCM_S16BE;
|
||||
} else if (startcode >= 0x20 && startcode <= 0x3f) {
|
||||
type = CODEC_TYPE_SUBTITLE;
|
||||
codec_id = CODEC_ID_DVD_SUBTITLE;
|
||||
} else if (startcode == 0x69 || startcode == 0x49) {
|
||||
type = CODEC_TYPE_DATA;
|
||||
codec_id = CODEC_ID_MPEG2VBI;
|
||||
} else {
|
||||
skip:
|
||||
// skip packet
|
||||
url_fskip(ctx, len);
|
||||
goto redo;
|
||||
}
|
||||
// no stream found: add a new stream
|
||||
/* st = av_new_stream(s, startcode);
|
||||
if (!st)
|
||||
goto skip;
|
||||
st->codec->codec_type = type;
|
||||
st->codec->codec_id = codec_id;
|
||||
if (codec_id != CODEC_ID_PCM_S16BE)
|
||||
st->need_parsing = 1;
|
||||
|
||||
// notify the callback of the change in streams
|
||||
if (s->streams_changed) {
|
||||
s->streams_changed(s->stream_change_data);
|
||||
}
|
||||
|
||||
found:
|
||||
if(st->discard >= AVDISCARD_ALL)
|
||||
goto skip; */
|
||||
if (startcode >= 0xa0 && startcode <= 0xbf)
|
||||
{
|
||||
|
||||
// for LPCM, we just skip the header and consider it is raw
|
||||
// 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)
|
||||
len -= 3;
|
||||
//freq = (b1 >> 4) & 3;
|
||||
//st->codec->sample_rate = lpcm_freq_tab[freq];
|
||||
//st->codec->channels = 1 + (b1 & 7);
|
||||
//st->codec->bit_rate = st->codec->channels * st->codec->sample_rate * 2;
|
||||
|
||||
}
|
||||
// av_new_packet(pkt, len);
|
||||
/*
|
||||
printf ("Paquete de %lu bytes, codec_id=%d, type=%d\n",(unsigned long) len,
|
||||
codec_id, type);
|
||||
*/
|
||||
//get_buffer(fh, pkt->data, pkt->size);
|
||||
av.size=len;
|
||||
av.data=(unsigned char *) realloc (av.data,av.size);
|
||||
if (av.data==NULL)
|
||||
{
|
||||
fatal (EXIT_NOT_ENOUGH_MEMORY, "Not enough memory, realloc() failed. Giving up.\n");
|
||||
}
|
||||
av.codec_id=codec_id;
|
||||
av.type=type;
|
||||
buffered_read (ctx, av.data,av.size);
|
||||
ctx->past+=av.size;
|
||||
position_sanity_check();
|
||||
// LSEEK (fh,pkt->size,SEEK_CUR);
|
||||
av.pts = pts;
|
||||
av.dts = dts;
|
||||
// pkt->stream_index = st->index;
|
||||
|
||||
#if 0
|
||||
av_log(s, AV_LOG_DEBUG, "%d: pts=%0.3f dts=%0.3f size=%d\n",
|
||||
pkt->stream_index, pkt->pts / 90000.0, pkt->dts / 90000.0, pkt->size);
|
||||
#endif
|
||||
|
||||
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)
|
||||
{
|
||||
int rc;
|
||||
int has_vbi=0;
|
||||
LLONG saved = 0;
|
||||
struct cc_subtitle dec_sub;
|
||||
struct lib_cc_decode *dec_ctx = NULL;
|
||||
|
||||
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);
|
||||
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)
|
||||
{
|
||||
position_sanity_check();
|
||||
if (av.codec_id==CODEC_ID_MPEG2VBI && av.type==CODEC_TYPE_DATA)
|
||||
{
|
||||
if (!has_vbi)
|
||||
{
|
||||
mprint ("\rDetected VBI data, disabling user-data packet analysis (not needed).\n");
|
||||
has_vbi=1;
|
||||
}
|
||||
//fts_now=LLONG((processed_ccblocks*1000)/29.97);
|
||||
ProcessVBIDataPacket(ctx, &dec_sub);
|
||||
}
|
||||
/* This needs a lot more testing */
|
||||
if (av.codec_id==CODEC_ID_MPEG2VIDEO && av.type==CODEC_TYPE_VIDEO )
|
||||
{
|
||||
LLONG length = saved+av.size;
|
||||
if (length>desp_length) // Result of a lazy programmer. Make something decent.
|
||||
{
|
||||
desp_length=length*2; // *2, just to reduce possible future reallocs
|
||||
desp=(unsigned char *) realloc (desp,desp_length); // 16, some extra
|
||||
if (!desp)
|
||||
fatal (EXIT_NOT_ENOUGH_MEMORY, "Not enough memory.\n");
|
||||
}
|
||||
if (av.pts!=AV_NOPTS_VALUE)
|
||||
{
|
||||
current_pts=av.pts;
|
||||
if (pts_set==0)
|
||||
pts_set=1;
|
||||
}
|
||||
memcpy (desp+saved,av.data,av.size);
|
||||
LLONG used = process_m2v(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 th=cur_sec/10;
|
||||
if (ctx->last_reported_progress!=th)
|
||||
{
|
||||
activity_progress (-1, cur_sec/60, cur_sec%60);
|
||||
ctx->last_reported_progress = th;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (ctx->total_inputsize > 0 )
|
||||
{
|
||||
int progress = (int) ((((ctx->total_past+ctx->past)>>8)*100)/(ctx->total_inputsize>>8));
|
||||
if (ctx->last_reported_progress != progress)
|
||||
{
|
||||
int cur_sec = (int) (get_fts() / 1000);
|
||||
activity_progress (progress, cur_sec/60, cur_sec%60);
|
||||
|
||||
fflush (stdout);
|
||||
ctx->last_reported_progress = progress;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (dec_sub.got_output)
|
||||
{
|
||||
encode_sub(enc_ctx,&dec_sub);
|
||||
dec_sub.got_output = 0;
|
||||
}
|
||||
}
|
||||
if (desp)
|
||||
free (desp);
|
||||
free (av.data);
|
||||
}
|
||||
963
src/lib_ccx/networking.c
Normal file
963
src/lib_ccx/networking.c
Normal file
@@ -0,0 +1,963 @@
|
||||
#include "lib_ccx.h"
|
||||
#include "networking.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <assert.h>
|
||||
|
||||
#define DEBUG_OUT 0
|
||||
|
||||
/* Protocol constants: */
|
||||
#define INT_LEN 10
|
||||
#define OK 1
|
||||
#define PASSWORD 2
|
||||
#define BIN_MODE 3
|
||||
#define CC_DESC 4
|
||||
#pragma warning( suppress : 4005)
|
||||
#define ERROR 51
|
||||
#define UNKNOWN_COMMAND 52
|
||||
#define WRONG_PASSWORD 53
|
||||
#define CONN_LIMIT 54
|
||||
|
||||
#define DFT_PORT "2048" /* Default port for server and client */
|
||||
#define WRONG_PASSWORD_DELAY 2 /* Seconds */
|
||||
#define BUFFER_SIZE 50
|
||||
|
||||
int srv_sd = -1; /* Server socket descriptor */
|
||||
|
||||
/*
|
||||
* 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);
|
||||
|
||||
/*
|
||||
* Writes/reads data according to protocol to descriptor
|
||||
* block format: * command | lenght | data | \r\n
|
||||
* 1 byte | INT_LEN bytes | lenght bytes | 2 bytes
|
||||
*/
|
||||
ssize_t write_block(int fd, char command, const char *buf, size_t buf_len);
|
||||
ssize_t read_block(int fd, char *command, char *buf, size_t *buf_len);
|
||||
|
||||
/* Reads n bytes from descriptor */
|
||||
ssize_t readn(int fd, void *vptr, size_t n);
|
||||
|
||||
/* Writes n bytes to descriptor */
|
||||
ssize_t writen(int fd, const void *vptr, size_t n);
|
||||
|
||||
/* Convinence functions */
|
||||
ssize_t write_byte(int fd, char status);
|
||||
ssize_t read_byte(int fd, char *status);
|
||||
|
||||
void init_sockets (void);
|
||||
|
||||
#if DEBUG_OUT
|
||||
void pr_command(char c);
|
||||
#endif
|
||||
|
||||
void connect_to_srv(const char *addr, const char *port, const char *cc_desc)
|
||||
{
|
||||
if (NULL == addr)
|
||||
{
|
||||
mprint("Server addres is not set\n");
|
||||
fatal(EXIT_FAILURE, "Unable to connect\n");
|
||||
}
|
||||
|
||||
if (NULL == port)
|
||||
port = DFT_PORT;
|
||||
|
||||
mprint("\n\r----------------------------------------------------------------------\n");
|
||||
mprint("Connecting to %s:%s\n", addr, port);
|
||||
|
||||
if ((srv_sd = tcp_connect(addr, port)) < 0)
|
||||
fatal(EXIT_FAILURE, "Unable to connect\n");
|
||||
|
||||
if (ask_passwd(srv_sd) < 0)
|
||||
fatal(EXIT_FAILURE, "Unable to connect\n");
|
||||
|
||||
if (cc_desc != NULL &&
|
||||
write_block(srv_sd, CC_DESC, cc_desc, strlen(cc_desc)) < 0)
|
||||
{
|
||||
fatal(EXIT_FAILURE, "Unable to connect\n");
|
||||
}
|
||||
|
||||
mprint("Connected to %s:%s\n", addr, port);
|
||||
}
|
||||
|
||||
void net_send_header(const unsigned char *data, size_t len)
|
||||
{
|
||||
assert(srv_sd > 0);
|
||||
|
||||
#if DEBUG_OUT
|
||||
fprintf(stderr, "Sending header (len = %u): \n", 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)
|
||||
{
|
||||
printf("Can't send BIN header\n");
|
||||
return;
|
||||
}
|
||||
|
||||
char ok;
|
||||
if (read_byte(srv_sd, &ok) != 1)
|
||||
return;
|
||||
|
||||
#if DEBUG_OUT
|
||||
fprintf(stderr, "[S] ");
|
||||
pr_command(ok);
|
||||
fprintf(stderr, "\n");
|
||||
#endif
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
void net_send_cc(const unsigned char *data, size_t len)
|
||||
{
|
||||
assert(srv_sd > 0);
|
||||
|
||||
#if DEBUG_OUT
|
||||
fprintf(stderr, "[C] Sending %u bytes\n", len);
|
||||
#endif
|
||||
|
||||
ssize_t rc;
|
||||
if ((rc = writen(srv_sd, data, len)) != (int) len)
|
||||
{
|
||||
if (rc < 0)
|
||||
mprint("write() error: %s", strerror(errno));
|
||||
return;
|
||||
}
|
||||
|
||||
/* nanosleep((struct timespec[]){{0, 100000000}}, NULL); */
|
||||
/* Sleep(100); */
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* command | lenght | data | \r\n
|
||||
* 1 byte | INT_LEN bytes | lenght bytes | 2 bytes
|
||||
*/
|
||||
ssize_t write_block(int fd, char command, const char *buf, size_t buf_len)
|
||||
{
|
||||
assert(fd > 0);
|
||||
#if DEBUG_OUT
|
||||
fprintf(stderr, "[C] ");
|
||||
#endif
|
||||
|
||||
int rc;
|
||||
ssize_t nwritten = 0;
|
||||
|
||||
if ((rc = write_byte(fd, command)) < 0)
|
||||
return -1;
|
||||
else if (rc != 1)
|
||||
return 0;
|
||||
nwritten++;
|
||||
|
||||
#if DEBUG_OUT
|
||||
pr_command(command);
|
||||
fprintf(stderr, " ");
|
||||
#endif
|
||||
|
||||
char len_str[INT_LEN] = {0};
|
||||
snprintf(len_str, INT_LEN, "%zu", buf_len);
|
||||
if ((rc = writen(fd, len_str, INT_LEN)) < 0)
|
||||
return -1;
|
||||
else if (rc != INT_LEN)
|
||||
return 0;
|
||||
nwritten += rc;
|
||||
|
||||
#if DEBUG_OUT
|
||||
fwrite(len_str, sizeof(char), INT_LEN, stderr);
|
||||
fprintf(stderr, " ");
|
||||
#endif
|
||||
|
||||
if (buf_len > 0)
|
||||
{
|
||||
if ((rc = writen(fd, buf, buf_len)) < 0)
|
||||
return -1;
|
||||
else if (rc != (int) buf_len)
|
||||
return 0;
|
||||
nwritten += rc;
|
||||
}
|
||||
|
||||
#if DEBUG_OUT
|
||||
if (buf != NULL)
|
||||
{
|
||||
fwrite(buf, sizeof(char), buf_len, stderr);
|
||||
fprintf(stderr, " ");
|
||||
}
|
||||
#endif
|
||||
|
||||
if ((rc = write_byte(fd, '\r')) < 0)
|
||||
return -1;
|
||||
else if (rc != 1)
|
||||
return 0;
|
||||
nwritten++;
|
||||
|
||||
#if DEBUG_OUT
|
||||
fprintf(stderr, "\\r");
|
||||
#endif
|
||||
|
||||
if ((rc = write_byte(fd, '\n')) < 0)
|
||||
return -1;
|
||||
else if (rc != 1)
|
||||
return 0;
|
||||
nwritten++;
|
||||
|
||||
#if DEBUG_OUT
|
||||
fprintf(stderr, "\\n\n");
|
||||
#endif
|
||||
|
||||
return nwritten;
|
||||
}
|
||||
|
||||
int tcp_connect(const char *host, const char *port)
|
||||
{
|
||||
assert(host != NULL);
|
||||
assert(port != NULL);
|
||||
|
||||
init_sockets();
|
||||
|
||||
struct addrinfo hints;
|
||||
memset(&hints, 0, sizeof(struct addrinfo));
|
||||
hints.ai_family = AF_UNSPEC;
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
|
||||
struct addrinfo *ai;
|
||||
int rc = getaddrinfo(host, port, &hints, &ai);
|
||||
if (rc != 0) {
|
||||
mprint("getaddrinfo() error: %s\n", gai_strerror(rc));
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct addrinfo *p;
|
||||
int sockfd;
|
||||
|
||||
/* Try each address until we sucessfully connect */
|
||||
for (p = ai; p != NULL; p = p->ai_next) {
|
||||
sockfd = socket(p->ai_family, SOCK_STREAM, p->ai_protocol);
|
||||
|
||||
if (-1 == sockfd) {
|
||||
#if _WIN32
|
||||
wprintf(L"socket() eror: %ld\n", WSAGetLastError());
|
||||
#else
|
||||
mprint("socket() error: %s\n", strerror(errno));
|
||||
#endif
|
||||
if (p->ai_next != NULL)
|
||||
mprint("trying next addres ...\n");
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (connect(sockfd, p->ai_addr, p->ai_addrlen) == 0)
|
||||
break;
|
||||
#if _WIN32
|
||||
wprintf(L"connect() eror: %ld\n", WSAGetLastError());
|
||||
#else
|
||||
mprint("connect() error: %s\n", strerror(errno));
|
||||
#endif
|
||||
if (p->ai_next != NULL)
|
||||
mprint("trying next addres ...\n");
|
||||
|
||||
#if _WIN32
|
||||
closesocket(sockfd);
|
||||
#else
|
||||
close(sockfd);
|
||||
#endif
|
||||
}
|
||||
|
||||
freeaddrinfo(ai);
|
||||
|
||||
if (NULL == p)
|
||||
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)
|
||||
port = DFT_PORT;
|
||||
|
||||
mprint("\n\r----------------------------------------------------------------------\n");
|
||||
|
||||
mprint("Binding to %s\n", port);
|
||||
int fam;
|
||||
int listen_sd = tcp_bind(port, &fam);
|
||||
if (listen_sd < 0)
|
||||
fatal(EXIT_FAILURE, "Unable to start server\n");
|
||||
|
||||
if (pwd != NULL)
|
||||
mprint("Password: %s\n", pwd);
|
||||
|
||||
mprint("Waiting for connections\n");
|
||||
|
||||
int sockfd = -1;
|
||||
|
||||
while (1)
|
||||
{
|
||||
socklen_t clilen;
|
||||
if (AF_INET == fam)
|
||||
clilen = sizeof(struct sockaddr_in);
|
||||
else
|
||||
clilen = sizeof(struct sockaddr_in6);
|
||||
struct sockaddr *cliaddr = (struct sockaddr *) malloc(clilen);
|
||||
if (NULL == cliaddr)
|
||||
fatal(EXIT_FAILURE, "malloc() error: %s", strerror(errno));
|
||||
|
||||
if ((sockfd = accept(listen_sd, cliaddr, &clilen)) < 0)
|
||||
{
|
||||
if (EINTR == errno) /* TODO not necessary */
|
||||
{
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
#if _WIN32
|
||||
wprintf(L"accept() eror: %ld\n", WSAGetLastError());
|
||||
exit(EXIT_FAILURE);
|
||||
#else
|
||||
fatal(EXIT_FAILURE, "accept() error: %s\n", strerror(errno));
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
char host[NI_MAXHOST];
|
||||
char serv[NI_MAXSERV];
|
||||
int rc;
|
||||
if ((rc = getnameinfo(cliaddr, clilen,
|
||||
host, sizeof(host), serv, sizeof(serv), 0)) != 0)
|
||||
{
|
||||
mprint("getnameinfo() error: %s\n", gai_strerror(rc));
|
||||
}
|
||||
else
|
||||
{
|
||||
mprint("%s:%s Connceted\n", host, serv);
|
||||
}
|
||||
|
||||
free(cliaddr);
|
||||
|
||||
if (pwd != NULL && (rc = check_password(sockfd, pwd)) <= 0)
|
||||
goto close_conn;
|
||||
|
||||
#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);
|
||||
#else
|
||||
close(sockfd);
|
||||
#endif
|
||||
}
|
||||
|
||||
#if _WIN32
|
||||
closesocket(listen_sd);
|
||||
#else
|
||||
close(listen_sd);
|
||||
#endif
|
||||
|
||||
return sockfd;
|
||||
}
|
||||
|
||||
int check_password(int fd, const char *pwd)
|
||||
{
|
||||
assert(pwd != NULL);
|
||||
|
||||
char c;
|
||||
int rc;
|
||||
size_t len;
|
||||
char buf[BUFFER_SIZE];
|
||||
|
||||
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 (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;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
int tcp_bind(const char *port, int *family)
|
||||
{
|
||||
init_sockets();
|
||||
|
||||
struct addrinfo hints;
|
||||
memset(&hints, 0, sizeof(struct addrinfo));
|
||||
hints.ai_family = AF_UNSPEC;
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
hints.ai_flags = AI_PASSIVE;
|
||||
|
||||
struct addrinfo *ai;
|
||||
int rc = getaddrinfo(NULL, port, &hints, &ai);
|
||||
if (rc != 0)
|
||||
{
|
||||
mprint("getaddrinfo() error: %s\n", gai_strerror(rc));
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct addrinfo *p;
|
||||
int sockfd = -1;
|
||||
/* Try each address until we sucessfully bind */
|
||||
for (p = ai; p != NULL; p = p->ai_next)
|
||||
{
|
||||
sockfd = socket(p->ai_family, SOCK_STREAM, p->ai_protocol);
|
||||
|
||||
if (-1 == sockfd)
|
||||
{
|
||||
#if _WIN32
|
||||
wprintf(L"socket() eror: %ld\n", WSAGetLastError());
|
||||
#else
|
||||
mprint("socket() error: %s\n", strerror(errno));
|
||||
#endif
|
||||
|
||||
if (p->ai_next != NULL)
|
||||
mprint("trying next addres ...\n");
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (AF_INET6 == p->ai_family)
|
||||
{
|
||||
int no = 0;
|
||||
if (setsockopt(sockfd, IPPROTO_IPV6, IPV6_V6ONLY, (char*)&no, sizeof(no)) < 0)
|
||||
{
|
||||
#if _WIN32
|
||||
wprintf(L"setsockopt() eror: %ld\n", WSAGetLastError());
|
||||
#else
|
||||
mprint("setsockopt() error: %s\n", strerror(errno));
|
||||
#endif
|
||||
|
||||
if (p->ai_next != NULL)
|
||||
mprint("trying next addres ...\n");
|
||||
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (bind(sockfd, p->ai_addr, p->ai_addrlen) < 0)
|
||||
{
|
||||
#if _WIN32
|
||||
wprintf(L"bind() error: %ld\n", WSAGetLastError());
|
||||
closesocket(sockfd);
|
||||
#else
|
||||
mprint("bind() error: %s\n", strerror(errno));
|
||||
close(sockfd);
|
||||
#endif
|
||||
if (p->ai_next != NULL)
|
||||
mprint("trying next addres ...\n");
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
*family = p->ai_family;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
freeaddrinfo(ai);
|
||||
|
||||
if (NULL == p)
|
||||
return -1;
|
||||
|
||||
if (listen(sockfd, SOMAXCONN) != 0)
|
||||
{
|
||||
#if _WIN32
|
||||
wprintf(L"listen() eror: %ld\n", WSAGetLastError());
|
||||
closesocket(sockfd);
|
||||
#else
|
||||
perror("listen() error");
|
||||
close(sockfd);
|
||||
#endif
|
||||
return -1;
|
||||
}
|
||||
|
||||
return sockfd;
|
||||
}
|
||||
|
||||
ssize_t read_block(int fd, char *command, char *buf, size_t *buf_len)
|
||||
{
|
||||
assert(command != NULL);
|
||||
assert(buf != NULL);
|
||||
assert(buf_len != NULL);
|
||||
assert(*buf_len > 0);
|
||||
|
||||
ssize_t rc;
|
||||
ssize_t nread = 0;
|
||||
|
||||
if ((rc = readn(fd, command, 1)) < 0)
|
||||
return -1;
|
||||
else if ((size_t) rc != 1)
|
||||
return 0;
|
||||
nread += rc;
|
||||
|
||||
#if DEBUG_OUT
|
||||
fprintf(stderr, "[C] ");
|
||||
pr_command(*command);
|
||||
fprintf(stderr, " ");
|
||||
#endif
|
||||
|
||||
char len_str[INT_LEN] = {0};
|
||||
if ((rc = readn(fd, len_str, INT_LEN)) < 0)
|
||||
return -1;
|
||||
else if (rc != INT_LEN)
|
||||
return 0;
|
||||
nread += rc;
|
||||
|
||||
#if DEBUG_OUT
|
||||
fwrite(len_str, sizeof(char), INT_LEN, stderr);
|
||||
fprintf(stderr, " ");
|
||||
#endif
|
||||
|
||||
size_t len = atoi(len_str);
|
||||
|
||||
if (len > 0)
|
||||
{
|
||||
size_t ign_bytes = 0;
|
||||
if (len > *buf_len)
|
||||
{
|
||||
ign_bytes = len - *buf_len;
|
||||
mprint("read_block() warning: Buffer overflow, ignoring %d bytes\n",
|
||||
ign_bytes);
|
||||
len = *buf_len;
|
||||
}
|
||||
|
||||
if ((rc = readn(fd, buf, len)) < 0)
|
||||
return -1;
|
||||
else if ((size_t) rc != len)
|
||||
return 0;
|
||||
nread += rc;
|
||||
*buf_len = len;
|
||||
|
||||
if ((rc = readn(fd, 0, ign_bytes)) < 0)
|
||||
return -1;
|
||||
else if ((size_t) rc != ign_bytes)
|
||||
return 0;
|
||||
nread += rc;
|
||||
|
||||
#if DEBUG_OUT
|
||||
fwrite(buf, sizeof(char), len, stderr);
|
||||
fprintf(stderr, " ");
|
||||
#endif
|
||||
}
|
||||
|
||||
char end[2] = {0};
|
||||
if ((rc = readn(fd, end, sizeof(end))) < 0)
|
||||
return -1;
|
||||
else if ((size_t) rc != sizeof(end))
|
||||
return 0;
|
||||
nread += rc;
|
||||
|
||||
if (end[0] != '\r' || end[1] != '\n')
|
||||
{
|
||||
#if DEBUG_OUT
|
||||
fprintf(stderr, "read_block(): No end marker present\n");
|
||||
fprintf(stderr, "Closing connection\n");
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if DEBUG_OUT
|
||||
fprintf(stderr, "\\r\\n\n");
|
||||
#endif
|
||||
|
||||
return nread;
|
||||
}
|
||||
|
||||
|
||||
#if DEBUG_OUT
|
||||
void pr_command(char c)
|
||||
{
|
||||
switch(c)
|
||||
{
|
||||
case OK:
|
||||
fprintf(stderr, "OK");
|
||||
break;
|
||||
case BIN_MODE:
|
||||
fprintf(stderr, "BIN_MODE");
|
||||
break;
|
||||
case WRONG_PASSWORD:
|
||||
fprintf(stderr, "WRONG_PASSWORD");
|
||||
break;
|
||||
case UNKNOWN_COMMAND:
|
||||
fprintf(stderr, "UNKNOWN_COMMAND");
|
||||
break;
|
||||
case ERROR:
|
||||
fprintf(stderr, "ERROR");
|
||||
break;
|
||||
case CONN_LIMIT:
|
||||
fprintf(stderr, "CONN_LIMIT");
|
||||
break;
|
||||
case PASSWORD:
|
||||
fprintf(stderr, "PASSWORD");
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "UNKNOWN (%d)", (int) c);
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
ssize_t readn(int fd, void *vptr, size_t n)
|
||||
{
|
||||
assert(fd > 0);
|
||||
size_t nleft;
|
||||
ssize_t nread;
|
||||
char *ptr;
|
||||
|
||||
ptr = vptr;
|
||||
nleft = n;
|
||||
while (nleft > 0)
|
||||
{
|
||||
if (NULL == vptr) {
|
||||
char c;
|
||||
nread = recv(fd, &c, 1, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
nread = recv(fd, (void*)ptr, nleft, 0);
|
||||
}
|
||||
|
||||
if (nread < 0)
|
||||
{
|
||||
if (errno == EINTR)
|
||||
{
|
||||
nread = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
#if _WIN32
|
||||
wprintf(L"recv() eror: %ld\n", WSAGetLastError());
|
||||
#else
|
||||
mprint("recv() error: %s\n", strerror(errno));
|
||||
#endif
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
else if (0 == nread)
|
||||
{
|
||||
break; /* EOF */
|
||||
}
|
||||
|
||||
nleft -= nread;
|
||||
ptr += nread;
|
||||
}
|
||||
|
||||
return n - nleft;
|
||||
}
|
||||
|
||||
ssize_t writen(int fd, const void *vptr, size_t n)
|
||||
{
|
||||
assert(fd > 0);
|
||||
assert((n > 0 && vptr != NULL) || (n == 0 && vptr == NULL));
|
||||
|
||||
size_t nleft;
|
||||
ssize_t nwritten;
|
||||
const char *ptr;
|
||||
|
||||
ptr = vptr;
|
||||
nleft = n;
|
||||
while (nleft > 0)
|
||||
{
|
||||
if ((nwritten = send(fd, ptr, nleft, 0)) < 0)
|
||||
{
|
||||
if (errno == EINTR)
|
||||
{
|
||||
nwritten = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
#if _WIN32
|
||||
wprintf(L"send() eror: %ld\n", WSAGetLastError());
|
||||
#else
|
||||
mprint("send() error: %s\n", strerror(errno));
|
||||
#endif
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
else if (0 == nwritten)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
nleft -= nwritten;
|
||||
ptr += nwritten;
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
ssize_t write_byte(int fd, char ch)
|
||||
{
|
||||
assert(fd > 0);
|
||||
|
||||
return writen(fd, &ch, 1);
|
||||
}
|
||||
|
||||
ssize_t read_byte(int fd, char *ch)
|
||||
{
|
||||
assert(fd > 0);
|
||||
assert(ch != NULL);
|
||||
|
||||
return readn(fd, ch, 1);
|
||||
}
|
||||
|
||||
int start_upd_srv(const char *addr_str, unsigned port)
|
||||
{
|
||||
init_sockets();
|
||||
|
||||
in_addr_t addr;
|
||||
if (addr_str != NULL)
|
||||
{
|
||||
struct hostent *host = gethostbyname(addr_str);
|
||||
if (NULL == host)
|
||||
{
|
||||
fatal(EXIT_MALFORMED_PARAMETER, "Cannot look up udp network address: %s\n",
|
||||
addr_str);
|
||||
}
|
||||
else if (host->h_addrtype != AF_INET)
|
||||
{
|
||||
fatal(EXIT_MALFORMED_PARAMETER, "No support for non-IPv4 network addresses: %s\n",
|
||||
addr_str);
|
||||
}
|
||||
|
||||
addr = ntohl(((struct in_addr *)host->h_addr_list[0])->s_addr);
|
||||
}
|
||||
else
|
||||
{
|
||||
addr = INADDR_ANY;
|
||||
}
|
||||
|
||||
int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
|
||||
if (-1 == sockfd) {
|
||||
#if _WIN32
|
||||
wprintf(L"socket() eror: %ld\n", WSAGetLastError());
|
||||
exit(EXIT_FAILURE);
|
||||
#else
|
||||
mprint("socket() error: %s\n", strerror(errno));
|
||||
#endif
|
||||
}
|
||||
|
||||
if (IN_MULTICAST(addr))
|
||||
{
|
||||
int on = 1;
|
||||
if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on)) < 0)
|
||||
{
|
||||
#if _WIN32
|
||||
wprintf(L"setsockopt() error: %ld\n", WSAGetLastError());
|
||||
#else
|
||||
mprint("setsockopt() error: %s\n", strerror(errno));
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
struct sockaddr_in servaddr;
|
||||
servaddr.sin_family = AF_INET;
|
||||
servaddr.sin_port = htons(port);
|
||||
if (IN_MULTICAST(addr))
|
||||
servaddr.sin_addr.s_addr = htonl(addr);
|
||||
else
|
||||
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
|
||||
|
||||
if (bind(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) != 0)
|
||||
{
|
||||
#if _WIN32
|
||||
wprintf(L"bind() error: %ld\n", WSAGetLastError());
|
||||
exit(EXIT_FAILURE);
|
||||
#else
|
||||
fatal(CCX_COMMON_EXIT_BUG_BUG, "bind() error: %s\n", strerror(errno));
|
||||
#endif
|
||||
}
|
||||
|
||||
if (IN_MULTICAST(addr)) {
|
||||
struct ip_mreq group;
|
||||
group.imr_multiaddr.s_addr = htonl(addr);
|
||||
group.imr_interface.s_addr = htonl(INADDR_ANY);
|
||||
if (setsockopt(sockfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *)&group, sizeof(group)) < 0)
|
||||
{
|
||||
#if _WIN32
|
||||
wprintf(L"setsockopt() error: %ld\n", WSAGetLastError());
|
||||
#else
|
||||
mprint("setsockopt() error: %s\n", strerror(errno));
|
||||
#endif
|
||||
fatal(CCX_COMMON_EXIT_BUG_BUG, "Cannot join multicast group.");
|
||||
}
|
||||
}
|
||||
|
||||
mprint("\n\r----------------------------------------------------------------------\n");
|
||||
if (addr == INADDR_ANY)
|
||||
{
|
||||
mprint("\rReading from UDP socket %u\n", port);
|
||||
}
|
||||
else
|
||||
{
|
||||
struct in_addr in;
|
||||
in.s_addr = htonl(addr);
|
||||
mprint("\rReading from UDP socket %s:%u\n", inet_ntoa(in), port);
|
||||
}
|
||||
|
||||
return sockfd;
|
||||
}
|
||||
|
||||
void init_sockets (void)
|
||||
{
|
||||
static int socket_inited = 0;
|
||||
if (!socket_inited)
|
||||
{
|
||||
// Initialize Winsock
|
||||
#ifdef _WIN32
|
||||
WSADATA wsaData = {0};
|
||||
int iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
|
||||
if (iResult != 0)
|
||||
{
|
||||
wprintf(L"WSAStartup failed: %d\n", iResult);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
#endif
|
||||
|
||||
socket_inited = 1;
|
||||
}
|
||||
}
|
||||
15
src/lib_ccx/networking.h
Normal file
15
src/lib_ccx/networking.h
Normal file
@@ -0,0 +1,15 @@
|
||||
#ifndef NETWORKING_H
|
||||
#define NETWORKING_H
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
void connect_to_srv(const char *addr, const char *port, const char *cc_desc);
|
||||
|
||||
void net_send_header(const unsigned char *data, size_t len);
|
||||
void net_send_cc(const unsigned char *data, size_t len);
|
||||
|
||||
int start_tcp_srv(const char *port, const char *pwd);
|
||||
|
||||
int start_upd_srv(const char *addr, unsigned port);
|
||||
|
||||
#endif /* end of include guard: NETWORKING_H */
|
||||
329
src/lib_ccx/ocr.c
Normal file
329
src/lib_ccx/ocr.c
Normal file
@@ -0,0 +1,329 @@
|
||||
#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"
|
||||
|
||||
struct ocrCtx
|
||||
{
|
||||
TessBaseAPI* api;
|
||||
};
|
||||
|
||||
struct transIntensity
|
||||
{
|
||||
uint8_t *t;
|
||||
png_color *palette;
|
||||
};
|
||||
static int check_trans_tn_intensity(const void *p1, const void *p2, void *arg)
|
||||
{
|
||||
struct transIntensity *ti = arg;
|
||||
unsigned char* tmp = (unsigned char*)p1;
|
||||
unsigned char* act = (unsigned char*)p2;
|
||||
unsigned char tmp_i;
|
||||
unsigned char act_i;
|
||||
/** TODO verify that RGB follow ITU-R BT.709
|
||||
* Below fomula is valid only for 709 standurd
|
||||
* Y = 0.2126 R + 0.7152 G + 0.0722 B
|
||||
*/
|
||||
tmp_i = (0.2126 * ti->palette[*tmp].red) + (0.7152 * ti->palette[*tmp].green) + (0.0722 * ti->palette[*tmp].blue);
|
||||
act_i = (0.2126 * ti->palette[*act].red) + (0.7152 * ti->palette[*act].green) + (0.0722 * ti->palette[*act].blue);;
|
||||
|
||||
if (ti->t[*tmp] < ti->t[*act] || (ti->t[*tmp] == ti->t[*act] && tmp_i < act_i))
|
||||
return -1;
|
||||
else if (ti->t[*tmp] == ti->t[*act] && tmp_i == act_i)
|
||||
return 0;
|
||||
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int search_language_pack(const char *dirname,const char *lang)
|
||||
{
|
||||
DIR *dp;
|
||||
struct dirent *dirp;
|
||||
char filename[256];
|
||||
if ((dp = opendir(dirname)) == NULL)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
snprintf(filename, 256, "%s.traineddata",lang);
|
||||
while ((dirp = readdir(dp)) != NULL)
|
||||
{
|
||||
if(!strcmp(dirp->d_name, filename))
|
||||
{
|
||||
closedir(dp);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
closedir(dp);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void delete_ocr (struct ocrCtx* ctx)
|
||||
{
|
||||
TessBaseAPIEnd(ctx->api);
|
||||
TessBaseAPIDelete(ctx->api);
|
||||
freep(&ctx);
|
||||
}
|
||||
void* init_ocr(int lang_index)
|
||||
{
|
||||
int ret;
|
||||
struct ocrCtx* ctx;
|
||||
|
||||
ctx = (struct ocrCtx*)malloc(sizeof(struct ocrCtx));
|
||||
if(!ctx)
|
||||
return NULL;
|
||||
ctx->api = TessBaseAPICreate();
|
||||
|
||||
/* if language was undefined use english */
|
||||
if(lang_index == 0)
|
||||
{
|
||||
/* select english */
|
||||
lang_index = 1;
|
||||
}
|
||||
|
||||
/* if langauge pack not found use english */
|
||||
ret = search_language_pack("tessdata",language[lang_index]);
|
||||
if(ret < 0 )
|
||||
{
|
||||
/* select english */
|
||||
lang_index = 1;
|
||||
}
|
||||
ret = TessBaseAPIInit3(ctx->api,"", language[lang_index]);
|
||||
if(ret < 0)
|
||||
{
|
||||
goto fail;
|
||||
}
|
||||
return ctx;
|
||||
fail:
|
||||
delete_ocr(ctx);
|
||||
return NULL;
|
||||
|
||||
}
|
||||
char* ocr_bitmap(void* arg, png_color *palette,png_byte *alpha, unsigned char* indata,int w, int h)
|
||||
{
|
||||
PIX *pix;
|
||||
char*text_out= NULL;
|
||||
int i,j,index;
|
||||
unsigned int wpl;
|
||||
unsigned int *data,*ppixel;
|
||||
struct ocrCtx* ctx = arg;
|
||||
pix = pixCreate(w, h, 32);
|
||||
if(pix == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
wpl = pixGetWpl(pix);
|
||||
data = pixGetData(pix);
|
||||
#if LEPTONICA_VERSION > 69
|
||||
pixSetSpp(pix, 4);
|
||||
#endif
|
||||
for (i = 0; i < h; i++)
|
||||
{
|
||||
ppixel = data + i * wpl;
|
||||
for (j = 0; j < w; j++)
|
||||
{
|
||||
index = indata[i * w + (j)];
|
||||
composeRGBPixel(palette[index].red, palette[index].green,palette[index].blue, ppixel);
|
||||
SET_DATA_BYTE(ppixel, L_ALPHA_CHANNEL,alpha[index]);
|
||||
ppixel++;
|
||||
}
|
||||
}
|
||||
|
||||
text_out = TessBaseAPIProcessPage(ctx->api, pix, 0, NULL, NULL, 0);
|
||||
if(!text_out)
|
||||
printf("\nsomething messy\n");
|
||||
|
||||
//TessDeleteText(text_out);
|
||||
pixDestroy(&pix);
|
||||
|
||||
return text_out;
|
||||
}
|
||||
/*
|
||||
* @param alpha out
|
||||
* @param intensity in
|
||||
* @param palette out should be already initialized
|
||||
* @param bitmap in
|
||||
* @param size in size of bitmap
|
||||
* @param max_color in
|
||||
* @param nb_color in
|
||||
*/
|
||||
static int quantize_map(png_byte *alpha, png_color *palette,
|
||||
uint8_t *bitmap, int size, int max_color, int nb_color)
|
||||
{
|
||||
/*
|
||||
* occurrence of color in image
|
||||
*/
|
||||
uint32_t *histogram = NULL;
|
||||
/* intensity ordered table */
|
||||
uint8_t *iot = NULL;
|
||||
/* array of color with most occurrence according to histogram
|
||||
* save index of intensity order table
|
||||
*/
|
||||
uint32_t *mcit = NULL;
|
||||
struct transIntensity ti = { alpha,palette};
|
||||
|
||||
int ret = 0;
|
||||
|
||||
histogram = (uint32_t*) malloc(nb_color * sizeof(uint32_t));
|
||||
if (!histogram)
|
||||
{
|
||||
ret = -1;
|
||||
goto end;
|
||||
}
|
||||
|
||||
iot = (uint8_t*) malloc(nb_color * sizeof(uint8_t));
|
||||
if (!iot)
|
||||
{
|
||||
ret = -1;
|
||||
goto end;
|
||||
}
|
||||
|
||||
mcit = (uint32_t*) malloc(nb_color * sizeof(uint32_t));
|
||||
if (!mcit)
|
||||
{
|
||||
ret = -1;
|
||||
goto end;
|
||||
}
|
||||
|
||||
memset(histogram, 0, nb_color * sizeof(uint32_t));
|
||||
|
||||
/* initializing intensity ordered table with serial order of unsorted color table */
|
||||
for (int i = 0; i < nb_color; i++)
|
||||
{
|
||||
iot[i] = i;
|
||||
}
|
||||
memset(mcit, 0, nb_color * sizeof(uint32_t));
|
||||
|
||||
/* calculate histogram of image */
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
histogram[bitmap[i]]++;
|
||||
}
|
||||
/* sorted in increasing order of intensity */
|
||||
shell_sort((void*)iot, nb_color, sizeof(*iot), check_trans_tn_intensity, (void*)&ti);
|
||||
|
||||
#if OCR_DEBUG
|
||||
ccx_common_logging.log_ftn("Intensity ordered table\n");
|
||||
for (int i = 0; i < nb_color; i++)
|
||||
{
|
||||
ccx_common_logging.log_ftn("%02d) map %02d hist %02d\n",
|
||||
i, iot[i], histogram[iot[i]]);
|
||||
}
|
||||
#endif
|
||||
/**
|
||||
* using selection sort since need to find only max_color
|
||||
* Hostogram becomes invalid in this loop
|
||||
*/
|
||||
for (int i = 0; i < max_color; i++)
|
||||
{
|
||||
uint32_t max_val = 0;
|
||||
uint32_t max_ind = 0;
|
||||
int j;
|
||||
for (j = 0; j < nb_color; j++)
|
||||
{
|
||||
if (max_val < histogram[iot[j]])
|
||||
{
|
||||
max_val = histogram[iot[j]];
|
||||
max_ind = j;
|
||||
}
|
||||
}
|
||||
for (j = i; j > 0 && max_ind < mcit[j - 1]; j--)
|
||||
{
|
||||
mcit[j] = mcit[j - 1];
|
||||
}
|
||||
mcit[j] = max_ind;
|
||||
histogram[iot[max_ind]] = 0;
|
||||
}
|
||||
|
||||
#if OCR_DEBUG
|
||||
ccx_common_logging.log_ftn("max redundant intensities table\n");
|
||||
for (int i = 0; i < max_color; i++)
|
||||
{
|
||||
ccx_common_logging.log_ftn("%02d) mcit %02d\n",
|
||||
i, mcit[i]);
|
||||
}
|
||||
#endif
|
||||
for (int i = 0, mxi = 0; i < nb_color; i++)
|
||||
{
|
||||
int step, inc;
|
||||
if (i == mcit[mxi])
|
||||
{
|
||||
mxi = (mxi < max_color) ? mxi + 1 : mxi;
|
||||
continue;
|
||||
}
|
||||
inc = (mxi) ? -1 : 0;
|
||||
step = mcit[mxi + inc] + ((mcit[mxi] - mcit[mxi + inc]) / 2);
|
||||
if (i <= step)
|
||||
{
|
||||
int index = iot[mcit[mxi + inc]];
|
||||
alpha[iot[i]] = alpha[index];
|
||||
palette[iot[i]].red = palette[index].red;
|
||||
palette[iot[i]].blue = palette[index].blue;
|
||||
palette[iot[i]].green = palette[index].green;
|
||||
}
|
||||
else
|
||||
{
|
||||
int index = iot[mcit[mxi]];
|
||||
alpha[iot[i]] = alpha[index];
|
||||
palette[iot[i]].red = palette[index].red;
|
||||
palette[iot[i]].blue = palette[index].blue;
|
||||
palette[iot[i]].green = palette[index].green;
|
||||
}
|
||||
|
||||
}
|
||||
#if OCR_DEBUG
|
||||
ccx_common_logging.log_ftn("Colors present in quantized Image\n");
|
||||
for (int i = 0; i < nb_color; i++)
|
||||
{
|
||||
ccx_common_logging.log_ftn("%02d)r %03d g %03d b %03d a %03d\n",
|
||||
i, palette[i].red, palette[i].green, palette[i].blue, alpha[i]);
|
||||
}
|
||||
#endif
|
||||
end: freep(&histogram);
|
||||
freep(&mcit);
|
||||
freep(&iot);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int ocr_rect(void* arg, struct cc_bitmap *rect, char **str)
|
||||
{
|
||||
int ret = 0;
|
||||
png_color *palette = NULL;
|
||||
png_byte *alpha = NULL;
|
||||
|
||||
palette = (png_color*) malloc(rect[0].nb_colors * sizeof(png_color));
|
||||
if(!palette)
|
||||
{
|
||||
ret = -1;
|
||||
goto end;
|
||||
}
|
||||
alpha = (png_byte*) malloc(rect[0].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;
|
||||
|
||||
}
|
||||
#else
|
||||
char* ocr_bitmap(png_color *palette,png_byte *alpha, unsigned char* indata,unsigned char d,int w, int h)
|
||||
{
|
||||
mprint("ocr not supported without tesseract\n");
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
9
src/lib_ccx/ocr.h
Normal file
9
src/lib_ccx/ocr.h
Normal file
@@ -0,0 +1,9 @@
|
||||
#ifndef OCR_H
|
||||
#define OCR_H
|
||||
#include <png.h>
|
||||
|
||||
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);
|
||||
|
||||
#endif
|
||||
@@ -1,24 +1,27 @@
|
||||
#include "ccextractor.h"
|
||||
#include "lib_ccx.h"
|
||||
#include "ccx_common_option.h"
|
||||
#ifdef _WIN32
|
||||
#include <io.h>
|
||||
#else
|
||||
#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)
|
||||
void init_write (struct ccx_s_write *wb,char *filename)
|
||||
{
|
||||
memset(wb, 0, sizeof(struct ccx_s_write));
|
||||
wb->fh=-1;
|
||||
wb->filename=NULL;
|
||||
}
|
||||
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, struct s_context_cc608 *context)
|
||||
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)
|
||||
@@ -26,24 +29,24 @@ void writedata(const unsigned char *data, int length, struct s_context_cc608 *co
|
||||
|
||||
if (ccx_options.write_format==CCX_OF_RAW || ccx_options.write_format==CCX_OF_DVDRAW)
|
||||
{
|
||||
if (context->out)
|
||||
if (context && context->out)
|
||||
writeraw (data,length,context->out);
|
||||
}
|
||||
else if (ccx_options.write_format==CCX_OF_SMPTETT ||
|
||||
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);
|
||||
process608 (data,length,context, sub);
|
||||
else
|
||||
fatal(EXIT_BUG_BUG, "Should not be reached!");
|
||||
fatal(CCX_COMMON_EXIT_BUG_BUG, "Should not be reached!");
|
||||
}
|
||||
|
||||
void flushbuffer (struct ccx_s_write *wb, int closefile)
|
||||
void flushbuffer (struct lib_ccx_ctx *ctx, struct ccx_s_write *wb, int closefile)
|
||||
{
|
||||
if (closefile && wb!=NULL && wb->fh!=-1 && !cc_to_stdout)
|
||||
close (wb->fh);
|
||||
if (closefile && wb!=NULL && wb->fh!=-1 && !ctx->cc_to_stdout)
|
||||
close (wb->fh);
|
||||
}
|
||||
|
||||
void writeDVDraw (const unsigned char *data1, int length1,
|
||||
@@ -51,7 +54,7 @@ void writeDVDraw (const unsigned char *data1, int length1,
|
||||
struct ccx_s_write *wb)
|
||||
{
|
||||
/* these are only used by DVD raw mode: */
|
||||
static int loopcount = 1; /* loop 1: 5 elements, loop 2: 8 elements,
|
||||
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 */
|
||||
|
||||
@@ -65,26 +68,26 @@ void writeDVDraw (const unsigned char *data1, int length1,
|
||||
if (loopcount==3)
|
||||
{
|
||||
write (wb->fh,lc3,sizeof (lc3));
|
||||
if (data2 && length2)
|
||||
if (data2 && length2)
|
||||
write (wb->fh,data2,length2);
|
||||
}
|
||||
if (loopcount>3)
|
||||
{
|
||||
write (wb->fh,lc4,sizeof (lc4));
|
||||
if (data2 && length2)
|
||||
if (data2 && length2)
|
||||
write (wb->fh,data2,length2);
|
||||
}
|
||||
}
|
||||
datacount++;
|
||||
write (wb->fh,lc5,sizeof (lc5));
|
||||
if (data1 && length1)
|
||||
if (data1 && length1)
|
||||
write (wb->fh,data1,length1);
|
||||
if (((loopcount == 1) && (datacount < 5)) || ((loopcount == 2) &&
|
||||
(datacount < 8)) || (( loopcount == 3) && (datacount < 11)) ||
|
||||
if (((loopcount == 1) && (datacount < 5)) || ((loopcount == 2) &&
|
||||
(datacount < 8)) || (( loopcount == 3) && (datacount < 11)) ||
|
||||
((loopcount > 3) && (datacount < 15)))
|
||||
{
|
||||
write (wb->fh,lc6,sizeof (lc6));
|
||||
if (data2 && length2)
|
||||
if (data2 && length2)
|
||||
write (wb->fh,data2,length2);
|
||||
}
|
||||
else
|
||||
@@ -92,46 +95,52 @@ void writeDVDraw (const unsigned char *data1, int length1,
|
||||
if (loopcount==1)
|
||||
{
|
||||
write (wb->fh,lc6,sizeof (lc6));
|
||||
if (data2 && length2)
|
||||
if (data2 && length2)
|
||||
write (wb->fh,data2,length2);
|
||||
}
|
||||
loopcount++;
|
||||
datacount=0;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
void printdata (const unsigned char *data1, int length1,
|
||||
const unsigned char *data2, int length2)
|
||||
void printdata (struct lib_cc_decode *ctx, const unsigned char *data1, int length1,
|
||||
const unsigned char *data2, int length2, struct cc_subtitle *sub)
|
||||
{
|
||||
if (ccx_options.write_format==CCX_OF_DVDRAW)
|
||||
writeDVDraw (data1,length1,data2,length2,&wbout1);
|
||||
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);
|
||||
else /* Broadcast raw or any non-raw */
|
||||
{
|
||||
if (length1 && ccx_options.extract!=2)
|
||||
{
|
||||
writedata(data1, length1, &context_cc608_field_1);
|
||||
}
|
||||
if (length2)
|
||||
{
|
||||
if (length1 && ccx_options.extract != 2)
|
||||
{
|
||||
if (ccx_options.extract!=1)
|
||||
writedata(data2, length2, &context_cc608_field_2);
|
||||
writedata(data1, length1, field_1, sub);
|
||||
}
|
||||
if (length2)
|
||||
{
|
||||
if (ccx_options.extract != 1)
|
||||
writedata(data2, length2, field_2, sub);
|
||||
else // User doesn't want field 2 data, but we want XDS.
|
||||
writedata (data2,length2,NULL);
|
||||
}
|
||||
}
|
||||
writedata (data2,length2,NULL, sub);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Buffer data with the same FTS and write when a new FTS or data==NULL
|
||||
* is encountered */
|
||||
void writercwtdata (const unsigned char *data)
|
||||
void writercwtdata (struct lib_cc_decode *ctx, const unsigned char *data)
|
||||
{
|
||||
static LLONG prevfts = -1;
|
||||
LLONG currfts = fts_now + 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];
|
||||
static LLONG prevfts = -1;
|
||||
LLONG currfts = fts_now + 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
|
||||
@@ -178,8 +187,16 @@ void writercwtdata (const unsigned char *data)
|
||||
|
||||
if (cbcount > 0)
|
||||
{
|
||||
writeraw(cbheader,10,&wbout1);
|
||||
writeraw(cbbuffer,3*cbcount, &wbout1);
|
||||
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;
|
||||
@@ -187,7 +204,7 @@ void writercwtdata (const unsigned char *data)
|
||||
|
||||
if ( data )
|
||||
{
|
||||
// Store the data while the FTS is unchanged
|
||||
// Store the data while the FTS is unchanged
|
||||
|
||||
unsigned char cc_valid = (*data & 4) >> 2;
|
||||
unsigned char cc_type = *data & 3;
|
||||
@@ -222,8 +239,16 @@ void writercwtdata (const unsigned char *data)
|
||||
memcpy(cbbuffer, "\x04\x80\x80", 3); // Field 1 padding
|
||||
memcpy(cbbuffer+3, "\x05\x80\x80", 3); // Field 2 padding
|
||||
|
||||
writeraw(cbheader,10,&wbout1);
|
||||
writeraw(cbbuffer,3*cbcount, &wbout1);
|
||||
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;
|
||||
1675
src/lib_ccx/params.c
Normal file
1675
src/lib_ccx/params.c
Normal file
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