From 6e0db8aa1daf0b6c0edc9536c5a813cf70b014be Mon Sep 17 00:00:00 2001 From: cfsmp3 Date: Sat, 12 Apr 2014 12:41:27 +0200 Subject: [PATCH] Initial git. --- .gitignore | 3 + OpenBSD/Makefile | 33 + docs/708_STATUS.TXT | 31 + docs/BINARY_FILE_FORMAT.TXT | 39 + docs/CHANGES.TXT | 794 ++ docs/CODE_ORGANIZATION.TXT | 2 + docs/FRONTEND_COMMUNICATIONS.TXT | 120 + docs/MAILINGLIST.TXT | 17 + docs/README.TXT | 87 + linux/Makefile | 104 + linux/build | 2 + linux/builddebug | 2 + mac/README.MAC.TXT | 8 + mac/build.command | 1 + src/608.cpp | 1338 +++ src/608.h | 188 + src/608_helpers.cpp | 441 + src/608_sami.cpp | 120 + src/608_smptett.cpp | 155 + src/608_spupng.cpp | 604 ++ src/608_spupng.h | 36 + src/608_srt.cpp | 194 + src/708.cpp | 1235 +++ src/708.h | 292 + src/708_encoding.cpp | 45 + src/activity.cpp | 147 + src/asf_constants.h | 31 + src/asf_functions.cpp | 1067 +++ src/avc_functions.cpp | 1046 +++ src/bitstream.h | 64 + src/cc_bitstream.cpp | 375 + src/cc_decoders_common.cpp | 25 + src/ccextractor.cpp | 867 ++ src/ccextractor.h | 555 ++ src/ccfont2.xbm | 1133 +++ src/constants.cpp | 125 + src/constants.h | 217 + src/disable_warnings.h | 7 + src/encoding.cpp | 835 ++ src/es_functions.cpp | 1127 +++ src/es_userdata.cpp | 479 ++ src/file_functions.cpp | 488 ++ src/general_loop.cpp | 817 ++ src/gpacmp4/ReadMe.txt | 40 + src/gpacmp4/av_parsers.c | 3145 +++++++ src/gpacmp4/avc_ext.c | 552 ++ src/gpacmp4/avilib.c | 2976 +++++++ src/gpacmp4/base_encoding.c | 280 + src/gpacmp4/bitstream.c | 903 ++ src/gpacmp4/box_code_3gpp.c | 1364 +++ src/gpacmp4/box_code_apple.c | 325 + src/gpacmp4/box_code_base.c | 8084 ++++++++++++++++++ src/gpacmp4/box_code_isma.c | 889 ++ src/gpacmp4/box_code_meta.c | 776 ++ src/gpacmp4/box_funcs.c | 1520 ++++ src/gpacmp4/data_map.c | 567 ++ src/gpacmp4/desc_private.c | 704 ++ src/gpacmp4/descriptors.c | 963 +++ src/gpacmp4/error.c | 685 ++ src/gpacmp4/gpac/avparse.h | 245 + src/gpacmp4/gpac/base_coding.h | 105 + src/gpacmp4/gpac/bitstream.h | 469 + src/gpacmp4/gpac/configuration.h | 219 + src/gpacmp4/gpac/constants.h | 587 ++ src/gpacmp4/gpac/ietf.h | 1332 +++ src/gpacmp4/gpac/internal/avilib.h | 439 + src/gpacmp4/gpac/internal/isomedia_dev.h | 3375 ++++++++ src/gpacmp4/gpac/internal/media_dev.h | 240 + src/gpacmp4/gpac/internal/odf_dev.h | 364 + src/gpacmp4/gpac/internal/odf_parse_common.h | 43 + src/gpacmp4/gpac/internal/ogg.h | 213 + src/gpacmp4/gpac/isomedia.h | 1995 +++++ src/gpacmp4/gpac/list.h | 168 + src/gpacmp4/gpac/math.h | 1038 +++ src/gpacmp4/gpac/media_tools.h | 395 + src/gpacmp4/gpac/media_tools.h.orig | 395 + src/gpacmp4/gpac/media_tools.h.rej | 11 + src/gpacmp4/gpac/mpeg4_odf.h | 1744 ++++ src/gpacmp4/gpac/network.h | 412 + src/gpacmp4/gpac/setup.h | 504 ++ src/gpacmp4/gpac/sync_layer.h | 128 + src/gpacmp4/gpac/tools.h | 812 ++ src/gpacmp4/gpac/utf.h | 99 + src/gpacmp4/gpac/version.h | 1 + src/gpacmp4/gpac_ogg.c | 1328 +++ src/gpacmp4/gpacmp4.vcxproj | 128 + src/gpacmp4/gpacmp4.vcxproj.filters | 153 + src/gpacmp4/gpacmp4.vcxproj.user | 7 + src/gpacmp4/hinting.c | 986 +++ src/gpacmp4/ipmpx_code.c | 2021 +++++ src/gpacmp4/ipmpx_parse.c | 681 ++ src/gpacmp4/isom_intern.c | 845 ++ src/gpacmp4/isom_read.c | 2666 ++++++ src/gpacmp4/isom_store.c | 1294 +++ src/gpacmp4/isom_write.c | 4011 +++++++++ src/gpacmp4/list.c | 732 ++ src/gpacmp4/media.c | 833 ++ src/gpacmp4/media_odf.c | 520 ++ src/gpacmp4/meta.c | 674 ++ src/gpacmp4/movie_fragments.c | 1465 ++++ src/gpacmp4/mp4.cpp | 331 + src/gpacmp4/odf_code.c | 3337 ++++++++ src/gpacmp4/odf_codec.c | 607 ++ src/gpacmp4/odf_command.c | 652 ++ src/gpacmp4/os_divers.c | 1801 ++++ src/gpacmp4/qos.c | 437 + src/gpacmp4/sample_descs.c | 555 ++ src/gpacmp4/slc.c | 449 + src/gpacmp4/stbl_read.c | 518 ++ src/gpacmp4/stbl_write.c | 1746 ++++ src/gpacmp4/targetver.h | 8 + src/gpacmp4/track.c | 862 ++ src/gpacmp4/tx3g.c | 721 ++ src/gpacmp4/url.c | 302 + src/gpacmp4/utf.c | 188 + src/hamming.h | 67 + src/inttypes.h | 305 + src/libpng/png.c | 1102 +++ src/libpng/png.h | 3810 +++++++++ src/libpng/pngconf.h | 1665 ++++ src/libpng/pngerror.c | 396 + src/libpng/pngmem.c | 641 ++ src/libpng/pngset.c | 1236 +++ src/libpng/pngtrans.c | 699 ++ src/libpng/pngwio.c | 260 + src/libpng/pngwrite.c | 1596 ++++ src/libpng/pngwtran.c | 582 ++ src/libpng/pngwutil.c | 2835 ++++++ src/myth.cpp | 901 ++ src/output.cpp | 240 + src/params.cpp | 1416 +++ src/params_dump.cpp | 202 + src/platform.h | 102 + src/sequencing.cpp | 315 + src/stdint.h | 247 + src/stdintmsc.h | 232 + src/stream_functions.cpp | 337 + src/teletext.h | 253 + src/telxcc.cpp | 1286 +++ src/timing.cpp | 288 + src/ts_functions.cpp | 620 ++ src/ts_tables.cpp | 567 ++ src/utility.cpp | 295 + src/wtv_constants.h | 29 + src/wtv_functions.cpp | 432 + src/xds.cpp | 909 ++ src/zlib/adler32.c | 167 + src/zlib/crc32.c | 403 + src/zlib/crc32.h | 441 + src/zlib/deflate.c | 1903 +++++ src/zlib/deflate.h | 346 + src/zlib/gzguts.h | 209 + src/zlib/inffast.c | 338 + src/zlib/inffast.h | 11 + src/zlib/inffixed.h | 94 + src/zlib/inflate.c | 1475 ++++ src/zlib/inflate.h | 122 + src/zlib/inftrees.c | 305 + src/zlib/inftrees.h | 62 + src/zlib/trees.c | 1186 +++ src/zlib/trees.h | 128 + src/zlib/zconf.h | 511 ++ src/zlib/zlib.h | 1768 ++++ src/zlib/zutil.c | 309 + src/zlib/zutil.h | 253 + windows/README.TXT | 6 + windows/ccextractor.sln | 20 + windows/ccextractor.vcproj | 744 ++ windows/cygming.mak | 266 + 169 files changed, 120192 insertions(+) create mode 100644 .gitignore create mode 100644 OpenBSD/Makefile create mode 100644 docs/708_STATUS.TXT create mode 100644 docs/BINARY_FILE_FORMAT.TXT create mode 100644 docs/CHANGES.TXT create mode 100644 docs/CODE_ORGANIZATION.TXT create mode 100644 docs/FRONTEND_COMMUNICATIONS.TXT create mode 100644 docs/MAILINGLIST.TXT create mode 100644 docs/README.TXT create mode 100644 linux/Makefile create mode 100644 linux/build create mode 100644 linux/builddebug create mode 100644 mac/README.MAC.TXT create mode 100644 mac/build.command create mode 100644 src/608.cpp create mode 100644 src/608.h create mode 100644 src/608_helpers.cpp create mode 100644 src/608_sami.cpp create mode 100644 src/608_smptett.cpp create mode 100644 src/608_spupng.cpp create mode 100644 src/608_spupng.h create mode 100644 src/608_srt.cpp create mode 100644 src/708.cpp create mode 100644 src/708.h create mode 100644 src/708_encoding.cpp create mode 100644 src/activity.cpp create mode 100644 src/asf_constants.h create mode 100644 src/asf_functions.cpp create mode 100644 src/avc_functions.cpp create mode 100644 src/bitstream.h create mode 100644 src/cc_bitstream.cpp create mode 100644 src/cc_decoders_common.cpp create mode 100644 src/ccextractor.cpp create mode 100644 src/ccextractor.h create mode 100644 src/ccfont2.xbm create mode 100644 src/constants.cpp create mode 100644 src/constants.h create mode 100644 src/disable_warnings.h create mode 100644 src/encoding.cpp create mode 100644 src/es_functions.cpp create mode 100644 src/es_userdata.cpp create mode 100644 src/file_functions.cpp create mode 100644 src/general_loop.cpp create mode 100644 src/gpacmp4/ReadMe.txt create mode 100644 src/gpacmp4/av_parsers.c create mode 100644 src/gpacmp4/avc_ext.c create mode 100644 src/gpacmp4/avilib.c create mode 100644 src/gpacmp4/base_encoding.c create mode 100644 src/gpacmp4/bitstream.c create mode 100644 src/gpacmp4/box_code_3gpp.c create mode 100644 src/gpacmp4/box_code_apple.c create mode 100644 src/gpacmp4/box_code_base.c create mode 100644 src/gpacmp4/box_code_isma.c create mode 100644 src/gpacmp4/box_code_meta.c create mode 100644 src/gpacmp4/box_funcs.c create mode 100644 src/gpacmp4/data_map.c create mode 100644 src/gpacmp4/desc_private.c create mode 100644 src/gpacmp4/descriptors.c create mode 100644 src/gpacmp4/error.c create mode 100644 src/gpacmp4/gpac/avparse.h create mode 100644 src/gpacmp4/gpac/base_coding.h create mode 100644 src/gpacmp4/gpac/bitstream.h create mode 100644 src/gpacmp4/gpac/configuration.h create mode 100644 src/gpacmp4/gpac/constants.h create mode 100644 src/gpacmp4/gpac/ietf.h create mode 100644 src/gpacmp4/gpac/internal/avilib.h create mode 100644 src/gpacmp4/gpac/internal/isomedia_dev.h create mode 100644 src/gpacmp4/gpac/internal/media_dev.h create mode 100644 src/gpacmp4/gpac/internal/odf_dev.h create mode 100644 src/gpacmp4/gpac/internal/odf_parse_common.h create mode 100644 src/gpacmp4/gpac/internal/ogg.h create mode 100644 src/gpacmp4/gpac/isomedia.h create mode 100644 src/gpacmp4/gpac/list.h create mode 100644 src/gpacmp4/gpac/math.h create mode 100644 src/gpacmp4/gpac/media_tools.h create mode 100644 src/gpacmp4/gpac/media_tools.h.orig create mode 100644 src/gpacmp4/gpac/media_tools.h.rej create mode 100644 src/gpacmp4/gpac/mpeg4_odf.h create mode 100644 src/gpacmp4/gpac/network.h create mode 100644 src/gpacmp4/gpac/setup.h create mode 100644 src/gpacmp4/gpac/sync_layer.h create mode 100644 src/gpacmp4/gpac/tools.h create mode 100644 src/gpacmp4/gpac/utf.h create mode 100644 src/gpacmp4/gpac/version.h create mode 100644 src/gpacmp4/gpac_ogg.c create mode 100644 src/gpacmp4/gpacmp4.vcxproj create mode 100644 src/gpacmp4/gpacmp4.vcxproj.filters create mode 100644 src/gpacmp4/gpacmp4.vcxproj.user create mode 100644 src/gpacmp4/hinting.c create mode 100644 src/gpacmp4/ipmpx_code.c create mode 100644 src/gpacmp4/ipmpx_parse.c create mode 100644 src/gpacmp4/isom_intern.c create mode 100644 src/gpacmp4/isom_read.c create mode 100644 src/gpacmp4/isom_store.c create mode 100644 src/gpacmp4/isom_write.c create mode 100644 src/gpacmp4/list.c create mode 100644 src/gpacmp4/media.c create mode 100644 src/gpacmp4/media_odf.c create mode 100644 src/gpacmp4/meta.c create mode 100644 src/gpacmp4/movie_fragments.c create mode 100644 src/gpacmp4/mp4.cpp create mode 100644 src/gpacmp4/odf_code.c create mode 100644 src/gpacmp4/odf_codec.c create mode 100644 src/gpacmp4/odf_command.c create mode 100644 src/gpacmp4/os_divers.c create mode 100644 src/gpacmp4/qos.c create mode 100644 src/gpacmp4/sample_descs.c create mode 100644 src/gpacmp4/slc.c create mode 100644 src/gpacmp4/stbl_read.c create mode 100644 src/gpacmp4/stbl_write.c create mode 100644 src/gpacmp4/targetver.h create mode 100644 src/gpacmp4/track.c create mode 100644 src/gpacmp4/tx3g.c create mode 100644 src/gpacmp4/url.c create mode 100644 src/gpacmp4/utf.c create mode 100644 src/hamming.h create mode 100644 src/inttypes.h create mode 100644 src/libpng/png.c create mode 100644 src/libpng/png.h create mode 100644 src/libpng/pngconf.h create mode 100644 src/libpng/pngerror.c create mode 100644 src/libpng/pngmem.c create mode 100644 src/libpng/pngset.c create mode 100644 src/libpng/pngtrans.c create mode 100644 src/libpng/pngwio.c create mode 100644 src/libpng/pngwrite.c create mode 100644 src/libpng/pngwtran.c create mode 100644 src/libpng/pngwutil.c create mode 100644 src/myth.cpp create mode 100644 src/output.cpp create mode 100644 src/params.cpp create mode 100644 src/params_dump.cpp create mode 100644 src/platform.h create mode 100644 src/sequencing.cpp create mode 100644 src/stdint.h create mode 100644 src/stdintmsc.h create mode 100644 src/stream_functions.cpp create mode 100644 src/teletext.h create mode 100644 src/telxcc.cpp create mode 100644 src/timing.cpp create mode 100644 src/ts_functions.cpp create mode 100644 src/ts_tables.cpp create mode 100644 src/utility.cpp create mode 100644 src/wtv_constants.h create mode 100644 src/wtv_functions.cpp create mode 100644 src/xds.cpp create mode 100644 src/zlib/adler32.c create mode 100644 src/zlib/crc32.c create mode 100644 src/zlib/crc32.h create mode 100644 src/zlib/deflate.c create mode 100644 src/zlib/deflate.h create mode 100644 src/zlib/gzguts.h create mode 100644 src/zlib/inffast.c create mode 100644 src/zlib/inffast.h create mode 100644 src/zlib/inffixed.h create mode 100644 src/zlib/inflate.c create mode 100644 src/zlib/inflate.h create mode 100644 src/zlib/inftrees.c create mode 100644 src/zlib/inftrees.h create mode 100644 src/zlib/trees.c create mode 100644 src/zlib/trees.h create mode 100644 src/zlib/zconf.h create mode 100644 src/zlib/zlib.h create mode 100644 src/zlib/zutil.c create mode 100644 src/zlib/zutil.h create mode 100644 windows/README.TXT create mode 100644 windows/ccextractor.sln create mode 100644 windows/ccextractor.vcproj create mode 100644 windows/cygming.mak diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..06867f8e --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +#Ignore CVS +.CVS +CVS diff --git a/OpenBSD/Makefile b/OpenBSD/Makefile new file mode 100644 index 00000000..18f0b2a0 --- /dev/null +++ b/OpenBSD/Makefile @@ -0,0 +1,33 @@ +# $OpenBSD$ + +MAINTAINER = Marc Espie +CATEGORIES = multimedia +COMMENT = closed caption subtitles extractor +HOMEPAGE = http://ccextractor.sourceforge.net/ +V = 0.56 +DISTFILES = ccextractor.${V:S/.//}-src.zip +MASTER_SITES = ${MASTER_SITE_SOURCEFORGE:=ccextractor/} +DISTNAME = ccextractor-$V +WRKDIST = ${WRKDIR}/ccextractor.$V +WRKSRC = ${WRKDIST}/src + +# GPL 2.0 +PERMIT_DISTFILES_CDROM = Yes +PERMIT_PACKAGE_CDROM = Yes +PERMIT_DISTFILES_FTP = Yes +PERMIT_PACKAGE_FTP = Yes + +DOCDIR = ${PREFIX}/share/doc/ccextractor +do-configure: + ln -s ${FILESDIR}/Makefile ${WRKSRC}/Makefile + +do-install: + ${INSTALL_PROGRAM} ${WRKBUILD}/ccextractor ${PREFIX}/bin + ${INSTALL_DATA_DIR} ${DOCDIR} + for i in ${WRKDIST}/docs/*.TXT; do \ + ${INSTALL_DATA} $$i ${DOCDIR}; \ + done + +WANTLIB += c m stdc++ + +.include diff --git a/docs/708_STATUS.TXT b/docs/708_STATUS.TXT new file mode 100644 index 00000000..41ea665e --- /dev/null +++ b/docs/708_STATUS.TXT @@ -0,0 +1,31 @@ +The 708 decoder has been a work in progress for a long time, +but it's never been finished simply because there's no +pressure (all broadcasts come with 608 captions). Anyway it's +something that we want to complete sooner than later, so each +new CCExtractor release will at least take care of a couple +of items in this to-do list. + +The people at narlak.com were nice enough to generate a test +708 stream for CCExtractor, which helped quite a bit with the +initial stuff. + +Current status is that a (poor) .srt file can already be +generated, so should a broadcast with 708-only captions appear +a transcript would be available. + +To do: +- Timing: For each .srt line, it's off by a second or so. +- Colors: Currently unsupported. +- Pen sizes (useless in .srt of course, but the decoder should + handle them internally). +- Roll up captions. +- SAMI output. +- Move everything to a 16 bit character set, to support + non-Latin character sets. +- Someone (from Korea, I think), reported that they have + 708-only captions over there, in Korean. While I don't speak + Korean I'll be happy to work on it a bit and see if I can make + CCExtractor fully support Asian languages. I do need samples + though. No samples, no support. +- A few commands are not yet supported, specifically those related + to delay. \ No newline at end of file diff --git a/docs/BINARY_FILE_FORMAT.TXT b/docs/BINARY_FILE_FORMAT.TXT new file mode 100644 index 00000000..b08545ad --- /dev/null +++ b/docs/BINARY_FILE_FORMAT.TXT @@ -0,0 +1,39 @@ +Starting in CCExtractor 0.52, the default binary format changes. The previous one, +whichwas ported from McPoodle's tools is too simple. It just contains the closed caption +data, with no timing. It can also contain data from one field, which means one language. + +A new format, still simple but enough to transport all data with timing is as follows: + +RCWT (Raw Captions With Time), version 0.001 +============================================ +File header (11 bytes), required: + +byte(s) value description +0-2 CCCCED magic number, for Closed Caption CC Extractor Data +3 CC Creating program. Currently Defined: + CC -> CCextractor. +4-5 0052 Program version number +6-7 0001 File format version +8-10 000000 Reserved + +Time header, required for every group of data packets +0-7 FTS value (Time in ms) +8-9 (1-65535) Number of caption data blocks with this time + stamp. If the number of blocks exceeds 65535 write + another time header for the remaining blocks. + +Caption data +0-2 Three byte caption data. + +Notes: + +- Data is in process order, i.e. the 608 or 708 decoders can process it + sequentially. Programs generating files in this format are responsible + for sorting the data when needed. +- FTS is the time, in milliseconds since the beginning of the file, + starting in 0. +- The closed caption data is always 3 bytes, the first of which is the + cc_type and cc_valid fields and the other two are the actual data + pairs. Depending on cc_type, they will be EIA-608 or EIA-708 data. + + diff --git a/docs/CHANGES.TXT b/docs/CHANGES.TXT new file mode 100644 index 00000000..8179e970 --- /dev/null +++ b/docs/CHANGES.TXT @@ -0,0 +1,794 @@ +0.69 +---- +- A few patches from Christopher Small, including proper support + for multiple multicast clients listening on the same port. +- GUI: Fixed teletext preview. +- GUI: Added a small indicator of data being received when reading from + UDP. +- GUI: Added UTF-8 support to preview Window (used for teletext). +- Fixes in Makefile and build script, compilation in linux and OSX failed + if another libpng was found in the system. +- WTV support directly in CCExtractor (no need for wtvccdump any more). +- Started refactoring and clean-up. +- Fix: MPEG clock rollover (happens each 26 hours) caused a time + discontinuity. +- Windows GUI: Started work on HDHomeRun support. For now it just looks + for HDHomeRun devices. Lots of other things will arrive in the next + versions. +- Windows GUI: Some code refactoring, since the HDHomeRun support makes + the code larger enough to require more than one source file :-) + +0.68 +---- +- A couple of shared variables between 608 decoders were causing + problems when both fields were processed at the same time with + -12, fixed. +- Added BOM for UTF-8 files. +- Corrected a few extended characters in the UTF-8 encoding, + probably never used in real world captioning but since we got + a good test sample file... +- Color and fonts in PAC commands were ignored, fixed (Helen Buus). +- Added a new output format, spupng. It consists on one .png file + for each subtitle frame and one .xml with all the timing + (Heleen Buus). +- Some fixes (Chris Small). + +0.67 +---- +- 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. +- MythTV's branch had a fixed size buffer that could not be enough + some times. Made dynamic. +- Better support for PAT changing mid stream. +- Removed quotes in Start in .smi (format fix). +- Added multicast support (Chris Small) +- Added ability to select IP address to bind in UDP (Chris Small) +- Fixes in -unixts and -delay for teletext. +- Added -autodash : When two people are talking, add a dash as + needed (this is based on subtitle position). Only in .srt and + with -trim. Quite experimental, feedback appreciated. +- Added -latin1 to select Latin 1 as encoding. Default is now + UTF-8 (-utf8 still exists but it's not needed). +- Added -ru1, which emulates a (non-existing in real life) 1 line + roll-up mode. + + +0.66 +---- +- Fixed bug in auto detection code that triggered a message + about file being auto of sync. +- Added -investigate_packets + The PMT is used to select the most promising elementary stream + to get captions from. Sometimes captions are where you least + expect it so -datapid allows you to select a elementary stream + manually, in case the CC location is not obvious from the PMT + contents. To assist looking for the right stream, the parameter + "-investigate_packets" will have CCExtractor look inside each + stream, looking for CC markers, and report the streams that + are likely to contain CC data even if it can't be determined from + their PMT entry. +- Added -datastreamtype to manually selecting a stream based on + its type instead of its PID. Useful if your recording program + always hides the caption under the stream stream type. +- Added -streamtype so if an elementary stream is selected manually + for processing the streamtype can be selected too. This can be + needed if you process for example a stream that is declared as + "private MPEG" in the PMT, so CCExtractor can't tell what it is. + Usually you'll want -streamtype 2 (MPEG video) or -streamtype 6 + (MPEG private data). +- PMT content listing improved, it now shows the stream type for + more types. +- Fixes in roll-up, cursor was being moved to column 1 if a + RU2, RU3 or RU4 was received even if already in roll-up mode. +- Added -autoprogram. If a multiprogram TS is processed and + -autoprogram is used CCExtractor will analyze all PMTs and use + the first program that has a suitable data stream. +- Timed transcript (ttxt) now also exports the caption mode + (roll-up, paint-on, etc) next to each line, as it's useful to + detect things like commercials. +- Content Advisory information from XDS is now decoded if it's + transmitted in "US TV parental guidelines" or "MPA". + Other encoding such as Canada's are not supported yet due + to lack of samples. +- Copy Management information from XDS is now decoded. +- Added -xds. If present and export format is timed transcript + (only), XDS information will be saved to file (same file as the + transcript, with XDS being clearly marked). Note that for now + all XDS data is exported even if it doesn't change, so the + transcript file will be significantly larger. +- Added some PaintOn support, at least enough to prevent it + from breaking things when the other modes are used. +- Removed afd_data() warning. AFD doesn't carry any caption related + data. AFD still detected in code in case we want to do something + with it later anyway. +- Ported last changes from Petr Kutalek's telxcc. Current version + is 2.4.4. +- In teletext mode when exporting to transcript (not .srt), an effort + is made to detect and merge line duplicates. This is done by using + the Levenshtein's distance, which is the number of changes requires + to convert one string to another. To simplify things, strings are + compared up to the length of the shortest one. + There are 3 parameters that can be used to tweak the thresholds: + -deblev: Enable debug so the calculated distance for each two + strings is displayed. The output includes both strings, the + calculated distance, the maximum allowed distance, and whether + the strings are ultimately considered equivalent or not, i.e. + the calculated distance is less or equal than the max allowed. + -levdistmincnt value: Minimum distance we always allow + regardless of the length of the strings. Default 2. This means + that if the calculated distance is 0, 1 or 2, we consider the + strings to be equivalent. + -levdistmaxpct value: Maximum distance we allow, as a + percentage of the shortest string length. Default 10%. For + example, consider a comparison of one string of 30 characters + and one of 60 characters. We want to determine whether the + first 30 characters of the longer string are more or less the + same as the shortest string, i.e. whether the longest string + is the shortest one plus new characters and maybe some + corrections. Since the shortest string is 30 characters and + the default percentage is 10%, we would allow a distance of + up to 3 between the first 30 characters. +- Added -lf : Use UNIX line terminator (LF) instead of Windows (CRLF). +- Added -noautotimeref: Prevent UTC reference from being auto set from + the stream data. + +0.65 +---- +- Minor GUI changes for teletext +- Added end timestamps in timed transcripts +- Added support for SMPTE (patch by John Kemp) +- Initial support for MPEG2 video tracks inside MP4 files (thanks a + lot to GPAC's Jean who assisted in analyzing the sample and + doing the required changes in GPAC). +- Improved MP4 auto detection +- Support for PCR if PTS is not available (needed for some teletext + samples, and probably useful for everything else). +- Support for UDP streaming - finally. Use "-udp $port" to have + CCExtractor listen for a stream. I've only been able to test it + with an European HDHomeRun, but it should work fine with any other + tuner. +- Refactored PMT / PAT processing in transport streams, now allows to + display their contents (-parsePAT and -parsePMT) which makes + troubleshooting easier. + +0.64 +---- +- Changed Window GUI size (larger). +- Added Teletext options to GUI. +- Added -teletext to force teletext mode even if not detected +- Added -noteletext to disable teletext detection. This can be needed + for streams that have both 608 data and teletext packets if you + need to process the 608 data (if teletext is detected it will + take precedence otherwise). +- Added -datapid to force a specific elementary stream to be used for + data (bypassing detections). +- Added -ru2 and -ru3 to limit the number of visible lines in roll-up + captions (bypassing whatever the broadcast says). +- Added support for a .hex (hexadecimal) dump of data. +- Added support for wtv in Windows. This is done by using a new program + (wtvccdump.exe) and a new DirectShow filter (CCExtractorDump.dll) that + process the .wtv using DirecShow's filters and export the line 21 data + to a .hex file. The GUI calls wtvccdump.exe as needed. +- Added --nogoptime to force PTS timing even when CCExtractor would + use GOP timing otherwise. + +0.63 +---- +- 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 + he's abandoning telxcc so further development will happen directly in + CCExtractor. +- Some bug fixes, as usual. + +0.62 +---- +- 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 + caption data during all the video (such as in commercial breaks). +- Added -mp4 and -in:mp4 to force the input to be processed as MP4. +- CC608 data embedded in a separate stream (as opposed as in the video + stream itself) in MP4 files is now supported (not heavily tested). + This should be rather useful since closed captioned files from iTunes + use this format. +- More CEA-708 work. The debugger is now able to dump the "TV" contents for + the first time. Also, a .srt can be generated, however timing is not quite + good yet (still need to figure out why). +- Added -svc (or --service) to select the CEA-708 services to be processed. + For example, -svc 1,2 will process the primary and secondary language + services. Valid values are 1-63, where 1 is the primary language, 2 is + the secondary language (this is part of the specification) and 3-63 are + provider defined. +- Rajesh Hingorani sent a fix for the MPEG decoder that fixes garbled output + or certain samples (we had none like this in our test collection). Thanks, + Rajesh. + +0.61 +---- +- 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. +- Fix: Part of the changes for MP4 support (CC packets buffering in + particular) broke some stuff for other files, causing at least very + annoying character duplication. We hope we've fixed it without breaking + anything but please report). +- Some non-interesting cleanup. + +0.60 +---- +- 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. +- Fix: The Windows version was writing text files with double \r. +- Fix: Closed captions blocks with no data could cause a crash. +- Fix: -noru (to generate files without duplicate lines in + roll-up) was broken, with complete lines being missing. +- Fix: bin format not working as input. + +0.59 +---- +- 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. +- Added a timed text transcript output format, probably only useful for + roll-up captions. Use --timedtranscript or -ttxt. Output is like this: + +00:01:25,485 | HOST: LAST NIGHT THE REPUBLICAN +00:01:29,522 | HOPEFULS INTRODUCE THEMSELVES TO +00:01:30,623 | PRIMARY VOTERS. + +- XDS parser. Not complete (no point in dealing with V-Chip stuff for + example), but enough to extract program and station information. +- Input streams can now come from standard input using - (just an hyphen) + as parameter. +- Added a new output format called 'null' (use -null or -out=null). This + format means "Don't produce any file", and is useful to have CCExtractor + process the stream (for XDS messages, debugging, etc) without actually + generating anything. +- Updated Windows GUI. +- Added -quiet => If used, CCExtractor will not write any message. +- Added -stdout => If used, the captions will be sent to stdout (console) + instead of file. Combined with -, CCExtractor can work as a filter in + a larger process, receiving the stream from stdin and sending the + captions to stdout. +- Some code clean up, minor refactoring. +- Teletext detection (not yet processing). + +0.58 +---- +- 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. +- Removed a couple of those annoying "Impossible!" error messages + that appears when processing some (possibly broken, unsure) files. +- Added -nots --notypesettings to prevent italics and underline + codes from being displayed. +- Note to those not liking the paragraph symbol being used for the + music note: Submit a VALID replacement in latin-1. +- Added preliminary support for multiple program TS files. The + parameter --program-number (or -pn) will let you choose which + program number to process. If no number is passed and the TS + file contains more than one, CCExtractor will display a list of + found programs and terminate. +- Added support (basic, because I only received one sample) for some + Hauppauge cards that save CC data in their own format. Use the + parameter -haup to enable it (CCExtractor will display a notice + if it thinks that it's processing a Hauppauge capture anyway). +- Fixed bug in roll-up. +- More AVC work, now TS files from echostar that provided garbled + output are processed OK. +- Updated Windows GUI. + +0.57 +---- +- Bugfixes in the Windows version. Some debug code was unintentionally + left in the released version. + +0.56 +---- +- H264 support +- Other minor changes a lot less important + +0.55 +---- +- Replace pattern matching code with improved parser for MPEG-2 elementary + streams. +- Fix parsing of ReplayTV 5000 captions. +- Add ability to decode SCTE 20 encoded captions. +- Make decoding of TS files more error tolerant. +- Start implementation of EIA-708 decoding (not active yet). +- Add -gt / --goptime switch to use GOP timing instead of PTS timing. +- Start implementation of AVC/H.264 decoding (not active yet). +- Fixed: The basic problem is that when 24fps movie film gets converted to 30fps NTSC + they repeat every 4th frame. Some pics have 3 fields of CC data with field 3 CC data + belongs to the same channel as field 1. The following pics have the fields reversed + 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 +---- +- Add -nosync and -fullbin switches for debugging purposes. +- Remove -lg (--largegops) switch. +- Improve syncronization of captions for source files with + jumps in their time information or gaps in the caption + information. +- [R. Abarca] Changed Mac script, it now compiles/link + everything from the /src directory. +- It's now possible to have CCExtractor add credits + automatically. +- Added a feature to add start and end messages (for credits). + See help screen for details. + +0.53 +---- +- 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. +- Fix sync check for raw/rcwt files. +- Improve timing of dvr-ms NTSC captions. +- Add -in=bin switch to read CCExtractor's own binary format. +- Fix problem with short input files (smaller 1MB). +- Clean up regular and debug output. +- Add -out=bin switch to write RCWT data. +- Remove -bo/--bufferoutput switch and functionality. +- [Volker] Added new generic binary format (RCWT + for Raw Captions With Time). This new format + allows one file to contain all the available + closed caption data instead of just one stream. +- Added --no_progress_bar to disable status + information (mostly used when debugging, as the + progress information is annoying in the middle + of debug logs). +- The Windows GUI was reported to freeze in some + conditions. Fixed. +- The Windows GUI is now targeted for .NET 2.0 + instead of 3.5. This allows Windows 2000 to run + it (there's not .NET 3.5 for Windows 2000), as + requested by a couple of key users. + +0.51 +---- +- Removed -autopad and -goppad, no longer needed. +- In preparation to a new binary format we have + renamed the current .bin to .raw. Raw files + have only CC data (with no header, timing, etc). +- The input file format (when forced) is now + specified with + -in=format + such as -in=ts, -in=raw, -in=ps ... + The old switches (-ts, -ps, etc) still work. + The only exception is -bin which has been removed + (reserved for the new binary format). Use + -in=raw to process a raw file. +- Removed -d, which when produced a raw file used + a DVD format. This has been merged into a new + output type "dvdraw". So now instead of using + -raw -d as before, use -out=dvdraw if you need + this. +- Removed --noff +- Added gui_mode_reports for frontend communications, + see related file. +- Windows GUI rewritten. Source code now included, + too. +- [Volker] Dish Network clean-up + +0.50 +---- +- [Volker] Fix in DVR-MS NTSC timing +- [Volker] More clean-up +- Minor fixes + +0.49 +---- +- [Volker] Major MPEG parser rework. Code much + cleaner now. +- Some stations transmit broken roll-up captions, + and for some reason don't send CRs but RUs... + Added work-around code to make captions readable. +- Started work on EIA-708 (DTV). Right now you can + add -debug-708 to get a dump of the 708 data. + An actually useful decoder will come soon. +- Some of the changes MIGHT HAVE BROKEN MythTV's + code. I don't use MythTV myself so I rely on + other people's samples and reports. If MythTV + is broken please let me know. +- Added new debug options. +- [Volker] Added support for DVR-MS NTSC files. +- Other minor bugfixes and changes. + +0.46 +---- +- Added support for live streaming, ccextractor + can now process files that are being recorded + at the same time. + +- [Volker] Added a new DVR-MS loop - this is + completely new, DVR-MS specific code, so we no + longer use the generic MPEG code for DVR-MS. + DVR-MS should (or will be eventually at least) + be as reliable as TS. + Note: For now, it's only ATSC recordings, not + NTSC (analog) recordings. + + +0.45 +---- +- Added autodetection of DVR-MS files. +- Added -asf to force DVR-MS mode. +- Added some specific support for DVR-MS + files. These format used to work + correcty in 0.34 (pure luck) but the + MPEG code rework broke it. It should + work as it used to. +- Updated Windows GUI to support the + new options. +- Added -lg --largegops + From the help screen: + Each Group-of-Picture comes with timing + information. When this info is too separate + (for example because there are a lot of + frames in a GOP) ccextractor may prefer not + to use GOP timing. Use this option is you + need ccextractor to use GOP timing in large + GOPs. + +0.44 +---- +- Added an option to the GUI to process + individual files in batch, i.e. call + ccextractor once per file. Use it if you + want to process several unrelated files + in one go. +- Added an option to prevent duplicate + lines in roll-up captions. +- Several minor bugfixes. +- Updated the GUI to add the new options. + +0.43 +---- +- Fixed a bug in the read loop (no less) + that caused some files to fail when + reading without buffering (which is + the default in the linux build). +- Several improvements in the GUI, such as + saving current options as default. + +0.42 +---- +- 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 +---- +- Default output is now .srt instead of .bin, + use -raw if you need the data dump instead of + .srt. +- Added -trim, which removes blank spaces at + the left and rights of each line in .srt. + Note that those spaces are there to help + deaf people know if the person talking is + at the left or the right of the screen, i.e. + there aren't useless. But if they annoy + you go ahead... + +0.40 +---- +- 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 +---- +- Added a -transcript. If used, the output will + have no time information. Also, if in roll-up + mode there will be no repeated lines. +- Lots of changes in the MPEG parser, most of + them submitted by Volker Quetschke. +- Fixed a bug in the CC decoder that could cause + the first line not to be cleared in roll-up + mode. +- ccextractor can now follow number sequences in + file names, by suffixing the name with +. + For example, + + DVD0001.VOB+ + + means DVD0001.VOB, DVD0002.VOB, etc. This works + for all files, so part001.ts+ does what you + could expect. +- Added -90090 which changes the clock frequency + from the MPEG standard 90000 to 90090. It + *could* (remains to be seen) help if there are + timing issues. +- Better support for Tivo files. +- By default ccextractor now considers the whole + input file list a one large file, instead of + several, independent, video files. This has + been changed because most programs (for example + DVDDecrypt) just cut the files by size. + If you need the old behaviour (because you + actually edited the video files and want to + join the subs), use -ve. + + +0.36 +---- +- Fixed bug in SMI, nbsp was missing a ;. +- Footer for SAMI files was incorrect ( and + tags were being opened again instead of + being closed). +- Displayed memory is now written to disk at end + of stream even if there is no command requesting + so (may prevent losing the last screenful). +- Important change that could break scripts, but + that have been added because old behaviour was + annoying to most people: _1 and _2 at the end + of the output file names is now added ONLY if + -12 is used (ie when there are two output + files to produce). So + + ccextractor -srt sopranos.mpg + + now produces sopranos.srt instead of sopranos_1.srt. + If you use -12, i.e. + + ccextractor -srt -12 sopranos.mpg + + You get + + sopranos_1.srt and + sopranos_2.srt + + as usual. + + +0.35 +---- +- Added --defaultcolor to the help screen. Code + was already in 0.34 but the documentation wasn't + updated. +- Buffer is larger now, since I've found a sample + where 256 Kb isn't enough for a PES (go figure). +- At the end of the process, a ratio between + video length and time to process is displayed. + +0.34 +---- +- Added some basic letter case and capitalization + support. For captions that broadcast in ALL + UPPERCASE (most of them), ccextractor can now + do the first part of the job. + + --sentencecap or -sc will tell ccextractor to + follow the typical capitalization rules, such + as capitalize months, days of week, etc. + + So from + YOU BETTER RESPECT + THIS ROBE, ALAN + + You get + + You better respect + this robe, alan. + + --capfile or -caf also enables the case + processing part and adds an extra list of + words in the specified file, for example: + + --capfile names.txt + + where names.txt is just a plain text file + with the proper spelling for some words, + such as + + Alan + Tony + + So you get + + You better respect + this robe, Alan. + + Which is the correct spelling. You can + have a different spelling file per TV + show, or a large file with a lot of + words, etc. +- ccextractor has been reported to + compile and run on Mac with a minor + change in the build script, so I've + created a mac directory with the + modified script. I haven't tested it + myself. +- Windows build comes with a File Version + Number (0.0.0.34 in this version) in case + you want to check for version info. + +0.33 +---- +- Added -scr or --screenfuls, to select the + number of screenfuls ccextractor should + write before exiting. A screenful is + a change of screen contents caused by + a CC command (not new characters). In + practice, this means that for .srt each + group of lines is a screenful, except when + using -dru (which produces a lot of + groups of lines because each new character + produces a new group). +- Completed tables for all encodings. +- Fixed bug in .srt related to milliseconds + in time lines. +- Font colors are back for .srt (apparently + some programs do support them after all). + Use -nofc or --nofontcolor if you don't + want these tags. + +0.32 +---- +- Added -delay ms, which adds (or substracts) + a number of milliseconds to all times in + .srt/.sami files. For example, + + -delay 400 + + causes all subtitles to appear 400 ms later + than they would normally do, and + + -delay -400 + + causes all substitles to appear 400 ms before + they would normally do. +- Added -startat at -endat which lets you + select just a portion of data to be processed, + such as from minute 3 to minute 5. Check + help screen for exact syntax. + +0.31 +---- +- Added -dru (direct rollup), which causes + roll-up captions to be written as + they would on TV instead of line by line. + This makes .srt/.sami files a lot longer, + and ugly too (each line is written many + times, two characters at time). + +0.30 +---- +- 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 +---- +- Minor bugfix. + +0.28 +---- +- Fixed a buffering related issue. Short version, + the first 2 Mb in non-TS mode were being + discarded. +- .srt no longer has tags. No player + seems to process them so my guess is that + they are not part of the .srt "standard" + even if McPoodle add them. + +0.27 +---- +- Modified sanitizing code, it's less aggresive + now. Ideally it should mean that characters + won't be missed anymore. We'll see. + +0.26 +---- +- 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 +---- +- 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 + make MythTV's MPEG decoder to choke. So, if it + doesn't work correctly automatically: Try + -nomyth and -myth. Hopefully one of the two + options will work. + + +0.24 +---- +- Fixed a bug that caused dvr-ms (Windows Media Center) + files to be incorrectly processed (letters out of + order all the time). +- Reworked input buffer code, faster now. +- Completed MythTV's MPEG decoder for Program Streams, + which results in better processing of some specific + files. +- Automatic file format detection for all kind of + files and closed caption storage method. No need to + tell ccextractor anything about your file (but you + still can). + + +0.22 +---- +- Added text mode handling into decoder, which gets rids + of junk when text mode data is present. +- Added support for certain (possibly non standard + compliant) DVDs that add more captions block in a + user data block than they should (such as Red October). +- Fix in roll-up init code that caused the previous popup + captions not to be written to disk. +- Other Minor bug fixes. + + +0.20 +---- +- Unicode should be decent now. +- Added support for Hauppauge PVR 250 cards, and (possibly) + many others (bttv) with the same closed caption recording + format. + This is the result of hacking MythTV's MPEG parser into + ccextractor. Integration is not very good (to put it + midly) but it seems to work. Depending on the feedback I + may continue working on this or just leave it 'as it' + (good enough). + If you want to process a file generated by one of these + analog cards, use -myth. This is essential as it will + make the program take a totally different code path. +- Added .SAMI generation. I'm sure this can be improved, + though. If you have a good CSS for .SAMI files let me + know. + +0.19 +---- +- 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 + Jack Ha who sent me a couple of samples and a first + implementation of a semiworking fix. +- Added support for several input files (see help screen for + details). +- Added Unicode and Latin-1 encoding. + + +0.17 +---- +- 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 + so I can't test). +- Minor bug fixes. +- Automatic TS/non-TS mode detection. + +0.14 +---- +- Work on handling special cases related to the MPEG reference + clock: Roll over, jumps, etc. +- Modified padding code a bit: In particular, padding occurs + on B-Frames now. +- Started work on CC data parsing (use -608 to see output). +- Added built-in input buffering. +- Major code reorganization. +- Added a decent progress indicator. +- Added TS header synchronization (so the input file no longer + needs to start with a TS header). +- Minor bug fixes. + +0.07 +---- +- Added MPEG reference clock parsing. +- Added autopadding in TS. Does miracles with timing. +- Added video information (as extracted from sequence header). +- Some code clean-up. +- FF sanity check enabled by default. + diff --git a/docs/CODE_ORGANIZATION.TXT b/docs/CODE_ORGANIZATION.TXT new file mode 100644 index 00000000..5a143670 --- /dev/null +++ b/docs/CODE_ORGANIZATION.TXT @@ -0,0 +1,2 @@ +Please check the GSOC pages in the website. Information will be added there +frequently to assist GSOC students get started. diff --git a/docs/FRONTEND_COMMUNICATIONS.TXT b/docs/FRONTEND_COMMUNICATIONS.TXT new file mode 100644 index 00000000..debf9f52 --- /dev/null +++ b/docs/FRONTEND_COMMUNICATIONS.TXT @@ -0,0 +1,120 @@ +Starting with version 0.51, ccextractor has a mode +that allows frontends and other programs know what +the current progress is as well as get information +on interesting events, such as a file being open +or video information (resolution, frame rate and +so on) being found. + +The Windows GUI source code is good reference +implementation. + +Usage: + +Add --gui_mode_reports + +to the command line as the first parameter (this +is not essential but because the mode is enabled +after this option is parsed, if there is any +event regarding the previous parameters you +would miss them). + +When this option is used, ccextractor reports +progress and other information to stderr. + +This does not disable stdout (the regular stuff) +reporting. + +Information it sent to stderr, one line at a time. +Information lines that are supposed to be parsed +by programs starts with ###. All other lines, if +any, must be discarded. stderr is not guaranteed +to have only information lines - so do check. + +After ### the data starts. Tokens are also +separated by #. The first token specified the +data type, and the rest, if any, are parameters. + +The following data types are currently defined: + +VERSION - Get CCExtractor version + Program + Version + + Example: ###VERSION#CCExtractor#0.51 + +PROGRESS - Get processed percentage and current + timing + Percentage (or -1 for streaming mode) + Minutes + Seconds + + Example: ###PROGRESS#45#12#34 + (for 45%, 12:34) + +INPUTFILECLOSED - CCExtractor closed whatever + input file it had open (which had + been reported before). + No parameters. + +INPUTFILEOPEN - A new file was successfully + open for reading. + Filename + + Note that the filename CAN contain + #, so don't split the line after the + first token and treat the remainder + as the filename. + + Example: ###INPUTFILEOPEN#/tmp/This is # a file.mpg + +MESSAGE - Something interesting happened. + Message text. + + This can be anything from a fatal + error message to something merely + informative. + + Example: ###MESSAGE#Timing broken in this file + +VIDEOINFO - New video information found + Horizontal resolution + Vertical resolution + Aspect ratio + Framerate + + Example: ###VIDEOINFO#1980#1080#16:9#29.97 + +SUBTITLE - New subtitle screen + Start time + End time + Line + + Time is empty if it's the same of the previous + line (i.e. they're supposed to appear together). + + Example: ###SUBTITLE#10:23#10:25#This is 1 + ###SUBTITLE###This is 2 + + Note that time is in MM:SS format. No milliseconds + since this is for a preview window. + Text is trimmed and there is no color or font + information, to save space. + +TSPROGRAMNUMBER - A multiprogram TS was detected, but + no program was manually selected. CCExtractor will + send a list of programs so the GUI can show it to + the user for selection. + + + Example: ###TSPROGRAMNUMBER#3 + +XDSPROGRAMNAME - Program Name as received from XDS is + available. + + Example: + + ###XDSPROGRAMNAME#Star Trek + + You may receive this more than once per stream. + +*** TODO : Add the rest of XDS stuff **** diff --git a/docs/MAILINGLIST.TXT b/docs/MAILINGLIST.TXT new file mode 100644 index 00000000..46a60902 --- /dev/null +++ b/docs/MAILINGLIST.TXT @@ -0,0 +1,17 @@ +A mailing list is now available from sourceforge: + +https://lists.sourceforge.net/lists/listinfo/ccextractor-users + +I expect it to be very low traffic (right now there's around 10 +people actively helping with ccextractor in one way or +another), so almost everything goes here: + +- Bug reports +- Feature requests +- Announcements + +NOT here: + +- Samples + + diff --git a/docs/README.TXT b/docs/README.TXT new file mode 100644 index 00000000..205c5387 --- /dev/null +++ b/docs/README.TXT @@ -0,0 +1,87 @@ +ccextractor, 0.63 +----------------- +Authors: Carlos Fernández (cfsmp3), Volker Quetschke. +Maintainer: cfsmp3 + +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 + +You can subscribe to new releases notifications at freshmeat: + +http://freshmeat.net/projects/ccextractor + +License +------- +GPL 2.0. + +Description +----------- +ccextractor was originally a mildly optimized C port of McPoodle's excellent +but painfully slow Perl script SCC_RIP. It lets you rip the raw closed +captions (read: subtitles) data from a number of sources, such as DVD or +ATSC (digital TV) streams. + +Since the original port, lots of changes have been made, such as HDTV +support, analog captures support (via bttv cards), direct .srt/.smi +generation, time adjusting, and more. + +Basic Usage +----------- +(please run ccextractor with no parameters for the complete manual - +this is for your convenience, really). + +ccextractor reads a video stream looking for closed captions (subtitles). +It can do two things: + +- Save the data to a "raw", unprocessed file which you can later use + as input for other tools, such as McPoodle's excellent suite. +- Generate a subtitles file (.srt,.smi, or .txt) which you can directly + use with your favourite player. + +Running ccextractor without parameters shows the help screen. Usage is +trivial - you just need to pass the input file and (optionally) some +details about the input and output files. + + +Languages +--------- +Usually English captions are transmitted in line 21 field 1 data, +using channel 1, so the default values are correct so you don't +need to do anything and you don't need to understand what it all +means. + +If you want the Spanish captions, you may need to play a bit with +the parameters. From what I've been, Spanish captions are usually +sent in field 2, and sometimes in channel 2. + +So try adding these parameter combinations to your other parameters. + +-2 +-cc2 +-2 -cc2 + +If there are Spanish subtitles, one of them should work. + +McPoodle's page +--------------- +http://www.geocities.com/mcpoodle43/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. + +Future work +----------- +- Finish EIA-708 decoder +- Network support + diff --git a/linux/Makefile b/linux/Makefile new file mode 100644 index 00000000..1e278ac8 --- /dev/null +++ b/linux/Makefile @@ -0,0 +1,104 @@ +# Comment out the following line if you don't have libpng + +CPPFLAGS += -DGPAC_CONFIG_LINUX -D_FILE_OFFSET_BITS=64 -I../src/gpacmp4 -I../src/libpng/ -I../src/zlib/ +CFLAGS += -g -Wno-write-strings + +CXXFLAGS = $(CFLAGS) +CC = $(CXX) + +bindir = $(HOME)/bin +INSTALL = cp -f -p +INSTALL_PROGRAM = cp -f -p + +VPATH = ../src:../src/gpacmp4:../src/libpng:../src/zlib + +src_OBJS = \ + 608_helpers.o 608.o 608_sami.o 608_smptett.o 608_srt.o 608_spupng.o \ + 708_encoding.o 708.o activity.o asf_functions.o avc_functions.o \ + cc_bitstream.o cc_decoders_common.o ccextractor.o constants.o \ + encoding.o es_functions.o es_userdata.o file_functions.o \ + general_loop.o mp4.o myth.o output.o params_dump.o params.o \ + sequencing.o stream_functions.o telxcc.o timing.o ts_functions.o \ + utility.o wtv_functions.o xds.o + +gpacmp4_OBJS = \ + avc_ext.o avilib.o av_parsers.o base_encoding.o bitstream.o \ + box_code_3gpp.o box_code_apple.o box_code_base.o box_code_isma.o \ + box_code_meta.o box_funcs.o data_map.o desc_private.o descriptors.o \ + error.o gpac_ogg.o hinting.o ipmpx_code.o ipmpx_parse.o \ + isom_intern.o isom_read.o isom_store.o isom_write.o list.o \ + media.o media_odf.o meta.o movie_fragments.o \ + odf_code.o odf_codec.o odf_command.o os_divers.o qos.o sample_descs.o \ + slc.o stbl_read.o stbl_write.o track.o tx3g.o url.o utf.o + +src_SRC = \ + ../src/608.cpp ../src/608_helpers.cpp ../src/608_sami.cpp \ + ../src/608_smptett.cpp ../src/608_spupng.cpp ../src/608_srt.cpp \ + ../src/708.cpp ../src/708_encoding.cpp ../src/activity.cpp \ + ../src/asf_functions.cpp ../src/avc_functions.cpp ../src/cc_bitstream.cpp \ + ../src/cc_decoders_common.cpp ../src/ccextractor.cpp ../src/constants.cpp \ + ../src/encoding.cpp ../src/es_functions.cpp ../src/es_userdata.cpp \ + ../src/file_functions.cpp ../src/general_loop.cpp ../src/myth.cpp \ + ../src/output.cpp ../src/params.cpp ../src/params_dump.cpp \ + ../src/sequencing.cpp ../src/stream_functions.cpp ../src/telxcc.cpp \ + ../src/timing.cpp ../src/ts_functions.cpp ../src/utility.cpp \ + ../src/wtv_functions.cpp ../src/xds.cpp + +gpacmp4_SRC = \ + ../src/gpacmp4/avc_ext.c ../src/gpacmp4/avilib.c \ + ../src/gpacmp4/av_parsers.c ../src/gpacmp4/base_encoding.c \ + ../src/gpacmp4/bitstream.c ../src/gpacmp4/box_code_3gpp.c \ + ../src/gpacmp4/box_code_apple.c ../src/gpacmp4/box_code_base.c \ + ../src/gpacmp4/box_code_isma.c ../src/gpacmp4/box_code_meta.c \ + ../src/gpacmp4/box_funcs.c ../src/gpacmp4/data_map.c \ + ../src/gpacmp4/desc_private.c ../src/gpacmp4/descriptors.c \ + ../src/gpacmp4/error.c ../src/gpacmp4/gpac_ogg.c \ + ../src/gpacmp4/hinting.c ../src/gpacmp4/ipmpx_code.c \ + ../src/gpacmp4/ipmpx_parse.c ../src/gpacmp4/isom_intern.c \ + ../src/gpacmp4/isom_read.c ../src/gpacmp4/isom_store.c \ + ../src/gpacmp4/isom_write.c ../src/gpacmp4/list.c \ + ../src/gpacmp4/media.c ../src/gpacmp4/media_odf.c \ + ../src/gpacmp4/meta.c ../src/gpacmp4/movie_fragments.c \ + ../src/gpacmp4/mp4.cpp ../src/gpacmp4/odf_code.c \ + ../src/gpacmp4/odf_codec.c ../src/gpacmp4/odf_command.c \ + ../src/gpacmp4/os_divers.c ../src/gpacmp4/qos.c \ + ../src/gpacmp4/sample_descs.c ../src/gpacmp4/slc.c \ + ../src/gpacmp4/stbl_read.c ../src/gpacmp4/stbl_write.c \ + ../src/gpacmp4/track.c ../src/gpacmp4/tx3g.c \ + ../src/gpacmp4/url.c ../src/gpacmp4/utf.c + +libpng_SRC = \ + ../src/libpng/png.c ../src/libpng/pngerror.c ../src/libpng/pngmem.c \ + ../src/libpng/pngset.c ../src/libpng/pngtrans.c ../src/libpng/pngwio.c \ + ../src/libpng/pngwrite.c ../src/libpng/pngwtran.c ../src/libpng/pngwutil.c + +libpng_OBJS = \ + png.o pngerror.o pngmem.o pngset.o pngtrans.o pngwio.o \ + pngwrite.o pngwtran.o pngwutil.o + +zlib_SRC = \ + ../src/zlib/adler32.c ../src/zlib/crc32.c ../src/zlib/deflate.c \ + ../src/zlib/inffast.c ../src/zlib/inflate.c ../src/zlib/inftrees.c \ + ../src/zlib/trees.c ../src/zlib/zutil.c + +zlib_OBJS = \ + adler32.o crc32.o deflate.o inffast.o inflate.o inftrees.o \ + trees.o zutil.o + +.PHONY: all +all: ccextractor + +.PHONY: clean +clean: + rm -f *.o ccextractor depend + +ccextractor: $(src_OBJS) $(gpacmp4_OBJS) $(libpng_OBJS) $(zlib_OBJS) + +.PHONY: install +install: ccextractor + $(INSTALL_PROGRAM) ccextractor $(bindir) + +depend: + $(CXX) $(CXXFLAGS) -E -MM $(src_SRC) $(gpacmp4_SRC) $(libpng_SRC) $(zlib_SRC) > depend + +include depend diff --git a/linux/build b/linux/build new file mode 100644 index 00000000..e91b6e68 --- /dev/null +++ b/linux/build @@ -0,0 +1,2 @@ +#!/bin/bash +g++ -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') diff --git a/linux/builddebug b/linux/builddebug new file mode 100644 index 00000000..4e199e99 --- /dev/null +++ b/linux/builddebug @@ -0,0 +1,2 @@ +#!/bin/bash +g++ -Wno-write-strings -g -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') diff --git a/mac/README.MAC.TXT b/mac/README.MAC.TXT new file mode 100644 index 00000000..f890dac8 --- /dev/null +++ b/mac/README.MAC.TXT @@ -0,0 +1,8 @@ +Note: I don't currently have a Mac to test Mac builds. An effort is done to ensure that CCExtractor is portable, +which is why it compiles and works in Mac without any effort. But the build script (any of its 2 lines) is not +maintained. If it doesn't compile for this version please fix and send me the new file so I can add it to the +official version. + +I know this sucks but I can't really do much more. + +Carlos diff --git a/mac/build.command b/mac/build.command new file mode 100644 index 00000000..9dfdd4cd --- /dev/null +++ b/mac/build.command @@ -0,0 +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') diff --git a/src/608.cpp b/src/608.cpp new file mode 100644 index 00000000..f732825f --- /dev/null +++ b/src/608.cpp @@ -0,0 +1,1338 @@ +#include "ccextractor.h" + +static const int rowdata[] = {11,-1,1,2,3,4,12,13,14,15,5,6,7,8,9,10}; +// Relationship between the first PAC byte and the row number +int in_xds_mode=0; + +#define INITIAL_ENC_BUFFER_CAPACITY 2048 + +unsigned char *enc_buffer=NULL; // Generic general purpose buffer +unsigned char str[2048]; // Another generic general purpose buffer +unsigned enc_buffer_used; +unsigned enc_buffer_capacity; + + + +LLONG minimum_fts=0; // No screen should start before this FTS + +int general_608_init (void) +{ + enc_buffer=(unsigned char *) malloc (INITIAL_ENC_BUFFER_CAPACITY); + if (enc_buffer==NULL) + return -1; + enc_buffer_capacity=INITIAL_ENC_BUFFER_CAPACITY; + return 0; +} + +// Preencoded strings +unsigned char encoded_crlf[16]; +unsigned int encoded_crlf_length; +unsigned char encoded_br[16]; +unsigned int encoded_br_length; + +unsigned char *subline; // Temp storage for .srt lines +int new_sentence=1; // Capitalize next letter? + +// Default color +unsigned char usercolor_rgb[8]=""; + + +static const char *sami_header= // TODO: Revise the \n\ +\n\ +\n\n\ +\n"; + +static const char *smptett_header = +"\n\ +\n\ +\n
\n" ; + +static const char *command_type[] = +{ + "Unknown", + "EDM - EraseDisplayedMemory", + "RCL - ResumeCaptionLoading", + "EOC - End Of Caption", + "TO1 - Tab Offset, 1 column", + "TO2 - Tab Offset, 2 column", + "TO3 - Tab Offset, 3 column", + "RU2 - Roll up 2 rows", + "RU3 - Roll up 3 rows", + "RU4 - Roll up 4 rows", + "CR - Carriage Return", + "ENM - Erase non-displayed memory", + "BS - Backspace", + "RTD - Resume Text Display", + "AOF - Not Used (Alarm Off)", + "AON - Not Used (Alarm On)", + "DER - Delete to End of Row", + "RDC - Resume Direct Captioning", + "RU1 - Fake Roll up 1 rows" +}; + +static const char *font_text[]= +{ + "regular", + "italics", + "underlined", + "underlined italics" +}; + +static const char *cc_modes_text[]= +{ + "Pop-Up captions" +}; + +const char *color_text[][2]= +{ + {"white",""}, + {"green",""}, + {"blue",""}, + {"cyan",""}, + {"red",""}, + {"yellow",""}, + {"magenta",""}, + {"userdefined","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; + } + data->empty=1; +} + +void init_eia608 (struct eia608 *data) +{ + data->cursor_column=0; + data->cursor_row=0; + clear_eia608_cc_buffer (&data->buffer1); + clear_eia608_cc_buffer (&data->buffer2); + data->visible_buffer=1; + data->last_c1=0; + data->last_c2=0; + data->mode=MODE_POPON; + // data->current_visible_start_cc=0; + data->current_visible_start_ms=0; + data->srt_counter=0; + data->screenfuls_counter=0; + data->channel=1; + data->color=ccx_options.cc608_default_color; + data->font=FONT_REGULAR; + data->rollup_base_row=14; + data->ts_start_of_current_line=-1; + data->ts_last_char_received=-1; + data->new_channel=1; +} + +eia608_screen *get_writing_buffer (struct ccx_s_write *wb) +{ + eia608_screen *use_buffer=NULL; + switch (wb->data608->mode) + { + case MODE_POPON: // Write on the non-visible buffer + if (wb->data608->visible_buffer==1) + use_buffer = &wb->data608->buffer2; + else + use_buffer = &wb->data608->buffer1; + break; + case MODE_FAKE_ROLLUP_1: // Write directly to screen + case MODE_ROLLUP_2: + case MODE_ROLLUP_3: + case MODE_ROLLUP_4: + case MODE_PAINTON: + case MODE_TEXT: + // TODO: Fix this. Text uses a different buffer, and contains non-program information. + if (wb->data608->visible_buffer==1) + use_buffer = &wb->data608->buffer1; + else + use_buffer = &wb->data608->buffer2; + break; + default: + fatal (EXIT_BUG_BUG, "Caption mode has an illegal value at get_writing_buffer(), this is a bug.\n"); + } + return use_buffer; +} + +void delete_to_end_of_row (struct ccx_s_write *wb) +{ + if (wb->data608->mode!=MODE_TEXT) + { + eia608_screen * use_buffer=get_writing_buffer(wb); + for (int i=wb->data608->cursor_column; i<=31 ; i++) + { + // TODO: This can change the 'used' situation of a column, so we'd + // need to check and correct. + use_buffer->characters[wb->data608->cursor_row][i]=' '; + use_buffer->colors[wb->data608->cursor_row][i]=ccx_options.cc608_default_color; + use_buffer->fonts[wb->data608->cursor_row][i]=wb->data608->font; + } + } +} + +void write_char (const unsigned char c, struct ccx_s_write *wb) +{ + if (wb->data608->mode!=MODE_TEXT) + { + eia608_screen * use_buffer=get_writing_buffer(wb); + /* printf ("\rWriting char [%c] at %s:%d:%d\n",c, + use_buffer == &wb->data608->buffer1?"B1":"B2", + wb->data608->cursor_row,wb->data608->cursor_column); */ + use_buffer->characters[wb->data608->cursor_row][wb->data608->cursor_column]=c; + use_buffer->colors[wb->data608->cursor_row][wb->data608->cursor_column]=wb->data608->color; + use_buffer->fonts[wb->data608->cursor_row][wb->data608->cursor_column]=wb->data608->font; + use_buffer->row_used[wb->data608->cursor_row]=1; + + if (use_buffer->empty) + { + if (MODE_POPON != wb->data608->mode) + wb->data608->current_visible_start_ms = get_visible_start(); + } + use_buffer->empty=0; + + if (wb->data608->cursor_column<31) + wb->data608->cursor_column++; + if (wb->data608->ts_start_of_current_line == -1) + wb->data608->ts_start_of_current_line=get_fts(); + wb->data608->ts_last_char_received=get_fts(); + } + +} + +/* Handle MID-ROW CODES. */ +void handle_text_attr (const unsigned char c1, const unsigned char c2, struct ccx_s_write *wb) +{ + // Handle channel change + wb->data608->channel=wb->data608->new_channel; + if (wb->data608->channel!=ccx_options.cc_channel) + return; + dbg_print(CCX_DMT_608, "\r608: text_attr: %02X %02X",c1,c2); + if ( ((c1!=0x11 && c1!=0x19) || + (c2<0x20 || c2>0x2f))) + { + dbg_print(CCX_DMT_608, "\rThis is not a text attribute!\n"); + } + else + { + int i = c2-0x20; + wb->data608->color=pac2_attribs[i][0]; + wb->data608->font=pac2_attribs[i][1]; + dbg_print(CCX_DMT_608, " -- Color: %s, font: %s\n", + color_text[wb->data608->color][0], + font_text[wb->data608->font]); + // Mid-row codes should put a non-transparent space at the current position + // and advance the cursor + //so use write_char + write_char(0x20, wb); + } +} + + +void write_subtitle_file_footer (struct ccx_s_write *wb) +{ + switch (ccx_options.write_format) + { + case CCX_OF_SAMI: + sprintf ((char *) str,"\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 (wb->fh, enc_buffer,enc_buffer_used); + break; + case CCX_OF_SMPTETT: + sprintf ((char *) str,"
\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 (wb->fh, enc_buffer,enc_buffer_used); + break; + case CCX_OF_SPUPNG: + write_spumux_footer(wb); + break; + default: // Nothing to do, no footer on this format + break; + } +} + + +void write_subtitle_file_header (struct ccx_s_write *wb) +{ + 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(strlen (sami_header)*3); + enc_buffer_used=encode_line (enc_buffer,(unsigned char *) sami_header); + write (wb->fh, enc_buffer,enc_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(strlen (smptett_header)*3); + enc_buffer_used=encode_line (enc_buffer,(unsigned char *) smptett_header); + write (wb->fh, enc_buffer,enc_buffer_used); + break; + case CCX_OF_RCWT: // Write header + write (wb->fh, rcwt_header, sizeof(rcwt_header)); + break; + case CCX_OF_SPUPNG: + write_spumux_header(wb); + break; + case CCX_OF_TRANSCRIPT: // No header. Fall thru + default: + break; + } +} + +void write_cc_line_as_transcript (struct eia608_screen *data, struct ccx_s_write *wb, int line_number) +{ + unsigned h1,m1,s1,ms1; + unsigned h2,m2,s2,ms2; + if (ccx_options.sentence_cap) + { + capitalize (line_number,data); + correct_case(line_number,data); + } + int length = get_decoder_line_basic (subline, line_number, data); + if (ccx_options.encoding!=CCX_ENC_UNICODE) + { + dbg_print(CCX_DMT_608, "\r"); + dbg_print(CCX_DMT_608, "%s\n",subline); + } + if (length>0) + { + if (timestamps_on_transcript) + { + const char *mode="???"; + switch (wb->data608->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; + } + + if (wb->data608->ts_start_of_current_line == -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.ucla_settings) + { + mstotime (wb->data608->ts_start_of_current_line+subs_delay,&h1,&m1,&s1,&ms1); + mstotime (get_fts()+subs_delay,&h2,&m2,&s2,&ms2); + + // SSC-1182 BEGIN + // Changed output format to be more like ZVBI + + char buffer[80]; + time_t start_time_int = (wb->data608->ts_start_of_current_line+subs_delay)/1000; + int start_time_dec = (wb->data608->ts_start_of_current_line+subs_delay)%1000; + struct tm *start_time_struct = gmtime(&start_time_int); + strftime(buffer, sizeof(buffer), "%Y%m%d%H%M%S", start_time_struct); + fdprintf(wb->fh, "%s.%03d|", buffer, start_time_dec); + + time_t end_time_int = (get_fts()+subs_delay)/1000; + int end_time_dec = (get_fts()+subs_delay)%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.%03d|", buffer, end_time_dec); + + fdprintf(wb->fh, "CC%d|%s|", wb->my_field==1?wb->data608->channel:wb->data608->channel+2, // Data from field 2 is CC3 or 4 + mode); + // SSC-1182 END + } + else + { + char buf1[80], buf2[80]; + char timeline[256]; + millis_to_date (wb->data608->ts_start_of_current_line+subs_delay,buf1); + millis_to_date (get_fts()+subs_delay, buf2); + sprintf (timeline, "%s|%s|%s|", + buf1, buf2,mode); + enc_buffer_used=encode_line (enc_buffer,(unsigned char *) timeline); + write (wb->fh, enc_buffer,enc_buffer_used); + } + } + write (wb->fh, subline, length); + write (wb->fh, encoded_crlf, encoded_crlf_length); + } + // fprintf (wb->fh,encoded_crlf); +} + +int write_cc_buffer_as_transcript (struct eia608_screen *data, struct ccx_s_write *wb) +{ + int wrote_something = 0; + wb->data608->ts_start_of_current_line=wb->data608->current_visible_start_ms; + dbg_print(CCX_DMT_608, "\n- - - TRANSCRIPT caption - - -\n"); + + for (int i=0;i<15;i++) + { + if (data->row_used[i]) + { + write_cc_line_as_transcript (data,wb, i); + } + wrote_something=1; + } + dbg_print(CCX_DMT_608, "- - - - - - - - - - - -\r\n"); + return wrote_something; +} + + + + +struct eia608_screen *get_current_visible_buffer (struct ccx_s_write *wb) +{ + struct eia608_screen *data; + if (wb->data608->visible_buffer==1) + data = &wb->data608->buffer1; + else + data = &wb->data608->buffer2; + return data; +} + + +int write_cc_buffer (struct ccx_s_write *wb) +{ + struct eia608_screen *data; + int wrote_something=0; + if (ccx_options.screens_to_process!=-1 && + wb->data608->screenfuls_counter>=ccx_options.screens_to_process) + { + // We are done. + processed_enough=1; + return 0; + } + if (wb->data608->visible_buffer==1) + data = &wb->data608->buffer1; + else + data = &wb->data608->buffer2; + + if (!data->empty) + { + if (wb->data608->mode==MODE_FAKE_ROLLUP_1 && // Use the actual start of data instead of last buffer change + wb->data608->ts_start_of_current_line!=-1) + wb->data608->current_visible_start_ms=wb->data608->ts_start_of_current_line; + + new_sentence=1; + switch (ccx_options.write_format) + { + case CCX_OF_SRT: + if (!startcredits_displayed && ccx_options.start_credits_text!=NULL) + try_to_add_start_credits(wb); + wrote_something = write_cc_buffer_as_srt (data, wb); + break; + case CCX_OF_SAMI: + if (!startcredits_displayed && ccx_options.start_credits_text!=NULL) + try_to_add_start_credits(wb); + wrote_something = write_cc_buffer_as_sami (data,wb); + break; + case CCX_OF_SMPTETT: + if (!startcredits_displayed && ccx_options.start_credits_text!=NULL) + try_to_add_start_credits(wb); + wrote_something = write_cc_buffer_as_smptett (data,wb); + break; + case CCX_OF_TRANSCRIPT: + wrote_something = write_cc_buffer_as_transcript (data,wb); + break; + case CCX_OF_SPUPNG: + wrote_something = write_cc_buffer_as_spupng (data,wb); + break; + default: + break; + } + if (wrote_something) + last_displayed_subs_ms=get_fts()+subs_delay; + + if (ccx_options.gui_mode_reports) + write_cc_buffer_to_gui (data,wb); + } + return wrote_something; +} + +// Check if a rollup would cause a line to go off the visible area +int check_roll_up (struct ccx_s_write *wb) +{ + int keep_lines=0; + int firstrow=-1, lastrow=-1; + eia608_screen *use_buffer; + if (wb->data608->visible_buffer==1) + use_buffer = &wb->data608->buffer1; + else + use_buffer = &wb->data608->buffer2; + + switch (wb->data608->mode) + { + case MODE_FAKE_ROLLUP_1: + keep_lines=1; + break; + case MODE_ROLLUP_2: + keep_lines=2; + break; + case MODE_ROLLUP_3: + keep_lines=3; + break; + case MODE_ROLLUP_4: + keep_lines=4; + break; + case MODE_TEXT: + keep_lines=7; // CFS: can be 7 to 15 according to the handbook. No idea how this is selected. + break; + default: // Shouldn't happen + return 0; + break; + } + if (use_buffer->row_used[0]) // If top line is used it will go off the screen no matter what + return 1; + int rows_orig=0; // Number of rows in use right now + for (int i=0;i<15;i++) + { + if (use_buffer->row_used[i]) + { + rows_orig++; + if (firstrow==-1) + firstrow=i; + lastrow=i; + } + } + if (lastrow==-1) // Empty screen, nothing to rollup + return 0; + if ((lastrow-firstrow+1)>=keep_lines) + return 1; // We have the roll-up area full, so yes + + if ((firstrow-1)<=wb->data608->cursor_row-keep_lines) // Roll up will delete those lines. + return 1; + return 0; +} + +// Roll-up: Returns true if a line was rolled over the visible area (it dissapears from screen), false +// if the rollup didn't delete any line. +int roll_up(struct ccx_s_write *wb) +{ + eia608_screen *use_buffer; + if (wb->data608->visible_buffer==1) + use_buffer = &wb->data608->buffer1; + else + use_buffer = &wb->data608->buffer2; + int keep_lines; + switch (wb->data608->mode) + { + case MODE_FAKE_ROLLUP_1: + keep_lines=1; + break; + case MODE_ROLLUP_2: + keep_lines=2; + break; + case MODE_ROLLUP_3: + keep_lines=3; + break; + case MODE_ROLLUP_4: + keep_lines=4; + break; + case MODE_TEXT: + keep_lines=7; // CFS: can be 7 to 15 according to the handbook. No idea how this is selected. + break; + default: // Shouldn't happen + keep_lines=0; + break; + } + int firstrow=-1, lastrow=-1; + // Look for the last line used + int rows_orig=0; // Number of rows in use right now + for (int i=0;i<15;i++) + { + if (use_buffer->row_used[i]) + { + rows_orig++; + if (firstrow==-1) + firstrow=i; + lastrow=i; + } + } + + dbg_print(CCX_DMT_608, "\rIn roll-up: %d lines used, first: %d, last: %d\n", rows_orig, firstrow, lastrow); + + if (lastrow==-1) // Empty screen, nothing to rollup + return 0; + + for (int j=lastrow-keep_lines+1;j=0) + { + memcpy (use_buffer->characters[j],use_buffer->characters[j+1],CC608_SCREEN_WIDTH+1); + memcpy (use_buffer->colors[j],use_buffer->colors[j+1],CC608_SCREEN_WIDTH+1); + memcpy (use_buffer->fonts[j],use_buffer->fonts[j+1],CC608_SCREEN_WIDTH+1); + use_buffer->row_used[j]=use_buffer->row_used[j+1]; + } + } + for (int j=0;j<(1+wb->data608->cursor_row-keep_lines);j++) + { + memset(use_buffer->characters[j],' ',CC608_SCREEN_WIDTH); + memset(use_buffer->colors[j],ccx_options.cc608_default_color,CC608_SCREEN_WIDTH); + memset(use_buffer->fonts[j],FONT_REGULAR,CC608_SCREEN_WIDTH); + use_buffer->characters[j][CC608_SCREEN_WIDTH]=0; + use_buffer->row_used[j]=0; + } + memset(use_buffer->characters[lastrow],' ',CC608_SCREEN_WIDTH); + memset(use_buffer->colors[lastrow],ccx_options.cc608_default_color,CC608_SCREEN_WIDTH); + memset(use_buffer->fonts[lastrow],FONT_REGULAR,CC608_SCREEN_WIDTH); + + use_buffer->characters[lastrow][CC608_SCREEN_WIDTH]=0; + use_buffer->row_used[lastrow]=0; + + // Sanity check + int rows_now=0; + for (int i=0;i<15;i++) + if (use_buffer->row_used[i]) + rows_now++; + if (rows_now>keep_lines) + mprint ("Bug in roll_up, should have %d lines but I have %d.\n", + keep_lines, rows_now); + + // If the buffer is now empty, let's set the flag + // This will allow write_char to set visible start time appropriately + if (0 == rows_now) + use_buffer->empty = 1; + + return (rows_now != rows_orig); +} + +void erase_memory (struct ccx_s_write *wb, int displayed) +{ + eia608_screen *buf; + if (displayed) + { + if (wb->data608->visible_buffer==1) + buf=&wb->data608->buffer1; + else + buf=&wb->data608->buffer2; + } + else + { + if (wb->data608->visible_buffer==1) + buf=&wb->data608->buffer2; + else + buf=&wb->data608->buffer1; + } + clear_eia608_cc_buffer (buf); +} + +int is_current_row_empty (struct ccx_s_write *wb) +{ + eia608_screen *use_buffer; + if (wb->data608->visible_buffer==1) + use_buffer = &wb->data608->buffer1; + else + use_buffer = &wb->data608->buffer2; + for (int i=0;icharacters[wb->data608->rollup_base_row][i]!=' ') + return 0; + } + return 1; +} + +/* Process GLOBAL CODES */ +void handle_command (/*const */ unsigned char c1, const unsigned char c2, struct ccx_s_write *wb) +{ + int changes=0; + + // Handle channel change + wb->data608->channel=wb->data608->new_channel; + if (wb->data608->channel!=ccx_options.cc_channel) + return; + + command_code command = COM_UNKNOWN; + if (c1==0x15) + c1=0x14; + if ((c1==0x14 || c1==0x1C) && c2==0x2C) + command = COM_ERASEDISPLAYEDMEMORY; + if ((c1==0x14 || c1==0x1C) && c2==0x20) + command = COM_RESUMECAPTIONLOADING; + if ((c1==0x14 || c1==0x1C) && c2==0x2F) + command = COM_ENDOFCAPTION; + if ((c1==0x14 || c1==0x1C) && c2==0x22) + command = COM_ALARMOFF; + if ((c1==0x14 || c1==0x1C) && c2==0x23) + command = COM_ALARMON; + if ((c1==0x14 || c1==0x1C) && c2==0x24) + command = COM_DELETETOENDOFROW; + if ((c1==0x17 || c1==0x1F) && c2==0x21) + command = COM_TABOFFSET1; + if ((c1==0x17 || c1==0x1F) && c2==0x22) + command = COM_TABOFFSET2; + if ((c1==0x17 || c1==0x1F) && c2==0x23) + command = COM_TABOFFSET3; + if ((c1==0x14 || c1==0x1C) && c2==0x25) + command = COM_ROLLUP2; + if ((c1==0x14 || c1==0x1C) && c2==0x26) + command = COM_ROLLUP3; + if ((c1==0x14 || c1==0x1C) && c2==0x27) + command = COM_ROLLUP4; + if ((c1==0x14 || c1==0x1C) && c2==0x29) + command = COM_RESUMEDIRECTCAPTIONING; + if ((c1==0x14 || c1==0x1C) && c2==0x2D) + command = COM_CARRIAGERETURN; + if ((c1==0x14 || c1==0x1C) && c2==0x2E) + command = COM_ERASENONDISPLAYEDMEMORY; + if ((c1==0x14 || c1==0x1C) && c2==0x21) + command = COM_BACKSPACE; + if ((c1==0x14 || c1==0x1C) && c2==0x2b) + command = COM_RESUMETEXTDISPLAY; + + if ((command == COM_ROLLUP2 || command == COM_ROLLUP3 || command==COM_ROLLUP4) && ccx_options.forced_ru==1) + command=COM_FAKE_RULLUP1; + + if ((command == COM_ROLLUP3 || command==COM_ROLLUP4) && ccx_options.forced_ru==2) + command=COM_ROLLUP2; + else if (command==COM_ROLLUP4 && ccx_options.forced_ru==3) + command=COM_ROLLUP3; + + dbg_print(CCX_DMT_608, "\rCommand begin: %02X %02X (%s)\n",c1,c2,command_type[command]); + dbg_print(CCX_DMT_608, "\rCurrent mode: %d Position: %d,%d VisBuf: %d\n",wb->data608->mode, + wb->data608->cursor_row,wb->data608->cursor_column, wb->data608->visible_buffer); + + switch (command) + { + case COM_BACKSPACE: + if (wb->data608->cursor_column>0) + { + wb->data608->cursor_column--; + get_writing_buffer(wb)->characters[wb->data608->cursor_row][wb->data608->cursor_column]=' '; + } + break; + case COM_TABOFFSET1: + if (wb->data608->cursor_column<31) + wb->data608->cursor_column++; + break; + case COM_TABOFFSET2: + wb->data608->cursor_column+=2; + if (wb->data608->cursor_column>31) + wb->data608->cursor_column=31; + break; + case COM_TABOFFSET3: + wb->data608->cursor_column+=3; + if (wb->data608->cursor_column>31) + wb->data608->cursor_column=31; + break; + case COM_RESUMECAPTIONLOADING: + wb->data608->mode=MODE_POPON; + // TODO: Is this right? + // Needed for 2013-03-23_0100_US_MSNBC_The_Rachel_Maddow_Show.mpg + // But I can't find anything in the specs about RCL requiring a + // visible buffer switch + // The currently *visible* buffer is leaving, so now we know its ending + // time. Time to actually write it to file. + /* + if (write_cc_buffer (wb)) + wb->data608->screenfuls_counter++; + wb->data608->visible_buffer = (wb->data608->visible_buffer==1) ? 2 : 1; + wb->data608->current_visible_start_ms=get_visible_start(); + wb->data608->cursor_column=0; + wb->data608->cursor_row=0; + wb->data608->color=default_color; + wb->data608->font=FONT_REGULAR; */ + + break; + case COM_RESUMETEXTDISPLAY: + wb->data608->mode=MODE_TEXT; + break; + case COM_FAKE_RULLUP1: + case COM_ROLLUP2: + case COM_ROLLUP3: + case COM_ROLLUP4: + if (wb->data608->mode==MODE_POPON || wb->data608->mode==MODE_PAINTON) + { + /* CEA-608 C.10 Style Switching (regulatory) + [...]if pop-up or paint-on captioning is already present in + either memory it shall be erased[...] */ + if (write_cc_buffer (wb)) + wb->data608->screenfuls_counter++; + erase_memory (wb, true); + } + erase_memory (wb, false); + + // If the reception of data for a row is interrupted by data for the alternate + // data channel or for text mode, the display of caption text will resume from the same + // cursor position if a roll-up caption command is received and no PAC is given [...] + if (wb->data608->mode==MODE_TEXT) + { + wb->data608->cursor_row=14; // Default if the previous mode wasn't roll up already. + wb->data608->cursor_column=0; + } + else + { + // Cursor position only reset if not already in rollup. + // If no Preamble Address Code is received, the base row shall default to + // Row 15 or, ifa roll-up caption is currently displayed, to the same + //base row last received, and the cursor shall be placed atColumn 1. + //" This rule is meant to be applied only when no roll-up caption is + // currently displayed, + if (wb->data608->mode!=MODE_FAKE_ROLLUP_1 && + wb->data608->mode!=MODE_ROLLUP_2 && + wb->data608->mode!=MODE_ROLLUP_3 && + wb->data608->mode==MODE_ROLLUP_4) + { + wb->data608->cursor_row=wb->data608->rollup_base_row; + wb->data608->cursor_column=0; + } + } + switch (command) + { + case COM_FAKE_RULLUP1: + wb->data608->mode=MODE_FAKE_ROLLUP_1; + break; + case COM_ROLLUP2: + wb->data608->mode=MODE_ROLLUP_2; + break; + case COM_ROLLUP3: + wb->data608->mode=MODE_ROLLUP_3; + break; + case COM_ROLLUP4: + wb->data608->mode=MODE_ROLLUP_4; + break; + default: // Impossible, but remove compiler warnings + break; + } + break; + case COM_CARRIAGERETURN: + if (wb->data608->mode==MODE_PAINTON) // CR has no effect on painton mode according to zvbis' code + break; + if (wb->data608->mode==MODE_POPON) // CFS: Not sure about this. Is there a valid reason for CR in popup? + { + wb->data608->cursor_column=0; + if (wb->data608->cursor_row<15) + wb->data608->cursor_row++; + break; + } + if (ccx_options.write_format==CCX_OF_TRANSCRIPT) + { + write_cc_line_as_transcript(get_current_visible_buffer (wb), wb, wb->data608->cursor_row); + } + + // In transcript mode, CR doesn't write the whole screen, to avoid + // repeated lines. + changes=check_roll_up (wb); + if (changes) + { + // Only if the roll up would actually cause a line to disappear we write the buffer + if (ccx_options.write_format!=CCX_OF_TRANSCRIPT) + { + if (write_cc_buffer(wb)) + wb->data608->screenfuls_counter++; + if (ccx_options.norollup) + erase_memory (wb,true); // Make sure the lines we just wrote aren't written again + } + } + roll_up(wb); // The roll must be done anyway of course. + wb->data608->ts_start_of_current_line = -1; // Unknown. + if (changes) + wb->data608->current_visible_start_ms=get_visible_start(); + wb->data608->cursor_column=0; + break; + case COM_ERASENONDISPLAYEDMEMORY: + erase_memory (wb,false); + break; + case COM_ERASEDISPLAYEDMEMORY: + // Write it to disk before doing this, and make a note of the new + // time it became clear. + if (ccx_options.write_format==CCX_OF_TRANSCRIPT && + (wb->data608->mode==MODE_FAKE_ROLLUP_1 || + wb->data608->mode==MODE_ROLLUP_2 || wb->data608->mode==MODE_ROLLUP_3 || + wb->data608->mode==MODE_ROLLUP_4)) + { + // In transcript mode we just write the cursor line. The previous lines + // should have been written already, so writing everything produces + // duplicate lines. + write_cc_line_as_transcript(get_current_visible_buffer (wb), wb, wb->data608->cursor_row); + } + else + { + if (write_cc_buffer (wb)) + wb->data608->screenfuls_counter++; + } + erase_memory (wb,true); + wb->data608->current_visible_start_ms=get_visible_start(); + break; + case COM_ENDOFCAPTION: // Switch buffers + // The currently *visible* buffer is leaving, so now we know its ending + // time. Time to actually write it to file. + if (write_cc_buffer (wb)) + wb->data608->screenfuls_counter++; + wb->data608->visible_buffer = (wb->data608->visible_buffer==1) ? 2 : 1; + wb->data608->current_visible_start_ms=get_visible_start(); + wb->data608->cursor_column=0; + wb->data608->cursor_row=0; + wb->data608->color=ccx_options.cc608_default_color; + wb->data608->font=FONT_REGULAR; + wb->data608->mode=MODE_POPON; + break; + case COM_DELETETOENDOFROW: + delete_to_end_of_row (wb); + break; + case COM_ALARMOFF: + case COM_ALARMON: + // These two are unused according to Robson's, and we wouldn't be able to do anything useful anyway + break; + case COM_RESUMEDIRECTCAPTIONING: + wb->data608->mode = MODE_PAINTON; + //mprint ("\nWarning: Received ResumeDirectCaptioning, this mode is almost impossible.\n"); + //mprint ("to transcribe to a text file.\n"); + break; + default: + dbg_print(CCX_DMT_608, "\rNot yet implemented.\n"); + break; + } + dbg_print(CCX_DMT_608, "\rCurrent mode: %d Position: %d,%d VisBuf: %d\n",wb->data608->mode, + wb->data608->cursor_row,wb->data608->cursor_column, wb->data608->visible_buffer); + dbg_print(CCX_DMT_608, "\rCommand end: %02X %02X (%s)\n",c1,c2,command_type[command]); + +} + +void handle_end_of_data (struct ccx_s_write *wb) +{ + // We issue a EraseDisplayedMemory here so if there's any captions pending + // they get written to file. + handle_command (0x14, 0x2c, wb); // EDM +} + +// CEA-608, Anex F 1.1.1. - Character Set Table / Special Characters +void handle_double (const unsigned char c1, const unsigned char c2, struct ccx_s_write *wb) +{ + unsigned char c; + if (wb->data608->channel!=ccx_options.cc_channel) + return; + if (c2>=0x30 && c2<=0x3f) + { + c=c2 + 0x50; // So if c>=0x80 && c<=0x8f, it comes from here + dbg_print(CCX_DMT_608, "\rDouble: %02X %02X --> %c\n",c1,c2,c); + write_char(c,wb); + } +} + +/* Process EXTENDED CHARACTERS */ +unsigned char handle_extended (unsigned char hi, unsigned char lo, struct ccx_s_write *wb) +{ + // Handle channel change + if (wb->data608->new_channel > 2) + { + wb->data608->new_channel -= 2; + dbg_print(CCX_DMT_608, "\nChannel correction, now %d\n", wb->data608->new_channel); + } + wb->data608->channel=wb->data608->new_channel; + if (wb->data608->channel!=ccx_options.cc_channel) + return 0; + + // For lo values between 0x20-0x3f + unsigned char c=0; + + dbg_print(CCX_DMT_608, "\rExtended: %02X %02X\n",hi,lo); + if (lo>=0x20 && lo<=0x3f && (hi==0x12 || hi==0x13)) + { + switch (hi) + { + case 0x12: + c=lo+0x70; // So if c>=0x90 && c<=0xaf it comes from here + break; + case 0x13: + c=lo+0x90; // So if c>=0xb0 && c<=0xcf it comes from here + break; + } + // This column change is because extended characters replace + // the previous character (which is sent for basic decoders + // to show something similar to the real char) + if (wb->data608->cursor_column>0) + wb->data608->cursor_column--; + + write_char (c,wb); + } + return 1; +} + +/* Process PREAMBLE ACCESS CODES (PAC) */ +void handle_pac (unsigned char c1, unsigned char c2, struct ccx_s_write *wb) +{ + // Handle channel change + if (wb->data608->new_channel > 2) + { + wb->data608->new_channel -= 2; + dbg_print(CCX_DMT_608, "\nChannel correction, now %d\n", wb->data608->new_channel); + } + wb->data608->channel=wb->data608->new_channel; + if (wb->data608->channel!=ccx_options.cc_channel) + return; + + int row=rowdata[((c1<<1)&14)|((c2>>5)&1)]; + + dbg_print(CCX_DMT_608, "\rPAC: %02X %02X",c1,c2); + + if (c2>=0x40 && c2<=0x5f) + { + c2=c2-0x40; + } + else + { + if (c2>=0x60 && c2<=0x7f) + { + c2=c2-0x60; + } + else + { + dbg_print(CCX_DMT_608, "\rThis is not a PAC!!!!!\n"); + return; + } + } + wb->data608->color=pac2_attribs[c2][0]; + wb->data608->font=pac2_attribs[c2][1]; + int indent=pac2_attribs[c2][2]; + dbg_print(CCX_DMT_608, " -- Position: %d:%d, color: %s, font: %s\n",row, + indent,color_text[wb->data608->color][0],font_text[wb->data608->font]); + if (ccx_options.cc608_default_color==COL_USERDEFINED && (wb->data608->color==COL_WHITE || wb->data608->color==COL_TRANSPARENT)) + wb->data608->color=COL_USERDEFINED; + if (wb->data608->mode!=MODE_TEXT) + { + // According to Robson, row info is discarded in text mode + // but column is accepted + wb->data608->cursor_row=row-1 ; // Since the array is 0 based + } + wb->data608->rollup_base_row=row-1; + wb->data608->cursor_column=indent; + if (wb->data608->mode==MODE_FAKE_ROLLUP_1 || wb->data608->mode==MODE_ROLLUP_2 || + wb->data608->mode==MODE_ROLLUP_3 || wb->data608->mode==MODE_ROLLUP_4) + { + /* In roll-up, delete lines BELOW the PAC. Not sure (CFS) this is correct (possibly we may have to move the + buffer around instead) but it's better than leaving old characters in the buffer */ + eia608_screen *use_buffer = get_writing_buffer (wb); // &wb->data608->buffer1; + + for (int j=row;j<15;j++) + { + if (use_buffer->row_used[j]) + { + memset(use_buffer->characters[j],' ',CC608_SCREEN_WIDTH); + memset(use_buffer->colors[j],ccx_options.cc608_default_color,CC608_SCREEN_WIDTH); + memset(use_buffer->fonts[j],FONT_REGULAR,CC608_SCREEN_WIDTH); + use_buffer->characters[j][CC608_SCREEN_WIDTH]=0; + use_buffer->row_used[j]=0; + } + } + } + +} + + +void handle_single (const unsigned char c1, struct ccx_s_write *wb) +{ + if (c1<0x20 || wb->data608->channel!=ccx_options.cc_channel) + return; // We don't allow special stuff here + dbg_print(CCX_DMT_608, "%c",c1); + + write_char (c1,wb); +} + +void erase_both_memories (struct ccx_s_write *wb) +{ + erase_memory (wb,false); + // For the visible memory, we write the contents to disk + // The currently *visible* buffer is leaving, so now we know its ending + // time. Time to actually write it to file. + if (write_cc_buffer (wb)) + wb->data608->screenfuls_counter++; + wb->data608->current_visible_start_ms=get_visible_start(); + wb->data608->cursor_column=0; + wb->data608->cursor_row=0; + wb->data608->color=ccx_options.cc608_default_color; + wb->data608->font=FONT_REGULAR; + + erase_memory (wb,true); +} + +int check_channel (unsigned char c1, struct ccx_s_write *wb) +{ + int newchan=wb->data608->channel; + if (c1>=0x10 && c1<=0x17) + newchan=1; + else if (c1>=0x18 && c1<=0x1e) + newchan=2; + if (newchan!=wb->data608->channel) + { + dbg_print(CCX_DMT_608, "\nChannel change, now %d\n", newchan); + if (wb->data608->channel!=3) // Don't delete memories if returning from XDS. + { + // erase_both_memories (wb); // 47cfr15.119.pdf, page 859, part f + // CFS: Removed this because the specs say memories should be deleted if THE USER + // changes the channel. + } + } + return newchan; +} + +/* Handle Command, special char or attribute and also check for +* channel changes. +* Returns 1 if something was written to screen, 0 otherwise */ +int disCommand (unsigned char hi, unsigned char lo, struct ccx_s_write *wb) +{ + int wrote_to_screen=0; + + /* Full channel changes are only allowed for "GLOBAL CODES", + * "OTHER POSITIONING CODES", "BACKGROUND COLOR CODES", + * "MID-ROW CODES". + * "PREAMBLE ACCESS CODES", "BACKGROUND COLOR CODES" and + * SPECIAL/SPECIAL CHARACTERS allow only switching + * between 1&3 or 2&4. */ + wb->data608->new_channel = check_channel (hi,wb); + //if (wb->data608->channel!=cc_channel) + // continue; + + if (hi>=0x18 && hi<=0x1f) + hi=hi-8; + + switch (hi) + { + case 0x10: + if (lo>=0x40 && lo<=0x5f) + handle_pac (hi,lo,wb); + break; + case 0x11: + if (lo>=0x20 && lo<=0x2f) + handle_text_attr (hi,lo,wb); + if (lo>=0x30 && lo<=0x3f) + { + wrote_to_screen=1; + handle_double (hi,lo,wb); + } + if (lo>=0x40 && lo<=0x7f) + handle_pac (hi,lo,wb); + break; + case 0x12: + case 0x13: + if (lo>=0x20 && lo<=0x3f) + { + wrote_to_screen=handle_extended (hi,lo,wb); + } + if (lo>=0x40 && lo<=0x7f) + handle_pac (hi,lo,wb); + break; + case 0x14: + case 0x15: + if (lo>=0x20 && lo<=0x2f) + handle_command (hi,lo,wb); + if (lo>=0x40 && lo<=0x7f) + handle_pac (hi,lo,wb); + break; + case 0x16: + if (lo>=0x40 && lo<=0x7f) + handle_pac (hi,lo,wb); + break; + case 0x17: + if (lo>=0x21 && lo<=0x23) + handle_command (hi,lo,wb); + if (lo>=0x2e && lo<=0x2f) + handle_text_attr (hi,lo,wb); + if (lo>=0x40 && lo<=0x7f) + handle_pac (hi,lo,wb); + break; + } + return wrote_to_screen; +} + +/* If wb is NULL, then only XDS will be processed */ +void process608 (const unsigned char *data, int length, struct ccx_s_write *wb) +{ + static int textprinted = 0; + if (wb) + wb->bytes_processed_608+=length; + if (data!=NULL) + { + for (int i=0;i=0x01 && hi<=0x0E && (wb==NULL || wb->my_field==2)) // XDS can only exist in field 2. + { + if (wb) + wb->data608->channel=3; + if (!in_xds_mode) + { + ts_start_of_xds=get_fts(); + in_xds_mode=1; + } + } + if (hi==0x0F && in_xds_mode && (wb==NULL || wb->my_field==2)) // End of XDS block + { + in_xds_mode=0; + do_end_of_xds (lo); + if (wb) + wb->data608->channel=wb->data608->new_channel; // Switch from channel 3 + continue; + } + if (hi>=0x10 && hi<=0x1F) // Non-character code or special/extended char + // http://www.geocities.com/mcpoodle43/SCC_TOOLS/DOCS/CC_CODES.HTML + // http://www.geocities.com/mcpoodle43/SCC_TOOLS/DOCS/CC_CHARS.HTML + { + // We were writing characters before, start a new line for + // diagnostic output from disCommand() + if (textprinted == 1 ) + { + dbg_print(CCX_DMT_608, "\n"); + textprinted = 0; + } + if (!wb || wb->my_field==2) + in_xds_mode=0; // Back to normal (CEA 608-8.6.2) + if (!wb) // Not XDS and we don't have a writebuffer, nothing else would have an effect + continue; + if (wb->data608->last_c1==hi && wb->data608->last_c2==lo) + { + // Duplicate dual code, discard. Correct to do it only in + // non-XDS, XDS codes shall not be repeated. + dbg_print(CCX_DMT_608, "Skipping command %02X,%02X Duplicate\n", hi, lo); + // Ignore only the first repetition + wb->data608->last_c1=-1; + wb->data608->last_c2=-1; + continue; + } + wb->data608->last_c1=hi; + wb->data608->last_c2=lo; + wrote_to_screen=disCommand (hi,lo,wb); + } + else + { + if (in_xds_mode && (wb==NULL || wb->my_field==2 )) + { + process_xds_bytes (hi,lo); + continue; + } + if (!wb) // No XDS code after this point, and user doesn't want captions. + continue; + + wb->data608->last_c1=-1; + wb->data608->last_c2=-1; + + if (hi>=0x20) // Standard characters (always in pairs) + { + // Only print if the channel is active + if (wb->data608->channel!=ccx_options.cc_channel) + continue; + + if( textprinted == 0 ) + { + dbg_print(CCX_DMT_608, "\n"); + textprinted = 1; + } + + handle_single(hi,wb); + handle_single(lo,wb); + wrote_to_screen=1; + wb->data608->last_c1=0; + wb->data608->last_c2=0; + } + + if (!textprinted && wb->data608->channel==ccx_options.cc_channel ) + { // Current FTS information after the characters are shown + dbg_print(CCX_DMT_608, "Current FTS: %s\n", print_mstime(get_fts())); + //printf(" N:%u", unsigned(fts_now) ); + //printf(" G:%u", unsigned(fts_global) ); + //printf(" F:%d %d %d %d\n", + // current_field, cb_field1, cb_field2, cb_708 ); + } + + if (wrote_to_screen && ccx_options.direct_rollup && // If direct_rollup is enabled and + (wb->data608->mode==MODE_FAKE_ROLLUP_1 || // we are in rollup mode, write now. + wb->data608->mode==MODE_ROLLUP_2 || + wb->data608->mode==MODE_ROLLUP_3 || + wb->data608->mode==MODE_ROLLUP_4)) + { + // We don't increase screenfuls_counter here. + write_cc_buffer (wb); + wb->data608->current_visible_start_ms=get_visible_start(); + } + } + if (wrote_to_screen && cc_to_stdout) + fflush (stdout); + } // for + } +} + + +/* Return a pointer to a string that holds the printable characters + * of the caption data block. FOR DEBUG PURPOSES ONLY! */ +unsigned char *debug_608toASC (unsigned char *cc_data, int channel) +{ + static unsigned char output[3]; + + unsigned char cc_valid = (cc_data[0] & 4) >>2; + unsigned char cc_type = cc_data[0] & 3; + unsigned char hi, lo; + + output[0]=' '; + output[1]=' '; + output[2]='\x00'; + + if (cc_valid && cc_type==channel) + { + hi = cc_data[1] & 0x7F; // Get rid of parity bit + lo = cc_data[2] & 0x7F; // Get rid of parity bit + if (hi>=0x20) + { + output[0]=hi; + output[1]=(lo>=20 ? lo : '.'); + output[2]='\x00'; + } + else + { + output[0]='<'; + output[1]='>'; + output[2]='\x00'; + } + } + return output; +} diff --git a/src/608.h b/src/608.h new file mode 100644 index 00000000..ede29ea3 --- /dev/null +++ b/src/608.h @@ -0,0 +1,188 @@ +#ifndef __608_H__ + +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 ccx_s_write *wb); +void write_stringz_as_srt (char *string, struct ccx_s_write *wb, LLONG ms_start, LLONG ms_end); +void mstotime (LLONG milli, unsigned *hours, unsigned *minutes, + unsigned *seconds, unsigned *ms); +void mstotime (LLONG milli, unsigned *hours, unsigned *minutes, + unsigned *seconds, unsigned *ms); +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 ccx_s_write *wb); +void write_stringz_as_sami (char *string, struct ccx_s_write *wb, LLONG ms_start, LLONG ms_end); +int write_cc_buffer_as_smptett (struct eia608_screen *data, struct ccx_s_write *wb); +void write_stringz_as_smptett (char *string, struct ccx_s_write *wb, LLONG ms_start, LLONG ms_end); +unsigned encode_line (unsigned char *buffer, unsigned char *text); +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 ccx_s_write *wb); +void try_to_add_end_credits (struct ccx_s_write *wb); +void write_cc_buffer_to_gui (struct eia608_screen *data, struct ccx_s_write *wb); + +void handle_end_of_data (struct ccx_s_write *wb); +void process608 (const unsigned char *data, int length, struct ccx_s_write *wb); +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); + +void write_spumux_header(struct ccx_s_write* wb); +void write_spumux_footer(struct ccx_s_write* wb); +int write_cc_buffer_as_spupng (struct eia608_screen* data, struct ccx_s_write* wb); + +#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 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 +}; + +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 +}; + + +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 eia608 +{ + eia608_screen buffer1; + 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? + 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 +}; + + + +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 +}; + + +const unsigned char pac2_attribs[][3]= // Color, font, ident +{ + {COL_WHITE, FONT_REGULAR, 0}, // 0x40 || 0x60 + {COL_WHITE, FONT_UNDERLINED, 0}, // 0x41 || 0x61 + {COL_GREEN, FONT_REGULAR, 0}, // 0x42 || 0x62 + {COL_GREEN, FONT_UNDERLINED, 0}, // 0x43 || 0x63 + {COL_BLUE, FONT_REGULAR, 0}, // 0x44 || 0x64 + {COL_BLUE, FONT_UNDERLINED, 0}, // 0x45 || 0x65 + {COL_CYAN, FONT_REGULAR, 0}, // 0x46 || 0x66 + {COL_CYAN, FONT_UNDERLINED, 0}, // 0x47 || 0x67 + {COL_RED, FONT_REGULAR, 0}, // 0x48 || 0x68 + {COL_RED, FONT_UNDERLINED, 0}, // 0x49 || 0x69 + {COL_YELLOW, FONT_REGULAR, 0}, // 0x4a || 0x6a + {COL_YELLOW, FONT_UNDERLINED, 0}, // 0x4b || 0x6b + {COL_MAGENTA, FONT_REGULAR, 0}, // 0x4c || 0x6c + {COL_MAGENTA, FONT_UNDERLINED, 0}, // 0x4d || 0x6d + {COL_WHITE, FONT_ITALICS, 0}, // 0x4e || 0x6e + {COL_WHITE, FONT_UNDERLINED_ITALICS, 0}, // 0x4f || 0x6f + {COL_WHITE, FONT_REGULAR, 0}, // 0x50 || 0x70 + {COL_WHITE, FONT_UNDERLINED, 0}, // 0x51 || 0x71 + {COL_WHITE, FONT_REGULAR, 4}, // 0x52 || 0x72 + {COL_WHITE, FONT_UNDERLINED, 4}, // 0x53 || 0x73 + {COL_WHITE, FONT_REGULAR, 8}, // 0x54 || 0x74 + {COL_WHITE, FONT_UNDERLINED, 8}, // 0x55 || 0x75 + {COL_WHITE, FONT_REGULAR, 12}, // 0x56 || 0x76 + {COL_WHITE, FONT_UNDERLINED, 12}, // 0x57 || 0x77 + {COL_WHITE, FONT_REGULAR, 16}, // 0x58 || 0x78 + {COL_WHITE, FONT_UNDERLINED, 16}, // 0x59 || 0x79 + {COL_WHITE, FONT_REGULAR, 20}, // 0x5a || 0x7a + {COL_WHITE, FONT_UNDERLINED, 20}, // 0x5b || 0x7b + {COL_WHITE, FONT_REGULAR, 24}, // 0x5c || 0x7c + {COL_WHITE, FONT_UNDERLINED, 24}, // 0x5d || 0x7d + {COL_WHITE, FONT_REGULAR, 28}, // 0x5e || 0x7e + {COL_WHITE, FONT_UNDERLINED, 28} // 0x5f || 0x7f +}; + +#define __608_H__ +#endif diff --git a/src/608_helpers.cpp b/src/608_helpers.cpp new file mode 100644 index 00000000..a3c5cfbf --- /dev/null +++ b/src/608_helpers.cpp @@ -0,0 +1,441 @@ +#include "ccextractor.h" + +// 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; +} + +#define ISSEPARATOR(c) (c==' ' || c==0x89 || ispunct(c) \ + || c==0x99) // This is the apostrofe. We get it here in CC encoding, not ASCII + + +void correct_case (int line_num, struct eia608_screen *data) +{ + int i=0; + while (icharacters[line_num]; + size_t len=strlen (spell_correct[i]); + while ((c=strstr (c,spell_lower[i]))!=NULL) + { + // Make sure it's a whole word (start of line or + // preceded by space, and end of line or followed by + // space) + unsigned char prev; + if (c==(char *) data->characters[line_num]) // Beginning of line... + prev=' '; // ...Pretend we had a blank before + else + prev=*(c-1); + unsigned char next; + if (c-(char *) data->characters[line_num]+len==CC608_SCREEN_WIDTH) // End of line... + next=' '; // ... pretend we have a blank later + else + next=*(c+len); + if ( ISSEPARATOR(prev) && ISSEPARATOR(next)) + { + memcpy (c,spell_correct[i],len); + } + c++; + } + i++; + } +} + +void capitalize (int line_num, struct eia608_screen *data) +{ + for (int i=0;icharacters[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;icharacters[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 *) ""); + (*pchanged_font)--; + break; + case 'U': + buffer+=encode_line (buffer, (unsigned char *) ""); + (*punderlined)--; + break; + case 'I': + buffer+=encode_line (buffer, (unsigned char *) ""); + (*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 + // 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 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 *) ""); + 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 *) ""); + 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 ccx_s_write *wb) +{ + unsigned h1,m1,s1,ms1; + unsigned h2,m2,s2,ms2; + int with_data=0; + + for (int i=0;i<15;i++) + { + if (data->row_used[i]) + with_data=1; + } + if (!with_data) + return; + + LLONG ms_start= wb->data608->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 ccx_s_write *wb) +{ + if (wb->fh==-1) + return; + LLONG window=get_fts()-last_displayed_subs_ms-1; + if (window window ? + window : ccx_options.endcreditsforatmost.time_in_ms; + + LLONG st=get_fts()-length-1; + LLONG end=get_fts(); + + switch (ccx_options.write_format) + { + case CCX_OF_SRT: + write_stringz_as_srt(ccx_options.end_credits_text,wb,st,end); + break; + case CCX_OF_SAMI: + write_stringz_as_sami(ccx_options.end_credits_text,wb,st,end); + break; + case CCX_OF_SMPTETT: + write_stringz_as_smptett(ccx_options.end_credits_text,wb,st,end); + break ; + default: + // Do nothing for the rest + break; + } +} + +void try_to_add_start_credits (struct ccx_s_write *wb) +{ + LLONG l = wb->data608->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.startcreditsnotafter.time_in_ms) // Too late + return; + + LLONG 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 + + LLONG end = ccx_options.startcreditsnotafter.time_in_ms<(l-1) ? + ccx_options.startcreditsnotafter.time_in_ms : (l-1); + + LLONG window = end-st; // Allowable time in MS + + if (ccx_options.startcreditsforatleast.time_in_ms>window) // Window is too short + return; + + LLONG 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,wb,st,end); + break; + case CCX_OF_SAMI: + write_stringz_as_sami(ccx_options.start_credits_text,wb,st,end); + break; + case CCX_OF_SMPTETT: + write_stringz_as_smptett(ccx_options.start_credits_text,wb,st,end); + break; + default: + // Do nothing for the rest + break; + } + startcredits_displayed=1; + return; + + +} diff --git a/src/608_sami.cpp b/src/608_sami.cpp new file mode 100644 index 00000000..452e9195 --- /dev/null +++ b/src/608_sami.cpp @@ -0,0 +1,120 @@ +#include "ccextractor.h" + +void write_stringz_as_sami (char *string, struct ccx_s_write *wb, LLONG ms_start, LLONG ms_end) +{ + sprintf ((char *) str,"

\r\n",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 (wb->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_rfh, el, u); + write (wb->fh, encoded_br, encoded_br_length); + + write (wb->fh, encoded_crlf, encoded_crlf_length); + begin+= strlen ((const char *) begin)+1; + } + + sprintf ((char *) str,"

\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 (wb->fh, enc_buffer,enc_buffer_used); + sprintf ((char *) str,"

 

\r\n\r\n",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 (wb->fh, enc_buffer,enc_buffer_used); +} + + + +int write_cc_buffer_as_sami (struct eia608_screen *data, struct ccx_s_write *wb) +{ + int wrote_something=0; + LLONG startms = wb->data608->current_visible_start_ms; + + startms+=subs_delay; + if (startms<0) // Drop screens that because of subs_delay start too early + return 0; + + LLONG endms = get_visible_end()+subs_delay; + endms--; // To prevent overlapping with next line. + sprintf ((char *) str,"

\r\n",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 (wb->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 (wb->fh, subline, length); + wrote_something=1; + if (i!=14) + write (wb->fh, encoded_br, encoded_br_length); + write (wb->fh,encoded_crlf, encoded_crlf_length); + } + } + sprintf ((char *) str,"

\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 (wb->fh, enc_buffer,enc_buffer_used); + sprintf ((char *) str,"

 

\r\n\r\n",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 (wb->fh, enc_buffer,enc_buffer_used); + return wrote_something; +} diff --git a/src/608_smptett.cpp b/src/608_smptett.cpp new file mode 100644 index 00000000..2b898f4a --- /dev/null +++ b/src/608_smptett.cpp @@ -0,0 +1,155 @@ +#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 ccx_s_write *wb, 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,"

\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 (wb->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_rfh, el, u); + //write (wb->fh, encoded_br, encoded_br_length); + + write (wb->fh, encoded_crlf, encoded_crlf_length); + begin+= strlen ((const char *) begin)+1; + } + + sprintf ((char *) str,"

\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 (wb->fh, enc_buffer,enc_buffer_used); + sprintf ((char *) str,"

\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 (wb->fh, enc_buffer,enc_buffer_used); + sprintf ((char *) str,"

\n"); +} + + + +int write_cc_buffer_as_smptett (struct eia608_screen *data, struct ccx_s_write *wb) +{ + unsigned h1,m1,s1,ms1; + unsigned h2,m2,s2,ms2; + int wrote_something=0; + LLONG startms = wb->data608->current_visible_start_ms; + + startms+=subs_delay; + if (startms<0) // Drop screens that because of subs_delay start too early + return 0; + + LLONG 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,"

\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 (wb->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 (wb->fh, subline, length); + wrote_something=1; + + write (wb->fh,encoded_crlf, encoded_crlf_length); + } + } + sprintf ((char *) str,"

\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 (wb->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; +} diff --git a/src/608_spupng.cpp b/src/608_spupng.cpp new file mode 100644 index 00000000..b5e7886d --- /dev/null +++ b/src/608_spupng.cpp @@ -0,0 +1,604 @@ +#include +#include + +#ifdef _WIN32 +#include +#define mkdir(path, mode) _mkdir(path) +#endif + +#include "608_spupng.h" + +// #include "wstfont2.xbm" // Teletext font, not used +#include "ccfont2.xbm" // CC font from libzvbi + +// CC page dimensions +#define ROWS 15 +#define COLUMNS 32 + +/* Closed Caption character cell dimensions */ +#define CCW 16 +#define CCH 26 /* line doubled */ +#define CCPL (ccfont2_width / CCW * ccfont2_height / CCH) + +void +write_spumux_header(struct ccx_s_write *wb) +{ + if (0 == wb->spupng_data) + wb->spupng_data = new SpuPng(wb); + ((SpuPng*)wb->spupng_data) -> writeHeader(); +} + +void +write_spumux_footer(struct ccx_s_write *wb) +{ + if (0 != wb->spupng_data) + { + ((SpuPng*)wb->spupng_data) -> writeFooter(); + delete (SpuPng*)wb->spupng_data; + wb->spupng_data = 0; + wb->fh = -1; + } +} + +int +write_cc_buffer_as_spupng(struct eia608_screen *data, struct ccx_s_write *wb) +{ + if (0 != wb->spupng_data) + { + return ((SpuPng*)wb->spupng_data) -> writeCCBuffer(data, wb); + } + return 0; +} + +static int initialized = 0; + +SpuPng::SpuPng(struct ccx_s_write* wb) +{ + if (!initialized) + { + initialized = 1; + initFont(); + } + + if ((fpxml = fdopen(wb->fh, "w")) == NULL) + { + fatal(EXIT_FILE_CREATION_FAILED, "Cannot open %s: %s\n", wb->filename, strerror(errno)); + } + dirname = new char [strlen(wb->filename) + 3]; + strcpy(dirname, wb->filename); + char* p = strrchr(dirname, '.'); + if (0 == p) + p = dirname + strlen(dirname); + *p = '\0'; + strcat(dirname, ".d"); + if (mkdir(dirname, 0777) != 0) + { + if (errno != EEXIST) + { + fatal(EXIT_FILE_CREATION_FAILED, "Cannot create %s: %s\n", dirname, strerror(errno)); + } + // If dirname isn't a directory or if we don't have write permission, + // the first attempt to create a .png file will fail and we'll exit. + } + + // enough to append /subNNNN.png + pngfile = new char [ strlen(dirname) + 13 ]; + fileIndex = 0; + + // For NTSC closed captions and 720x480 DVD subtitle resolution: + // Each character is 16x26. + // 15 rows by 32 columns, plus 2 columns for left & right padding + // So each .png image will be 16*34 wide and 26*15 high, or 544x390 + // To center image in 720x480 DVD screen, offset image by 88 and 45 + // Need to keep yOffset even to prevent flicker on interlaced displays + // Would need to do something different for PAL format and teletext. + xOffset = 88; + yOffset = 46; +} + +SpuPng::~SpuPng() +{ + delete [] dirname; + delete [] pngfile; +} + + +void +SpuPng::writeHeader() +{ + fprintf(fpxml, "\n\n"); + if (num_input_files > 0) + fprintf(fpxml, "\n", inputfile[0]); +} + +void +SpuPng::writeFooter() +{ + fprintf(fpxml, "\n\n"); + fflush(fpxml); + fclose(fpxml); +} + +int +SpuPng::writeCCBuffer(struct eia608_screen* data, struct ccx_s_write *wb) +{ + LLONG ms_start = wb->data608->current_visible_start_ms + subs_delay; + if (ms_start < 0) + { + dbg_print(CCX_DMT_VERBOSE, "Negative start\n"); + return 0; + } + + int row; + int empty_buf = 1; + for (row = 0; row < 15; row++) + { + if (data->row_used[row]) + { + empty_buf = 0; + break; + } + } + if (empty_buf) + { + dbg_print(CCX_DMT_VERBOSE, "Blank page\n"); + return 0; + } + + LLONG ms_end=get_visible_end()+subs_delay; + + sprintf(pngfile, "%s/sub%04d.png", dirname, fileIndex++); + if ((fppng = fopen(pngfile, "wb")) == NULL) + { + fatal(EXIT_FILE_CREATION_FAILED, "Cannot open %s: %s\n", pngfile, strerror(errno)); + } + if (!exportPNG(data)) + { + fatal(EXIT_FILE_CREATION_FAILED, "Cannot write %s: %s\n", pngfile, strerror(errno)); + } + fclose(fppng); + + fprintf(fpxml, "\n\n"); + dbg_print(CCX_DMT_608, "-->\n"); + + fflush(fpxml); + + return 1; +} + +// +// Begin copy from http://zapping.cvs.sourceforge.net/viewvc/zapping/vbi/src/exp-gfx.c?view=markup&pathrev=zvbi-0-2-33 +// + +void +SpuPng::initFont(void) +{ + uint8_t *t, *p; + int i, j; + + /* de-interleave font image (puts all chars in row 0) */ +#if 0 + if (!(t = malloc(wstfont2_width * wstfont2_height / 8))) + exit(EXIT_FAILURE); + + for (p = t, i = 0; i < TCH; i++) + for (j = 0; j < wstfont2_height; p += wstfont2_width / 8, j += TCH) + memcpy(p, wstfont2_bits + (j + i) * wstfont2_width / 8, + wstfont2_width / 8); + + memcpy(wstfont2_bits, t, wstfont2_width * wstfont2_height / 8); + free (t); +#endif + if (!(t = (uint8_t*)malloc(ccfont2_width * ccfont2_height / 8))) + exit(EXIT_FAILURE); + + for (p = t, i = 0; i < CCH; i++) + for (j = 0; j < ccfont2_height; p += ccfont2_width / 8, j += CCH) + memcpy(p, ccfont2_bits + (j + i) * ccfont2_width / 8, + ccfont2_width / 8); + + memcpy(ccfont2_bits, t, ccfont2_width * ccfont2_height / 8); + free(t); +} + +/** + * @internal + * @param c Unicode. + * @param italic @c TRUE to switch to slanted character set. + * + * Translate Unicode character to glyph number in ccfont2 image. + * + * @return + * Glyph number. + */ +static unsigned int +unicode_ccfont2(unsigned int c, int italic) +{ + static const unsigned short specials[] = { + 0x00E1, 0x00E9, + 0x00ED, 0x00F3, 0x00FA, 0x00E7, 0x00F7, 0x00D1, 0x00F1, 0x25A0, + 0x00AE, 0x00B0, 0x00BD, 0x00BF, 0x2122, 0x00A2, 0x00A3, 0x266A, + 0x00E0, 0x0020, 0x00E8, 0x00E2, 0x00EA, 0x00EE, 0x00F4, 0x00FB }; + unsigned int i; + + if (c < 0x0020) + c = 15; /* invalid */ + else if (c < 0x0080) + c = c; + else { + for (i = 0; i < sizeof(specials) / sizeof(specials[0]); i++) + if (specials[i] == c) { + c = i + 6; + goto slant; + } + + c = 15; /* invalid */ + } + +slant: + if (italic) + c += 4 * 32; + + return c; +} + +/** + * @internal + * @param p Plane of @a canvas_type char, short, int. + * @param i Index. + * + * @return + * Pixel @a i in plane @a p. + */ +#define peek(p, i) \ +((canvas_type == sizeof(uint8_t)) ? ((uint8_t *)(p))[i] : \ + ((canvas_type == sizeof(uint16_t)) ? ((uint16_t *)(p))[i] : \ + ((uint32_t *)(p))[i])) + +/** + * @internal + * @param p Plane of @a canvas_type char, short, int. + * @param i Index. + * @param v Value. + * + * Set pixel @a i in plane @a p to value @a v. + */ +#define poke(p, i, v) \ +((canvas_type == sizeof(uint8_t)) ? (((uint8_t *)(p))[i] = (v)) : \ + ((canvas_type == sizeof(uint16_t)) ? (((uint16_t *)(p))[i] = (v)) : \ + (((uint32_t *)(p))[i] = (v)))) + +/** + * @internal + * @param canvas_type sizeof(char, short, int). + * @param canvas Pointer to image plane where the character is to be drawn. + * @param rowstride @a canvas byte distance from line to line. + * @param pen Pointer to color palette of @a canvas_type (index 0 background + * pixels, index 1 foreground pixels). + * @param font Pointer to font image with width @a cpl x @a cw pixels, height + * @a ch pixels, depth one bit, bit '1' is foreground. + * @param cpl Chars per line (number of characters in @a font image). + * @param cw Character cell width in pixels. + * @param ch Character cell height in pixels. + * @param glyph Glyph number in font image, 0 ... @a cpl - 1. + * @param underline Bit mask of character rows. For each bit + * 1 << (n = 0 ... @a ch - 1) set all of character row n to + * foreground color. + * @param size Size of character, either NORMAL, DOUBLE_WIDTH (draws left + * and right half), DOUBLE_HEIGHT (draws upper half only), + * DOUBLE_SIZE (left and right upper half), DOUBLE_HEIGHT2 + * (lower half), DOUBLE_SIZE2 (left and right lower half). + * + * Draw one character (function template - define a static version with + * constant @a canvas_type, @a font, @a cpl, @a cw, @a ch). + */ +static inline void +draw_char(int canvas_type, uint8_t *canvas, int rowstride, + uint8_t *pen, uint8_t *font, int cpl, int cw, int ch, + int glyph, unsigned int underline) +{ + uint8_t *src; + int shift, x, y; + + assert(cw >= 8 && cw <= 16); + assert(ch >= 1 && ch <= 31); + + x = glyph * cw; + shift = x & 7; + src = font + (x >> 3); + + for (y = 0; y < ch; underline >>= 1, y++) { + int bits = ~0; + + if (!(underline & 1)) { +#ifdef __i386__ + bits = (*((uint16_t *) src) >> shift); +#else + /* unaligned/little endian */ + bits = ((src[1] * 256 + src[0]) >> shift); +#endif + } + + for (x = 0; x < cw; bits >>= 1, x++) + poke(canvas, x, peek(pen, bits & 1)); + + canvas += rowstride; + + src += cpl * cw / 8; + } +} + +/** + * @internal + * @param canvas_type sizeof(char, short, int). + * @param canvas Pointer to image plane where the character is to be drawn. + * @param rowstride @a canvas byte distance from line to line. + * @param color Color value of @a canvas_type. + * @param cw Character width in pixels. + * @param ch Character height in pixels. + * + * Draw blank character. + */ +static inline void +draw_blank(int canvas_type, uint8_t *canvas, unsigned int rowstride, + unsigned int color, int cw, int ch) +{ + int x, y; + + for (y = 0; y < ch; y++) { + for (x = 0; x < cw; x++) + poke(canvas, x, color); + + canvas += rowstride; + } +} + +/* + * PNG and XPM drawing functions (palette-based) + */ +static void +draw_char_indexed(uint8_t * canvas, int rowstride, uint8_t * pen, + int unicode, int italic, int underline) +{ + draw_char(sizeof(*canvas), canvas, rowstride, + pen, (uint8_t *) ccfont2_bits, CCPL, CCW, CCH, + unicode_ccfont2(unicode, italic), + underline * (3 << 24) /* cell row 24, 25 */); +} + + +void +draw_row(struct eia608_screen* data, int row, uint8_t * canvas, int rowstride) +{ + int column; + int unicode = 0; + uint8_t pen[2]; + uint8_t* cell; + int first = -1; + int last = 0; + + pen[0] = COL_BLACK; + + for (column = 0; column < COLUMNS ; column++) { + + if (COL_TRANSPARENT != data->colors[row][column]) + { + cell = canvas + ((column+1) * CCW); + get_char_in_unicode((unsigned char*)&unicode, data->characters[row][column]); + pen[1] = data->colors[row][column]; + + int attr = data->fonts[row][column]; + draw_char_indexed(cell, rowstride, pen, unicode, (attr & FONT_ITALICS) != 0, (attr & FONT_UNDERLINED) != 0); + if (first < 0) + { + // draw a printable space before the first non-space char + first = column; + if (unicode != 0x20) + { + cell = canvas + ((first) * CCW); + draw_char_indexed(cell, rowstride, pen, 0x20, 0, 0); + } + } + last = column; + } + } + // draw a printable space after the last non-space char + // unicode should still contain the last character + // check whether it is a space + if (unicode != 0x20) + { + cell = canvas + ((last+2) * CCW); + draw_char_indexed(cell, rowstride, pen, 0x20, 0, 0); + } +} + +static png_color palette[10] = +{ + { 0xff, 0xff, 0xff }, // COL_WHITE = 0, + { 0x00, 0xff, 0x00 }, // COL_GREEN = 1, + { 0x00, 0x00, 0xff }, // COL_BLUE = 2, + { 0x00, 0xff, 0xff }, // COL_CYAN = 3, + { 0xff, 0x00, 0x00 }, // COL_RED = 4, + { 0xff, 0xff, 0x00 }, // COL_YELLOW = 5, + { 0xff, 0x00, 0xff }, // COL_MAGENTA = 6, + { 0xff, 0xff, 0xff }, // COL_USERDEFINED = 7, + { 0x00, 0x00, 0x00 }, // COL_BLACK = 8 + { 0x00, 0x00, 0x00 } // COL_TRANSPARENT = 9 +}; + +static png_byte alpha[10] = +{ + 255, + 255, + 255, + 255, + 255, + 255, + 255, + 255, + 255, + 0 +}; + +int +SpuPng::writePNG(struct eia608_screen* data, + png_structp png_ptr, png_infop info_ptr, + png_bytep image, + png_bytep* row_pointer, + unsigned int ww, + unsigned int wh) +{ + unsigned int i; + + if (setjmp(png_jmpbuf(png_ptr))) + return 0; + + png_init_io (png_ptr, fppng); + + png_set_IHDR (png_ptr, + info_ptr, + ww, + wh, + /* bit_depth */ 8, + PNG_COLOR_TYPE_PALETTE, + PNG_INTERLACE_NONE, + PNG_COMPRESSION_TYPE_DEFAULT, + PNG_FILTER_TYPE_DEFAULT); + + png_set_PLTE (png_ptr, info_ptr, palette, sizeof(palette) / sizeof(palette[0])); + png_set_tRNS (png_ptr, info_ptr, alpha, sizeof(alpha) / sizeof(alpha[0]), NULL); + + png_set_gAMA (png_ptr, info_ptr, 1.0 / 2.2); + + png_write_info (png_ptr, info_ptr); + + for (i = 0; i < wh; i++) + row_pointer[i] = image + i * ww; + + png_write_image (png_ptr, row_pointer); + + png_write_end (png_ptr, info_ptr); + + return 1; +} + +int +SpuPng::exportPNG(struct eia608_screen* data) +{ + png_structp png_ptr; + png_infop info_ptr; + png_bytep *row_pointer; + png_bytep image; + int ww, wh, rowstride, row_adv; + int row; + + assert ((sizeof(png_byte) == sizeof(uint8_t)) + && (sizeof(*image) == sizeof(uint8_t))); + + // Allow space at beginning and end of each row for a padding space + ww = CCW * (COLUMNS+2); + wh = CCH * ROWS; + row_adv = (COLUMNS+2) * CCW * CCH; + + rowstride = ww * sizeof(*image); + + if (!(row_pointer = (png_bytep*)malloc(sizeof(*row_pointer) * wh))) { + mprint("Unable to allocate %d byte buffer.\n", + sizeof(*row_pointer) * wh); + return 0; + } + + if (!(image = (png_bytep)malloc(wh * ww * sizeof(*image)))) { + mprint("Unable to allocate %d KB image buffer.", + wh * ww * sizeof(*image) / 1024); + free(row_pointer); + return 0; + } + // Initialize image to transparent + memset(image, COL_TRANSPARENT, wh * ww * sizeof(*image)); + + /* draw the image */ + + for (row = 0; row < ROWS; row++) { + if (data->row_used[row]) + draw_row(data, row, image + row * row_adv, rowstride); + } + + /* Now save the image */ + + if (!(png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, + NULL, NULL, NULL))) + goto unknown_error; + + if (!(info_ptr = png_create_info_struct(png_ptr))) { + png_destroy_write_struct(&png_ptr, (png_infopp) NULL); + goto unknown_error; + } + + if (!writePNG (data, png_ptr, info_ptr, image, row_pointer, ww, wh)) { + png_destroy_write_struct (&png_ptr, &info_ptr); + goto write_error; + } + + png_destroy_write_struct (&png_ptr, &info_ptr); + + free (row_pointer); + + free (image); + + return 1; + +write_error: + +unknown_error: + free (row_pointer); + + free (image); + + return 0; +} + +// +// End copy from http://zapping.cvs.sourceforge.net/viewvc/zapping/vbi/src/exp-gfx.c?view=markup&pathrev=zvbi-0-2-33 +// + diff --git a/src/608_spupng.h b/src/608_spupng.h new file mode 100644 index 00000000..0be909f4 --- /dev/null +++ b/src/608_spupng.h @@ -0,0 +1,36 @@ +#ifndef __608_SPUPNG_H__ + +#include "png.h" +#include "ccextractor.h" + +class SpuPng +{ + public: + SpuPng(struct ccx_s_write* wb); + ~SpuPng(); + + void writeHeader(); + void writeFooter(); + + int writeCCBuffer(struct eia608_screen* data, struct ccx_s_write *wb); + + private: + static void initFont(void); + int writePNG(struct eia608_screen* data, + png_structp png_ptr, png_infop info_ptr, + png_bytep image, + png_bytep* row_pointer, + unsigned int ww, + unsigned int wh); + int exportPNG(struct eia608_screen* data); + + FILE* fpxml; + FILE* fppng; + char* dirname; + char* pngfile; + int fileIndex; + int xOffset; + int yOffset; +}; + +#endif /* __608_SPUPNG_H__ */ diff --git a/src/608_srt.cpp b/src/608_srt.cpp new file mode 100644 index 00000000..97705403 --- /dev/null +++ b/src/608_srt.cpp @@ -0,0 +1,194 @@ +#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 ccx_s_write *wb, 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]; + wb->data608->srt_counter++; + sprintf (timeline,"%u\r\n",wb->data608->srt_counter); + enc_buffer_used=encode_line (enc_buffer,(unsigned char *) timeline); + write (wb->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 (wb->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_rfh, el, u); + write (wb->fh, encoded_crlf, encoded_crlf_length); + begin+= strlen ((const char *) begin)+1; + } + + dbg_print(CCX_DMT_608, "- - - - - - - - - - - -\r\n"); + + write (wb->fh, encoded_crlf, encoded_crlf_length); + free(el); +} + +int write_cc_buffer_as_srt (struct eia608_screen *data, struct ccx_s_write *wb) +{ + unsigned h1,m1,s1,ms1; + unsigned h2,m2,s2,ms2; + int wrote_something = 0; + LLONG ms_start= wb->data608->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; + + LLONG 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]; + wb->data608->srt_counter++; + sprintf (timeline,"%u\r\n",wb->data608->srt_counter); + enc_buffer_used=encode_line (enc_buffer,(unsigned char *) timeline); + write (wb->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", wb->data608->srt_counter); + dbg_print(CCX_DMT_608, "%s",timeline); + + write (wb->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 && lastprev_line_start && firstprev_line_start && last=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 (wb->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 (wb->fh, subline, length); + write (wb->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 (wb->fh, encoded_crlf, encoded_crlf_length); + return wrote_something; +} diff --git a/src/708.cpp b/src/708.cpp new file mode 100644 index 00000000..0b723900 --- /dev/null +++ b/src/708.cpp @@ -0,0 +1,1235 @@ +#include "ccextractor.h" +#include + +/* Portions by Daniel Kristjansson, extracted from MythTV's source */ + +// #define DEBUG_708_PACKETS // Already working. + +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 + +void clearTV (cc708_service_decoder *decoder, int buffer); + +const char *COMMANDS_C0[32]= +{ + "NUL", // 0 = NUL + NULL, // 1 = Reserved + NULL, // 2 = Reserved + "ETX", // 3 = ETX + NULL, // 4 = Reserved + NULL, // 5 = Reserved + NULL, // 6 = Reserved + NULL, // 7 = Reserved + "BS", // 8 = Backspace + NULL, // 9 = Reserved + NULL, // A = Reserved + NULL, // B = Reserved + "FF", // C = FF + "CR", // D = CR + "HCR", // E = HCR + NULL, // F = Reserved + "EXT1",// 0x10 = EXT1, + NULL, // 0x11 = Reserved + NULL, // 0x12 = Reserved + NULL, // 0x13 = Reserved + NULL, // 0x14 = Reserved + NULL, // 0x15 = Reserved + NULL, // 0x16 = 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 +}; + +struct S_COMMANDS_C1 COMMANDS_C1[32]= +{ + {CW0,"CW0","SetCurrentWindow0", 1}, + {CW1,"CW1","SetCurrentWindow1", 1}, + {CW2,"CW2","SetCurrentWindow2", 1}, + {CW3,"CW3","SetCurrentWindow3", 1}, + {CW4,"CW4","SetCurrentWindow4", 1}, + {CW5,"CW5","SetCurrentWindow5", 1}, + {CW6,"CW6","SetCurrentWindow6", 1}, + {CW7,"CW7","SetCurrentWindow7", 1}, + {CLW,"CLW","ClearWindows", 2}, + {DSW,"DSW","DisplayWindows", 2}, + {HDW,"HDW","HideWindows", 2}, + {TGW,"TGW","ToggleWindows", 2}, + {DLW,"DLW","DeleteWindows", 2}, + {DLY,"DLY","Delay", 2}, + {DLC,"DLC","DelayCancel", 1}, + {RST,"RST","Reset", 1}, + {SPA,"SPA","SetPenAttributes", 3}, + {SPC,"SPC","SetPenColor", 4}, + {SPL,"SPL","SetPenLocation", 3}, + {RSV93,"RSV93","Reserved", 1}, + {RSV94,"RSV94","Reserved", 1}, + {RSV95,"RSV95","Reserved", 1}, + {RSV96,"RSV96","Reserved", 1}, + {SWA,"SWA","SetWindowAttributes", 5}, + {DF0,"DF0","DefineWindow0", 7}, + {DF1,"DF0","DefineWindow1", 7}, + {DF2,"DF0","DefineWindow2", 7}, + {DF3,"DF0","DefineWindow3", 7}, + {DF4,"DF0","DefineWindow4", 7}, + {DF5,"DF0","DefineWindow5", 7}, + {DF6,"DF0","DefineWindow6", 7}, + {DF7,"DF0","DefineWindow7", 7} +}; + + +cc708_service_decoder decoders[63]; // For primary and secondary languages + +void clear_packet(void) +{ + current_packet_length=0; + memset (current_packet,0,MAX_708_PACKET_LENGTH); +} + +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++) + { + decoder->windows[j].is_defined=0; + 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)); + } + decoder->current_window=-1; + decoder->current_visible_start_ms=0; + clearTV(decoder,1); + clearTV(decoder,2); + decoder->tv=&decoder->tv1; + decoder->inited=1; +} + +void cc708_reset() +{ + 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++; +} + +int compWindowsPriorities (const void *a, const void *b) +{ + e708Window *w1=*(e708Window **)a; + e708Window *w2=*(e708Window **)b; + return w1->priority-w2->priority; +} + +void clearTV (cc708_service_decoder *decoder, int buffer) // Buffer => 1 or 2 +{ + tvscreen *tv= (buffer==1) ? &decoder->tv1 : &decoder->tv2; + for (int i=0; ichars[i], ' ', I708_SCREENGRID_COLUMNS); + tv->chars[i][I708_SCREENGRID_COLUMNS]=0; + } +}; + +void printTVtoSRT (cc708_service_decoder *decoder, int which) +{ + 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; + int empty=1; + ms_start+=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) + 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); + if (decoder->fh==-1) + { + fatal (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]; + 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)); + for (int i=0;i<75;i++) + { + int empty=1; + for (int j=0;j<210;j++) + if (decoder->tv->chars[i][j]!=' ') + empty=0; + if (!empty) + { + int f,l; // First,last used char + for (f=0;f<210;f++) + if (decoder->tv->chars[i][f]!=' ') + break; + for (l=209;l>0;l--) + 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,"\r\n",2); + } + } + write (decoder->fh,"\r\n",2); +} + +void printTVtoConsole (cc708_service_decoder *decoder, int which) +{ + tvscreen *tv = (which==1)? &decoder->tv1:&decoder->tv2; + 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); + for (int i=0;i<75;i++) + { + int empty=1; + for (int j=0;j<210;j++) + if (decoder->tv->chars[i][j]!=' ') + empty=0; + if (!empty) + { + int f,l; // First,last used char + for (f=0;f<210;f++) + if (decoder->tv->chars[i][f]!=' ') + break; + for (l=209;l>0;l--) + 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"); + } + } +} + +void updateScreen (cc708_service_decoder *decoder) +{ + // Print the previous screenful, which wasn't possible until now because we had + // no timing info. + if (decoder->cur_tv==1) + { + printTVtoConsole (decoder,2); + printTVtoSRT (decoder,2); + clearTV (decoder,2); + decoder->cur_tv=2; + decoder->tv=&decoder->tv2; + + } + else + { + printTVtoConsole (decoder,1); + printTVtoSRT (decoder,1); + clearTV (decoder,1); + decoder->cur_tv=1; + decoder->tv=&decoder->tv1; + } + + // THIS FUNCTION WILL DO THE MAGIC OF ACTUALLY EXPORTING THE DECODER STATUS + // TO SEVERAL FILES + e708Window *wnd[I708_MAX_WINDOWS]; // We'll store here the visible windows that contain anything + int visible=0; + for (int i=0;iwindows[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: "); + for (int i=0;inumber, wnd[i]->priority); + } + dbg_print(CCX_DMT_708, "\n"); + for (int i=0;ianchor_point) + { + case anchorpoint_top_left: + top=wnd[i]->anchor_vertical; + left=wnd[i]->anchor_horizontal; + break; + case anchorpoint_top_center: + top=wnd[i]->anchor_vertical; + left=wnd[i]->anchor_horizontal - wnd[i]->col_count/2; + break; + case anchorpoint_top_right: + top=wnd[i]->anchor_vertical; + left=wnd[i]->anchor_horizontal - wnd[i]->col_count; + break; + case anchorpoint_middle_left: + top=wnd[i]->anchor_vertical - wnd[i]->row_count/2; + left=wnd[i]->anchor_horizontal; + break; + case anchorpoint_middle_center: + top=wnd[i]->anchor_vertical - wnd[i]->row_count/2; + left=wnd[i]->anchor_horizontal - wnd[i]->col_count/2; + break; + case anchorpoint_middle_right: + top=wnd[i]->anchor_vertical - wnd[i]->row_count/2; + left=wnd[i]->anchor_horizontal - wnd[i]->col_count; + break; + case anchorpoint_bottom_left: + top=wnd[i]->anchor_vertical - wnd[i]->row_count; + left=wnd[i]->anchor_horizontal; + break; + case anchorpoint_bottom_center: + top=wnd[i]->anchor_vertical - wnd[i]->row_count; + left=wnd[i]->anchor_horizontal - wnd[i]->col_count/2; + break; + case anchorpoint_bottom_right: + top=wnd[i]->anchor_vertical - wnd[i]->row_count; + left=wnd[i]->anchor_horizontal - wnd[i]->col_count; + break; + 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", + wnd[i]->number, wnd[i]->anchor_point, wnd[i]->row_count,wnd[i]->col_count, + top,left); + if (top<0) + top=0; + if (left<0) + left=0; + int copyrows=top + wnd[i]->row_count >= I708_SCREENGRID_ROWS ? + 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); + for (int j=0;jtv->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. +Note: EXT1 not included */ +// C2: Extended Miscellaneous Control Codes +// TODO: This code is completely untested due to lack of samples. Just following specs! +int handle_708_C2 (cc708_service_decoder *decoder, unsigned char *data, int data_length) +{ + if (data[0]<=0x07) // 00-07... + return 1; // ... Single-byte control bytes (0 additional bytes) + else if (data[0]<=0x0f) // 08-0F ... + return 2; // ..two-byte control codes (1 additional byte) + else if (data[0]<=0x17) // 10-17 ... + return 3; // ..three-byte control codes (2 additional bytes) + return 4; // 18-1F => four-byte control codes (3 additional bytes) +} + +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."); + 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). + // 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"); + 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. +// 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). +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); + unsigned char c=0x20; // Default to space + unsigned char code=data[0]; + if (/* data[i]>=0x00 && */ code<=0x1F) // Comment to silence warning + { + used=handle_708_C2 (decoder, data, data_length); + } + // Group G2 - Extended Miscellaneous Characters + else if (code>=0x20 && code<=0x7F) + { + c=get_internal_from_G2 (code); + used=1; + process_character (decoder, c); + } + // Group C3 + else if (code>=0x80 && code<=0x9F) + { + used=handle_708_C3 (decoder, data, data_length); + // TODO: Something + } + // Group G3 + else + { + c=get_internal_from_G3 (code); + used=1; + process_character (decoder, c); + } + return used; +} + +void process_cr (cc708_service_decoder *decoder) +{ + switch (decoder->windows[decoder->current_window].attribs.print_dir) + { + case pd_left_to_right: + decoder->windows[decoder->current_window].pen_column=0; + if (decoder->windows[decoder->current_window].pen_row+1 < decoder->windows[decoder->current_window].row_count) + decoder->windows[decoder->current_window].pen_row++; + break; + case pd_right_to_left: + decoder->windows[decoder->current_window].pen_column=decoder->windows[decoder->current_window].col_count; + if (decoder->windows[decoder->current_window].pen_row+1 < decoder->windows[decoder->current_window].row_count) + decoder->windows[decoder->current_window].pen_row++; + break; + case pd_top_to_bottom: + decoder->windows[decoder->current_window].pen_row=0; + if (decoder->windows[decoder->current_window].pen_column+1 < decoder->windows[decoder->current_window].col_count) + decoder->windows[decoder->current_window].pen_column++; + break; + case pd_bottom_to_top: + decoder->windows[decoder->current_window].pen_row=decoder->windows[decoder->current_window].row_count; + if (decoder->windows[decoder->current_window].pen_column+1 < decoder->windows[decoder->current_window].col_count) + decoder->windows[decoder->current_window].pen_column++; + break; + } +} + +int handle_708_C0 (cc708_service_decoder *decoder, unsigned char *data, int data_length) +{ + 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); + int len=-1; + // 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]) + { + case 0x0d: //CR + process_cr (decoder); + break; + case 0x0e: // HCR (Horizontal Carriage Return) + // TODO: Process HDR + break; + case 0x0c: // FF (Form Feed) + // TODO: Process FF + break; + } + len=1; + } + else if (data[0]>=0x10 && data[0]<=0x17) + { + // Note that 0x10 is actually EXT1 and is dealt with somewhere else. Rest is undefined as per + // CEA-708-D + len=2; + } + else if (data[0]>=0x18 && data[0]<=0x1F) + { + // Only PE16 is defined. + if (data[0]==0x18) // PE16 + { + ; // TODO: Handle PE16 + } + len=3; + } + if (len==-1) + { + dbg_print(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); + 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, + 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; + switch (internal_char) + { + default: + decoder->windows[decoder->current_window].is_empty=0; + decoder->windows[decoder->current_window]. + rows[decoder->windows[decoder->current_window].pen_row] + [decoder->windows[decoder->current_window].pen_column]=internal_char; + /* Not positive this interpretation is correct. Word wrapping is optional, so + let's assume we don't need to autoscroll */ + switch (decoder->windows[decoder->current_window].attribs.print_dir) + { + case pd_left_to_right: + if (decoder->windows[decoder->current_window].pen_column+1 < decoder->windows[decoder->current_window].col_count) + decoder->windows[decoder->current_window].pen_column++; + break; + case pd_right_to_left: + if (decoder->windows->pen_column>0) + decoder->windows[decoder->current_window].pen_column--; + break; + case pd_top_to_bottom: + if (decoder->windows[decoder->current_window].pen_row+1 < decoder->windows[decoder->current_window].row_count) + decoder->windows[decoder->current_window].pen_row++; + break; + case pd_bottom_to_top: + if (decoder->windows[decoder->current_window].pen_row>0) + decoder->windows[decoder->current_window].pen_row--; + break; + } + break; + } + +} + +// G0 - Code Set - ASCII printable characters +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]); + unsigned char c=get_internal_from_G0 (data[0]); + process_character (decoder, c); + return 1; +} + +// 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]); + unsigned char c=get_internal_from_G1 (data[0]); + process_character (decoder, c); + return 1; +} + +/*------------------------------------------------------- + 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); + if (decoder->windows[new_window].is_defined) + decoder->current_window=new_window; +} + +void clearWindow (cc708_service_decoder *decoder, int window) +{ + // TODO: Removes any existing text from the specified window. When a window + // is cleared the entire window is filled with the window fill color. +} + +void handle_708_CLW_ClearWindows (cc708_service_decoder *decoder, int windows_bitmap) +{ + dbg_print(CCX_DMT_708, " Entry in handle_708_CLW_ClearWindows, windows: "); + if (windows_bitmap==0) + dbg_print(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); + } + windows_bitmap>>=1; + } + } + dbg_print(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: "); + if (windows_bitmap==0) + dbg_print(CCX_DMT_708, "None\n"); + else + { + int changes=0; + for (int i=0; i<8; i++) + { + if (windows_bitmap & 1) + { + dbg_print(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"); + 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: "); + if (windows_bitmap==0) + dbg_print(CCX_DMT_708, "None\n"); + else + { + int changes=0; + for (int i=0; i<8; i++) + { + if (windows_bitmap & 1) + { + dbg_print(CCX_DMT_708, "[Window %d] ",i ); + if (decoder->windows[i].is_defined && decoder->windows[i].visible && !decoder->windows[i].is_empty) + { + changes=1; + decoder->windows[i].visible=0; + } + // TODO: Actually Hide Window + } + windows_bitmap>>=1; + } + dbg_print(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: "); + if (windows_bitmap==0) + dbg_print(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; + } + windows_bitmap>>=1; + } + updateScreen(decoder); + } + dbg_print(CCX_DMT_708, "\n"); +} + +void clearWindowText (e708Window *window) +{ + for (int i=0;irows[i],' ',I708_MAX_COLUMNS); + window->rows[i][I708_MAX_COLUMNS]=0; + } + memset (window->rows[I708_MAX_ROWS],0,I708_MAX_COLUMNS+1); + window->is_empty=1; + +} + +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); + 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"); + return; + } + decoder->windows[window].number=window; + int priority = (data[1] ) & 0x7; + int col_lock = (data[1]>>3) & 0x1; + int row_lock = (data[1]>>4) & 0x1; + int visible = (data[1]>>5) & 0x1; + int anchor_vertical = data[2] & 0x7f; + int relative_pos = (data[2]>>7); + int anchor_horizontal = data[3]; + int row_count = data[4] & 0xf; + int anchor_point = data[4]>>4; + 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", + priority, col_lock?"Yes": "No", row_lock?"Yes": "No"); + dbg_print(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", + anchor_horizontal, row_count, anchor_point); + dbg_print(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 + decoder->windows[window].priority=priority; + decoder->windows[window].col_lock=col_lock; + decoder->windows[window].row_lock=row_lock; + decoder->windows[window].visible=visible; + decoder->windows[window].anchor_vertical=anchor_vertical; + decoder->windows[window].relative_pos=relative_pos; + decoder->windows[window].anchor_horizontal=anchor_horizontal; + decoder->windows[window].row_count=row_count; + decoder->windows[window].anchor_point=anchor_point; + decoder->windows[window].col_count=col_count; + decoder->windows[window].pen_style=pen_style; + decoder->windows[window].win_style=win_style; + if (!decoder->windows[window].is_defined) + { + // 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) + decoder->windows[window].pen_column=0; + decoder->windows[window].pen_row=0; + if (!decoder->windows[window].memory_reserved) + { + for (int i=0;i<=I708_MAX_ROWS;i++) + { + 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->current_window=-1; + for (int j=0;jwindows[window].rows[j]); + return; // TODO: Warn somehow + } + } + decoder->windows[window].memory_reserved=1; + } + decoder->windows[window].is_defined=1; + clearWindowText (&decoder->windows[window]); + } + else + { + // 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); +} + +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"); + int fill_color = (data[1] ) & 0x3f; + int fill_opacity = (data[1]>>6) & 0x03; + int border_color = (data[2] ) & 0x3f; + int border_type01 = (data[2]>>6) & 0x03; + int justify = (data[3] ) & 0x03; + int scroll_dir = (data[3]>>2) & 0x03; + int print_dir = (data[3]>>4) & 0x03; + int word_wrap = (data[3]>>6) & 0x01; + int border_type = (data[3]>>5) | border_type01; + 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", + 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", + 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", + border_type, display_eff, effect_dir, effect_speed); + if (decoder->current_window==-1) + { + // Can't do anything yet - we need a window to be defined first. + return; + } + decoder->windows[decoder->current_window].attribs.fill_color=fill_color; + decoder->windows[decoder->current_window].attribs.fill_opacity=fill_opacity; + decoder->windows[decoder->current_window].attribs.border_color=border_color; + decoder->windows[decoder->current_window].attribs.border_type01=border_type01; + decoder->windows[decoder->current_window].attribs.justify=justify; + decoder->windows[decoder->current_window].attribs.scroll_dir=scroll_dir; + decoder->windows[decoder->current_window].attribs.print_dir=print_dir; + decoder->windows[decoder->current_window].attribs.word_wrap=word_wrap; + decoder->windows[decoder->current_window].attribs.border_type=border_type; + decoder->windows[decoder->current_window].attribs.display_eff=display_eff; + decoder->windows[decoder->current_window].attribs.effect_dir=effect_dir; + decoder->windows[decoder->current_window].attribs.effect_speed=effect_speed; + +} + +void deleteWindow (cc708_service_decoder *decoder, int window) +{ + if (window==decoder->current_window) + { + // If the current window is deleted, then the decoder's current window ID + // is unknown and must be reinitialized with either the SetCurrentWindow + // or DefineWindow command. + decoder->current_window=-1; + } + // TODO: Do the actual deletion (remove from display if needed, etc), mark as + // not defined, etc + if (decoder->windows[window].is_defined) + { + clearWindowText(&decoder->windows[window]); + } + decoder->windows[window].is_defined=0; +} + +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: "); + if (windows_bitmap==0) + dbg_print(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 ); + if (decoder->windows[i].is_defined && decoder->windows[i].visible && !decoder->windows[i].is_empty) + changes=1; + deleteWindow (decoder, i); + } + windows_bitmap>>=1; + } + } + dbg_print(CCX_DMT_708, "\n"); + if (changes) + updateScreen (decoder); + +} + +/*------------------------------------------------------- + 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"); + int pen_size = (data[1] ) & 0x3; + int offset = (data[1]>>2) & 0x3; + int text_tag = (data[1]>>4) & 0xf; + int font_tag = (data[2] ) & 0x7; + 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", + pen_size, offset, text_tag, font_tag); + dbg_print(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. + return; + } + decoder->windows[decoder->current_window].pen.pen_size=pen_size; + decoder->windows[decoder->current_window].pen.offset=offset; + decoder->windows[decoder->current_window].pen.text_tag=text_tag; + decoder->windows[decoder->current_window].pen.font_tag=font_tag; + decoder->windows[decoder->current_window].pen.edge_type=edge_type; + decoder->windows[decoder->current_window].pen.underline=underline; + decoder->windows[decoder->current_window].pen.italic=italic; +} + +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"); + 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", + fg_color, fg_opacity); + dbg_print(CCX_DMT_708, " Background color: [%d] Background opacity: [%d]\n", + bg_color, bg_opacity); + dbg_print(CCX_DMT_708, " Edge color: [%d]\n", + edge_color); + if (decoder->current_window==-1) + { + // Can't do anything yet - we need a window to be defined first. + return; + } + + decoder->windows[decoder->current_window].pen_color.fg_color=fg_color; + decoder->windows[decoder->current_window].pen_color.fg_opacity=fg_opacity; + decoder->windows[decoder->current_window].pen_color.bg_color=bg_color; + decoder->windows[decoder->current_window].pen_color.bg_opacity=bg_opacity; + decoder->windows[decoder->current_window].pen_color.edge_color=edge_color; +} + + +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"); + int row = data[1] & 0x0f; + int col = data[2] & 0x3f; + dbg_print(CCX_DMT_708, " row: [%d] Column: [%d]\n", + row, col); + if (decoder->current_window==-1) + { + // Can't do anything yet - we need a window to be defined first. + return; + } + decoder->windows[decoder->current_window].pen_row=row; + decoder->windows[decoder->current_window].pen_column=col; +} + + +/*------------------------------------------------------- + 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); + // 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."); + // TODO: See above +} + +// C1 Code Set - Captioning Commands Control Codes +int handle_708_C1 (cc708_service_decoder *decoder, unsigned char *data, int data_length) +{ + S_COMMANDS_C1 com=COMMANDS_C1[data[0]-0x80]; + dbg_print(CCX_DMT_708, "%s | C1: [%02X] [%s] [%s] (%d)\n", + print_mstime(get_fts()), + 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"); + return -1; + } + switch (com.code) + { + case CW0: /* SetCurrentWindow */ + case CW1: + case CW2: + case CW3: + case CW4: + case CW5: + case CW6: + case CW7: + handle_708_CWx_SetCurrentWindow (decoder, com.code-CW0); /* Window 0 to 7 */ + break; + case CLW: + handle_708_CLW_ClearWindows (decoder, data[1]); + break; + case DSW: + handle_708_DSW_DisplayWindows (decoder, data[1]); + break; + case HDW: + handle_708_HDW_HideWindows (decoder, data[1]); + break; + case TGW: + handle_708_TGW_ToggleWindows (decoder, data[1]); + break; + case DLW: + handle_708_DLW_DeleteWindows (decoder, data[1]); + break; + case DLY: + handle_708_DLY_Delay (decoder, data[1]); + break; + case DLC: + handle_708_DLC_DelayCancel (decoder); + break; + case RST: + cc708_service_reset(decoder); + break; + case SPA: + handle_708_SPA_SetPenAttributes (decoder, data); + break; + case SPC: + handle_708_SPC_SetPenColor (decoder, data); + break; + case SPL: + handle_708_SPL_SetPenLocation (decoder, data); + break; + case RSV93: + case RSV94: + case RSV95: + case RSV96: + dbg_print(CCX_DMT_708, "Warning, found Reserved codes, ignored.\n"); + break; + case SWA: + handle_708_SWA_SetWindowAttributes (decoder, data); + break; + case DF0: + case DF1: + case DF2: + case DF3: + case DF4: + case DF5: + case DF6: + case DF7: + handle_708_DFx_DefineWindow (decoder, com.code-DF0, data); /* Window 0 to 7 */ + break; + default: + mprint ("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; + while (i=0x00 && */ data[i]<=0x1F) // Comment to silence warning + { + used=handle_708_C0 (decoder,data+i,data_length-i); + } + // Group G0 + else if (data[i]>=0x20 && data[i]<=0x7F) + { + used=handle_708_G0 (decoder,data+i,data_length-i); + } + // Group C1 + else if (data[i]>=0x80 && data[i]<=0x9F) + { + used=handle_708_C1 (decoder,data+i,data_length-i); + } + // Group C2 + else + 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"); + // TODO: Not sure if a local reset is going to be helpful here. + cc708_service_reset (decoder); + return; + } + } + else // Use extended set + { + used=handle_708_extended_char (decoder, data+i+1,data_length-1); + used++; // Since we had EXT1 + } + i+=used; + } +} + + +void process_current_packet (void) +{ + 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); +#endif + if (current_packet_length==0) + return; + if (len==0) // This is well defined in EIA-708; no magic. + 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); +#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, + current_packet[0], current_packet[1], pict_types[current_picture_coding_type]); + cc708_reset(); + 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", + seq,(last_seq+1)%4); + cc708_reset(); + return; + } + last_seq=seq; + + unsigned char *pos=current_packet+1; + + while (pos>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); + + if (service_number==7) // There is an extended header + { + pos++; + service_number=(pos[0] & 0x3F); // 6 more significant bits + // printf ("Extended header: Service number: [%d]\n",service_number); + if (service_number<7) + { + dbg_print(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"); + // 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."); + pos = current_packet+len; // Move to end + break; + } + if (service_number>0 && decoders[service_number-1].inited) + process_service_block (&decoders[service_number-1], pos, block_length); + + 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(); + } + + 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"); + } +} + +void do_708 (const unsigned char *data, int datalength) +{ + /* 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) + return; + + for (int i=0;i253) + { + dbg_print(CCX_DMT_708, "Warning: Legal packet size exceeded, data not added.\n"); + } + else + { + current_packet[current_packet_length++]=data[i+2]; + current_packet[current_packet_length++]=data[i+3]; + } + } + break; + case 3: + dbg_print (CCX_DMT_708, "708: DTVCC Channel Packet Start\n"); + process_current_packet(); + if (cc_valid) + { + if (current_packet_length>253) + { + dbg_print(CCX_DMT_708, "Warning: Legal packet size exceeded, data not added.\n"); + } + else + { + current_packet[current_packet_length++]=data[i+2]; + current_packet[current_packet_length++]=data[i+3]; + } + } + break; + default: + fatal (EXIT_BUG_BUG, "708: shouldn't be here - cc_type: %d\n", cc_type); + } + } +} + +void init_708(void) +{ + for (int i=0;i<63;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].fh=-1; + decoders[i].srt_counter=0; + } +} diff --git a/src/708.h b/src/708.h new file mode 100644 index 00000000..98a41dbc --- /dev/null +++ b/src/708.h @@ -0,0 +1,292 @@ +#ifndef _INCLUDE_708_ +#define _INCLUDE_708_ +#define MAX_708_PACKET_LENGTH 128 + +#define I708_MAX_ROWS 15 +#define I708_MAX_COLUMNS 42 + +#define I708_SCREENGRID_ROWS 75 +#define I708_SCREENGRID_COLUMNS 210 + +#define I708_MAX_WINDOWS 8 + +enum COMMANDS_C0_CODES +{ + NUL=0, + ETX=3, + BS=8, + FF=0xC, + CR=0xD, + HCR=0xE, + EXT1=0x10, + P16=0x18 +}; + +enum COMMANDS_C1_CODES +{ + CW0=0x80, + CW1=0x81, + CW2=0x82, + CW3=0x83, + CW4=0x84, + CW5=0x85, + CW6=0x86, + CW7=0x87, + CLW=0x88, + DSW=0x89, + HDW=0x8A, + TGW=0x8B, + DLW=0x8C, + DLY=0x8D, + DLC=0x8E, + RST=0x8F, + SPA=0x90, + SPC=0x91, + SPL=0x92, + RSV93=0x93, + RSV94=0x94, + RSV95=0x95, + RSV96=0x96, + SWA=0x97, + DF0=0x98, + DF1=0x99, + DF2=0x9A, + DF3=0x9B, + DF4=0x9C, + DF5=0x9D, + DF6=0x9E, + DF7=0x9F +}; + +struct S_COMMANDS_C1 +{ + int code; + const char *name; + const char *description; + int length; +}; + + +enum eWindowsAttribJustify +{ + left=0, + right=1, + center=2, + full=3 +}; + +enum eWindowsAttribPrintDirection +{ + pd_left_to_right=0, + pd_right_to_left=1, + pd_top_to_bottom=2, + pd_bottom_to_top=3 +}; + +enum eWindowsAttribScrollDirection +{ + sd_left_to_right=0, + sd_right_to_left=1, + sd_top_to_bottom=2, + sd_bottom_to_top=3 +}; + +enum eWindowsAttribScrollDisplayEffect +{ + snap=0, + fade=1, + wipe=2 +}; + +enum eWindowsAttribEffectDirection +{ + left_to_right=0, + right_to_left=1, + top_to_bottom=2, + bottom_to_top=3 +}; + +enum eWindowsAttribFillOpacity +{ + solid=0, + flash=1, + traslucent=2, + transparent=3 +}; + +enum eWindowsAttribBorderType +{ + none=0, + raised=1, + depressed=2, + uniform=3, + shadow_left=4, + shadow_right=5 +}; + +enum ePenAttribSize +{ + pensize_small=0, + pensize_standard=1, + pensize_large=2 +}; + +enum ePenAttribFontStyle +{ + fontstyle_default_or_undefined=0, + monospaced_with_serifs=1, + proportionally_spaced_with_serifs=2, + monospaced_without_serifs=3, + proportionally_spaced_without_serifs=4, + casual_font_type=5, + cursive_font_type=6, + small_capitals=7 +}; + +enum ePanAttribTextTag +{ + texttag_dialog=0, + texttag_source_or_speaker_id=1, + texttag_electronic_voice=2, + texttag_foreign_language=3, + texttag_voiceover=4, + texttag_audible_translation=5, + texttag_subtitle_translation=6, + texttag_voice_quality_description=7, + texttag_song_lyrics=8, + texttag_sound_effect_description=9, + texttag_musical_score_description=10, + texttag_expletitive=11, + texttag_undefined_12=12, + texttag_undefined_13=13, + texttag_undefined_14=14, + texttag_not_to_be_displayed=15 +}; + +enum ePanAttribOffset +{ + offset_subscript=0, + offset_normal=1, + offset_superscript=2 +}; + +enum ePanAttribEdgeType +{ + edgetype_none=0, + edgetype_raised=1, + edgetype_depressed=2, + edgetype_uniform=3, + edgetype_left_drop_shadow=4, + edgetype_right_drop_shadow=5 +}; + +enum eAnchorPoints +{ + anchorpoint_top_left = 0, + anchorpoint_top_center = 1, + anchorpoint_top_right =2, + anchorpoint_middle_left = 3, + anchorpoint_middle_center = 4, + anchorpoint_middle_right = 5, + anchorpoint_bottom_left = 6, + anchorpoint_bottom_center = 7, + anchorpoint_bottom_right = 8 +}; + +typedef struct e708Pen_color +{ + int fg_color; + int fg_opacity; + int bg_color; + int bg_opacity; + int edge_color; +} e708Pen_color; + +typedef struct e708Pen_attribs +{ + int pen_size; + int offset; + int text_tag; + int font_tag; + int edge_type; + int underline; + int italic; +} e708Pen_attribs; + +typedef struct e708Window_attribs +{ + int fill_color; + int fill_opacity; + int border_color; + int border_type01; + int justify; + int scroll_dir; + int print_dir; + int word_wrap; + int border_type; + int display_eff; + int effect_dir; + int effect_speed; +} e708Window_attribs; + +typedef struct e708Window +{ + int is_defined; + int number; // Handy, in case we only have a pointer to the window + int priority; + int col_lock; + int row_lock; + int visible; + int anchor_vertical; + int relative_pos; + int anchor_horizontal; + int row_count; + int anchor_point; + int col_count; + int pen_style; + int win_style; + unsigned char commands[6]; // Commands used to create this window + e708Window_attribs attribs; + e708Pen_attribs pen; + e708Pen_color pen_color; + int pen_row; + int pen_column; + unsigned char *rows[I708_MAX_ROWS+1]; // Max is 15, but we define an extra one for convenience + int memory_reserved; + int is_empty; +} e708Window; + +typedef struct tvscreen +{ + unsigned char chars[I708_SCREENGRID_ROWS][I708_SCREENGRID_COLUMNS+1]; +} +tvscreen; + +typedef struct cc708_service_decoder +{ + e708Window windows[I708_MAX_WINDOWS]; + int current_window; + int inited; + LLONG current_visible_start_ms; + tvscreen tv1, tv2; // Current and previous "screenfuls", note that we switch between them + int is_empty_tv1, is_empty_tv2; + int cur_tv; // 1 or 2 rather than 0 or 1, to at least be consistent with the decoder + tvscreen *tv; // Pointer to the current TV buffer + char *filename; // Where we are going to write our output + int fh; // Handle to output file. -1 if not yet open + int srt_counter; +} +cc708_service_decoder; + +extern int resets_708; + +void do_708 (const unsigned char *data, int datalength); +void init_708(void); + +unsigned char get_internal_from_G0 (unsigned char g0_char); +unsigned char get_internal_from_G1 (unsigned char g1_char); +unsigned char get_internal_from_G2 (unsigned char g2_char); +unsigned char get_internal_from_G3 (unsigned char g3_char); +void process_character (cc708_service_decoder *decoder, unsigned char internal_char); + +#endif diff --git a/src/708_encoding.cpp b/src/708_encoding.cpp new file mode 100644 index 00000000..3cbaf3f2 --- /dev/null +++ b/src/708_encoding.cpp @@ -0,0 +1,45 @@ +/******************************************************** +256 BYTES IS ENOUGH FOR ALL THE SUPPORTED CHARACTERS IN +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 + 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 + (there are several blank characters here, that's OK) +A0-FF -> Group G1 as is - non-English characters and symbols +*/ + +unsigned char get_internal_from_G0 (unsigned char g0_char) +{ + return g0_char; +} + +unsigned char get_internal_from_G1 (unsigned char g1_char) +{ + return g1_char; +} + +// TODO: Probably not right +// G2: Extended Control Code Set 1 +unsigned char get_internal_from_G2 (unsigned char g2_char) +{ + if (g2_char>=0x20 && g2_char<=0x3F) + return g2_char-0x20; + if (g2_char>=0x60 && g2_char<=0x7F) + return g2_char+0x20; + // Rest unmapped, so we return a blank space + return 0x20; +} + +// TODO: Probably not right +// G3: Future Characters and Icon Expansion +unsigned char get_internal_from_G3 (unsigned char g3_char) +{ + if (g3_char==0xa0) // The "CC" (closed captions) sign + return 0x06; + // Rest unmapped, so we return a blank space + return 0x20; +} diff --git a/src/activity.cpp b/src/activity.cpp new file mode 100644 index 00000000..5b354435 --- /dev/null +++ b/src/activity.cpp @@ -0,0 +1,147 @@ +/* This file contains functions that report the user of the GUI of +relevant events. */ + +#include "ccextractor.h" + +static int credits_shown=0; +unsigned long net_activity_gui=0; + +/* Print current progress. For percentaje, -1 -> streaming mode */ +void activity_progress (int percentaje, int cur_min, int cur_sec) +{ + if (!ccx_options.no_progress_bar) + { + if (percentaje==-1) + mprint ("\rStreaming | %02d:%02d", cur_min, cur_sec); + else + mprint ("\r%3d%% | %02d:%02d",percentaje, cur_min, cur_sec); + } + fflush (stdout); + if (ccx_options.gui_mode_reports) + { + fprintf (stderr, "###PROGRESS#%d#%d#%d\n", percentaje, cur_min, cur_sec); + fflush (stderr); + } +} + +void activity_input_file_open (const char *filename) +{ + if (ccx_options.gui_mode_reports) + { + fprintf (stderr, "###INPUTFILEOPEN#%s\n", filename); + 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_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, + 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); + } +} + + +void activity_message (const char *fmt, ...) +{ + if (ccx_options.gui_mode_reports) + { + va_list args; + fprintf (stderr, "###MESSAGE#"); + va_start(args, fmt); + fprintf(stderr, fmt, args); + fprintf(stderr, "\n"); + va_end(args); + fflush (stderr); + } +} + +void activity_input_file_closed (void) +{ + if (ccx_options.gui_mode_reports) + { + fprintf (stderr, "###INPUTFILECLOSED\n"); + fflush (stderr); + } +} + +void activity_program_number (unsigned program_number) +{ + if (ccx_options.gui_mode_reports) + { + fprintf (stderr, "###TSPROGRAMNUMBER#%u\n",program_number); + fflush (stderr); + } +} + +void activity_report_version (void) +{ + if (ccx_options.gui_mode_reports) + { + fprintf (stderr, "###VERSION#CCExtractor#%s\n",VERSION); + fflush (stderr); + } +} + +void activity_report_data_read (void) +{ + if (ccx_options.gui_mode_reports) + { + fprintf (stderr, "###DATAREAD#%lu\n",net_activity_gui/1000); + fflush (stderr); + } +} + + +void activity_header (void) +{ + if (!credits_shown) + { + 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"); + } +} + diff --git a/src/asf_constants.h b/src/asf_constants.h new file mode 100644 index 00000000..b1589f62 --- /dev/null +++ b/src/asf_constants.h @@ -0,0 +1,31 @@ +// ASF GUIDs +// 10.1 +#define ASF_HEADER "\x30\x26\xB2\x75\x8E\x66\xCF\x11\xA6\xD9\x00\xAA\x00\x62\xCE\x6C" +#define ASF_DATA "\x36\x26\xB2\x75\x8E\x66\xCF\x11\xA6\xD9\x00\xAA\x00\x62\xCE\x6C" + +// 10.2 +#define ASF_FILE_PROPERTIES "\xA1\xDC\xAB\x8C\x47\xA9\xCF\x11\x8E\xE4\x00\xC0\x0C\x20\x53\x65" +#define ASF_STREAM_PROPERTIES "\x91\x07\xDC\xB7\xB7\xA9\xCF\x11\x8E\xE6\x00\xC0\x0C\x20\x53\x65" +#define ASF_HEADER_EXTENSION "\xB5\x03\xBF\x5F\x2E\xA9\xCF\x11\x8E\xE3\x00\xC0\x0C\x20\x53\x65" +#define ASF_CONTENT_DESCRIPTION "\x33\x26\xB2\x75\x8E\x66\xCF\x11\xA6\xD9\x00\xAA\x00\x62\xCE\x6C" +#define ASF_EXTENDED_CONTENT_DESCRIPTION "\x40\xA4\xD0\xD2\x07\xE3\xD2\x11\x97\xF0\x00\xA0\xC9\x5E\xA8\x50" +#define ASF_STREAM_BITRATE_PROPERTIES "\xCE\x75\xF8\x7B\x8D\x46\xD1\x11\x8D\x82\x00\x60\x97\xC9\xA2\xB2" +// 10.3 +#define ASF_EXTENDED_STREAM_PROPERTIES "\xCB\xA5\xE6\x14\x72\xC6\x32\x43\x83\x99\xA9\x69\x52\x06\x5B\x5A" +#define ASF_METADATA "\xEA\xCB\xF8\xC5\xAF\x5B\x77\x48\x84\x67\xAA\x8C\x44\xFA\x4C\xCA" +#define ASF_METADATA_LIBRARY "\x94\x1C\x23\x44\x98\x94\xD1\x49\xA1\x41\x1D\x13\x4E\x45\x70\x54" +#define ASF_COMPATIBILITY2 "\x5D\x8B\xF1\x26\x84\x45\xEC\x47\x9F\x5F\x0E\x65\x1F\x04\x52\xC9" +// Actually 10.2 +#define ASF_PADDING "\x74\xD4\x06\x18\xDF\xCA\x09\x45\xA4\xBA\x9A\xAB\xCB\x96\xAA\xE8" +// 10.4 +#define ASF_AUDIO_MEDIA "\x40\x9E\x69\xF8\x4D\x5B\xCF\x11\xA8\xFD\x00\x80\x5F\x5C\x44\x2B" +#define ASF_VIDEO_MEDIA "\xC0\xEF\x19\xBC\x4D\x5B\xCF\x11\xA8\xFD\x00\x80\x5F\x5C\x44\x2B" +#define ASF_BINARY_MEDIA "\xE2\x65\xFB\x3A\xEF\x47\xF2\x40\xAC\x2C\x70\xA9\x0D\x71\xD3\x43" + +// ASF_BINARY_MEDIA : Major media types +#define DVRMS_AUDIO "\x9D\x8C\x17\x31\xE1\x03\x28\x45\xB5\x82\x3D\xF9\xDB\x22\xF5\x03" +#define DVRMS_NTSC "\x80\xEA\x0A\x67\x82\x3A\xD0\x11\xB7\x9B\x00\xAA\x00\x37\x67\xA7" +#define DVRMS_ATSC "\x89\x8A\x8B\xB8\x49\xB0\x80\x4C\xAD\xCF\x58\x98\x98\x5E\x22\xC1" + +// 10.13 - Undocumented DVR-MS properties +#define DVRMS_PTS "\x2A\xC0\x3C\xFD\xDB\x06\xFA\x4C\x80\x1C\x72\x12\xD3\x87\x45\xE4" diff --git a/src/asf_functions.cpp b/src/asf_functions.cpp new file mode 100644 index 00000000..3f5e9c6a --- /dev/null +++ b/src/asf_functions.cpp @@ -0,0 +1,1067 @@ +#include "ccextractor.h" +#include "asf_constants.h" + +// Indicate first / subsequent calls to asf_getmoredata() +int firstcall; + +// For ASF parsing +// 0, 1, 2, 3 means none, BYTE, WORD, DWORD +#define ASF_TypeLength(A) ( A == 3 ? 4 : A ) + + +uint32_t asf_readval(void *val, int ltype) +{ + uint32_t rval; + + switch (ltype) + { + case 0: + rval = 0; + break; + case 1: + rval = *((uint8_t*)val); + break; + case 2: + rval = *((uint16_t*)val); + break; + case 4: + rval = *((uint32_t*)val); + break; + default: + fatal (EXIT_BUG_BUG, "Wrong type ...\n"); + break; + } + return rval; +} + +char *guidstr(void *val) +{ + static char sbuf[32]; + + sprintf(sbuf,"%08lX-%04X-%04X-", + (long)*((uint32_t*)((char*)val+0)), + (int)*((uint16_t*)((char*)val+4)), + (int)*((uint16_t*)((char*)val+6))); + for(int ii=0; ii<2; ii++) + sprintf(sbuf+19+ii*2,"%02X-",*((unsigned char*)val+8+ii)); + for(int ii=0; ii<6; ii++) + sprintf(sbuf+24+ii*2,"%02X",*((unsigned char*)val+10+ii)); + + return sbuf; +} + +/* ASF container specific data parser + * The following function reads an ASF file and returns the included + * video stream. The function returns after a complete Media Object + * is read (the Media Object Number increases by one). A Media Object + * seems to consist of one frame. + * 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) +{ + int enough = 0; + int payload_read = 0; + + // The fist call to this function (per file) is indicated with + // firstcall == 1 + // Except for the first call of this function we will reenter + // the Data Packet loop below. + int reentry = 1; + + // Variables for + // Header Object + static int64_t HeaderObjectSize; + // File Properties Object + static int64_t FileSize = 0; + int64_t DataPacketsCount = 0; + int BroadcastFlag = 0; + int SeekableFlag = 0; + uint32_t MinPacketSize = 0; + uint32_t MaxPacketSize = 0; + static uint32_t PacketSize = 0; // Infered, see explanation below. + // Stream Properties Object + static int VideoStreamNumber = 0; + static int AudioStreamNumber = 0; + static int CaptionStreamNumber = 0; + static int CaptionStreamStyle = 0; // 1 = NTSC, 2 = ATSC + static int DecodeStreamNumber = 0; // The stream that is chosen to be decoded + static int DecodeStreamPTS = 0; // This will be used for the next returned block + static int currDecodeStreamPTS = 0; // Time of the data returned by the function + static int prevDecodeStreamPTS = 0; // Previous time + static int VideoStreamMS = 0; // See ableve, just for video + static int currVideoStreamMS = 0; + static int prevVideoStreamMS = 0; + static int VideoJump = 0; // Remember a jump in the video timeline + // Extended Stream Properties - for DVR-MS presentation timestamp + // Store the Payload Extension System Data Size. First index holds the + // stream number and the second index holds the Extension System entry. + // I.e. PayloadExtSize[1][2] is the third Payload Extension System + // entry for stream 1. (The streams are numbered starting from 1) + // FIXME: What happens if we have more than 9 streams with more than + // 10 entries. + const int STREAMNUM = 10; + const int PAYEXTNUM = 10; + static int PayloadExtSize[STREAMNUM][PAYEXTNUM]; + // Remember which entry held the DVRMS_PTS information + static int PayloadExtPTSEntry[STREAMNUM]; + + // Data Object Header + static int64_t DataObjectSize; + static uint32_t TotalDataPackets; + // Remember some values + static int VideoClosedCaptioningFlag = 0; + + // Data Object Loop + static uint32_t datapacketcur = 0; // Current packet number + static int64_t dobjectread = 50; // Bytes read in Data Object + int datapacketlength = 0; // Collect the read header bytes + + // Payload parsing information + static int MultiplePayloads = 0; // ASF + int SequenceType = 0; // ASF + int PaddingLType = 0; // ASF + static int PacketLType = 0; // ASF + static int ReplicatedLType = 0; // ASF + static int OffsetMediaLType = 0; // ASF + static int MediaNumberLType = 0; // ASF + static int StreamNumberLType = 0; // ASF + static uint32_t PacketLength = 0; + uint32_t Sequence = 0; + static uint32_t PaddingLength = 0; + uint32_t SendTime = 0; + + int payloadparsersize = 0; // Infered (PacketLType + SequenceType + PaddingLType + 6); + // Single/multiple payloads + static int PayloadLType = 0; // ASF - Payload Length Type. <>0 for multiple payloads + static uint32_t PayloadLength = 0; // ASF - Payload Length + static int NumberOfPayloads = 0; // ASF - Number of payloads. + static int payloadcur = 0; // local + + static int PayloadStreamNumber = 0; // ASF + static int KeyFrame = 0; // ASF + + static uint32_t PayloadMediaNumber = 0; // ASF + uint32_t OffsetMediaLength = 0; // ASF + uint32_t ReplicatedLength = 0; // ASF + + // Last media number. Used to determine a new PES, mark uninitialized. + uint32_t curmedianumber = 0xFFFFFFFF; + + + // Generic buffer to hold some data + static unsigned char *parsebuf = (unsigned char*)malloc(1024); + static long parsebufsize = 1024; + + unsigned char *curpos; + int64_t getbytes; + + // Read Header Object and the Top-level Data Object header only once + if(firstcall) + { + // Make sure the stream numbers are invalid when a new file begins + // so that they are only set when detected. + VideoStreamNumber = 0; + AudioStreamNumber = 0; + CaptionStreamNumber = 0; + CaptionStreamStyle = 0; + DecodeStreamNumber = 0; + + // Initialize the Payload Extension System + for(int stream=0; stream parsebufsize) { + parsebuf = (unsigned char*)realloc(parsebuf, (size_t) HeaderObjectSize); + if (!parsebuf) + fatal(EXIT_NOT_ENOUGH_MEMORY, "Out of memory"); + parsebufsize = (long) HeaderObjectSize; + } + + curpos = parsebuf + 30; + getbytes = HeaderObjectSize - 30; + + buffered_read(curpos, (int) getbytes); + past+=result; + if (result!=getbytes) + { + mprint("Premature end of file!\n"); + end_of_file=1; + return payload_read; + } + + dbg_print(CCX_DMT_PARSE, "Reading header objects\n"); + + while( curpos < parsebuf + HeaderObjectSize ) + { + int64_t hpobjectsize = *((int64_t*)(curpos+16)); // Local + + if( !memcmp(curpos, ASF_FILE_PROPERTIES, 16 ) ) + { + // Mandatory Object, only one. + dbg_print(CCX_DMT_PARSE, "\nFile Properties Object (size: %lld)\n", hpobjectsize); + + FileSize = *((int64_t*)(curpos+40)); + DataPacketsCount = *((int64_t*)(curpos+56)); + BroadcastFlag = 0x1 & curpos[88]; + 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", 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 ) ) + { + dbg_print(CCX_DMT_PARSE, "\nStream Properties Object (size: %lld)\n", hpobjectsize); + if( !memcmp(curpos+24, ASF_VIDEO_MEDIA, 16 ) ) + { + 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", VideoStreamNumber ); + + } + else if( !memcmp(curpos+24, ASF_AUDIO_MEDIA, 16 ) ) + { + AudioStreamNumber = *(curpos+72) & 0x7F; + dbg_print(CCX_DMT_PARSE, "Stream Type: ASF_Audio_Media\n"); + dbg_print(CCX_DMT_PARSE, "Audio Stream Number: %d\n", AudioStreamNumber ); + } + else + { + dbg_print(CCX_DMT_PARSE, "Stream Type: %s\n", + guidstr(curpos+24)); + dbg_print(CCX_DMT_PARSE, "Stream Number: %d\n", *(curpos+72) & 0x7F ); + } + } + else if( !memcmp(curpos,ASF_HEADER_EXTENSION, 16 ) ) + { + dbg_print(CCX_DMT_PARSE, "\nHeader Extension Object (size: %lld)\n", hpobjectsize); + + int32_t HeaderExtensionDataSize = *((uint32_t*)(curpos+42)); + // Process Header Extension Data + if ( HeaderExtensionDataSize ) + { + unsigned char *hecurpos=curpos+46; + + if ( HeaderExtensionDataSize != hpobjectsize - 46 ) + fatal(EXIT_NOT_CLASSIFIED, "HeaderExtensionDataSize size wrong"); + + dbg_print(CCX_DMT_PARSE, "\nReading Header Extension Sub-Objects\n"); + while( hecurpos < curpos+46 + HeaderExtensionDataSize ) + { + int64_t heobjectsize = *((int64_t*)(hecurpos+16)); // Local + + if( !memcmp(hecurpos,ASF_EXTENDED_STREAM_PROPERTIES, 16 ) ) + { + dbg_print(CCX_DMT_PARSE, "\nExtended Stream Properties Object (size: %lld)\n", heobjectsize); + int StreamNumber = *((uint16_t*)(hecurpos+72)); + int StreamNameCount = *((uint16_t*)(hecurpos+84)); + int PayloadExtensionSystemCount = *((uint16_t*)(hecurpos+86)); + + unsigned char *estreamproppos=hecurpos+88; + + int streamnamelength; + + dbg_print(CCX_DMT_PARSE, "Stream Number: %d NameCount: %d ESCount: %d\n", + StreamNumber, StreamNameCount, PayloadExtensionSystemCount); + + if ( StreamNumber >= STREAMNUM ) + fatal(EXIT_BUG_BUG, "STREAMNUM too small. Send bug report!/n"); + + for(int i=0; i PAYEXTNUM ) + fatal(EXIT_BUG_BUG, "PAYEXTNUM too small. Send bug report!/n"); + + for(int i=0; i8) + dbg_print(CCX_DMT_PARSE, "skipped %d more",DescriptorValueLength-8); + dbg_print(CCX_DMT_PARSE, " (BYTES)\n"); + break; + case 2: // BOOL + dbg_print(CCX_DMT_PARSE, "%d (BOOL)\n",*((int32_t*)edescval)); + break; + case 3: // DWORD + dbg_print(CCX_DMT_PARSE, "%u (DWORD)\n",*((uint32_t*)edescval)); + break; + case 4: // QWORD + dbg_print(CCX_DMT_PARSE, "%llu (QWORD)\n",*((uint64_t*)edescval)); + break; + case 5: // WORD + dbg_print(CCX_DMT_PARSE, "%u (WORD)\n",(int)*((uint16_t*)edescval)); + break; + default: + fatal(EXIT_BUG_BUG, "Wrong type ...\n"); + break; + } + + if(!memcmp(econtentpos+2, L"WM/VideoClosedCaptioning" + ,DescriptorNameLength)) + { + // This flag would be really usefull if it would be + // reliable - it isn't. + VideoClosedCaptioningFlag = *((int32_t*)edescval); + dbg_print(CCX_DMT_PARSE, "Found WM/VideoClosedCaptioning flag: %d\n", + VideoClosedCaptioningFlag); + } + + econtentpos+=6+DescriptorNameLength+DescriptorValueLength; + } + } + else if( !memcmp(curpos,ASF_STREAM_BITRATE_PROPERTIES, 16 ) ) + { + dbg_print(CCX_DMT_PARSE, "\nStream Bitrate Properties Object (size: %lld)\n", hpobjectsize); + } + else + { + dbg_print(CCX_DMT_PARSE, "\nGUID: %s size: %lld\n", + guidstr(curpos), hpobjectsize); + dump(CCX_DMT_PARSE, curpos, 16, 0, 0); + } + + curpos += hpobjectsize; + } + if (curpos - parsebuf != HeaderObjectSize) + fatal(EXIT_NOT_CLASSIFIED, "Parsing problem: read bytes %ld != header length %lld\nAbort!\n", + (long)(curpos - parsebuf), HeaderObjectSize); + + if ( VideoStreamNumber == 0) + fatal(EXIT_NOT_CLASSIFIED, "No Video Stream Properties Object found. Unable to continue ...\n"); + + // Wouldn't it be nice if VideoClosedCaptioningFlag would be usable, unfortunately + // it is not reliable. + + // Now decide where we are going to expect the captions + ccx_bufferdatatype = CCX_PES; // Except for NTSC captions + if ( CaptionStreamNumber > 0 + && (CaptionStreamStyle == 1 || + (CaptionStreamStyle == 2 && !ccx_options.wtvconvertfix)) ) + { + mprint("\nNote: If you converted a WTV into a DVR-MS and CCEextractor finds no captions, try passing -wtvconvertfix to work around bug in the conversion process."); + } + if ( CaptionStreamNumber > 0 + && (CaptionStreamStyle == 1 || + (CaptionStreamStyle == 2 && ccx_options.wtvconvertfix)) ) + { + //if (debug_parse) + mprint("\nNTSC captions in stream #%d\n\n", CaptionStreamNumber); + ccx_bufferdatatype = CCX_RAW; + DecodeStreamNumber = CaptionStreamNumber; + } + else if ( CaptionStreamNumber > 0 && CaptionStreamStyle == 2 ) + { + //if (debug_parse) + mprint("\nATSC captions (probably) in stream #%d - Decode the video stream #%d instead\n\n", + CaptionStreamNumber, VideoStreamNumber); + DecodeStreamNumber = VideoStreamNumber; + } + else + { + DecodeStreamNumber = VideoStreamNumber; + //if (debug_parse) + mprint("\nAssume CC info in video stream #%d (No caption stream found)\n\n", + DecodeStreamNumber); + } + + // When reading "Payload parsing information" it occured that "Packet Lenght" + // was not set (Packet Length Type 00) and for "Single Payloads" this means + // the Payload Data Length cannot be infered. Use MinPacketSize, MaxPacketSize instead. + if ((MinPacketSize > 0) && (MinPacketSize == MaxPacketSize)) + PacketSize = MinPacketSize; + + // Now the Data Object, except for the packages + buffered_read(parsebuf,50); // No realloc needed. + past+=result; + if (result!=50) + { + mprint("Premature end of file!\n"); + end_of_file=1; + return payload_read; + } + + // Expecting ASF Data object + if( !memcmp(parsebuf, ASF_DATA, 16 ) ) + { + dbg_print(CCX_DMT_PARSE, "\nASF Data Object\n"); + } + else + { + fatal(EXIT_NOT_CLASSIFIED, "Missing ASF Data Object. Abort.\n"); + } + + DataObjectSize = *((int64_t*)(parsebuf+16)); + TotalDataPackets = *((uint32_t*)(parsebuf+40)); + dbg_print(CCX_DMT_PARSE, "Size: %lld\n", DataObjectSize); + dbg_print(CCX_DMT_PARSE, "Number of data packets: %ld\n", (long)TotalDataPackets); + + + reentry = 0; // Make sure we read the Data Packet Headers + } // End of if (firstcall) + firstcall = 0; + + // Start loop over Data Packets + while ( datapacketcur < TotalDataPackets && !enough) + { + // Skip reading the headers the first time when reentering the loop + if(!reentry) + { + int ecinfo = 0; + + datapacketlength = 0; + + dbg_print(CCX_DMT_PARSE,"\nReading packet %d/%d\n", datapacketcur+1, TotalDataPackets); + + // First packet + buffered_read(parsebuf,1); // No realloc needed. + past+=result; + dobjectread+=result; + if (result!=1) + { + mprint("Premature end of file!\n"); + end_of_file=1; + return payload_read; + } + datapacketlength+=1; + if (*parsebuf & 0x80) + { + int ecdatalength = *parsebuf & 0x0F; // Small, no realloc needed + if ( *parsebuf & 0x60 ) + { + fatal(EXIT_NOT_CLASSIFIED, "Error Correction Length Type not 00 - reserved - aborting ...\n"); + } + buffered_read(parsebuf+1,ecdatalength); + past+=result; + dobjectread+=result; + if (result!=ecdatalength) + { + mprint("Premature end of file!\n"); + end_of_file=1; + return payload_read; + } + datapacketlength+=ecdatalength; + if ( parsebuf[1] & 0x0F ) + fatal(EXIT_NOT_CLASSIFIED, "Error correction present. Unable to continue ...\n"); + } + else + { + // When no ecinfo is present the byte we just read belongs + // to the payload parsing information. + ecinfo = 1; + } + + // Now payload parsing information + buffered_read(parsebuf+ecinfo,2-ecinfo); // No realloc needed + past+=result; + dobjectread+=result; + if (result!=2) + { + mprint("Premature end of file!\n"); + end_of_file=1; + return payload_read; + } + datapacketlength+=2; + + MultiplePayloads = *parsebuf & 0x01; + + SequenceType = (*parsebuf >> 1) & 0x03; + SequenceType = ASF_TypeLength(SequenceType); + + PaddingLType = (*parsebuf >> 3) & 0x03; + PaddingLType = ASF_TypeLength(PaddingLType); + + PacketLType = (*parsebuf >> 5) & 0x03; + PacketLType = ASF_TypeLength(PacketLType); + + ReplicatedLType = (parsebuf[1]) & 0x03; + ReplicatedLType = ASF_TypeLength(ReplicatedLType); + + OffsetMediaLType = (parsebuf[1] >> 2) & 0x03; + OffsetMediaLType = ASF_TypeLength(OffsetMediaLType); + + MediaNumberLType = (parsebuf[1] >> 4) & 0x03; + MediaNumberLType = ASF_TypeLength(MediaNumberLType); + + StreamNumberLType = (parsebuf[1] >> 6) & 0x03; + StreamNumberLType = ASF_TypeLength(StreamNumberLType); + + payloadparsersize = PacketLType + SequenceType + PaddingLType + 6; + + buffered_read(parsebuf+2, payloadparsersize); // No realloc needed + past+=result; + dobjectread+=result; + if (result!=payloadparsersize) + { + mprint("Premature end of file!\n"); + end_of_file=1; + return payload_read; + } + datapacketlength+=payloadparsersize; + + PacketLength = asf_readval(parsebuf+2, PacketLType); + Sequence = asf_readval(parsebuf+2+PacketLType, SequenceType); + PaddingLength = asf_readval(parsebuf+2+PacketLType+SequenceType, PaddingLType); + // Data Packet ms time stamp + SendTime = *((uint32_t*)(parsebuf+2+PacketLType+SequenceType+PaddingLType)); + + // If Packet Length is not set use global setting if possible + if (PacketLength == 0) + { + PacketLength = PacketSize; + // For multiple payloads we can get away without a given + // Packet Lenght as individual payload length are given + if (PacketLength == 0 && MultiplePayloads == 0) + fatal(EXIT_NOT_CLASSIFIED, "No idea how long the data packet will be. Abort.\n"); + } + + dbg_print(CCX_DMT_PARSE, "Lengths - Packet: %d / Sequence %d / Padding %d\n", + PacketLength, Sequence, PaddingLength); + + PayloadLType = 0; // Payload Length Type. <>0 for multiple payloads + PayloadLength = 0; // Payload Length (for multiple payloads) + NumberOfPayloads = 1; // Number of payloads. + + if (MultiplePayloads != 0) + { + unsigned char plheader[1]; + + buffered_read(plheader, 1); + past+=result; + dobjectread+=result; + if (result!=1) + { + mprint("Premature end of file!\n"); + end_of_file=1; + return payload_read; + } + datapacketlength+=1; + PayloadLType = (*plheader >> 6) & 0x03; + PayloadLType = ASF_TypeLength(PayloadLType); + + // Number of payloads + NumberOfPayloads = *plheader & 0x3F; + } + payloadcur = 0; + } + else + { // Rely on + // NumberOfPayloads, payloadcur, PayloadLength, PaddingLength + // and related variables being kept as static variables to be + // able to reenter the loop here. + dbg_print(CCX_DMT_PARSE, "\nReentry into asf_getmoredata()\n"); + } + + // The following repeats NumberOfPayloads times + while( payloadcur < NumberOfPayloads && !enough) + { + // Skip reading the Payload headers the first time when reentering the loop + if (!reentry) + { + if (NumberOfPayloads < 2) + dbg_print(CCX_DMT_PARSE, "\nSingle payload\n"); + else + dbg_print(CCX_DMT_PARSE, "\nMultiple payloads %d/%d\n", payloadcur+1, NumberOfPayloads); + + int payloadheadersize = 1+MediaNumberLType+OffsetMediaLType+ReplicatedLType; + + buffered_read(parsebuf, payloadheadersize); // No realloc needed + past+=result; + dobjectread+=result; + if (result!=payloadheadersize) + { + mprint("Premature end of file!\n"); + end_of_file=1; + return payload_read; + } + datapacketlength+=payloadheadersize; + + PayloadStreamNumber = *parsebuf & 0x7F; + KeyFrame = (*parsebuf & 0x80) && 1; + + PayloadMediaNumber = asf_readval(parsebuf+1, MediaNumberLType); + OffsetMediaLength = asf_readval(parsebuf+1+MediaNumberLType, OffsetMediaLType); + ReplicatedLength = asf_readval(parsebuf+1+MediaNumberLType+OffsetMediaLType, ReplicatedLType); + + if (ReplicatedLength == 1) + fatal(EXIT_NOT_CLASSIFIED, "Cannot handle compressed data...\n"); + + if ( (long)ReplicatedLength > parsebufsize) + { + parsebuf = (unsigned char*)realloc(parsebuf, ReplicatedLength); + if (!parsebuf) + fatal(EXIT_NOT_ENOUGH_MEMORY, "Out of memory"); + parsebufsize = ReplicatedLength; + } + buffered_read(parsebuf, (long)ReplicatedLength); + past+=result; + dobjectread+=result; + if (result!=ReplicatedLength) + { + mprint("Premature end of file!\n"); + end_of_file=1; + return payload_read; + } + // Parse Replicated data + unsigned char *reppos = parsebuf; + int MediaObjectSize = 0; + int PresentationTimems = 0; //Payload ms time stamp + int extsize = 0; + int32_t dwVersion = 0; + int32_t unknown = 0; + int64_t rtStart = 0; // dvr-ms 100ns time stamp start + int64_t rtEnd = 0; // dvr-ms 100ns time stamp end + + // Always at least 8 bytes long, see 7.3.1 + MediaObjectSize = *((uint16_t*)(parsebuf)); + PresentationTimems = *((uint16_t*)(parsebuf+4)); + reppos += 8; + + dbg_print(CCX_DMT_PARSE, "Stream# %d[%d] Media# %d Offset/Size: %d/%d\n", + PayloadStreamNumber, KeyFrame, PayloadMediaNumber, + OffsetMediaLength, MediaObjectSize); + + // Loop over Payload Extension Systems + for(int i=0; i 0 ) + { + dwVersion = *((uint32_t*)(reppos+0)); + unknown = *((uint32_t*)(reppos+4)); + rtStart = *((int64_t*)(reppos+8)); + rtEnd = *((int64_t*)(reppos+16)); + + //printf("dwVersion: %d unknown: 0x%04X\n", dwVersion, unknown); + } + + // Avoid problems with unset PTS times + if (rtStart == -1) + { + rtStart = 0; + rtEnd = 0; + dbg_print(CCX_DMT_PARSE, "dvr-ms time not defined\n"); + } + + // print_mstime uses a static buffer + dbg_print(CCX_DMT_PARSE, "Stream #%d PacketTime: %s", + PayloadStreamNumber, print_mstime(SendTime)); + dbg_print(CCX_DMT_PARSE," PayloadTime: %s", + 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 + if (MultiplePayloads != 0) + { + unsigned char plheader[4]; + + buffered_read(plheader, PayloadLType); + past+=result; + dobjectread+=result; + if (result!=PayloadLType) + { + mprint("Premature end of file!\n"); + end_of_file=1; + return payload_read; + } + PayloadLength = asf_readval(plheader, PayloadLType); + } + else + { + PayloadLength = PacketLength - datapacketlength - PaddingLength; + } + dbg_print(CCX_DMT_PARSE, "Size - Replicated %d + Payload %d = %d\n", + ReplicatedLength, PayloadLength, ReplicatedLength+PayloadLength); + + // Remember the last video time stamp - only when captions are separate + // from video stream. + if (PayloadStreamNumber == VideoStreamNumber + && DecodeStreamNumber != VideoStreamNumber + && OffsetMediaLength == 0) + { + prevVideoStreamMS = currVideoStreamMS; + currVideoStreamMS = VideoStreamMS; + + // Use PresentationTimems (SendTime might also work) when the + // dvr-ms time stamp is not present. + if ( PayloadExtPTSEntry[PayloadStreamNumber] > 0 ) + { + // When rstart is not set, keep the previous value + if (rtStart > 0) + VideoStreamMS = (int) (rtStart/10000); + } + else + { + // Add 1ms to avoid 0ms start times getting rejected + VideoStreamMS = PresentationTimems+1; + } + // This checks if there is a video time jump in the timeline + // between caption information. + if ( abs(currVideoStreamMS - prevVideoStreamMS) > 500 ) + { + // Found a more than 500ms jump in the video timeline + VideoJump = 1; + // This is remembered until the next caption block is + // found. + } + } + + // Remember the PTS values + if (PayloadStreamNumber == DecodeStreamNumber + && OffsetMediaLength == 0) + { + prevDecodeStreamPTS = currDecodeStreamPTS; + currDecodeStreamPTS = DecodeStreamPTS; + + // Use PresentationTimems (SendTime might also work) when the + // dvr-ms time stamp is not present. + if ( PayloadExtPTSEntry[PayloadStreamNumber] > 0 ) + { + // When rstart is not set, keep the previous value + if (rtStart > 0) + DecodeStreamPTS = (int) (rtStart/10000); + } + else + { + // Add 1ms to avoid 0ms start times getting rejected + DecodeStreamPTS = PresentationTimems+1; + } + + // Check the caption stream for jumps in the timeline. This + // is needed when caption information is transmitted in a + // different stream then the video information. (For example + // NTSC recordings.) Otherwise a gap in the caption + // information would look like a jump in the timeline. + if (DecodeStreamNumber != VideoStreamNumber) + { + // Check if there is a gap larger than 500ms. + if ( currDecodeStreamPTS - prevDecodeStreamPTS > 500 ) + { + // Found more than 500ms since the previous caption, + // now check the video timeline. If there was a also + // a jump this needs synchronizing, otherwise it was + // just a gap in the captions. + if ( !VideoJump ) + CaptionGap = 1; + else + CaptionGap = 0; + } + VideoJump = 0; + } + + // Remember, we are reading the previous package. + current_pts = currDecodeStreamPTS*(MPEG_CLOCK_FREQ/1000); + if (pts_set==0) + pts_set=1; + } + } + + // A new media number. The old "object" finished, we stop here to + // continue later. + // To continue later we need to remember: + // NumberOfPayloads + // payloadcur + // PayloadLength + // PaddingLength + + // Now, the next loop is no reentry anymore: + reentry = 0; + + // Video streams need several packages to complete a PES. Leave + // the loop when the next package starts a new Media Object. + if ( curmedianumber != 0xFFFFFFFF // Is initialized + && PayloadStreamNumber == DecodeStreamNumber + && PayloadMediaNumber != curmedianumber) + { + if ( DecodeStreamNumber == CaptionStreamNumber ) + dbg_print(CCX_DMT_PARSE, "\nCaption stream object"); + else + dbg_print(CCX_DMT_PARSE, "\nVideo stream object"); + dbg_print(CCX_DMT_PARSE, " read with PTS: %s\n", + print_mstime(currDecodeStreamPTS)); + + + // Enough for now + enough = 1; + break; + } + + // Read it!! + if ( PayloadStreamNumber == DecodeStreamNumber ) + { + curmedianumber = PayloadMediaNumber; // Remember current value + + // Read the data + dbg_print(CCX_DMT_PARSE, "Reading Stream #%d data ...\n", PayloadStreamNumber); + + int want = (long) ((BUFSIZE-inbuf)>PayloadLength ? + PayloadLength : (BUFSIZE-inbuf)); + if (want < (long)PayloadLength ) + fatal(EXIT_BUG_BUG, "Buffer size to small for ASF payload!\nPlease file a bug report!\n"); + buffered_read (buffer+inbuf,want); + payload_read+=(int) result; + inbuf+=result; + past+=result; + if (result!=PayloadLength) + { + mprint("Premature end of file!\n"); + end_of_file=1; + return payload_read; + } + dobjectread+=PayloadLength; + } + else + { + // Skip non-cc data + dbg_print(CCX_DMT_PARSE, "Skipping Stream #%d data ...\n", PayloadStreamNumber); + buffered_skip ((int) PayloadLength); + past+=result; + if (result!=PayloadLength) + { + mprint("Premature end of file!\n"); + end_of_file=1; + return payload_read; + } + dobjectread+=result; + } + + payloadcur++; + } + if (enough) + break; + + // Skip padding bytes + dbg_print(CCX_DMT_PARSE, "Skip %d padding\n", PaddingLength); + buffered_skip((long)PaddingLength); + past+=result; + if (result!=PaddingLength) + { + mprint("Premature end of file!\n"); + end_of_file=1; + return payload_read; + } + dobjectread+=result; + + datapacketcur++; + dbg_print(CCX_DMT_PARSE, "Bytes read: %lld/%lld\n", dobjectread, DataObjectSize); + } + + if ( datapacketcur == TotalDataPackets ) + { + dbg_print(CCX_DMT_PARSE, "\nWe read the last packet!\n\n"); + + // Skip the rest of the file + dbg_print(CCX_DMT_PARSE, "Skip the rest: %d\n",int(FileSize - HeaderObjectSize - DataObjectSize)); + buffered_skip(int(FileSize - HeaderObjectSize - DataObjectSize)); + past+=result; + // Don not set end_of_file (although it is true) as this would + // produce an premature end error. + //end_of_file=1; + + // parsebuf is freed automatically when the program closes. + } + + return payload_read; +} diff --git a/src/avc_functions.cpp b/src/avc_functions.cpp new file mode 100644 index 00000000..20709336 --- /dev/null +++ b/src/avc_functions.cpp @@ -0,0 +1,1046 @@ +#include "ccextractor.h" +#include + +// 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; + +// local functions +static unsigned char *remove_03emu(unsigned char *from, unsigned char *to); +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 unsigned char cc_count; +// buffer to hold cc data +static unsigned char *cc_data = (unsigned char*)malloc(1024); +static long cc_databufsize = 1024; +int cc_buffer_saved=1; // Was the CC buffer saved after it was last updated? + +static int got_seq_para=0; +static unsigned nal_ref_idc; +static LLONG seq_parameter_set_id; +static int log2_max_frame_num=0; +static int pic_order_cnt_type; +static int log2_max_pic_order_cnt_lsb=0; +static int frame_mbs_only_flag; + +// Use and throw stats for debug, remove this uglyness soon +long num_nal_unit_type_7=0; +long num_vcl_hrd=0; +long num_nal_hrd=0; +long num_jump_in_frames=0; +long num_unexpected_sei_length=0; + +inline double roundportable(double x) { return floor(x + 0.5); } + +int ebsp_to_rbsp(char* rbsp, char* ebsp, int length); +static char s_rbsp[1024*1024]; + +void do_NAL (unsigned char *NALstart, LLONG NAL_length) +{ + unsigned char *NALstop; + unsigned nal_unit_type = *NALstart & 0x1F; + + 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. + { + mprint ("\rNotice: NAL of type %u had to be skipped because remove_03emu failed.\n", nal_unit_type); + return; + } + + if ( nal_unit_type == CCX_NAL_TYPE_ACCESS_UNIT_DELIMITER_9 ) + { + // Found Access Unit Delimiter + } + else if ( nal_unit_type == CCX_NAL_TYPE_SEQUENCE_PARAMETER_SET_7 ) + { + // Found sequence parameter set + // We need this to parse NAL type 1 (CCX_NAL_TYPE_CODED_SLICE_NON_IDR_PICTURE_1) + 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 || + nal_unit_type == CCX_NAL_TYPE_CODED_SLICE_IDR_PICTURE)) // Only if nal_unit_type=1 + { + // Found coded slice of a non-IDR picture + // We only need the slice header data, no need to implement + // slice_layer_without_partitioning_rbsp( ); + slice_header(NALstart+1, NALstop, nal_unit_type); + } + else if ( got_seq_para && nal_unit_type == CCX_NAL_TYPE_SEI ) + { + // Found SEI (used for subtitles) + //set_fts(); // FIXME - check this!!! + sei_rbsp(NALstart+1, NALstop); + } + else if ( got_seq_para && nal_unit_type == CCX_NAL_TYPE_PICTURE_PARAMETER_SET ) + { + // Found Picture parameter set + } + if (temp_debug) + { + mprint ("NAL process failed.\n"); + mprint ("\n After decoding, the actual thing was (length =%d)\n", NALstop-(NALstart+1)); + dump (CCX_DMT_GENERIC_NOTICES,NALstart+1, NALstop-(NALstart+1),0, 0); + } + +} + +// 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) +{ + unsigned char *bpos = avcbuf; + unsigned char *NALstart; + unsigned char *NALstop; + + // At least 5 bytes are needed for a NAL unit + if(avcbuflen <= 5) + { + fatal(EXIT_BUG_BUG, + "NAL unit need at last 5 bytes ..."); + } + + // Warning there should be only leading zeros, nothing else + if( !(bpos[0]==0x00 && bpos[1]==0x00) ) + { + fatal(EXIT_BUG_BUG, + "Broken AVC stream - no 0x0000 ..."); + } + bpos = bpos+2; + + int firstloop=1; // Check for valid start code at buffer start + + // Loop over NAL units + while(bpos < avcbuf + avcbuflen - 2) // bpos points to 0x01 plus at least two bytes + { + int zeropad=0; // Count leading zeros + + // Find next NALstart + while (bpos < avcbuf + avcbuflen) + { + if(*bpos == 0x01) + { + // OK, found a start code + break; + } + else if(firstloop && *bpos != 0x00) + { + // Not 0x00 or 0x01 + fatal(EXIT_BUG_BUG, + "Broken AVC stream - no 0x00 ..."); + } + bpos++; + zeropad++; + } + firstloop=0; + if (bpos >= avcbuf + avcbuflen) + { + // No new start sequence + break; + } + NALstart = bpos+1; + + // Find next start code or buffer end + LLONG restlen; + do + { + // Search for next 000000 or 000001 + bpos++; + restlen = avcbuf - bpos + avcbuflen - 2; // leave room for two more bytes + + // Find the next zero + if (restlen > 0) + { + bpos = (unsigned char *) memchr (bpos, 0x00, (size_t) restlen); + + if(!bpos) + { + // No 0x00 till the end of the buffer + NALstop = avcbuf + avcbuflen; + bpos = NALstop; + break; + } + + if(bpos[1]==0x00 && (bpos[2]|0x01)==0x01) + { + // Found new start code + NALstop = bpos; + bpos = bpos + 2; // Move after the two leading 0x00 + break; + } + } + else + { + NALstop = avcbuf + avcbuflen; + bpos = NALstop; + break; + } + } + while(restlen); // Should never be true - loop is exited via break + + if(*NALstart & 0x80) + { + dump(CCX_DMT_GENERIC_NOTICES, NALstart-4,10, 0, 0); + fatal(EXIT_BUG_BUG, + "Broken AVC stream - forbidden_zero_bit not zero ..."); + } + + nal_ref_idc = *NALstart >> 5; + unsigned nal_unit_type = *NALstart & 0x1F; + + 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); + + 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 + +// Copied for reference decoder, see if it behaves different that Volker's code +int EBSPtoRBSP(unsigned char *streamBuffer, int end_bytepos, int begin_bytepos) +{ + int i, j, count; + count = 0; + + if(end_bytepos < begin_bytepos) + return end_bytepos; + + j = 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; + 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 + if((i < end_bytepos-1) && (streamBuffer[i+1] > 0x03)) + return -1; + //if cabac_zero_word is used, the final byte of this NAL unit(0x03) is discarded, and the last two bytes of RBSP must be 0x0000 + if(i == end_bytepos-1) + return j; + + ++i; + count = 0; + } + streamBuffer[j] = streamBuffer[i]; + if(streamBuffer[i] == 0x00) + ++count; + else + count = 0; + ++j; + } + + return j; +} +typedef unsigned int u32; + + +// Remove 0x000003 emulation sequences. We do this on-site (same buffer) because the new array +// will never be longer than the source. +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 newsize = EBSPtoRBSP (from,num,0); //TODO: Do something if newsize == -1 (broken NAL) + if (newsize==-1) + return NULL; + return from+newsize; +} + + +// Process SEI payload in AVC data. This function combines sei_rbsp() +// and rbsp_trailing_bits(). +void sei_rbsp (unsigned char *seibuf, unsigned char *seiend) +{ + unsigned char *tbuf = seibuf; + while(tbuf < seiend - 1) // Use -1 because of trailing marker + { + tbuf = sei_message(tbuf, seiend - 1); + } + if(tbuf == seiend - 1 ) + { + if(*tbuf != 0x80) + mprint("Strange rbsp_trailing_bits value: %02X\n",*tbuf); + } + else + { + // TODO: This really really looks bad + mprint ("WARNING: Unexpected SEI unit length...trying to continue."); + temp_debug = 1; + mprint ("\n Failed block (at sei_rbsp) was:\n"); + dump (CCX_DMT_GENERIC_NOTICES,(unsigned char *) seibuf, seiend-seibuf,0,0); + + num_unexpected_sei_length++; + } +} + + +// This combines sei_message() and sei_payload(). +unsigned char *sei_message (unsigned char *seibuf, unsigned char *seiend) +{ + int payloadType = 0; + while (*seibuf==0xff) + { + payloadType+=255; + seibuf++; + } + payloadType += *seibuf; + seibuf++; + + + int payloadSize = 0; + while (*seibuf==0xff) + { + payloadSize+=255; + seibuf++; + } + payloadSize += *seibuf; + seibuf++; + + int broken=0; + unsigned char *paystart = seibuf; + seibuf+=payloadSize; + + dvprint("Payload type: %d size: %d - ", payloadType, payloadSize); + if(seibuf > seiend ) + { + // TODO: What do we do here? + broken=1; + if (payloadType==4) + { + dbg_print(CCX_DMT_VERBOSE, "Warning: Subtitles payload seems incorrect (too long), continuing but it doesn't look good.."); + } + else + { + dbg_print(CCX_DMT_VERBOSE, "Warning: Non-subtitles payload seems incorrect (too long), continuing but it doesn't look good.."); + } + } + dbg_print(CCX_DMT_VERBOSE, "\n"); + // Ignore all except user_data_registered_itu_t_t35() payload + if(!broken && payloadType == 4) + user_data_registered_itu_t_t35(paystart, paystart+payloadSize); + + return seibuf; +} + +void copy_ccdata_to_buffer (char *source, int new_cc_count) +{ + ccblocks_in_avc_total++; + if (cc_buffer_saved==0) + { + 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); + cc_count+=new_cc_count; + cc_buffer_saved=0; +} + + +void user_data_registered_itu_t_t35 (unsigned char *userbuf, unsigned char *userend) +{ + unsigned char *tbuf = userbuf; + unsigned char *cc_tmpdata; + unsigned char process_cc_data_flag; + int user_data_type_code; + int user_data_len; + int local_cc_count=0; + // int cc_count; + int itu_t_t35_country_code = *((uint8_t*)tbuf); + tbuf++; + int itu_t_35_provider_code = *tbuf * 256 + *(tbuf+1); + tbuf+=2; + + // ANSI/SCTE 128 2008: + // itu_t_t35_country_code == 0xB5 + // itu_t_35_provider_code == 0x0031 + // see spec for details - no example -> no support + + // Example files (sample.ts, ...): + // itu_t_t35_country_code == 0xB5 + // itu_t_35_provider_code == 0x002F + // user_data_type_code == 0x03 (cc_data) + // user_data_len == next byte (length after this byte up to (incl) marker.) + // cc_data struct (CEA-708) + // marker == 0xFF + + if(itu_t_t35_country_code != 0xB5) + { + mprint("Not a supported user data SEI\n"); + mprint(" itu_t_35_country_code: %02x\n", itu_t_t35_country_code); + return; + } + + 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 + { + dbg_print(CCX_DMT_VERBOSE, "ATSC1_data()...\n"); + tbuf+=4; + unsigned char user_data_type_code=*tbuf; + tbuf++; + switch (user_data_type_code) + { + case 0x03: + dbg_print(CCX_DMT_VERBOSE, "cc_data (finally)!\n"); + /* + cc_count = 2; // Forced test + process_cc_data_flag = (*tbuf & 2) >> 1; + mandatory_1 = (*tbuf & 1); + mandatory_0 = (*tbuf & 4) >>2; + if (!mandatory_1 || mandatory_0) + { + printf ("Essential tests not passed.\n"); + break; + } + */ + 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. + if (! (*tbuf & 0x80)) // First bit must be 1 + { + printf ("Fixed bit should be 1, but it's 0 - skipping this caption block.\n"); + break; + } + if (*tbuf & 0x20) // Third bit must be 0 + { + printf ("Fixed bit should be 0, but it's 1 - skipping this caption block.\n"); + break; + } */ + tbuf++; + /* + Another test that the samples ignore. They contain 00! + if (*tbuf!=0xFF) + { + printf ("Fixed value should be 0xFF, but it's %02X - skipping this caption block.\n", *tbuf); + } */ + // OK, all checks passed! + tbuf++; + cc_tmpdata = tbuf; + + /* TODO: I don't think we have user_data_len here + if (cc_count*3+3 != user_data_len) + fatal(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, + "Syntax problem: Too many caption blocks."); + if (cc_tmpdata[local_cc_count*3]!=0xFF) + fatal(EXIT_BUG_BUG, + "Syntax problem: Final 0xFF marker missing."); + + // Save the data and process once we know the sequence number + if (local_cc_count*3+1 > cc_databufsize) + { + cc_data = (unsigned char*)realloc(cc_data, (size_t) cc_count*6+1); + if (!cc_data) + fatal(EXIT_NOT_ENOUGH_MEMORY, "Out of memory"); + cc_databufsize = (long) cc_count*6+1; + } + // Copy new cc data into cc_data + copy_ccdata_to_buffer ((char *) cc_tmpdata, local_cc_count); + break; + case 0x06: + dbg_print(CCX_DMT_VERBOSE, "bar_data (unsupported for now)\n"); + break; + 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 + // 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 + // rectangular picture area containing + // useful information extends to the fullheight and width of the + // coded frame." + } + else + { + dbg_print(CCX_DMT_VERBOSE, "SCTE/ATSC reserved.\n"); + } + break; + case 0x002F: + dbg_print(CCX_DMT_VERBOSE, "ATSC1_data() - provider_code = 0x002F\n"); + + user_data_type_code = *((uint8_t*)tbuf); + if(user_data_type_code != 0x03) + { + dbg_print(CCX_DMT_VERBOSE, "Not supported user_data_type_code: %02x\n", + user_data_type_code); + return; + } + tbuf++; + user_data_len = *((uint8_t*)tbuf); + tbuf++; + + 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; + } + cc_tmpdata = tbuf+2; + + if (local_cc_count*3+3 != user_data_len) + fatal(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, + "Syntax problem: Too many caption blocks."); + if (cc_tmpdata[local_cc_count*3]!=0xFF) + fatal(EXIT_BUG_BUG, + "Syntax problem: Final 0xFF marker missing."); + + // Save the data and process once we know the sequence number + if (cc_count*3+1 > cc_databufsize) + { + cc_data = (unsigned char*)realloc(cc_data, (size_t) cc_count*6+1); + if (!cc_data) + fatal(EXIT_NOT_ENOUGH_MEMORY, "Out of memory"); + cc_databufsize = (long) cc_count*6+1; + } + // Copy new cc data into cc_data - replace command below. + copy_ccdata_to_buffer ((char *) cc_tmpdata, local_cc_count); + + //dump(tbuf,user_data_len-1,0); + break; + default: + mprint("Not a supported user data SEI\n"); + mprint(" itu_t_35_provider_code: %04x\n", itu_t_35_provider_code); + break; + } +} + + +// Process sequence parameters in AVC data. +void seq_parameter_set_rbsp (unsigned char *seqbuf, unsigned char *seqend) +{ + LLONG tmp, tmp1; + struct bitstream q1; + init_bitstream(&q1, seqbuf, seqend); + + dvprint("SEQUENCE PARAMETER SET (bitlen: %lld)\n", q1.bitsleft); + tmp=u(&q1,8); + dvprint("profile_idc= %llX\n", tmp); + tmp=u(&q1,1); + dvprint("constraint_set0_flag= %llX\n", tmp); + tmp=u(&q1,1); + dvprint("constraint_set1_flag= %llX\n", tmp); + tmp=u(&q1,1); + dvprint("constraint_set2_flag= %llX\n", tmp); + tmp=u(&q1,5); + dvprint("reserved= %llX\n", tmp); + tmp=u(&q1,8); + dvprint("level_idc= %llX\n", 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); + pic_order_cnt_type = (int)ue(&q1); + dvprint("pic_order_cnt_type= %X\n", 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); + } + 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); + tmp = se(&q1); + dvprint("offset_for_non_ref_pic= %llX\n", tmp); + tmp = se(&q1); + dvprint("offset_for_top_to_bottom_field %llX\n", 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); + for (int i=0; i 1 is not yet supported."); + } + + tmp=ue(&q1); + dvprint("num_ref_frames= %llX\n", tmp); + + tmp=u(&q1,1); + dvprint("gaps allowed= %llX\n", tmp); + tmp=ue(&q1); + dvprint("pic_width_in_mbs_minus1= %llX\n", tmp); + tmp=ue(&q1); + dvprint("pic_height_in_map_units_minus1= %llX\n", tmp); + frame_mbs_only_flag = (int)u(&q1,1); + dvprint("frame_mbs_only_flag= %X\n", frame_mbs_only_flag); + if ( !frame_mbs_only_flag ) + { + tmp=u(&q1,1); + dvprint("mb_adaptive_fr_fi_flag= %llX\n", tmp); + } + tmp=u(&q1,1); + dvprint("direct_8x8_inference_f= %llX\n", tmp); + tmp=u(&q1,1); + dvprint("frame_cropping_flag= %llX\n", tmp); + if ( tmp ) + { + tmp=ue(&q1); + dvprint("frame_crop_left_offset= %llX\n", tmp); + tmp=ue(&q1); + dvprint("frame_crop_right_offset= %llX\n", tmp); + tmp=ue(&q1); + dvprint("frame_crop_top_offset= %llX\n", tmp); + tmp=ue(&q1); + dvprint("frame_crop_bottom_offset= %llX\n", tmp); + } + tmp=u(&q1,1); + dvprint("vui_parameters_present= %llX\n", tmp); + if ( tmp ) + { + dvprint("\nVUI parameters\n"); + tmp=u(&q1,1); + dvprint("aspect_ratio_info_pres= %llX\n", tmp); + if ( tmp ) + { + tmp=u(&q1,8); + dvprint("aspect_ratio_idc= %llX\n", tmp); + if ( tmp == 255 ) + { + tmp=u(&q1,16); + dvprint("sar_width= %llX\n", tmp); + tmp=u(&q1,16); + dvprint("sar_height= %llX\n", tmp); + } + } + tmp=u(&q1,1); + dvprint("overscan_info_pres_flag= %llX\n", tmp); + if ( tmp ) + { + tmp=u(&q1,1); + dvprint("overscan_appropriate_flag= %llX\n", tmp); + } + tmp=u(&q1,1); + dvprint("video_signal_type_present_flag= %llX\n", tmp); + if ( tmp ) + { + tmp=u(&q1,3); + dvprint("video_format= %llX\n", tmp); + tmp=u(&q1,1); + dvprint("video_full_range_flag= %llX\n", tmp); + tmp=u(&q1,1); + dvprint("colour_description_present_flag= %llX\n", tmp); + if ( tmp ) + { + tmp=u(&q1,8); + dvprint("colour_primaries= %llX\n", tmp); + tmp=u(&q1,8); + dvprint("transfer_characteristics= %llX\n", tmp); + tmp=u(&q1,8); + dvprint("matrix_coefficients= %llX\n", tmp); + } + } + tmp=u(&q1,1); + dvprint("chroma_loc_info_present_flag= %llX\n", tmp); + if ( tmp ) + { + tmp=ue(&q1); + dvprint("chroma_sample_loc_type_top_field= %llX\n", tmp); + tmp=ue(&q1); + dvprint("chroma_sample_loc_type_bottom_field= %llX\n", tmp); + } + tmp=u(&q1,1); + dvprint("timing_info_present_flag= %llX\n", tmp); + if ( tmp ) + { + tmp=u(&q1,32); + dvprint("num_units_in_tick= %llX\n", tmp); + tmp=u(&q1,32); + dvprint("time_scale= %llX\n", tmp); + tmp=u(&q1,1); + dvprint("fixed_frame_rate_flag= %llX\n", tmp); + } + tmp=u(&q1,1); + dvprint("nal_hrd_parameters_present_flag= %llX\n", tmp); + if ( tmp ) + { + 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"); + num_vcl_hrd++; + // exit(1); + } + if ( tmp || tmp1 ) + { + tmp=u(&q1,1); + dvprint("low_delay_hrd_flag= %llX\n", tmp); + return; + } + tmp=u(&q1,1); + dvprint("pic_struct_present_flag= %llX\n", tmp); + tmp=u(&q1,1); + dvprint("bitstream_restriction_flag= %llX\n", 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 + // currently not needed. + } + //exit(1); +} + + +// Process slice header in AVC data. +void slice_header (unsigned char *heabuf, unsigned char *heaend, int nal_unit_type) +{ + LLONG tmp; + struct bitstream q1; + if (init_bitstream(&q1, heabuf, heaend)) + { + mprint ("Skipping slice header due to failure in init_bitstream.\n"); + return; + } + + LLONG slice_type, bottom_field_flag=0, pic_order_cnt_lsb=-1; + static LLONG frame_num=-1, lastframe_num = -1; + static int currref=0, maxidx=-1, lastmaxidx=-1; + // Used to find tref zero in PTS mode + static int minidx=10000, lastminidx=10000; + int curridx; + int IdrPicFlag = ((nal_unit_type == 5 )?1:0); + + // Used to remember the max temporal reference number (poc mode) + static int maxtref = 0; + static int last_gop_maxtref = 0; + + // Used for PTS ordering of CC blocks + static LLONG currefpts = 0; + + dvprint("\nSLICE HEADER\n"); + tmp=ue(&q1); + dvprint("first_mb_in_slice= %llX\n", tmp); + slice_type=ue(&q1); + dvprint("slice_type= %llX\n", slice_type); + tmp=ue(&q1); + dvprint("pic_parameter_set_id= %llX\n", tmp); + + lastframe_num = frame_num; + int maxframe_num = int((1< frame_num) + FrameNumOffset = lastframe_num + maxframe_num; + else + FrameNumOffset = lastframe_num; + + LLONG tempPicOrderCnt=0; + if (IdrPicFlag == 1) + tempPicOrderCnt=0; + else if (nal_ref_idc == 0) + tempPicOrderCnt = 2*(FrameNumOffset + frame_num) -1 ; + else + tempPicOrderCnt = 2*(FrameNumOffset + frame_num); + + LLONG TopFieldOrderCnt = tempPicOrderCnt; + LLONG BottomFieldOrderCnt = tempPicOrderCnt; + + if (!field_pic_flag) { + TopFieldOrderCnt = tempPicOrderCnt; + BottomFieldOrderCnt = tempPicOrderCnt; + } else if (bottom_field_flag) + BottomFieldOrderCnt = tempPicOrderCnt; + else + TopFieldOrderCnt = 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."); + //TODO + // Calculate picture order count (POC) according to 8.2.1 + } + // The rest of the data in slice_header() is currently unused. + + // A reference pic (I or P is always the last displayed picture of a POC + // sequence. B slices can be reference pics, so ignore nal_ref_idc. + int isref = 0; + switch (slice_type) + { + // P-SLICES + case 0: + case 5: + // I-SLICES + case 2: + case 7: + isref=1; + break; + } + + int maxrefcnt = int((1< -1 && (dif < 0 || dif > 1) ) + { + num_jump_in_frames++; + dvprint("\nJump in frame numbers (%lld/%lld)\n", frame_num, lastframe_num); + // This will prohibit setting current_tref on potential + // jumps. + maxidx=-1; + lastmaxidx=-1; + } + + // 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 + { // 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"); + } + + // if slices are buffered - flush + if (isref && !bottom_field_flag) + { + dvprint("\nReference pic! [%s]\n", slice_types[slice_type]); + dbg_print(CCX_DMT_TIME, "\nReference pic! [%s] maxrefcnt: %3d\n", + slice_types[slice_type], maxrefcnt); + + // Flush buffered cc blocks before doing the housekeeping + if (has_ccdata_buffered) + { + process_hdcc(); + } + last_gop_length = frames_since_last_gop; + frames_since_last_gop=0; + last_gop_maxtref = maxtref; + maxtref = 0; + lastmaxidx = maxidx; + maxidx=0; + lastminidx = minidx; + minidx=10000; + + if ( ccx_options.usepicorder ) { + // Use pic_order_cnt_lsb + + // Make sure that curridx never wraps for curidx values that + // are smaller than currref + currref = (int)pic_order_cnt_lsb; + if (currref < maxrefcnt/3) + { + currref += maxrefcnt+1; + } + + // If we wrapped arround lastmaxidx might be larger than + // the current index - fix this. + if (lastmaxidx > currref + maxrefcnt/2) // implies lastmaxidx > 0 + lastmaxidx -=maxrefcnt+1; + } else { + // Use PTS ordering + currefpts = current_pts; + currref = 0; + } + + anchor_hdcc( currref ); + } + + if ( ccx_options.usepicorder ) { + // Use pic_order_cnt_lsb + // Wrap (add max index value) curridx if needed. + if( currref - pic_order_cnt_lsb > maxrefcnt/2 ) + curridx = (int)pic_order_cnt_lsb + maxrefcnt+1; + else + curridx = (int)pic_order_cnt_lsb; + + // Track maximum index for this GOP + if ( curridx > maxidx ) + maxidx = curridx; + + // Calculate tref + if ( lastmaxidx > 0 ) { + current_tref = curridx - lastmaxidx -1; + // 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 ) { + current_tref = current_tref/2; + } + } + else + current_tref = 0; + + if ( current_tref < 0 ) { + mprint("current_tref is negative!?\n"); + } + } else { + // Use PTS ordering - calculate index position from PTS difference and + // frame rate + // The 2* accounts for a discrepancy between current and actual FPS + // seen in some files (CCSample2.mpg) + curridx = (int)roundportable(2*(current_pts - currefpts)/(MPEG_CLOCK_FREQ/current_fps)); + + if (abs(curridx) >= MAXBFRAMES) { + // Probably a jump in the timeline. Warn and handle gracefully. + mprint("\nFound large gap in PTS! Trying to recover ...\n"); + curridx = 0; + } + + // Track maximum index for this GOP + if ( curridx > maxidx ) + maxidx = curridx; + + // Track minimum index for this GOP + if ( curridx < minidx ) + minidx = curridx; + + current_tref = 1; + if ( curridx == lastminidx ) { + // This implies that the minimal index (assuming its number is + // fairly constant) sets the temporal reference to zero - needed to set sync_pts. + current_tref = 0; + } + if ( lastmaxidx == -1) { + // Set temporal reference to zero on minimal index and in the first GOP + // to avoid setting a wrong fts_offset + current_tref = 0; + } + } + + set_fts(); // Keep frames_since_ref_time==0, use current_tref + + dbg_print(CCX_DMT_TIME, "PTS: %s (%8u)", + print_mstime(current_pts/(MPEG_CLOCK_FREQ/1000)), + unsigned(current_pts)); + dbg_print(CCX_DMT_TIME, " picordercnt:%3lld tref:%3d idx:%3d refidx:%3d lmaxidx:%3d maxtref:%3d\n", + pic_order_cnt_lsb, current_tref, + curridx, currref, lastmaxidx, maxtref); + dbg_print(CCX_DMT_TIME, "FTS: %s", + print_mstime(get_fts())); + dbg_print(CCX_DMT_TIME, " sync_pts:%s (%8u)", + print_mstime(sync_pts/(MPEG_CLOCK_FREQ/1000)), + unsigned(sync_pts)); + dbg_print(CCX_DMT_TIME, " - %s since GOP: %2u", + slice_types[slice_type], + unsigned(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 + if ( lastmaxidx > -1 && current_tref == 0 ) + { + if (ccx_options.debug_mask & CCX_DMT_TIME ) + { + dbg_print(CCX_DMT_TIME, "\nNew temporal reference:\n"); + print_debug_timing(); + } + } + + total_frames_count++; + frames_since_last_gop++; + + store_hdcc(cc_data, cc_count, curridx, fts_now); + 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 diff --git a/src/bitstream.h b/src/bitstream.h new file mode 100644 index 00000000..843f56ba --- /dev/null +++ b/src/bitstream.h @@ -0,0 +1,64 @@ +#ifndef _BITSTREAM_ +#define _BITSTREAM_ + + +// The structure holds the current position in the bitstream. +// pos points to the current byte position and bpos counts the +// bits left unread at the current byte pos. No bit read means +// bpos = 8. +// end is used to check that nothing is read at "end" or after it. +struct bitstream +{ + unsigned char *pos; + int bpos; + unsigned char *end; + // Indicate how many bits are left in the stream after the previous + // read call. A negative number indicates that a read after the + // end of the stream was attempted. + int64_t bitsleft; + // Indicate an error occured while parsing the bitstream. + // This is meant to store high level syntax errors, i.e a function + // using the bitstream functions found a syntax error. + int error; + // Internal (private) variable - used to store the the bitstream + // position until it is decided if the bitstream pointer will be + // increased by the calling function, or not. + unsigned char *_i_pos; + int _i_bpos; +}; + +#define read_u8(bstream) (uint8_t)bitstream_get_num(bstream,1,1) +#define read_u16(bstream) (uint16_t)bitstream_get_num(bstream,2,1) +#define read_u32(bstream) (uint32_t)bitstream_get_num(bstream,4,1) +#define read_u64(bstream) (uint64_t)bitstream_get_num(bstream,8,1) +#define read_i8(bstream) (int8_t)bitstream_get_num(bstream,1,1) +#define read_i16(bstream) (int16_t)bitstream_get_num(bstream,2,1) +#define read_i32(bstream) (int32_t)bitstream_get_num(bstream,4,1) +#define read_i64(bstream) (int64_t)bitstream_get_num(bstream,8,1) + +#define next_u8(bstream) (uint8_t)bitstream_get_num(bstream,1,0) +#define next_u16(bstream) (uint16_t)bitstream_get_num(bstream,2,0) +#define next_u32(bstream) (uint32_t)bitstream_get_num(bstream,4,0) +#define next_u64(bstream) (uint64_t)bitstream_get_num(bstream,8,0) +#define next_i8(bstream) (int8_t)bitstream_get_num(bstream,1,0) +#define next_i16(bstream) (int16_t)bitstream_get_num(bstream,2,0) +#define next_i32(bstream) (int32_t)bitstream_get_num(bstream,4,0) +#define next_i64(bstream) (int64_t)bitstream_get_num(bstream,8,0) + + +int init_bitstream(struct bitstream *bstr, unsigned char *start, unsigned char *end); +uint64_t next_bits(struct bitstream *bstr, unsigned bnum); +uint64_t read_bits(struct bitstream *bstr, unsigned bnum); +int skip_bits(struct bitstream *bstr, unsigned bnum); +int is_byte_aligned(struct bitstream *bstr); +void make_byte_aligned(struct bitstream *bstr); +unsigned char *next_bytes(struct bitstream *bstr, unsigned bynum); +unsigned char *read_bytes(struct bitstream *bstr, unsigned bynum); +uint64_t bitstream_get_num(struct bitstream *bstr, unsigned bytes, int advance); +uint64_t ue(struct bitstream *bstr); +int64_t se(struct bitstream *bstr); +uint64_t u(struct bitstream *bstr, unsigned bnum); +int64_t i(struct bitstream *bstr, unsigned bnum); +uint8_t reverse8(uint8_t data); + +#endif diff --git a/src/cc_bitstream.cpp b/src/cc_bitstream.cpp new file mode 100644 index 00000000..b30938e2 --- /dev/null +++ b/src/cc_bitstream.cpp @@ -0,0 +1,375 @@ +#include "ccextractor.h" + +// Hold functions to read streams on a bit or byte oriented basis +// plus some data related helper functions. + + +// Guidelines for all bitsream functions: +// * No function shall advance the pointer past the end marker +// * If bitstream.bitsleft < 0 do not attempt any read access, +// but decrease bitsleft by the number of bits that were +// attempted to read. + +// Initialize bitstream +int init_bitstream(struct bitstream *bstr, unsigned char *start, unsigned char *end) +{ + bstr->pos = start; + bstr->bpos = 8; + bstr->end = end; + bstr->bitsleft = (bstr->end - bstr->pos)*8; + bstr->error = 0; + bstr->_i_pos = NULL; + bstr->_i_bpos = 0; + + 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; + } + return 0; +} + + +// Read bnum bits from bitstream bstr with the most significant +// bit read first without advancing the bitstream pointer. +// A 64 bit unsigned integer is returned. 0 is returned when +// there are not enough bits left in the bitstream. +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); + + // Sanity check + if(bstr->end - bstr->pos < 0) + fatal(EXIT_BUG_BUG, "next_bits: bitstream has negative length!"); + + // Keep a negative bitstream.bitsleft, but correct it. + if (bstr->bitsleft <= 0) + { + bstr->bitsleft -= bnum; + return 0; + } + + // Calculate the remaining number of bits in bitstream after reading. + bstr->bitsleft = 0LL + (bstr->end - bstr->pos - 1)*8 + bstr->bpos - bnum; + if (bstr->bitsleft < 0) + return 0; + + // Special case for reading zero bits. Return zero + if(bnum == 0) + return 0; + + int vbit = bstr->bpos; + unsigned char *vpos = bstr->pos; + + if(vbit < 1 || vbit > 8) + { + fatal(EXIT_BUG_BUG, "next_bits: Illegal bit position value %d!", vbit); + } + + while( 1 ) + { + if(vpos >= bstr->end) + { + // We should not get here ... + fatal(EXIT_BUG_BUG, "next_bits: Reading after end of data ..."); + } + + res |= (*vpos & (0x01 << (vbit-1)) ? 1 : 0); + vbit--; + bnum--; + + if(vbit == 0) + { + vpos++; + vbit = 8; + } + + if(bnum) + { + res <<= 1; + } + else + break; + } + + // Remember the bitstream position + bstr->_i_bpos = vbit; + bstr->_i_pos = vpos; + + return res; +} + + +// Read bnum bits from bitstream bstr with the most significant +// bit read first. A 64 bit unsigned integer is returned. +uint64_t read_bits(struct bitstream *bstr, unsigned bnum) +{ + uint64_t res = next_bits(bstr, bnum); + + // Special case for reading zero bits. Also abort when not enough + // bits are left. Return zero + if(bnum == 0 || bstr->bitsleft < 0) + return 0; + + // Advance the bitstream + bstr->bpos = bstr->_i_bpos; + bstr->pos = bstr->_i_pos; + + return res; +} + + +// This function will advance the bitstream by bnum bits, if possible. +// Advancing of more than 64 bits is possible. +// Return TRUE when successfull, otherwise FALSE +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!"); + + // Keep a negative bstr->bitsleft, but correct it. + if (bstr->bitsleft < 0) + { + bstr->bitsleft -= bnum; + return 0; + } + + // Calculate the remaining number of bits in bitstream after reading. + bstr->bitsleft = 0LL + (bstr->end - bstr->pos - 1)*8 + bstr->bpos - bnum; + if (bstr->bitsleft < 0) + return 0; + + // Special case for reading zero bits. Return zero + if(bnum == 0) + return 1; + + bstr->bpos -= bnum%8; + bstr->pos += bnum/8; + + if (bstr->bpos < 1) + { + bstr->bpos += 8; + bstr->pos += 1; + } + return 1; +} + + +// Return TRUE if the current position in the bitstream is on a byte +// boundary, i.e., the next bit in the bitstream is the first bit in +// a byte, otherwise return FALSE +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!"); + + int vbit = bstr->bpos; + + if(vbit == 0 || vbit > 8) + { + fatal(EXIT_BUG_BUG, "is_byte_aligned: Illegal bit position value %d!\n", vbit); + } + + if (vbit == 8) + return 1; + else + return 0; +} + + +// Move bitstream to next byte border. Adjust bitsleft. +void make_byte_aligned(struct bitstream *bstr) +{ + // Sanity check + if(bstr->end - bstr->pos < 0) + fatal(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); + } + + // Keep a negative bstr->bitsleft, but correct it. + if (bstr->bitsleft < 0) + { + // Pay attention to the bit alignment + bstr->bitsleft = (bstr->bitsleft-7)/8 *8; + return; + } + + if(bstr->bpos != 8) + { + bstr->bpos = 8; + bstr->pos += 1; + } + // Reset, in case a next_???() function was used before + bstr->bitsleft = 0LL + 8*(bstr->end-bstr->pos-1)+bstr->bpos; + + return; +} + + +// Return pointer to first of bynum bytes from the bitstream if the +// following conditions are TRUE: +// The bitstream is byte aligned and there are enough bytes left in +// it to read bynum bytes. Otherwise return NULL. +// This function does not advance the bitstream pointer. +unsigned char *next_bytes(struct bitstream *bstr, unsigned bynum) +{ + // Sanity check + if(bstr->end - bstr->pos < 0) + fatal(EXIT_BUG_BUG, "next_bytes: bitstream has negative length!"); + + // Keep a negative bstr->bitsleft, but correct it. + if (bstr->bitsleft < 0) + { + bstr->bitsleft -= bynum*8; + return NULL; + } + + bstr->bitsleft = 0LL + (bstr->end - bstr->pos - 1)*8 + bstr->bpos - bynum*8; + + if (!is_byte_aligned(bstr) || bstr->bitsleft < 0 || bynum < 1) + return NULL; + + // Remember the bitstream position + bstr->_i_bpos = 8; + bstr->_i_pos = bstr->pos + bynum; + + return bstr->pos; +} + + +// Return pointer to first of bynum bytes from the bitstream if the +// following conditions are TRUE: +// The bitstream is byte aligned and there are enough bytes left in +// it to read bynum bytes. Otherwise return NULL. +// This function does advance the bitstream pointer. +unsigned char *read_bytes(struct bitstream *bstr, unsigned bynum) +{ + unsigned char *res = next_bytes(bstr, bynum); + + // Advance the bitstream when a read was possible + if(res) + { + bstr->bpos = bstr->_i_bpos; + bstr->pos = bstr->_i_pos; + } + return res; +} + + +// Return an integer number with "bytes" precision from the current +// bitstream position. Allowed "bytes" values are 1,2,4,8. +// This function does advance the bitstream pointer when "advance" is +// set to TRUE. +// Numbers come MSB (most significant first), and we need to account for +// little-endian and big-endian CPUs. +uint64_t bitstream_get_num(struct bitstream *bstr, unsigned bytes, int advance) +{ + void *bpos; + uint64_t rval=0; + + if (advance) + bpos = read_bytes(bstr, bytes); + else + bpos = next_bytes(bstr, bytes); + + if (!bpos) + return 0; + + switch (bytes) + { + case 1: + case 2: + case 4: + case 8: + break; + default: + fatal (EXIT_BUG_BUG, "bitstream_get_num: Illegal precision value [%u]!", + bytes); + break; + } + for (unsigned i=0;iminimum_fts) + minimum_fts=fts; + dbg_print(CCX_DMT_608, "Visible End time=%s\n", print_mstime(fts)); + return fts; +} diff --git a/src/ccextractor.cpp b/src/ccextractor.cpp new file mode 100644 index 00000000..52754c5b --- /dev/null +++ b/src/ccextractor.cpp @@ -0,0 +1,867 @@ +/* CCExtractor, carlos at ccextractor org +Credits: See CHANGES.TXT +License: GPL 2.0 +*/ +#include +#include "ccextractor.h" +#include +#include + +void xds_cea608_test(); + +struct ccx_s_options ccx_options; + +extern unsigned char *filebuffer; +extern int bytesinbuffer; // Number of bytes we actually have on buffer + +// PTS timing related stuff +LLONG min_pts, max_pts, sync_pts; +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 + +// global TS PCR value, moved from telxcc. TODO: Rename, see if how to relates to fts_global +uint32_t global_timestamp = 0, min_global_timestamp=0; +int global_timestamp_inited=0; + +// Count 608 (per field) and 708 blocks since last set_fts() call +int cb_field1, cb_field2, cb_708; +int saw_caption_block; + +int pts_set; //0 = No, 1 = received, 2 = min_pts set + +unsigned pts_big_change; + +int MPEG_CLOCK_FREQ = 90000; // This "constant" is part of the standard + +// Stuff common to both loops +unsigned char *buffer = NULL; +LLONG past; /* Position in file, if in sync same as ftell() */ +unsigned char *pesheaderbuf = NULL; +LLONG inputsize; +LLONG total_inputsize=0, total_past=0; // Only in binary concat mode + +int last_reported_progress; +int processed_enough; // If 1, we have enough lines, time, etc. + + +// 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_frames_count; +unsigned total_pulldownfields; +unsigned total_pulldownframes; +int cc_stats[4]; +int false_pict_header; +int resets_708=0; + +/* GOP-based timing */ +struct gop_time_code gop_time, first_gop_time, printed_gop; +int saw_gop_header=0; +int frames_since_last_gop=0; +LLONG fts_at_gop_start=0; + +/* Time info for timed-transcript */ +LLONG ts_start_of_xds=-1; // Time at which we switched to XDS mode, =-1 hasn't happened yet +int timestamps_on_transcript=0; /* Write time info on transcripts? */ +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 */ + +int max_gop_length=0; // (Maximum) length of a group of pictures +int last_gop_length=0; // Length of the previous group of pictures +int frames_since_ref_time=0; + +int gop_rollover=0; +// int hex_mode=HEX_NONE; // Are we processing an hex file? + +/* Detect gaps in caption stream - only used for dvr-ms/NTSC. */ +int CaptionGap=0; + +/* Parameters */ +void init_options (struct ccx_s_options *options) +{ +#ifdef _WIN32 + options->buffer_input = 1; // In Windows buffering seems to help +#else + options->buffer_input = 0; // In linux, not so much. +#endif + options->direct_rollup=0; // Write roll-up captions directly instead of line by line? + options->nofontcolor=0; // 1 = don't put tags + options->notypesetting=0; // 1 = Don't put , , etc typesetting tags + /* 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->norollup=0; // If 1, write one line at a time + options->forced_ru=0; // 0=Disabled, 1, 2 or 3=max lines in roll-up mode + 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 + /* 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->export_xds=0; // Export XDS to transcript? + 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. + 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->teletext_mode=CCX_TXT_AUTO_NOT_YET_FOUND; // 0=Disabled, 1 = Not found, 2=Found + options->ucla_settings=0; // Enables convenient settings for UCLA's project. + options->millis_separator=','; + options->screens_to_process=-1; // How many screenfuls we want? + 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 = 0; + options->udpport=0; // Non-zero => Listen for UDP packets on this port, no files. + 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->cc608_default_color=COL_TRANSPARENT; +} + +ccx_stream_mode_enum stream_mode = CCX_SM_ELEMENTARY_OR_NOT_FOUND; // Data parse mode: 0=elementary, 1=transport, 2=program stream, 3=ASF container +ccx_stream_mode_enum auto_stream = CCX_SM_AUTODETECT; + + +int rawmode = 0; // Broadcast or DVD +// See -d from + +int cc_to_stdout=0; // If 1, captions go to stdout instead of file + + +LLONG subs_delay=0; // ms to delay (or advance) subs + +int startcredits_displayed=0, end_credits_displayed=0; +LLONG last_displayed_subs_ms=0; // When did the last subs end? +LLONG screens_to_process=-1; // How many screenfuls we want? +char *basefilename=NULL; // Input filename without the extension +char **inputfile=NULL; // List of files to process + +const char *extension; // Output extension +int current_file=-1; // If current_file!=1, we are processing *inputfile[current_file] + +int num_input_files=0; // How many? +int do_cea708=0; // Process 708 data? +int cea708services[63]; // [] -> 1 for services to be processed + +// Case arrays +char **spell_lower=NULL; +char **spell_correct=NULL; +int spell_words=0; +int spell_capacity=0; + + +/* Hauppauge support */ +unsigned hauppauge_warning_shown=0; // Did we detect a possible Hauppauge capture and told the user already? +unsigned teletext_warning_shown=0; // Did we detect a possible PAL (with teletext subs) and told the user already? + + + +struct sockaddr_in servaddr, cliaddr; + + +struct ccx_s_write wbout1, wbout2; // Output structures +struct ccx_s_write *wbxdsout=NULL; // Pointer, so it can share the same output file + +/* File handles */ +FILE *fh_out_elementarystream; +int infd=-1; // descriptor number to input. Set to -1 to indicate no file is open. +char *basefilename_for_stdin=(char *) "stdin"; // Default name for output files if input is stdin +char *basefilename_for_network=(char *) "network"; // Default name for output files if input is network +int PIDs_seen[65536]; +struct PMT_entry *PIDs_programs[65536]; + +int temp_debug=0; // This is a convenience variable used to enable/disable debug on variable conditions. Find references to understand. + +int main_telxcc (int argc, char *argv[]); +LLONG process_raw_with_field (void); + + + +int main(int argc, char *argv[]) +{ + char *c; + + // Initialize some constants + init_ts_constants(); + + init_options (&ccx_options); + + // Prepare write structures + init_write(&wbout1, 1); + init_write(&wbout2, 2); + + // Init XDS buffers + xds_init(); + //xds_cea608_test(); + + // Prepare time structures + init_boundary_time (&ccx_options.extraction_start); + init_boundary_time (&ccx_options.extraction_end); + init_boundary_time (&ccx_options.startcreditsnotbefore); + init_boundary_time (&ccx_options.startcreditsnotafter); + init_boundary_time (&ccx_options.startcreditsforatleast); + init_boundary_time (&ccx_options.startcreditsforatmost); + init_boundary_time (&ccx_options.endcreditsforatleast); + init_boundary_time (&ccx_options.endcreditsforatmost); + + int show_myth_banner = 0; + + memset (&cea708services[0],0,63*sizeof (int)); + parse_parameters (argc,argv); + + if (num_input_files==0 && ccx_options.input_source==CCX_DS_FILE) + { + usage (); + fatal (EXIT_NO_INPUT_FILES, "(This help screen was shown because there were no input files)\n"); + } + if (num_input_files>1 && ccx_options.live_stream) + { + fatal(EXIT_TOO_MANY_INPUT_FILES, "Live stream mode accepts only one input file.\n"); + } + if (num_input_files && ccx_options.input_source==CCX_DS_NETWORK) + { + fatal(EXIT_TOO_MANY_INPUT_FILES, "UDP mode is not compatible with input files.\n"); + } + if (ccx_options.input_source==CCX_DS_NETWORK) + { + ccx_options.buffer_input=1; // Mandatory, because each datagram must be read complete. + } + + // teletext page number out of range + if ((tlt_config.page != 0) && ((tlt_config.page < 100) || (tlt_config.page > 899))) { + fatal (EXIT_NOT_CLASSIFIED, "Teletext page number could not be lower than 100 or higher than 899\n"); + } + + if (ccx_options.output_filename!=NULL) + { + // Use the given output file name for the field specified by + // the -1, -2 switch. If -12 is used, the filename is used for + // field 1. + if (ccx_options.extract==2) + wbout2.filename=ccx_options.output_filename; + else + wbout1.filename=ccx_options.output_filename; + } + + switch (ccx_options.write_format) + { + case CCX_OF_RAW: + extension = ".raw"; + break; + case CCX_OF_SRT: + extension = ".srt"; + break; + case CCX_OF_SAMI: + extension = ".smi"; + break; + case CCX_OF_SMPTETT: + extension = ".ttml"; + break; + case CCX_OF_TRANSCRIPT: + extension = ".txt"; + break; + case CCX_OF_RCWT: + extension = ".bin"; + break; + case CCX_OF_SPUPNG: + extension = ".xml"; + break; + case CCX_OF_NULL: + extension = ""; + break; + case CCX_OF_DVDRAW: + extension = ".dvdraw"; + break; + default: + fatal (EXIT_BUG_BUG, "write_format doesn't have any legal value, this is a bug.\n"); + } + params_dump(); + + // default teletext page + if (tlt_config.page > 0) { + // dec to BCD, magazine pages numbers are in BCD (ETSI 300 706) + tlt_config.page = ((tlt_config.page / 100) << 8) | (((tlt_config.page / 10) % 10) << 4) | (tlt_config.page % 10); + } + + if (auto_stream==CCX_SM_MCPOODLESRAW && ccx_options.write_format==CCX_OF_RAW) + { + fatal (EXIT_INCOMPATIBLE_PARAMETERS, "-in=raw can only be used if the output is a subtitle file.\n"); + } + if (auto_stream==CCX_SM_RCWT && ccx_options.write_format==CCX_OF_RCWT && ccx_options.output_filename==NULL) + { + fatal (EXIT_INCOMPATIBLE_PARAMETERS, + "CCExtractor's binary format can only be used simultaneously for input and\noutput if the output file name is specified given with -o.\n"); + } + + buffer = (unsigned char *) malloc (BUFSIZE); + subline = (unsigned char *) malloc (SUBLINESIZE); + pesheaderbuf = (unsigned char *) malloc (188); // Never larger anyway + + switch (ccx_options.input_source) + { + case CCX_DS_FILE: + basefilename = (char *) malloc (strlen (inputfile[0])+1); + break; + case CCX_DS_STDIN: + basefilename = (char *) malloc (strlen (basefilename_for_stdin)+1); + break; + case CCX_DS_NETWORK: + basefilename = (char *) malloc (strlen (basefilename_for_network)+1); + break; + } + if (basefilename == NULL) + fatal (EXIT_NOT_ENOUGH_MEMORY, "Not enough memory\n"); + switch (ccx_options.input_source) + { + case CCX_DS_FILE: + strcpy (basefilename, inputfile[0]); + break; + case CCX_DS_STDIN: + strcpy (basefilename, basefilename_for_stdin); + break; + case CCX_DS_NETWORK: + strcpy (basefilename, basefilename_for_network); + break; + } + for (c=basefilename+strlen (basefilename)-1; c>basefilename && + *c!='.'; c--) {;} // Get last . + if (*c=='.') + *c=0; + + if (wbout1.filename==NULL) + { + wbout1.filename = (char *) malloc (strlen (basefilename)+3+strlen (extension)); + wbout1.filename[0]=0; + } + if (wbout2.filename==NULL) + { + wbout2.filename = (char *) malloc (strlen (basefilename)+3+strlen (extension)); + wbout2.filename[0]=0; + } + if (buffer == NULL || pesheaderbuf==NULL || + wbout1.filename == NULL || wbout2.filename == NULL || + subline==NULL || init_file_buffer() || general_608_init()) + { + fatal (EXIT_NOT_ENOUGH_MEMORY, "Not enough memory\n"); + } + + if (ccx_options.write_format!=CCX_OF_NULL) + { + /* # DVD format uses one raw file for both fields, while Broadcast requires 2 */ + if (ccx_options.write_format==CCX_OF_DVDRAW) + { + if (wbout1.filename[0]==0) + { + strcpy (wbout1.filename,basefilename); + strcat (wbout1.filename,".raw"); + } + if (cc_to_stdout) + { + wbout1.fh=STDOUT_FILENO; + mprint ("Sending captions to stdout.\n"); + } + else + { + mprint ("Creating %s\n", wbout1.filename); + wbout1.fh=open (wbout1.filename, O_RDWR | O_CREAT | O_TRUNC | O_BINARY, S_IREAD | S_IWRITE); + if (wbout1.fh==-1) + { + fatal (EXIT_FILE_CREATION_FAILED, "Failed\n"); + } + } + } + else + { + if (cc_to_stdout && ccx_options.extract==12) + fatal (EXIT_INCOMPATIBLE_PARAMETERS, "You can't extract both fields to stdout at the same time in broadcast mode."); + + if (ccx_options.write_format == CCX_OF_SPUPNG && cc_to_stdout) + fatal (EXIT_INCOMPATIBLE_PARAMETERS, "You cannot use -out=spupng with -stdout."); + + if (ccx_options.extract!=2) + { + if (cc_to_stdout) + { + wbout1.fh=STDOUT_FILENO; + mprint ("Sending captions to stdout.\n"); + } + else + { + if (wbout1.filename[0]==0) + { + strcpy (wbout1.filename,basefilename); + if (ccx_options.extract==12) // _1 only added if there's two files + strcat (wbout1.filename,"_1"); + strcat (wbout1.filename,(const char *) extension); + } + mprint ("Creating %s\n", wbout1.filename); + wbout1.fh=open (wbout1.filename, O_RDWR | O_CREAT | O_TRUNC | O_BINARY, S_IREAD | S_IWRITE); + if (wbout1.fh==-1) + { + fatal (EXIT_FILE_CREATION_FAILED, "Failed (errno=%d)\n",errno); + } + } + switch (ccx_options.write_format) + { + case CCX_OF_RAW: + writeraw (BROADCAST_HEADER,sizeof (BROADCAST_HEADER),&wbout1); + break; + case CCX_OF_DVDRAW: + break; + default: + if (ccx_options.encoding==CCX_ENC_UTF_8) // Write BOM + writeraw (UTF8_BOM, sizeof (UTF8_BOM), &wbout1); + if (ccx_options.encoding==CCX_ENC_UNICODE) // Write BOM + writeraw (LITTLE_ENDIAN_BOM, sizeof (LITTLE_ENDIAN_BOM), &wbout1); + write_subtitle_file_header (&wbout1); + } + } + if (ccx_options.extract == 12) + mprint (" and \n"); + if (ccx_options.extract!=1) + { + if (cc_to_stdout) + { + wbout1.fh=STDOUT_FILENO; + mprint ("Sending captions to stdout.\n"); + } + else + { + if (wbout2.filename[0]==0) + { + strcpy (wbout2.filename,basefilename); + if (ccx_options.extract==12) // _ only added if there's two files + strcat (wbout2.filename,"_2"); + strcat (wbout2.filename,(const char *) extension); + } + mprint ("Creating %s\n", wbout2.filename); + wbout2.fh=open (wbout2.filename, O_RDWR | O_CREAT | O_TRUNC | O_BINARY, S_IREAD | S_IWRITE); + if (wbout2.fh==-1) + { + fatal (EXIT_FILE_CREATION_FAILED, "Failed\n"); + } + } + switch (ccx_options.write_format) + { + case CCX_OF_RAW: + writeraw (BROADCAST_HEADER,sizeof (BROADCAST_HEADER),&wbout2); + break; + case CCX_OF_DVDRAW: + break; + default: + if (ccx_options.encoding==CCX_ENC_UTF_8) // Write BOM + writeraw (UTF8_BOM, sizeof (UTF8_BOM), &wbout2); + if (ccx_options.encoding==CCX_ENC_UNICODE) // Write BOM + writeraw (LITTLE_ENDIAN_BOM, sizeof (LITTLE_ENDIAN_BOM), &wbout2); + write_subtitle_file_header (&wbout2); + } + } + } + } + + if (ccx_options.export_xds) + { + if (ccx_options.write_format==CCX_OF_TRANSCRIPT) + { + if (wbout1.fh!=-1) + wbxdsout=&wbout1; + else + if (wbout2.fh!=-1) + wbxdsout=&wbout2; + } + else + mprint ("Warning: -xds ignored, XDS can only be exported to transcripts at this time.\n"); + } + + if (ccx_options.teletext_mode == CCX_TXT_IN_USE) // Here, it would mean it was forced by user + telxcc_init(); + + fh_out_elementarystream = NULL; + if (ccx_options.out_elementarystream_filename!=NULL) + { + if ((fh_out_elementarystream = fopen (ccx_options.out_elementarystream_filename,"wb"))==NULL) + { + fatal (EXIT_FILE_CREATION_FAILED, "Unable to open clean file: %s\n",ccx_options.out_elementarystream_filename); + } + } + if (ccx_options.line_terminator_lf) + encoded_crlf_length = encode_line (encoded_crlf,(unsigned char *) "\n"); + else + encoded_crlf_length = encode_line (encoded_crlf,(unsigned char *) "\r\n"); + + encoded_br_length = encode_line (encoded_br, (unsigned char *) "
"); + + + build_parity_table(); + + // Initialize HDTV caption buffer + init_hdcc(); + init_708(); // Init 708 decoders + + time_t start, final; + time(&start); + + processed_enough=0; + if (ccx_options.binary_concat) + { + total_inputsize=gettotalfilessize(); + if (total_inputsize==-1) + fatal (EXIT_UNABLE_TO_DETERMINE_FILE_SIZE, "Failed to determine total file size.\n"); + } + + while (switch_to_next_file(0) && !processed_enough) + { + prepare_for_new_file(); + + if (auto_stream == CCX_SM_AUTODETECT) + { + detect_stream_type(); + switch (stream_mode) + { + case CCX_SM_ELEMENTARY_OR_NOT_FOUND: + mprint ("\rFile seems to be an elementary stream, enabling ES mode\n"); + break; + case CCX_SM_TRANSPORT: + mprint ("\rFile seems to be a transport stream, enabling TS mode\n"); + break; + case CCX_SM_PROGRAM: + mprint ("\rFile seems to be a program stream, enabling PS mode\n"); + break; + case CCX_SM_ASF: + mprint ("\rFile seems to be an ASF, enabling DVR-MS mode\n"); + break; + case CCX_SM_WTV: + mprint ("\rFile seems to be a WTV, enabling WTV mode\n"); + break; + case CCX_SM_MCPOODLESRAW: + mprint ("\rFile seems to be McPoodle raw data\n"); + break; + case CCX_SM_RCWT: + mprint ("\rFile seems to be a raw caption with time data\n"); + break; + case CCX_SM_MP4: + mprint ("\rFile seems to be a MP4\n"); + break; + case CCX_SM_HEX_DUMP: + mprint ("\rFile seems to be an hexadecimal dump\n"); + break; + case CCX_SM_MYTH: + case CCX_SM_AUTODETECT: + fatal(EXIT_BUG_BUG, "Cannot be reached!"); + break; + } + } + else + { + stream_mode=auto_stream; + } + + /* ----------------------------------------------------------------- + MAIN LOOP + ----------------------------------------------------------------- */ + + // The myth loop autodetect will only be used with ES or PS streams + switch (ccx_options.auto_myth) + { + case 0: + // Use whatever stream mode says + break; + case 1: + // Force stream mode to myth + stream_mode=CCX_SM_MYTH; + break; + case 2: + // autodetect myth files, but only if it does not conflict with + // the current stream mode + switch (stream_mode) + { + case CCX_SM_ELEMENTARY_OR_NOT_FOUND: + case CCX_SM_PROGRAM: + if ( detect_myth() ) + { + stream_mode=CCX_SM_MYTH; + } + break; + default: + // Keep stream_mode + break; + } + break; + } + + switch (stream_mode) + { + case CCX_SM_ELEMENTARY_OR_NOT_FOUND: + if (!ccx_options.use_gop_as_pts) // If !0 then the user selected something + ccx_options.use_gop_as_pts = 1; // Force GOP timing for ES + case CCX_SM_TRANSPORT: + case CCX_SM_PROGRAM: + case CCX_SM_ASF: + case CCX_SM_WTV: + if (!ccx_options.use_gop_as_pts) // If !0 then the user selected something + ccx_options.use_gop_as_pts = 0; + mprint ("\rAnalyzing data in general mode\n"); + general_loop(); + break; + case CCX_SM_MCPOODLESRAW: + mprint ("\rAnalyzing data in McPoodle raw mode\n"); + raw_loop(); + break; + case CCX_SM_RCWT: + mprint ("\rAnalyzing data in CCExtractor's binary format\n"); + rcwt_loop(); + break; + case CCX_SM_MYTH: + mprint ("\rAnalyzing data in MythTV mode\n"); + show_myth_banner = 1; + myth_loop(); + break; + case CCX_SM_MP4: + mprint ("\rAnalyzing data with GPAC (MP4 library)\n"); + close_input_file(); // No need to have it open. GPAC will do it for us + processmp4 (inputfile[0]); + break; + case CCX_SM_HEX_DUMP: + close_input_file(); // processhex will open it in text mode + processhex (inputfile[0]); + break; + case CCX_SM_AUTODETECT: + fatal(EXIT_BUG_BUG, "Cannot be reached!"); + break; + } + + mprint("\n"); + dbg_print(CCX_DMT_608, "\nTime stamps after last caption block was written:\n"); + dbg_print(CCX_DMT_608, "Last time stamps: PTS: %s (%+2dF) ", + print_mstime( LLONG(sync_pts/(MPEG_CLOCK_FREQ/1000) + +frames_since_ref_time*1000.0/current_fps) ), + frames_since_ref_time); + dbg_print(CCX_DMT_608, "GOP: %s \n", print_mstime(gop_time.ms) ); + + // Blocks since last PTS/GOP time stamp. + dbg_print(CCX_DMT_608, "Calc. difference: PTS: %s (%+3lldms incl.) ", + print_mstime( LLONG((sync_pts-min_pts)/(MPEG_CLOCK_FREQ/1000) + + fts_offset + frames_since_ref_time*1000.0/current_fps)), + fts_offset + LLONG(frames_since_ref_time*1000.0/current_fps) ); + dbg_print(CCX_DMT_608, "GOP: %s (%+3dms incl.)\n", + print_mstime((LLONG)(gop_time.ms + -first_gop_time.ms + +get_fts_max()-fts_at_gop_start)), + (int)(get_fts_max()-fts_at_gop_start)); + // When padding is active the CC block time should be within + // 1000/29.97 us of the differences. + dbg_print(CCX_DMT_608, "Max. FTS: %s (without caption blocks since then)\n", + print_mstime(get_fts_max())); + + if (stat_hdtv) + { + mprint ("\rCC type 0: %d (%s)\n", cc_stats[0], cc_types[0]); + mprint ("CC type 1: %d (%s)\n", cc_stats[1], cc_types[1]); + mprint ("CC type 2: %d (%s)\n", cc_stats[2], cc_types[2]); + mprint ("CC type 3: %d (%s)\n", cc_stats[3], cc_types[3]); + } + mprint ("\nTotal frames time: %s (%u frames at %.2ffps)\n", + print_mstime( (LLONG)(total_frames_count*1000/current_fps) ), + total_frames_count, current_fps); + if (total_pulldownframes) + mprint ("incl. pulldown frames: %s (%u frames at %.2ffps)\n", + print_mstime( (LLONG)(total_pulldownframes*1000/current_fps) ), + total_pulldownframes, current_fps); + if (pts_set >= 1 && min_pts != 0x01FFFFFFFFLL) + { + LLONG postsyncms = LLONG(frames_since_last_gop*1000/current_fps); + mprint ("\nMin PTS: %s\n", + print_mstime( min_pts/(MPEG_CLOCK_FREQ/1000) - fts_offset)); + if (pts_big_change) + mprint ("(Reference clock was reset at some point, Min PTS is approximated)\n"); + mprint ("Max PTS: %s\n", + print_mstime( sync_pts/(MPEG_CLOCK_FREQ/1000) + postsyncms)); + + mprint ("Length: %s\n", + print_mstime( sync_pts/(MPEG_CLOCK_FREQ/1000) + postsyncms + - min_pts/(MPEG_CLOCK_FREQ/1000) + fts_offset )); + } + // dvr-ms files have invalid GOPs + if (gop_time.inited && first_gop_time.inited && stream_mode != CCX_SM_ASF) + { + mprint ("\nInitial GOP time: %s\n", + print_mstime(first_gop_time.ms)); + mprint ("Final GOP time: %s%+3dF\n", + print_mstime(gop_time.ms), + frames_since_last_gop); + mprint ("Diff. GOP length: %s%+3dF", + print_mstime(gop_time.ms - first_gop_time.ms), + frames_since_last_gop); + mprint (" (%s)\n", + print_mstime(gop_time.ms - first_gop_time.ms + +LLONG((frames_since_last_gop)*1000/29.97)) ); + } + + if (false_pict_header) + mprint ("\nNumber of likely false picture headers (discarded): %d\n",false_pict_header); + + if (stat_numuserheaders) + mprint("\nTotal user data fields: %d\n", stat_numuserheaders); + if (stat_dvdccheaders) + mprint("DVD-type user data fields: %d\n", stat_dvdccheaders); + if (stat_scte20ccheaders) + mprint("SCTE-20 type user data fields: %d\n", stat_scte20ccheaders); + if (stat_replay4000headers) + mprint("ReplayTV 4000 user data fields: %d\n", stat_replay4000headers); + if (stat_replay5000headers) + mprint("ReplayTV 5000 user data fields: %d\n", stat_replay5000headers); + if (stat_hdtv) + mprint("HDTV type user data fields: %d\n", stat_hdtv); + if (stat_dishheaders) + mprint("Dish Network user data fields: %d\n", stat_dishheaders); + if (stat_divicom) + { + mprint("CEA608/Divicom user data fields: %d\n", stat_divicom); + + mprint("\n\nNOTE! The CEA 608 / Divicom standard encoding for closed\n"); + mprint("caption is not well understood!\n\n"); + mprint("Please submit samples to the developers.\n\n\n"); + } + + // Add one frame as fts_max marks the beginning of the last frame, + // but we need the end. + fts_global += fts_max + LLONG(1000.0/current_fps); + // CFS: At least in Hauppage mode, cb_field can be responsible for ALL the + // timing (cb_fields having a huge number and fts_now and fts_global being 0 all + // the time), so we need to take that into account in fts_global before resetting + // counters. + if (cb_field1!=0) + fts_global += cb_field1*1001/3; + else + fts_global += cb_field2*1001/3; + // Reset counters - This is needed if some captions are still buffered + // and need to be written after the last file is processed. + cb_field1 = 0; cb_field2 = 0; cb_708 = 0; + fts_now = 0; + fts_max = 0; + } // file loop + close_input_file(); + + if (fh_out_elementarystream!=NULL) + fclose (fh_out_elementarystream); + + flushbuffer (&wbout1,false); + flushbuffer (&wbout2,false); + + prepare_for_new_file (); // To reset counters used by handle_end_of_data() + + if (wbout1.fh!=-1) + { + if (ccx_options.write_format==CCX_OF_SPUPNG) + { + handle_end_of_data (&wbout1); + } + 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) + { + handle_end_of_data (&wbout1); + } + else if(ccx_options.write_format==CCX_OF_RCWT) + { + // Write last header and data + writercwtdata (NULL); + } + if (ccx_options.end_credits_text!=NULL) + try_to_add_end_credits(&wbout1); + write_subtitle_file_footer (&wbout1); + } + if (wbout2.fh!=-1) + { + if (ccx_options.write_format==CCX_OF_SPUPNG) + { + handle_end_of_data (&wbout2); + } + 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) + { + handle_end_of_data (&wbout2); + } + if (ccx_options.end_credits_text!=NULL) + try_to_add_end_credits(&wbout2); + write_subtitle_file_footer (&wbout2); + } + telxcc_close(); + flushbuffer (&wbout1,true); + flushbuffer (&wbout2,true); + time (&final); + + long proc_time=(long) (final-start); + mprint ("\rDone, processing time = %ld seconds\n", proc_time); + if (proc_time>0) + { + LLONG ratio=(get_fts_max()/10)/proc_time; + unsigned s1=(unsigned) (ratio/100); + unsigned s2=(unsigned) (ratio%100); + mprint ("Performance (real length/process time) = %u.%02u\n", + s1, s2); + } + dbg_print(CCX_DMT_708, "The 708 decoder was reset [%d] times.\n",resets_708); + if (ccx_options.teletext_mode == CCX_TXT_IN_USE) + mprint ( "Teletext decoder: %"PRIu32" packets processed, %"PRIu32" SRT frames written.\n", tlt_packet_counter, tlt_frames_produced); + + if (processed_enough) + { + mprint ("\rNote: Processing was cancelled before all data was processed because\n"); + mprint ("\rone or more user-defined limits were reached.\n"); + } + if (ccblocks_in_avc_lost>0) + { + mprint ("Total caption blocks received: %d\n", ccblocks_in_avc_total); + mprint ("Total caption blocks lost: %d\n", ccblocks_in_avc_lost); + } + + mprint ("This is beta software. Report issues to carlos at ccextractor org...\n"); + if (show_myth_banner) + { + mprint ("NOTICE: Due to the major rework in 0.49, we needed to change part of the timing\n"); + mprint ("code in the MythTV's branch. Please report results to the address above. If\n"); + mprint ("something is broken it will be fixed. Thanks\n"); + } + return EXIT_OK; +} diff --git a/src/ccextractor.h b/src/ccextractor.h new file mode 100644 index 00000000..565735f9 --- /dev/null +++ b/src/ccextractor.h @@ -0,0 +1,555 @@ +#ifndef CCX_CCEXTRACTOR_H +#define CCX_CCEXTRACTOR_H + +#include +#include +#include +#include +#include +#include +#include +#include + +// compatibility across platforms +#include "platform.h" + +#define VERSION "0.69" + +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; +}; + +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 + /* 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 export_xds; // Export XDS to transcript? + 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. + 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 + unsigned ucla_settings; // Enables convenient settings for UCLA's project. + char millis_separator; + LLONG screens_to_process; // How many screenfuls we want? + ccx_encoding_type encoding ; + ccx_output_format write_format; // 0=Raw, 1=srt, 2=SMI + 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 */ + 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? + ccx_datasource input_source; // Files, stdin or network + color_code cc608_default_color; +}; + +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 +}; + +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; + struct eia608 *data608; + int my_field; // Used for sanity checks + long bytes_processed_608; // To be written ONLY by process_608 + 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; +}; + + + +// Stuff for telcc.cpp +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.cpp +void parse_parameters (int argc, char *argv[]); +void usage (void); + +// general_loop.cpp +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 ); + +// activity.cpp +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 unsigned long net_activity_gui; +extern LLONG result; +extern int end_of_file; +extern LLONG inbuf; +extern int ccx_bufferdatatype; // Can be RAW or PES + +// asf_functions.cpp +LLONG asf_getmoredata( void ); + +// wtv_functions.cpp +LLONG wtv_getmoredata( void ); + +// avc_functions.cpp +LLONG process_avc (unsigned char *avcbuf, LLONG avcbuflen); + +// es_functions.cpp +LLONG process_m2v (unsigned char *data, LLONG length); + +extern unsigned top_field_first; + +// es_userdata.cpp +int user_data(struct bitstream *ustream, int udtype); + +// bitstream.cpp - see bitstream.h + +// 608.cpp +int write_cc_buffer (struct ccx_s_write *wb); +unsigned char *debug_608toASC (unsigned char *ccdata, int channel); + + +// cc_decoders_common.cpp +LLONG get_visible_start (void); +LLONG get_visible_end (void); + +// file_functions.cpp +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.cpp +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.cpp +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.cpp +int processmp4 (char *file); + +// params_dump.cpp +void params_dump(void); + +// output.cpp +void init_write (struct ccx_s_write *wb, int field); +void writeraw (const unsigned char *data, int length, struct ccx_s_write *wb); +void writedata (const unsigned char *data, int length, struct ccx_s_write *wb); +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.cpp +void detect_stream_type (void); +int detect_myth( void ); +int read_video_pes_header (unsigned char *header, int *headerlength, int sbuflen); + +// ts_functions.cpp +void init_ts_constants( void ); +int ts_readpacket(void); +long ts_readstream(void); +LLONG ts_getmoredata( void ); +int parse_PMT (int pos); +int parse_PAT (void); + +// myth.cpp +void myth_loop(void); + +// mp4_bridge2bento4.cpp +void mp4_loop (char *filename); + +// xds.cpp +void process_xds_bytes (const unsigned char hi, int lo); +void do_end_of_xds (unsigned char expected_checksum); +void xds_init(); + +// ccextractor.cpp +LLONG calculate_gop_mstime (struct gop_time_code *g); +void set_fts(void); +LLONG get_fts ( void ); +LLONG get_fts_max ( void ); +char *print_mstime( LLONG mstime ); +void print_debug_timing( void ); +int switch_to_next_file (LLONG bytesinbuffer); + +// utility.cpp +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 (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_eia608 (struct eia608 *data); +unsigned encode_line (unsigned char *buffer, unsigned char *text); +void buffered_seek (int offset); +void write_subtitle_file_header (struct ccx_s_write *wb); +void write_subtitle_file_footer (struct ccx_s_write *wb); +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); + +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 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 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 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; + +/* 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 unsigned long net_activity_gui; +extern int end_of_file; +extern LLONG inbuf; +extern 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 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 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 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; + +extern int temp_debug; + +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 diff --git a/src/ccfont2.xbm b/src/ccfont2.xbm new file mode 100644 index 00000000..cf67e603 --- /dev/null +++ b/src/ccfont2.xbm @@ -0,0 +1,1133 @@ +/* */ +/* libzvbi -- Closed Caption font */ +/* */ +/* Copyright (C) 2000, 2001, 2002 Michael H. Schimek */ +/* */ +/* This library is free software; you can redistribute it and/or */ +/* modify it under the terms of the GNU Library General Public */ +/* License as published by the Free Software Foundation; either */ +/* version 2 of the License, or (at your option) any later version. */ +/* */ +/* This library 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 */ +/* Library General Public License for more details. */ +/* */ +/* You should have received a copy of the GNU Library General Public */ +/* License along with this library; if not, write to the */ +/* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, */ +/* Boston, MA 02110-1301 USA. */ +/* */ +#define ccfont2_width 512 +#define ccfont2_height 208 +static unsigned char ccfont2_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x0c, + 0x00, 0x06, 0x00, 0x0c, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x1f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, + 0x30, 0x00, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x0e, 0x00, 0x0e, 0x00, 0x07, 0x00, 0x0e, 0x00, 0x0e, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x0f, 0xf0, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x70, 0x00, 0x00, 0x00, 0x70, 0x00, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x03, 0xc0, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x07, 0x80, 0x03, 0x00, 0x07, + 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x1f, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xe0, 0x00, 0x00, 0x00, 0xe0, 0x00, 0xe0, 0x07, + 0xe0, 0x07, 0xe0, 0x07, 0xe0, 0x07, 0xe0, 0x07, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x03, 0x80, 0x03, + 0xc0, 0x01, 0x80, 0x03, 0x80, 0x03, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x30, + 0x18, 0x0f, 0xfc, 0x3f, 0xf0, 0x0f, 0xc0, 0x03, 0x18, 0x0c, 0x80, 0x01, + 0xfc, 0x21, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0xc0, 0x01, 0x00, 0x00, + 0xc0, 0x01, 0x70, 0x0e, 0x70, 0x0e, 0x70, 0x0e, 0x70, 0x0e, 0x70, 0x0e, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x80, 0x01, 0x80, 0x01, 0xc0, 0x00, 0x80, 0x01, 0x80, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x1c, 0x30, 0x00, 0x00, 0xfc, 0x3f, 0xf8, 0x1f, 0xe0, 0x07, + 0x1c, 0x0c, 0x80, 0x01, 0xfc, 0x33, 0x00, 0x00, 0xc0, 0x0f, 0xe0, 0x3f, + 0x80, 0x03, 0x00, 0x00, 0x80, 0x03, 0x30, 0x0c, 0x30, 0x0c, 0x30, 0x0c, + 0x30, 0x0c, 0x30, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x80, 0x01, 0x3c, 0x30, 0x00, 0x00, 0xfc, 0x3f, + 0x1c, 0x38, 0x70, 0x0e, 0x1c, 0x06, 0x00, 0x00, 0x30, 0x3f, 0x80, 0x01, + 0xc0, 0x1c, 0xe0, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x01, 0x3c, 0x30, + 0x00, 0x00, 0xfc, 0x3f, 0x0c, 0x30, 0x30, 0x0c, 0x18, 0x03, 0x00, 0x00, + 0x30, 0x3f, 0x80, 0x01, 0x60, 0x18, 0xe0, 0x3f, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xe0, 0x07, 0xe0, 0x07, 0xe0, 0x01, 0xe0, 0x07, 0x0c, 0x30, 0xc0, 0x0f, + 0x80, 0x01, 0x7c, 0x30, 0xec, 0x07, 0xfc, 0x3f, 0xe6, 0x63, 0x30, 0x0c, + 0x18, 0x03, 0x80, 0x01, 0x30, 0x33, 0xe0, 0x07, 0x60, 0x00, 0x60, 0x30, + 0xe0, 0x07, 0x00, 0x00, 0xe0, 0x07, 0xe0, 0x07, 0xe0, 0x07, 0xe0, 0x01, + 0xe0, 0x07, 0x0c, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xf8, 0x1f, 0xf8, 0x1f, 0xe0, 0x01, 0xf8, 0x1f, + 0x0c, 0x30, 0xf0, 0x3f, 0x00, 0x00, 0xec, 0x30, 0xfc, 0x1f, 0xfc, 0x3f, + 0xe6, 0x67, 0x70, 0x0e, 0x98, 0x01, 0x80, 0x01, 0x30, 0x33, 0xf8, 0x1f, + 0x60, 0x00, 0x60, 0x30, 0xf8, 0x1f, 0x00, 0x00, 0xf8, 0x1f, 0xf8, 0x1f, + 0xf8, 0x1f, 0xe0, 0x01, 0xf8, 0x1f, 0x0c, 0x30, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, + 0x80, 0x01, 0x18, 0x18, 0x0c, 0x30, 0x30, 0x30, 0x00, 0x00, 0xcc, 0x30, + 0x1c, 0x18, 0xfc, 0x3f, 0x66, 0x66, 0xe0, 0x07, 0x98, 0x01, 0xc0, 0x00, + 0x30, 0x33, 0x98, 0x19, 0x60, 0x00, 0x60, 0x30, 0x18, 0x18, 0x00, 0x00, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x80, 0x01, 0x18, 0x18, 0x0c, 0x30, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x30, 0x0c, 0x30, 0x80, 0x01, 0x0c, 0x30, 0x0c, 0x30, 0x18, 0x00, + 0xf8, 0x1f, 0xcc, 0x31, 0x0c, 0x30, 0xfc, 0x3f, 0x66, 0x66, 0xc0, 0x03, + 0xd8, 0x00, 0xf0, 0x00, 0x30, 0x33, 0x8c, 0x01, 0xf8, 0x07, 0x60, 0x30, + 0x00, 0x30, 0x00, 0x00, 0x0c, 0x30, 0x00, 0x30, 0x0c, 0x30, 0x80, 0x01, + 0x0c, 0x30, 0x0c, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xf0, 0x37, 0x0c, 0x30, 0x80, 0x01, 0x0c, 0x30, + 0x0c, 0x30, 0x18, 0x00, 0xf8, 0x1f, 0x8c, 0x33, 0x0c, 0x30, 0xfc, 0x3f, + 0xe6, 0x63, 0x00, 0x00, 0xc0, 0x1e, 0x38, 0x00, 0x00, 0x00, 0x8c, 0x01, + 0xf8, 0x07, 0x60, 0x30, 0xf0, 0x37, 0x00, 0x00, 0x0c, 0x30, 0xf0, 0x37, + 0x0c, 0x30, 0x80, 0x01, 0x0c, 0x30, 0x0c, 0x30, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x3f, 0xfc, 0x3f, + 0x80, 0x01, 0x0c, 0x30, 0x0c, 0x30, 0x18, 0x00, 0x00, 0x00, 0x0c, 0x33, + 0x0c, 0x30, 0xfc, 0x3f, 0xe6, 0x63, 0x00, 0x00, 0x60, 0x3f, 0x1c, 0x00, + 0x00, 0x00, 0x8c, 0x01, 0x60, 0x00, 0x60, 0x30, 0xf8, 0x3f, 0x00, 0x00, + 0xfc, 0x3f, 0xf8, 0x3f, 0xfc, 0x3f, 0x80, 0x01, 0x0c, 0x30, 0x0c, 0x30, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x38, 0xfc, 0x3f, 0x80, 0x01, 0x0c, 0x30, 0x0c, 0x30, 0x18, 0x00, + 0x00, 0x00, 0x0c, 0x37, 0x0c, 0x30, 0xfc, 0x3f, 0x66, 0x67, 0x00, 0x00, + 0x60, 0x33, 0x0c, 0x00, 0x00, 0x00, 0x8c, 0x01, 0x60, 0x00, 0x78, 0x3c, + 0x18, 0x38, 0x00, 0x00, 0xfc, 0x3f, 0x18, 0x38, 0xfc, 0x3f, 0x80, 0x01, + 0x0c, 0x30, 0x0c, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0c, 0x30, 0x0c, 0x00, 0x80, 0x01, 0x0c, 0x30, + 0x0c, 0x30, 0x18, 0x00, 0x80, 0x01, 0x0c, 0x3e, 0x0c, 0x30, 0xfc, 0x3f, + 0x66, 0x66, 0x00, 0x00, 0x30, 0x30, 0x0c, 0x30, 0x00, 0x00, 0x8c, 0x01, + 0x78, 0x00, 0x7c, 0x3e, 0x0c, 0x30, 0x00, 0x00, 0x0c, 0x00, 0x0c, 0x30, + 0x0c, 0x00, 0x80, 0x01, 0x0c, 0x30, 0x0c, 0x30, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x30, 0x0c, 0x00, + 0x80, 0x01, 0x0c, 0x30, 0x0c, 0x30, 0x18, 0x00, 0x80, 0x01, 0x0c, 0x3c, + 0x0c, 0x30, 0xfc, 0x3f, 0x0c, 0x30, 0x00, 0x00, 0x18, 0x1c, 0x0c, 0x30, + 0x00, 0x00, 0x8c, 0x01, 0x7c, 0x30, 0x7c, 0x3e, 0x0c, 0x30, 0x00, 0x00, + 0x0c, 0x00, 0x0c, 0x30, 0x0c, 0x00, 0x80, 0x01, 0x0c, 0x30, 0x0c, 0x30, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x38, 0x18, 0x18, 0x80, 0x01, 0x18, 0x18, 0x18, 0x38, 0x30, 0x30, + 0x80, 0x01, 0x0c, 0x3c, 0x0c, 0x30, 0xfc, 0x3f, 0x1c, 0x38, 0x00, 0x00, + 0x18, 0x06, 0x18, 0x18, 0x00, 0x00, 0x98, 0x19, 0xec, 0x38, 0x7c, 0x3e, + 0x18, 0x38, 0x00, 0x00, 0x18, 0x18, 0x18, 0x38, 0x18, 0x18, 0x80, 0x01, + 0x18, 0x18, 0x18, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xf8, 0x3f, 0xf8, 0x1f, 0xe0, 0x07, 0xf8, 0x1f, + 0xf8, 0x3f, 0xf0, 0x3f, 0x00, 0x00, 0x0c, 0x38, 0x0c, 0x30, 0xfc, 0x3f, + 0xf8, 0x1f, 0x00, 0x00, 0x0c, 0x3f, 0xf8, 0x1f, 0x00, 0x00, 0xf8, 0x1f, + 0xfc, 0x1f, 0x38, 0x1c, 0xf8, 0x3f, 0x00, 0x00, 0xf8, 0x1f, 0xf8, 0x3f, + 0xf8, 0x1f, 0xe0, 0x07, 0xf8, 0x1f, 0xf8, 0x3f, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x37, 0xe0, 0x07, + 0xe0, 0x07, 0xe0, 0x07, 0xe0, 0x37, 0xc0, 0x0f, 0x00, 0x00, 0x0c, 0x30, + 0x0c, 0x30, 0xfc, 0x3f, 0xf0, 0x0f, 0x00, 0x00, 0x0c, 0x3f, 0xe0, 0x07, + 0x00, 0x00, 0xe0, 0x07, 0x38, 0x0f, 0x00, 0x00, 0xe0, 0x37, 0x00, 0x00, + 0xe0, 0x07, 0xe0, 0x37, 0xe0, 0x07, 0xe0, 0x07, 0xe0, 0x07, 0xe0, 0x37, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc0, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x01, 0x60, 0x06, 0x00, 0x00, + 0xe0, 0x07, 0x78, 0x18, 0xe0, 0x01, 0x80, 0x01, 0x00, 0x03, 0xc0, 0x00, + 0x80, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, + 0xe0, 0x07, 0x80, 0x01, 0xe0, 0x07, 0xe0, 0x07, 0x80, 0x0f, 0xfc, 0x3f, + 0xe0, 0x07, 0xfc, 0x3f, 0xe0, 0x07, 0xe0, 0x07, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x07, 0x00, 0x00, 0x80, 0x01, + 0x60, 0x06, 0x60, 0x18, 0xf8, 0x1f, 0xfc, 0x18, 0xf0, 0x03, 0x80, 0x01, + 0x80, 0x03, 0xc0, 0x01, 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x18, 0xf8, 0x1f, 0x80, 0x01, 0xf8, 0x1f, 0xf8, 0x1f, + 0xc0, 0x0f, 0xfc, 0x3f, 0xf8, 0x1f, 0xfc, 0x3f, 0xf8, 0x1f, 0xf8, 0x1f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x1f, + 0x00, 0x00, 0x80, 0x01, 0x60, 0x06, 0x60, 0x18, 0x98, 0x39, 0xcc, 0x0c, + 0x38, 0x03, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x18, 0x18, 0xe0, 0x01, + 0x18, 0x18, 0x1c, 0x18, 0xc0, 0x0c, 0x0c, 0x00, 0x18, 0x18, 0x0c, 0x30, + 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, + 0x0c, 0x00, 0x18, 0x18, 0x00, 0x00, 0x80, 0x01, 0x60, 0x06, 0x60, 0x18, + 0x8c, 0x31, 0xcc, 0x0e, 0x18, 0x03, 0xc0, 0x00, 0xc0, 0x00, 0x00, 0x03, + 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, + 0x0c, 0x38, 0xe0, 0x01, 0x0c, 0x30, 0x0c, 0x30, 0xe0, 0x0c, 0x0c, 0x00, + 0x0c, 0x30, 0x0c, 0x38, 0x0c, 0x30, 0x0c, 0x30, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x3c, 0x00, 0x00, 0x3c, 0x00, 0x0c, 0x30, 0x00, 0x00, 0x80, 0x01, + 0x60, 0x06, 0xfc, 0x3f, 0x8c, 0x01, 0xfc, 0x06, 0x18, 0x03, 0xc0, 0x00, + 0xc0, 0x00, 0x00, 0x03, 0xe0, 0x07, 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x06, 0x0c, 0x3c, 0x80, 0x01, 0x0c, 0x30, 0x0c, 0x30, + 0x60, 0x0c, 0x0c, 0x00, 0x0c, 0x00, 0x00, 0x1c, 0x0c, 0x30, 0x0c, 0x30, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0xf0, 0x00, 0x0c, 0x30, + 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0xfc, 0x3f, 0x8c, 0x01, 0x78, 0x03, + 0x98, 0x03, 0x00, 0x00, 0x60, 0x00, 0x00, 0x06, 0xf8, 0x1f, 0x80, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x0c, 0x3e, 0x80, 0x01, + 0x00, 0x30, 0x00, 0x30, 0x70, 0x0c, 0xec, 0x07, 0x0c, 0x00, 0x00, 0x0c, + 0x0c, 0x30, 0x0c, 0x30, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x03, 0xf8, 0x1f, + 0xc0, 0x03, 0x00, 0x30, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x30, 0x0c, + 0x98, 0x01, 0x00, 0x03, 0xf8, 0x01, 0x00, 0x00, 0x60, 0x00, 0x00, 0x06, + 0x18, 0x18, 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, + 0x0c, 0x37, 0x80, 0x01, 0x00, 0x18, 0x00, 0x18, 0x30, 0x0c, 0xfc, 0x1f, + 0xec, 0x07, 0x00, 0x06, 0x1c, 0x38, 0x0c, 0x30, 0x00, 0x00, 0x00, 0x00, + 0xf0, 0x00, 0xf8, 0x1f, 0x00, 0x0f, 0x00, 0x38, 0x00, 0x00, 0x80, 0x01, + 0x00, 0x00, 0x30, 0x0c, 0xf8, 0x07, 0x80, 0x01, 0xf0, 0x00, 0x00, 0x00, + 0x60, 0x00, 0x00, 0x06, 0x00, 0x30, 0xf8, 0x1f, 0x00, 0x00, 0xf8, 0x1f, + 0x00, 0x00, 0x80, 0x01, 0x8c, 0x33, 0x80, 0x01, 0x00, 0x1e, 0x80, 0x1f, + 0x38, 0x0c, 0x3c, 0x18, 0xfc, 0x1f, 0x00, 0x06, 0xf8, 0x1f, 0x18, 0x38, + 0x80, 0x01, 0x80, 0x01, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x1c, + 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x30, 0x0c, 0xe0, 0x1f, 0x80, 0x01, + 0xf8, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x06, 0xf0, 0x37, 0xf8, 0x1f, + 0x00, 0x00, 0xf8, 0x1f, 0x00, 0x00, 0x80, 0x01, 0xcc, 0x31, 0x80, 0x01, + 0xc0, 0x0f, 0x80, 0x1f, 0x18, 0x0c, 0x00, 0x30, 0x1c, 0x18, 0x00, 0x03, + 0xf8, 0x1f, 0xf8, 0x3f, 0x80, 0x01, 0x80, 0x01, 0x3c, 0x00, 0x00, 0x00, + 0x00, 0x3c, 0x00, 0x0f, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x30, 0x0c, + 0x80, 0x19, 0xc0, 0x00, 0xdc, 0x31, 0x00, 0x00, 0x60, 0x00, 0x00, 0x06, + 0xf8, 0x3f, 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x00, + 0xec, 0x30, 0x80, 0x01, 0xf0, 0x03, 0x00, 0x18, 0x1c, 0x0c, 0x00, 0x30, + 0x0c, 0x30, 0x00, 0x03, 0x1c, 0x38, 0xe0, 0x37, 0x80, 0x01, 0x80, 0x01, + 0xf0, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x03, 0x00, 0x00, 0x80, 0x01, + 0x00, 0x00, 0xfc, 0x3f, 0x80, 0x31, 0xc0, 0x1e, 0x8c, 0x3b, 0x00, 0x00, + 0x60, 0x00, 0x00, 0x06, 0x18, 0x38, 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc0, 0x00, 0x7c, 0x30, 0x80, 0x01, 0x38, 0x00, 0x00, 0x30, + 0xfc, 0x3f, 0x00, 0x30, 0x0c, 0x30, 0x80, 0x01, 0x0c, 0x30, 0x00, 0x30, + 0x00, 0x00, 0x00, 0x00, 0xc0, 0x03, 0xf8, 0x1f, 0xc0, 0x03, 0x80, 0x01, + 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0xfc, 0x3f, 0x80, 0x31, 0x60, 0x3f, + 0x0c, 0x1f, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x03, 0x0c, 0x30, 0x80, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x3c, 0x30, 0x80, 0x01, + 0x18, 0x00, 0x0c, 0x30, 0xfc, 0x3f, 0x0c, 0x30, 0x0c, 0x30, 0x80, 0x01, + 0x0c, 0x30, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xf8, 0x1f, + 0xf0, 0x00, 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x06, + 0x8c, 0x31, 0x70, 0x33, 0x0c, 0x0e, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x03, + 0x0c, 0x30, 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, + 0x1c, 0x30, 0x80, 0x01, 0x0c, 0x00, 0x0c, 0x30, 0x00, 0x0c, 0x0c, 0x30, + 0x0c, 0x30, 0x80, 0x01, 0x0c, 0x30, 0x0c, 0x30, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x3c, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x06, 0x9c, 0x19, 0x30, 0x33, 0x18, 0x1f, 0x00, 0x00, + 0x80, 0x01, 0x80, 0x01, 0x18, 0x38, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, + 0x80, 0x01, 0x30, 0x00, 0x18, 0x18, 0x80, 0x01, 0x0c, 0x00, 0x18, 0x18, + 0x00, 0x0c, 0x18, 0x18, 0x18, 0x18, 0x80, 0x01, 0x18, 0x18, 0x18, 0x18, + 0x80, 0x01, 0x80, 0x01, 0x00, 0x30, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x18, 0x06, 0xf8, 0x1f, 0x18, 0x3f, + 0xf8, 0x3b, 0x00, 0x00, 0x80, 0x03, 0xc0, 0x01, 0xf8, 0x3f, 0x00, 0x00, + 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x18, 0x00, 0xf8, 0x1f, 0xe0, 0x07, + 0xfc, 0x3f, 0xf8, 0x1f, 0x00, 0x3f, 0xf8, 0x1f, 0xf8, 0x1f, 0x80, 0x01, + 0xf8, 0x1f, 0xf8, 0x1f, 0x80, 0x01, 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, + 0xe0, 0x07, 0x18, 0x1e, 0xe0, 0x31, 0x00, 0x00, 0x00, 0x03, 0xc0, 0x00, + 0xe0, 0x37, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x18, 0x00, + 0xe0, 0x07, 0xe0, 0x07, 0xfc, 0x3f, 0xe0, 0x07, 0x00, 0x3f, 0xe0, 0x07, + 0xe0, 0x07, 0x80, 0x01, 0xe0, 0x07, 0xe0, 0x07, 0x80, 0x01, 0x80, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, + 0x00, 0x06, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x0e, 0x00, 0x00, 0x00, 0x07, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x80, 0x03, 0x00, 0x07, + 0xe0, 0x07, 0xe0, 0x07, 0xfc, 0x03, 0xe0, 0x07, 0xfc, 0x03, 0xfc, 0x3f, + 0xfc, 0x3f, 0xe0, 0x07, 0x0c, 0x30, 0xe0, 0x07, 0x00, 0x30, 0x0c, 0x30, + 0x0c, 0x00, 0x0c, 0x30, 0x0c, 0x30, 0xe0, 0x07, 0xfc, 0x07, 0xe0, 0x07, + 0xfc, 0x07, 0xe0, 0x07, 0xfc, 0x3f, 0x0c, 0x30, 0x0c, 0x30, 0x0c, 0x30, + 0x0c, 0x30, 0x0c, 0x30, 0xfc, 0x3f, 0xe0, 0x07, 0x80, 0x03, 0xe0, 0x07, + 0xc0, 0x01, 0x80, 0x03, 0xf8, 0x1f, 0xf8, 0x1f, 0xfc, 0x0f, 0xf8, 0x1f, + 0xfc, 0x0f, 0xfc, 0x3f, 0xfc, 0x3f, 0xf8, 0x1f, 0x0c, 0x30, 0xe0, 0x07, + 0x00, 0x30, 0x0c, 0x38, 0x0c, 0x00, 0x1c, 0x38, 0x1c, 0x30, 0xf8, 0x1f, + 0xfc, 0x1f, 0xf8, 0x1f, 0xfc, 0x1f, 0xf8, 0x1f, 0xfc, 0x3f, 0x0c, 0x30, + 0x0c, 0x30, 0x0c, 0x30, 0x0c, 0x30, 0x0c, 0x30, 0xfc, 0x3f, 0xe0, 0x07, + 0x80, 0x01, 0xe0, 0x07, 0xc0, 0x00, 0x80, 0x01, 0x18, 0x18, 0x18, 0x18, + 0x0c, 0x0c, 0x18, 0x18, 0x0c, 0x0c, 0x0c, 0x00, 0x0c, 0x00, 0x18, 0x18, + 0x0c, 0x30, 0x80, 0x01, 0x00, 0x30, 0x0c, 0x1c, 0x0c, 0x00, 0x1c, 0x38, + 0x3c, 0x30, 0x18, 0x18, 0x0c, 0x18, 0x18, 0x18, 0x0c, 0x18, 0x18, 0x38, + 0x80, 0x01, 0x0c, 0x30, 0x0c, 0x30, 0x0c, 0x30, 0x1c, 0x38, 0x1c, 0x38, + 0x00, 0x30, 0x60, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, + 0x0c, 0x30, 0x0c, 0x30, 0x0c, 0x18, 0x0c, 0x30, 0x0c, 0x18, 0x0c, 0x00, + 0x0c, 0x00, 0x0c, 0x30, 0x0c, 0x30, 0x80, 0x01, 0x00, 0x30, 0x0c, 0x0e, + 0x0c, 0x00, 0x3c, 0x3c, 0x3c, 0x30, 0x0c, 0x30, 0x0c, 0x30, 0x0c, 0x30, + 0x0c, 0x30, 0x0c, 0x30, 0x80, 0x01, 0x0c, 0x30, 0x0c, 0x30, 0x0c, 0x30, + 0x38, 0x1c, 0x18, 0x18, 0x00, 0x38, 0x60, 0x00, 0x00, 0x00, 0x00, 0x06, + 0x00, 0x00, 0x00, 0x00, 0x0c, 0x3f, 0x0c, 0x30, 0x0c, 0x18, 0x0c, 0x30, + 0x0c, 0x18, 0x0c, 0x00, 0x0c, 0x00, 0x0c, 0x30, 0x0c, 0x30, 0x80, 0x01, + 0x00, 0x30, 0x0c, 0x07, 0x0c, 0x00, 0x3c, 0x3c, 0x7c, 0x30, 0x0c, 0x30, + 0x0c, 0x30, 0x0c, 0x30, 0x0c, 0x30, 0x0c, 0x00, 0x80, 0x01, 0x0c, 0x30, + 0x0c, 0x30, 0x0c, 0x30, 0x30, 0x0c, 0x30, 0x0c, 0x00, 0x1c, 0x60, 0x00, + 0xe0, 0x07, 0x00, 0x06, 0xe0, 0x01, 0xe0, 0x07, 0x8c, 0x3f, 0x0c, 0x30, + 0x0c, 0x18, 0x0c, 0x00, 0x0c, 0x30, 0x0c, 0x00, 0x0c, 0x00, 0x0c, 0x00, + 0x0c, 0x30, 0x80, 0x01, 0x00, 0x30, 0x8c, 0x03, 0x0c, 0x00, 0x6c, 0x36, + 0xec, 0x30, 0x0c, 0x30, 0x0c, 0x30, 0x0c, 0x30, 0x0c, 0x30, 0x0c, 0x00, + 0x80, 0x01, 0x0c, 0x30, 0x0c, 0x30, 0x0c, 0x30, 0x70, 0x0e, 0x70, 0x0e, + 0x00, 0x0e, 0x60, 0x00, 0xf8, 0x1f, 0x00, 0x06, 0xe0, 0x01, 0xf8, 0x1f, + 0xcc, 0x31, 0x0c, 0x30, 0x0c, 0x0c, 0x0c, 0x00, 0x0c, 0x30, 0x0c, 0x00, + 0x0c, 0x00, 0x0c, 0x00, 0x0c, 0x30, 0x80, 0x01, 0x00, 0x30, 0xcc, 0x01, + 0x0c, 0x00, 0x6c, 0x36, 0xcc, 0x30, 0x0c, 0x30, 0x0c, 0x30, 0x0c, 0x30, + 0x0c, 0x30, 0x18, 0x00, 0x80, 0x01, 0x0c, 0x30, 0x18, 0x18, 0x8c, 0x31, + 0xe0, 0x07, 0x60, 0x06, 0x00, 0x07, 0x60, 0x00, 0x18, 0x18, 0x00, 0x06, + 0x80, 0x01, 0x18, 0x18, 0xcc, 0x30, 0xfc, 0x3f, 0xfc, 0x0f, 0x0c, 0x00, + 0x0c, 0x30, 0xfc, 0x07, 0xfc, 0x07, 0x0c, 0x3e, 0xfc, 0x3f, 0x80, 0x01, + 0x00, 0x30, 0xfc, 0x00, 0x0c, 0x00, 0xcc, 0x33, 0xcc, 0x31, 0x0c, 0x30, + 0x0c, 0x18, 0x0c, 0x30, 0x0c, 0x18, 0xf8, 0x07, 0x80, 0x01, 0x0c, 0x30, + 0x18, 0x18, 0x8c, 0x31, 0xc0, 0x03, 0xe0, 0x07, 0x80, 0x03, 0x60, 0x00, + 0x0c, 0x30, 0x00, 0x06, 0x80, 0x01, 0x0c, 0x30, 0xcc, 0x30, 0xfc, 0x3f, + 0xfc, 0x1f, 0x0c, 0x00, 0x0c, 0x30, 0xfc, 0x07, 0xfc, 0x07, 0x0c, 0x3e, + 0xfc, 0x3f, 0x80, 0x01, 0x00, 0x30, 0xfc, 0x00, 0x0c, 0x00, 0xcc, 0x33, + 0x8c, 0x33, 0x0c, 0x30, 0xfc, 0x1f, 0x0c, 0x30, 0xfc, 0x1f, 0xe0, 0x1f, + 0x80, 0x01, 0x0c, 0x30, 0x30, 0x0c, 0xcc, 0x33, 0xc0, 0x03, 0xc0, 0x03, + 0xc0, 0x01, 0x60, 0x00, 0x0c, 0x30, 0x00, 0x06, 0x80, 0x01, 0x0c, 0x30, + 0xcc, 0x31, 0x0c, 0x30, 0x0c, 0x18, 0x0c, 0x00, 0x0c, 0x30, 0x0c, 0x00, + 0x0c, 0x00, 0x0c, 0x30, 0x0c, 0x30, 0x80, 0x01, 0x00, 0x30, 0xcc, 0x01, + 0x0c, 0x00, 0x8c, 0x31, 0x0c, 0x33, 0x0c, 0x30, 0xfc, 0x07, 0x0c, 0x30, + 0xfc, 0x07, 0x00, 0x18, 0x80, 0x01, 0x0c, 0x30, 0x30, 0x0c, 0xd8, 0x1b, + 0xe0, 0x07, 0xc0, 0x03, 0xe0, 0x00, 0x60, 0x00, 0xfc, 0x3f, 0x00, 0x06, + 0x80, 0x01, 0x0c, 0x30, 0x8c, 0x3f, 0x0c, 0x30, 0x0c, 0x30, 0x0c, 0x00, + 0x0c, 0x30, 0x0c, 0x00, 0x0c, 0x00, 0x0c, 0x30, 0x0c, 0x30, 0x80, 0x01, + 0x00, 0x30, 0x8c, 0x03, 0x0c, 0x00, 0x8c, 0x31, 0x0c, 0x37, 0x0c, 0x30, + 0x0c, 0x00, 0x0c, 0x30, 0x0c, 0x0c, 0x00, 0x30, 0x80, 0x01, 0x0c, 0x30, + 0x60, 0x06, 0x78, 0x1e, 0x70, 0x0e, 0x80, 0x01, 0x70, 0x00, 0x60, 0x00, + 0xfc, 0x3f, 0x00, 0x06, 0x80, 0x01, 0x0c, 0x30, 0x0c, 0x3f, 0x0c, 0x30, + 0x0c, 0x30, 0x0c, 0x30, 0x0c, 0x18, 0x0c, 0x00, 0x0c, 0x00, 0x0c, 0x30, + 0x0c, 0x30, 0x80, 0x01, 0x0c, 0x30, 0x0c, 0x07, 0x0c, 0x00, 0x0c, 0x30, + 0x0c, 0x3e, 0x0c, 0x30, 0x0c, 0x00, 0x0c, 0x36, 0x0c, 0x0c, 0x00, 0x30, + 0x80, 0x01, 0x0c, 0x30, 0x60, 0x06, 0x78, 0x1e, 0x30, 0x0c, 0x80, 0x01, + 0x38, 0x00, 0x60, 0x00, 0x0c, 0x00, 0x00, 0x06, 0x80, 0x01, 0x0c, 0x30, + 0x0c, 0x00, 0x0c, 0x30, 0x0c, 0x30, 0x0c, 0x30, 0x0c, 0x18, 0x0c, 0x00, + 0x0c, 0x00, 0x0c, 0x30, 0x0c, 0x30, 0x80, 0x01, 0x0c, 0x30, 0x0c, 0x0e, + 0x0c, 0x00, 0x0c, 0x30, 0x0c, 0x3c, 0x0c, 0x30, 0x0c, 0x00, 0x0c, 0x3e, + 0x0c, 0x18, 0x0c, 0x30, 0x80, 0x01, 0x0c, 0x30, 0xc0, 0x03, 0x38, 0x1c, + 0x38, 0x1c, 0x80, 0x01, 0x1c, 0x00, 0x60, 0x00, 0x0c, 0x00, 0x00, 0x06, + 0x80, 0x01, 0x0c, 0x30, 0x18, 0x00, 0x0c, 0x30, 0x0c, 0x18, 0x18, 0x18, + 0x0c, 0x0c, 0x0c, 0x00, 0x0c, 0x00, 0x18, 0x18, 0x0c, 0x30, 0x80, 0x01, + 0x18, 0x18, 0x0c, 0x1c, 0x0c, 0x00, 0x0c, 0x30, 0x0c, 0x3c, 0x18, 0x18, + 0x0c, 0x00, 0x18, 0x1c, 0x0c, 0x18, 0x1c, 0x18, 0x80, 0x01, 0x18, 0x18, + 0xc0, 0x03, 0x38, 0x1c, 0x1c, 0x38, 0x80, 0x01, 0x0c, 0x00, 0x60, 0x00, + 0x18, 0x18, 0x00, 0x06, 0x80, 0x01, 0x18, 0x18, 0xf8, 0x0f, 0x0c, 0x30, + 0xfc, 0x1f, 0xf8, 0x1f, 0xfc, 0x0f, 0xfc, 0x3f, 0x0c, 0x00, 0xf8, 0x1f, + 0x0c, 0x30, 0xe0, 0x07, 0xf8, 0x1f, 0x0c, 0x38, 0xfc, 0x3f, 0x0c, 0x30, + 0x0c, 0x38, 0xf8, 0x1f, 0x0c, 0x00, 0xf8, 0x3f, 0x0c, 0x30, 0xf8, 0x1f, + 0x80, 0x01, 0xf8, 0x1f, 0x80, 0x01, 0x18, 0x18, 0x0c, 0x30, 0x80, 0x01, + 0xfc, 0x3f, 0xe0, 0x07, 0xf8, 0x1f, 0xe0, 0x07, 0xe0, 0x07, 0xf8, 0x1f, + 0xe0, 0x0f, 0x0c, 0x30, 0xfc, 0x07, 0xe0, 0x07, 0xfc, 0x03, 0xfc, 0x3f, + 0x0c, 0x00, 0xe0, 0x07, 0x0c, 0x30, 0xe0, 0x07, 0xe0, 0x07, 0x0c, 0x30, + 0xfc, 0x3f, 0x0c, 0x30, 0x0c, 0x30, 0xe0, 0x07, 0x0c, 0x00, 0xe0, 0x37, + 0x0c, 0x30, 0xe0, 0x07, 0x80, 0x01, 0xe0, 0x07, 0x80, 0x01, 0x18, 0x18, + 0x0c, 0x30, 0x80, 0x01, 0xfc, 0x3f, 0xe0, 0x07, 0xe0, 0x07, 0xe0, 0x07, + 0xe0, 0x07, 0xe0, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xf0, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x1f, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x0f, + 0xf0, 0x18, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xf8, 0x1f, 0x00, 0x00, 0x80, 0x03, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, + 0x0c, 0x00, 0x80, 0x01, 0x80, 0x01, 0x0c, 0x00, 0xf0, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x30, 0x18, 0x0f, 0xfc, 0x3f, + 0x80, 0x01, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, + 0x80, 0x3f, 0x00, 0x00, 0x0c, 0x00, 0x80, 0x01, 0x80, 0x01, 0x0c, 0x00, + 0xf0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x30, + 0x00, 0x00, 0xfc, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x00, 0x30, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0c, 0x00, 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x80, 0x01, 0x3c, 0x30, 0x00, 0x00, 0xfc, 0x3f, 0x00, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x80, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x80, 0x01, 0x3c, 0x30, 0x00, 0x00, 0xfc, 0x3f, + 0x0c, 0x30, 0xe0, 0x07, 0xec, 0x07, 0xe0, 0x07, 0xe0, 0x37, 0xe0, 0x07, + 0x80, 0x01, 0xe0, 0x37, 0xec, 0x07, 0xe0, 0x01, 0xf0, 0x01, 0x0c, 0x30, + 0x80, 0x01, 0x6c, 0x07, 0xec, 0x07, 0xe0, 0x07, 0xec, 0x07, 0xe0, 0x37, + 0xec, 0x07, 0xf0, 0x1f, 0xf8, 0x07, 0x0c, 0x30, 0x0c, 0x30, 0x0c, 0x30, + 0x0c, 0x30, 0x0c, 0x30, 0xfc, 0x3f, 0xe0, 0x07, 0x80, 0x01, 0x7c, 0x30, + 0xec, 0x07, 0xfc, 0x3f, 0x0c, 0x30, 0xf8, 0x1f, 0xfc, 0x1f, 0xf8, 0x1f, + 0xf8, 0x3f, 0xf8, 0x1f, 0xf0, 0x3f, 0xf8, 0x3f, 0xfc, 0x1f, 0xe0, 0x01, + 0xf0, 0x01, 0x0c, 0x38, 0x80, 0x01, 0xfc, 0x1f, 0xfc, 0x1f, 0xf8, 0x1f, + 0xfc, 0x1f, 0xf8, 0x3f, 0xfc, 0x1f, 0xf8, 0x3f, 0xf8, 0x07, 0x0c, 0x30, + 0x0c, 0x30, 0x0c, 0x30, 0x1c, 0x38, 0x0c, 0x30, 0xfc, 0x3f, 0xf8, 0x1f, + 0x00, 0x00, 0xec, 0x30, 0xfc, 0x1f, 0xfc, 0x3f, 0x0c, 0x30, 0x18, 0x18, + 0x1c, 0x18, 0x18, 0x18, 0x18, 0x38, 0x18, 0x18, 0xf0, 0x3f, 0x18, 0x38, + 0x1c, 0x18, 0x80, 0x01, 0x80, 0x01, 0x0c, 0x1c, 0x80, 0x01, 0xdc, 0x18, + 0x1c, 0x18, 0x18, 0x18, 0x1c, 0x18, 0x18, 0x38, 0x1c, 0x18, 0x1c, 0x30, + 0x60, 0x00, 0x0c, 0x30, 0x18, 0x18, 0x8c, 0x31, 0x38, 0x1c, 0x0c, 0x30, + 0x00, 0x1c, 0x18, 0x18, 0x00, 0x00, 0xcc, 0x30, 0x1c, 0x18, 0xfc, 0x3f, + 0x0c, 0x30, 0x00, 0x30, 0x0c, 0x30, 0x0c, 0x00, 0x0c, 0x30, 0x0c, 0x30, + 0x80, 0x01, 0x0c, 0x30, 0x0c, 0x30, 0x80, 0x01, 0x80, 0x01, 0x0c, 0x0e, + 0x80, 0x01, 0x8c, 0x31, 0x0c, 0x30, 0x0c, 0x30, 0x0c, 0x30, 0x0c, 0x30, + 0x0c, 0x30, 0x0c, 0x00, 0x60, 0x00, 0x0c, 0x30, 0x18, 0x18, 0x8c, 0x31, + 0x70, 0x0e, 0x0c, 0x30, 0x00, 0x0e, 0x0c, 0x00, 0xf8, 0x1f, 0xcc, 0x31, + 0x0c, 0x30, 0xfc, 0x3f, 0x0c, 0x30, 0xf0, 0x37, 0x0c, 0x30, 0x0c, 0x00, + 0x0c, 0x30, 0x0c, 0x30, 0x80, 0x01, 0x0c, 0x30, 0x0c, 0x30, 0x80, 0x01, + 0x80, 0x01, 0x0c, 0x07, 0x80, 0x01, 0x8c, 0x31, 0x0c, 0x30, 0x0c, 0x30, + 0x0c, 0x30, 0x0c, 0x30, 0x0c, 0x30, 0x1c, 0x00, 0x60, 0x00, 0x0c, 0x30, + 0x30, 0x0c, 0xcc, 0x33, 0xe0, 0x07, 0x0c, 0x30, 0x00, 0x07, 0x0c, 0x00, + 0xf8, 0x1f, 0x8c, 0x33, 0x0c, 0x30, 0xfc, 0x3f, 0x0c, 0x30, 0xf8, 0x3f, + 0x0c, 0x30, 0x0c, 0x00, 0x0c, 0x30, 0xfc, 0x3f, 0x80, 0x01, 0x0c, 0x30, + 0x0c, 0x30, 0x80, 0x01, 0x80, 0x01, 0xfc, 0x03, 0x80, 0x01, 0x8c, 0x31, + 0x0c, 0x30, 0x0c, 0x30, 0x0c, 0x30, 0x0c, 0x30, 0x0c, 0x00, 0xf8, 0x0f, + 0x60, 0x00, 0x0c, 0x30, 0x30, 0x0c, 0xcc, 0x33, 0xc0, 0x03, 0x0c, 0x30, + 0x80, 0x03, 0x0c, 0x00, 0x00, 0x00, 0x0c, 0x33, 0x0c, 0x30, 0xfc, 0x3f, + 0x0c, 0x30, 0x18, 0x38, 0x0c, 0x30, 0x0c, 0x00, 0x0c, 0x30, 0xfc, 0x3f, + 0x80, 0x01, 0x0c, 0x30, 0x0c, 0x30, 0x80, 0x01, 0x80, 0x01, 0xfc, 0x03, + 0x80, 0x01, 0x8c, 0x31, 0x0c, 0x30, 0x0c, 0x30, 0x0c, 0x30, 0x0c, 0x30, + 0x0c, 0x00, 0xf0, 0x1f, 0x60, 0x00, 0x0c, 0x30, 0x60, 0x06, 0x78, 0x1e, + 0xc0, 0x03, 0x0c, 0x30, 0xc0, 0x01, 0x0c, 0x00, 0x00, 0x00, 0x0c, 0x37, + 0x0c, 0x30, 0xfc, 0x3f, 0x0c, 0x30, 0x0c, 0x30, 0x0c, 0x30, 0x0c, 0x00, + 0x0c, 0x30, 0x0c, 0x00, 0x80, 0x01, 0x0c, 0x30, 0x0c, 0x30, 0x80, 0x01, + 0x80, 0x01, 0x0c, 0x07, 0x80, 0x01, 0x8c, 0x31, 0x0c, 0x30, 0x0c, 0x30, + 0x0c, 0x30, 0x0c, 0x30, 0x0c, 0x00, 0x00, 0x38, 0x60, 0x00, 0x0c, 0x30, + 0x60, 0x06, 0x78, 0x1e, 0xe0, 0x07, 0x0c, 0x30, 0xe0, 0x00, 0x0c, 0x00, + 0x80, 0x01, 0x0c, 0x3e, 0x0c, 0x30, 0xfc, 0x3f, 0x0c, 0x30, 0x0c, 0x30, + 0x0c, 0x30, 0x0c, 0x00, 0x0c, 0x30, 0x0c, 0x00, 0x80, 0x01, 0x0c, 0x30, + 0x0c, 0x30, 0x80, 0x01, 0x80, 0x01, 0x0c, 0x0e, 0x80, 0x01, 0x8c, 0x31, + 0x0c, 0x30, 0x0c, 0x30, 0x0c, 0x30, 0x0c, 0x30, 0x0c, 0x00, 0x00, 0x30, + 0x60, 0x00, 0x0c, 0x30, 0xc0, 0x03, 0x38, 0x1c, 0x70, 0x0e, 0x0c, 0x30, + 0x70, 0x00, 0x0c, 0x00, 0x80, 0x01, 0x0c, 0x3c, 0x0c, 0x30, 0xfc, 0x3f, + 0x18, 0x38, 0x18, 0x38, 0x1c, 0x18, 0x18, 0x18, 0x18, 0x38, 0x18, 0x18, + 0x80, 0x01, 0x18, 0x38, 0x0c, 0x30, 0x80, 0x01, 0x80, 0x01, 0x0c, 0x1c, + 0x80, 0x01, 0x8c, 0x31, 0x0c, 0x30, 0x18, 0x18, 0x1c, 0x18, 0x18, 0x38, + 0x0c, 0x00, 0x0c, 0x38, 0x60, 0x00, 0x18, 0x38, 0xc0, 0x03, 0x38, 0x1c, + 0x38, 0x1c, 0x18, 0x38, 0x38, 0x00, 0x18, 0x18, 0x80, 0x01, 0x0c, 0x3c, + 0x0c, 0x30, 0xfc, 0x3f, 0xf8, 0x3f, 0xf8, 0x3f, 0xfc, 0x1f, 0xf8, 0x1f, + 0xf8, 0x3f, 0xf8, 0x1f, 0x80, 0x01, 0xf8, 0x3f, 0x0c, 0x30, 0xe0, 0x07, + 0x80, 0x01, 0x0c, 0x38, 0xf0, 0x0f, 0x8c, 0x31, 0x0c, 0x30, 0xf8, 0x1f, + 0xfc, 0x1f, 0xf8, 0x3f, 0x0c, 0x00, 0xfc, 0x1f, 0xc0, 0x1f, 0xf8, 0x3f, + 0x80, 0x01, 0x18, 0x18, 0x1c, 0x38, 0xf8, 0x3f, 0xfc, 0x3f, 0xf8, 0x1f, + 0x00, 0x00, 0x0c, 0x38, 0x0c, 0x30, 0xfc, 0x3f, 0xe0, 0x37, 0xe0, 0x37, + 0xec, 0x07, 0xe0, 0x07, 0xe0, 0x37, 0xe0, 0x07, 0x80, 0x01, 0xe0, 0x37, + 0x0c, 0x30, 0xe0, 0x07, 0x80, 0x01, 0x0c, 0x30, 0xf0, 0x0f, 0x8c, 0x31, + 0x0c, 0x30, 0xe0, 0x07, 0xec, 0x07, 0xe0, 0x37, 0x0c, 0x00, 0xf8, 0x0f, + 0xc0, 0x1f, 0xe0, 0x37, 0x80, 0x01, 0x18, 0x18, 0x0c, 0x30, 0xe0, 0x37, + 0xfc, 0x3f, 0xe0, 0x07, 0x00, 0x00, 0x0c, 0x30, 0x0c, 0x30, 0xfc, 0x3f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x86, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x30, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x86, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0xe0, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x1f, + 0x00, 0x00, 0x00, 0x00, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x1f, + 0x00, 0x00, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xe0, 0x07, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x30, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xf8, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc0, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x18, 0x00, 0x0c, 0x00, 0x18, + 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x7f, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x03, + 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x1c, + 0x00, 0x0e, 0x00, 0x1c, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x60, 0x3c, + 0xc0, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x00, 0x00, 0x00, + 0xe0, 0x00, 0x80, 0x07, 0x80, 0x07, 0x80, 0x07, 0x80, 0x07, 0x80, 0x07, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x0e, 0x00, 0x0e, 0x00, 0x07, 0x00, 0x0e, 0x00, 0x0e, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xe0, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xc0, 0x01, 0x00, 0x00, 0xc0, 0x01, 0xc0, 0x0f, 0xc0, 0x0f, 0xc0, 0x0f, + 0xc0, 0x0f, 0xc0, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x07, 0x80, 0x03, 0x00, 0x07, + 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x18, 0x60, 0x60, 0x3c, 0xf8, 0x7f, + 0xe0, 0x1f, 0xc0, 0x0f, 0x60, 0x18, 0x00, 0x03, 0xf8, 0x43, 0x00, 0x00, + 0x00, 0x06, 0x00, 0x00, 0x80, 0x03, 0x00, 0x00, 0x80, 0x03, 0xe0, 0x1c, + 0xe0, 0x1c, 0xe0, 0x1c, 0xe0, 0x1c, 0xe0, 0x1c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x03, + 0x80, 0x01, 0x00, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x38, 0x60, + 0x00, 0x00, 0xf8, 0x7f, 0xf0, 0x3f, 0xe0, 0x1f, 0x70, 0x18, 0x00, 0x03, + 0xf8, 0x67, 0x00, 0x00, 0x80, 0x1f, 0xc0, 0x7f, 0x00, 0x03, 0x00, 0x00, + 0x00, 0x03, 0x60, 0x18, 0x60, 0x18, 0x60, 0x18, 0x60, 0x18, 0x60, 0x18, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x03, 0x78, 0x60, 0x00, 0x00, 0xf8, 0x7f, 0x38, 0x70, 0x60, 0x18, + 0x70, 0x0c, 0x00, 0x00, 0x60, 0x7e, 0x00, 0x03, 0x80, 0x39, 0xc0, 0x7f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x78, 0x60, 0x00, 0x00, 0xf8, 0x7f, + 0x18, 0x60, 0x30, 0x18, 0x60, 0x06, 0x00, 0x00, 0x60, 0x7e, 0x00, 0x03, + 0xc0, 0x30, 0xc0, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x0f, 0xc0, 0x0f, + 0xc0, 0x03, 0xc0, 0x0f, 0x18, 0x60, 0xc0, 0x0f, 0x00, 0x03, 0xf8, 0x60, + 0xd8, 0x0f, 0xf8, 0x7f, 0xcc, 0xc7, 0x30, 0x18, 0x30, 0x07, 0x00, 0x03, + 0x30, 0x33, 0xe0, 0x0f, 0xc0, 0x00, 0xc0, 0x60, 0xc0, 0x0f, 0x00, 0x00, + 0xc0, 0x0f, 0xc0, 0x0f, 0xc0, 0x0f, 0xc0, 0x03, 0xc0, 0x0f, 0x18, 0x60, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xf8, 0x1f, 0xf8, 0x1f, 0xc0, 0x03, 0xf8, 0x1f, 0x18, 0x60, 0xf8, 0x1f, + 0x00, 0x00, 0xec, 0x30, 0xfc, 0x1f, 0xfc, 0x3f, 0xe6, 0xc7, 0x30, 0x0c, + 0xb0, 0x03, 0x80, 0x01, 0x30, 0x33, 0xf0, 0x1f, 0x60, 0x00, 0x60, 0x30, + 0xf8, 0x1f, 0x00, 0x00, 0xf8, 0x1f, 0xf8, 0x1f, 0xf8, 0x1f, 0xe0, 0x01, + 0xf8, 0x1f, 0x0c, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x00, 0x03, 0x18, 0x18, + 0x0c, 0x30, 0x18, 0x18, 0x00, 0x00, 0xcc, 0x30, 0x1c, 0x18, 0xfc, 0x3f, + 0x66, 0xc6, 0xf0, 0x0f, 0xb0, 0x01, 0xc0, 0x00, 0x30, 0x33, 0x98, 0x19, + 0x60, 0x00, 0x60, 0x30, 0x18, 0x18, 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x80, 0x01, 0x18, 0x18, 0x0c, 0x30, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x0c, 0x30, + 0x80, 0x01, 0x0c, 0x30, 0x0c, 0x30, 0x0c, 0x00, 0xf8, 0x1f, 0xcc, 0x31, + 0x0c, 0x30, 0xfc, 0x3f, 0x66, 0x66, 0xe0, 0x07, 0xc0, 0x00, 0xf0, 0x00, + 0x00, 0x00, 0x8c, 0x01, 0xf8, 0x07, 0x60, 0x30, 0x00, 0x30, 0x00, 0x00, + 0x0c, 0x30, 0x00, 0x30, 0x0c, 0x30, 0x80, 0x01, 0x0c, 0x30, 0x0c, 0x30, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xf0, 0x37, 0x0c, 0x30, 0x80, 0x01, 0x0c, 0x30, 0x0c, 0x30, 0x0c, 0x00, + 0xf8, 0x1f, 0x8c, 0x33, 0x0c, 0x30, 0xfc, 0x3f, 0xe6, 0x63, 0x00, 0x00, + 0xc0, 0x1e, 0x38, 0x00, 0x00, 0x00, 0x8c, 0x01, 0xf8, 0x07, 0x60, 0x30, + 0xf0, 0x37, 0x00, 0x00, 0x0c, 0x30, 0xf0, 0x37, 0x0c, 0x30, 0x80, 0x01, + 0x0c, 0x30, 0x0c, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xf8, 0x3f, 0xfc, 0x3f, 0x80, 0x01, 0x0c, 0x30, + 0x0c, 0x30, 0x0c, 0x00, 0x00, 0x00, 0x0c, 0x33, 0x0c, 0x30, 0xfc, 0x3f, + 0xf3, 0x63, 0x00, 0x00, 0x60, 0x3f, 0x1c, 0x00, 0x00, 0x00, 0x8c, 0x01, + 0x60, 0x00, 0x60, 0x30, 0xf8, 0x3f, 0x00, 0x00, 0xfc, 0x3f, 0xf8, 0x3f, + 0xfc, 0x3f, 0x80, 0x01, 0x0c, 0x30, 0x0c, 0x30, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x38, 0xfe, 0x3f, + 0x80, 0x01, 0x06, 0x30, 0x06, 0x30, 0x0c, 0x00, 0x00, 0x00, 0x0c, 0x37, + 0x0c, 0x30, 0xfc, 0x3f, 0x33, 0x63, 0x00, 0x00, 0x70, 0x33, 0x0c, 0x00, + 0x00, 0x00, 0x86, 0x01, 0x60, 0x00, 0x7c, 0x3e, 0x18, 0x38, 0x00, 0x00, + 0xfc, 0x3f, 0x18, 0x38, 0xfc, 0x3f, 0x80, 0x01, 0x0c, 0x30, 0x0c, 0x30, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x06, 0x18, 0x06, 0x00, 0xc0, 0x00, 0x06, 0x18, 0x06, 0x18, 0x06, 0x00, + 0xc0, 0x00, 0x06, 0x1f, 0x06, 0x18, 0xfe, 0x1f, 0x33, 0x33, 0x00, 0x00, + 0x38, 0x18, 0x06, 0x18, 0x00, 0x00, 0xc6, 0x00, 0x3c, 0x00, 0x7e, 0x3f, + 0x06, 0x18, 0x00, 0x00, 0x06, 0x00, 0x06, 0x18, 0x06, 0x00, 0xc0, 0x00, + 0x06, 0x18, 0x06, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x06, 0x18, 0x06, 0x00, 0xc0, 0x00, 0x06, 0x18, + 0x06, 0x18, 0x06, 0x00, 0xc0, 0x00, 0x06, 0x1e, 0x06, 0x18, 0xfe, 0x1f, + 0x06, 0x18, 0x00, 0x00, 0x18, 0x0e, 0x06, 0x18, 0x00, 0x00, 0xc6, 0x00, + 0x3e, 0x18, 0x3e, 0x1f, 0x06, 0x18, 0x00, 0x00, 0x06, 0x00, 0x06, 0x18, + 0x06, 0x00, 0xc0, 0x00, 0x06, 0x18, 0x06, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x1c, 0x0c, 0x0c, + 0xc0, 0x00, 0x0c, 0x0c, 0x0c, 0x1c, 0x0c, 0x0c, 0xc0, 0x00, 0x06, 0x1e, + 0x06, 0x18, 0xfe, 0x1f, 0x0e, 0x1c, 0x00, 0x00, 0x0c, 0x03, 0x0c, 0x0c, + 0x00, 0x00, 0xcc, 0x0c, 0x76, 0x1c, 0x3e, 0x1f, 0x0c, 0x1c, 0x00, 0x00, + 0x0c, 0x0c, 0x0c, 0x1c, 0x0c, 0x0c, 0xc0, 0x00, 0x0c, 0x0c, 0x0c, 0x1c, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xfc, 0x1f, 0xfc, 0x0f, 0xf0, 0x03, 0xfc, 0x0f, 0xfc, 0x1f, 0xfc, 0x0f, + 0x00, 0x00, 0x06, 0x1c, 0x06, 0x18, 0xfe, 0x1f, 0xfc, 0x0f, 0x00, 0x00, + 0x86, 0x1f, 0xfc, 0x0f, 0x00, 0x00, 0xfc, 0x0f, 0xfe, 0x0f, 0x1c, 0x0e, + 0xfc, 0x1f, 0x00, 0x00, 0xfc, 0x0f, 0xfc, 0x1f, 0xfc, 0x0f, 0xf0, 0x03, + 0xfc, 0x0f, 0xfc, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xf0, 0x1b, 0xf0, 0x03, 0xf0, 0x03, 0xf0, 0x03, + 0xf0, 0x1b, 0xf0, 0x03, 0x00, 0x00, 0x06, 0x18, 0x06, 0x18, 0xfe, 0x1f, + 0xf8, 0x07, 0x00, 0x00, 0x86, 0x1f, 0xf0, 0x03, 0x00, 0x00, 0xf0, 0x03, + 0x9c, 0x07, 0x00, 0x00, 0xf0, 0x1b, 0x00, 0x00, 0xf0, 0x03, 0xf0, 0x1b, + 0xf0, 0x03, 0xf0, 0x03, 0xf0, 0x03, 0xf0, 0x1b, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x03, 0xc0, 0x0c, 0x00, 0x00, 0xc0, 0x0f, 0xe0, 0x30, + 0x80, 0x03, 0x00, 0x03, 0x00, 0x06, 0x80, 0x01, 0x00, 0x07, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0xc0, 0x0f, 0x00, 0x03, + 0xe0, 0x0f, 0xc0, 0x0f, 0x00, 0x1f, 0xf8, 0x7f, 0xc0, 0x0f, 0xf8, 0x7f, + 0xc0, 0x0f, 0xc0, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc0, 0x0f, 0x00, 0x00, 0x00, 0x03, 0xc0, 0x0c, 0xc0, 0x30, + 0xf0, 0x3f, 0xf0, 0x31, 0xc0, 0x07, 0x00, 0x03, 0x00, 0x07, 0x80, 0x03, + 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, + 0xf0, 0x3f, 0x00, 0x03, 0xf0, 0x3f, 0xf0, 0x3f, 0x80, 0x1f, 0xf8, 0x7f, + 0xf0, 0x3f, 0xf8, 0x7f, 0xf0, 0x3f, 0xf0, 0x3f, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x3f, 0x00, 0x00, 0x00, 0x03, + 0xc0, 0x0c, 0xc0, 0x30, 0x30, 0x73, 0x98, 0x19, 0x30, 0x06, 0x00, 0x03, + 0x00, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x18, 0x30, 0x70, 0xc0, 0x03, 0x30, 0x30, 0x38, 0x30, + 0x80, 0x19, 0x18, 0x00, 0x30, 0x30, 0x18, 0x60, 0x30, 0x30, 0x30, 0x30, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x18, 0x00, 0x30, 0x30, + 0x00, 0x00, 0x00, 0x03, 0x60, 0x06, 0xc0, 0x30, 0x18, 0x63, 0x98, 0x1d, + 0x30, 0x06, 0x80, 0x01, 0x80, 0x01, 0x00, 0x06, 0x00, 0x00, 0x00, 0x03, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x18, 0x70, 0xc0, 0x03, + 0x18, 0x60, 0x18, 0x60, 0xc0, 0x19, 0x18, 0x00, 0x18, 0x60, 0x18, 0x70, + 0x18, 0x60, 0x18, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, + 0x78, 0x00, 0x18, 0x60, 0x00, 0x00, 0x00, 0x03, 0x60, 0x06, 0xf8, 0x7f, + 0x0c, 0x03, 0xf8, 0x0c, 0x18, 0x06, 0x80, 0x01, 0xc0, 0x00, 0x00, 0x06, + 0xc0, 0x0f, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, + 0x18, 0x78, 0x00, 0x03, 0x18, 0x60, 0x18, 0x60, 0xc0, 0x18, 0x18, 0x00, + 0x18, 0x00, 0x00, 0x38, 0x0c, 0x60, 0x18, 0x60, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x1e, 0x00, 0x00, 0xe0, 0x01, 0x18, 0x60, 0x00, 0x00, 0x80, 0x01, + 0x00, 0x00, 0xfc, 0x3f, 0x8c, 0x01, 0x70, 0x06, 0x98, 0x03, 0x00, 0x00, + 0xc0, 0x00, 0x00, 0x06, 0xf8, 0x1f, 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x06, 0x0c, 0x7e, 0x80, 0x01, 0x00, 0x30, 0x00, 0x30, + 0x70, 0x0c, 0xec, 0x07, 0x0c, 0x00, 0x00, 0x0c, 0x0c, 0x30, 0x0c, 0x60, + 0x00, 0x00, 0x00, 0x00, 0xc0, 0x03, 0xf8, 0x1f, 0xc0, 0x03, 0x00, 0x30, + 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x30, 0x0c, 0x98, 0x01, 0x00, 0x07, + 0xf8, 0x01, 0x00, 0x00, 0x60, 0x00, 0x00, 0x06, 0x18, 0x18, 0x80, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x0c, 0x37, 0x80, 0x01, + 0x00, 0x18, 0x00, 0x18, 0x30, 0x0c, 0xfc, 0x1f, 0xec, 0x07, 0x00, 0x06, + 0x1c, 0x38, 0x0c, 0x70, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x00, 0xf8, 0x1f, + 0x00, 0x0f, 0x00, 0x38, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x30, 0x0c, + 0xf8, 0x07, 0x80, 0x03, 0xf0, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x06, + 0x00, 0x30, 0xf8, 0x1f, 0x00, 0x00, 0xf8, 0x1f, 0x00, 0x00, 0x80, 0x03, + 0x8c, 0x33, 0x80, 0x01, 0x00, 0x1e, 0x80, 0x1f, 0x38, 0x0c, 0x3c, 0x18, + 0xfc, 0x1f, 0x00, 0x06, 0xf8, 0x1f, 0x18, 0x38, 0x80, 0x01, 0x80, 0x01, + 0x3c, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x1c, 0x00, 0x00, 0x80, 0x01, + 0x00, 0x00, 0x30, 0x0c, 0xe0, 0x1f, 0xc0, 0x01, 0xf8, 0x00, 0x00, 0x00, + 0x60, 0x00, 0x00, 0x06, 0xf0, 0x37, 0xf8, 0x1f, 0x00, 0x00, 0xf8, 0x1f, + 0x00, 0x00, 0xc0, 0x01, 0xcc, 0x31, 0x80, 0x01, 0xc0, 0x0f, 0x80, 0x1f, + 0x18, 0x0c, 0x00, 0x30, 0x1c, 0x18, 0x00, 0x03, 0xf8, 0x1f, 0xf8, 0x3f, + 0x80, 0x01, 0x80, 0x01, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x0f, + 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0x30, 0x0c, 0x80, 0x19, 0xe0, 0x00, + 0xdc, 0x31, 0x00, 0x00, 0x60, 0x00, 0x00, 0x06, 0xf8, 0x3f, 0x80, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x00, 0xec, 0x30, 0x80, 0x01, + 0xf0, 0x03, 0x00, 0x18, 0x1c, 0x0c, 0x00, 0x30, 0x0e, 0x30, 0x00, 0x03, + 0x1c, 0x38, 0xe0, 0x37, 0x80, 0x01, 0x80, 0x01, 0xf0, 0x00, 0x00, 0x00, + 0x00, 0x0f, 0x00, 0x03, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0xfc, 0x3f, + 0x80, 0x31, 0x60, 0x0e, 0x8c, 0x3b, 0x00, 0x00, 0x60, 0x00, 0x00, 0x03, + 0x18, 0x38, 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, + 0x7e, 0x30, 0x80, 0x01, 0x38, 0x00, 0x00, 0x30, 0xfc, 0x3f, 0x00, 0x30, + 0x06, 0x30, 0x80, 0x01, 0x0c, 0x30, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, + 0xc0, 0x03, 0xfc, 0x0f, 0xc0, 0x03, 0x80, 0x01, 0x00, 0x00, 0xc0, 0x00, + 0x00, 0x00, 0xfe, 0x1f, 0xc0, 0x30, 0x30, 0x1f, 0x86, 0x0f, 0x00, 0x00, + 0x60, 0x00, 0x00, 0x03, 0x06, 0x18, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x70, 0x00, 0x1e, 0x18, 0xc0, 0x00, 0x0c, 0x00, 0x06, 0x30, + 0xfe, 0x1f, 0x06, 0x18, 0x06, 0x18, 0xc0, 0x00, 0x06, 0x30, 0x00, 0x18, + 0x00, 0x00, 0x00, 0x00, 0x80, 0x07, 0xfc, 0x0f, 0x78, 0x00, 0xc0, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x03, 0xc6, 0x18, 0xb8, 0x19, + 0x06, 0x07, 0x00, 0x00, 0x60, 0x00, 0x80, 0x01, 0x06, 0x18, 0xc0, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x00, 0x0e, 0x18, 0xc0, 0x00, + 0x06, 0x00, 0x06, 0x18, 0x00, 0x06, 0x06, 0x18, 0x06, 0x18, 0xc0, 0x00, + 0x06, 0x18, 0x06, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, + 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x03, + 0xce, 0x0c, 0x98, 0x19, 0x8c, 0x0f, 0x00, 0x00, 0xc0, 0x00, 0xc0, 0x00, + 0x0c, 0x1c, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x18, 0x00, + 0x0e, 0x0c, 0xc0, 0x00, 0x06, 0x00, 0x0c, 0x0c, 0x00, 0x06, 0x0c, 0x0c, + 0x0c, 0x0c, 0xc0, 0x00, 0x0c, 0x0c, 0x0c, 0x0c, 0xc0, 0x00, 0xc0, 0x00, + 0x00, 0x18, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, + 0x00, 0x00, 0x0c, 0x03, 0xfc, 0x0f, 0x8c, 0x0f, 0xfc, 0x1d, 0x00, 0x00, + 0xc0, 0x01, 0xe0, 0x00, 0xfc, 0x1f, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, + 0xc0, 0x00, 0x0c, 0x00, 0xfc, 0x0f, 0xf0, 0x03, 0xfe, 0x1f, 0xfc, 0x0f, + 0x80, 0x1f, 0xfc, 0x0f, 0xfc, 0x0f, 0xc0, 0x00, 0xfc, 0x0f, 0xfc, 0x0f, + 0xc0, 0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x00, + 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x03, 0x0c, 0x07, + 0xf0, 0x18, 0x00, 0x00, 0x80, 0x01, 0x60, 0x00, 0xf0, 0x1b, 0x00, 0x00, + 0xc0, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x0c, 0x00, 0xf0, 0x03, 0xf0, 0x03, + 0xfe, 0x1f, 0xf0, 0x03, 0x80, 0x1f, 0xf0, 0x03, 0xf0, 0x03, 0xc0, 0x00, + 0xf0, 0x03, 0xf0, 0x03, 0xc0, 0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x18, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, + 0x00, 0x0e, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x0e, 0x00, 0x00, 0x00, 0x07, 0x00, 0x0e, 0xc0, 0x0f, 0xc0, 0x0f, + 0xf8, 0x07, 0xc0, 0x0f, 0xf8, 0x07, 0xf8, 0x7f, 0xf8, 0x7f, 0xc0, 0x0f, + 0x18, 0x60, 0xc0, 0x0f, 0x00, 0x60, 0x18, 0x60, 0x18, 0x00, 0x18, 0x60, + 0x18, 0x60, 0xc0, 0x0f, 0xf8, 0x0f, 0xc0, 0x0f, 0xf8, 0x0f, 0xc0, 0x0f, + 0xf8, 0x7f, 0x18, 0x60, 0x0c, 0x60, 0x0c, 0x60, 0x18, 0x60, 0x18, 0x60, + 0xf8, 0x7f, 0xc0, 0x0f, 0x00, 0x07, 0xc0, 0x0f, 0x80, 0x03, 0x00, 0x07, + 0xf0, 0x3f, 0xf0, 0x3f, 0xf8, 0x1f, 0xf0, 0x3f, 0xf8, 0x1f, 0xf8, 0x7f, + 0xf8, 0x7f, 0xf0, 0x3f, 0x18, 0x60, 0xc0, 0x0f, 0x00, 0x60, 0x18, 0x70, + 0x18, 0x00, 0x38, 0x70, 0x38, 0x60, 0xf0, 0x3f, 0xf8, 0x3f, 0xf0, 0x3f, + 0xf8, 0x3f, 0xf0, 0x3f, 0xf8, 0x7f, 0x18, 0x60, 0x0c, 0x60, 0x0c, 0x60, + 0x18, 0x60, 0x18, 0x60, 0xf8, 0x7f, 0xc0, 0x0f, 0x00, 0x03, 0xc0, 0x0f, + 0x80, 0x01, 0x00, 0x03, 0x30, 0x30, 0x30, 0x30, 0x18, 0x18, 0x30, 0x30, + 0x18, 0x18, 0x18, 0x00, 0x18, 0x00, 0x30, 0x30, 0x18, 0x60, 0x00, 0x03, + 0x00, 0x60, 0x18, 0x38, 0x18, 0x00, 0x38, 0x70, 0x78, 0x60, 0x30, 0x30, + 0x18, 0x30, 0x30, 0x30, 0x18, 0x30, 0x30, 0x70, 0x00, 0x03, 0x18, 0x60, + 0x0c, 0x60, 0x0c, 0x60, 0x38, 0x70, 0x38, 0x70, 0x00, 0x60, 0xc0, 0x00, + 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x18, 0x60, 0x18, 0x60, + 0x18, 0x30, 0x18, 0x60, 0x18, 0x30, 0x18, 0x00, 0x18, 0x00, 0x18, 0x60, + 0x18, 0x60, 0x00, 0x03, 0x00, 0x60, 0x18, 0x1c, 0x18, 0x00, 0x78, 0x78, + 0x78, 0x60, 0x18, 0x60, 0x18, 0x60, 0x18, 0x60, 0x18, 0x60, 0x18, 0x60, + 0x00, 0x03, 0x18, 0x60, 0x0c, 0x60, 0x0c, 0x60, 0x30, 0x38, 0x30, 0x38, + 0x00, 0x70, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x60, 0x18, 0x60, 0x18, 0x30, 0x18, 0x60, 0x18, 0x30, 0x18, 0x00, + 0x18, 0x00, 0x18, 0x60, 0x18, 0x60, 0x00, 0x03, 0x00, 0x60, 0x18, 0x0e, + 0x18, 0x00, 0x78, 0x7c, 0xf8, 0x60, 0x18, 0x60, 0x18, 0x60, 0x18, 0x60, + 0x18, 0x60, 0x0c, 0x00, 0x00, 0x03, 0x18, 0x60, 0x0c, 0x60, 0x0c, 0x60, + 0x60, 0x18, 0x30, 0x1c, 0x00, 0x38, 0xc0, 0x00, 0xc0, 0x0f, 0x00, 0x0c, + 0xc0, 0x03, 0xc0, 0x0f, 0x0c, 0x3f, 0x0c, 0x30, 0x0c, 0x18, 0x0c, 0x00, + 0x0c, 0x30, 0x0c, 0x00, 0x0c, 0x00, 0x0c, 0x00, 0x0c, 0x30, 0x80, 0x01, + 0x00, 0x30, 0x8c, 0x03, 0x0c, 0x00, 0x6c, 0x3e, 0xec, 0x30, 0x0c, 0x60, + 0x0c, 0x30, 0x0c, 0x30, 0x0c, 0x30, 0x0c, 0x00, 0x80, 0x01, 0x0c, 0x30, + 0x18, 0x30, 0x0c, 0x30, 0x60, 0x0e, 0x60, 0x0c, 0x00, 0x1c, 0x60, 0x00, + 0xf8, 0x1f, 0x00, 0x06, 0xc0, 0x03, 0xf8, 0x1f, 0x8c, 0x3f, 0x0c, 0x30, + 0x0c, 0x0c, 0x0c, 0x00, 0x0c, 0x30, 0x0c, 0x00, 0x0c, 0x00, 0x0c, 0x00, + 0x0c, 0x30, 0x80, 0x01, 0x00, 0x30, 0xcc, 0x01, 0x0c, 0x00, 0x6c, 0x36, + 0xcc, 0x30, 0x0c, 0x30, 0x0c, 0x30, 0x0c, 0x30, 0x0c, 0x30, 0x18, 0x00, + 0x80, 0x01, 0x0c, 0x30, 0x18, 0x18, 0x8c, 0x31, 0xc0, 0x07, 0x60, 0x06, + 0x00, 0x07, 0x60, 0x00, 0x18, 0x18, 0x00, 0x06, 0x00, 0x03, 0x18, 0x18, + 0xcc, 0x31, 0xfc, 0x3f, 0xfc, 0x0f, 0x0c, 0x00, 0x0c, 0x30, 0xfc, 0x07, + 0xfc, 0x07, 0x0c, 0x3e, 0xfc, 0x3f, 0x80, 0x01, 0x00, 0x30, 0xfc, 0x00, + 0x0c, 0x00, 0xcc, 0x33, 0xcc, 0x31, 0x0c, 0x30, 0x0c, 0x18, 0x0c, 0x30, + 0x0c, 0x18, 0xf8, 0x07, 0x80, 0x01, 0x0c, 0x30, 0x18, 0x18, 0x8c, 0x31, + 0xc0, 0x03, 0xe0, 0x07, 0x80, 0x03, 0x60, 0x00, 0x0c, 0x30, 0x00, 0x06, + 0x80, 0x01, 0x0c, 0x30, 0xcc, 0x31, 0xfc, 0x3f, 0xfc, 0x1f, 0x0c, 0x00, + 0x0c, 0x30, 0xfc, 0x07, 0xfc, 0x07, 0x0c, 0x3e, 0xfc, 0x3f, 0x80, 0x01, + 0x00, 0x30, 0xfc, 0x00, 0x0c, 0x00, 0xcc, 0x33, 0x8c, 0x33, 0x0c, 0x30, + 0xfc, 0x1f, 0x0c, 0x30, 0xfc, 0x1f, 0xe0, 0x1f, 0x80, 0x01, 0x0c, 0x30, + 0x30, 0x0c, 0xcc, 0x33, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x01, 0x60, 0x00, + 0x0c, 0x30, 0x00, 0x06, 0x80, 0x01, 0x0c, 0x30, 0xc6, 0x31, 0x0c, 0x30, + 0x0c, 0x18, 0x0c, 0x00, 0x0c, 0x18, 0x0c, 0x00, 0x0c, 0x00, 0x06, 0x30, + 0x0c, 0x30, 0x80, 0x01, 0x00, 0x30, 0xcc, 0x01, 0x0c, 0x00, 0x8c, 0x31, + 0x0c, 0x33, 0x0c, 0x30, 0xfc, 0x07, 0x0c, 0x30, 0xfc, 0x07, 0x00, 0x18, + 0x80, 0x01, 0x0c, 0x30, 0x30, 0x0c, 0xec, 0x1b, 0xe0, 0x03, 0xc0, 0x01, + 0xe0, 0x00, 0x60, 0x00, 0xfc, 0x3f, 0x00, 0x06, 0x80, 0x01, 0x0c, 0x30, + 0x86, 0x1f, 0x0c, 0x30, 0x0c, 0x30, 0x06, 0x00, 0x0c, 0x18, 0x0c, 0x00, + 0x0c, 0x00, 0x06, 0x30, 0x0c, 0x30, 0x80, 0x01, 0x00, 0x30, 0x8c, 0x03, + 0x0c, 0x00, 0x8c, 0x31, 0x0c, 0x37, 0x06, 0x30, 0x0c, 0x00, 0x0c, 0x30, + 0x0c, 0x0c, 0x00, 0x30, 0x80, 0x01, 0x06, 0x30, 0x30, 0x06, 0x7c, 0x1e, + 0x70, 0x06, 0xc0, 0x01, 0x38, 0x00, 0x60, 0x00, 0xfe, 0x3f, 0x00, 0x06, + 0x80, 0x01, 0x06, 0x30, 0x86, 0x0f, 0x06, 0x18, 0x06, 0x30, 0x06, 0x18, + 0x06, 0x18, 0x06, 0x00, 0x06, 0x00, 0x06, 0x18, 0x06, 0x18, 0xc0, 0x00, + 0x06, 0x18, 0x86, 0x03, 0x06, 0x00, 0x06, 0x18, 0x06, 0x1f, 0x06, 0x18, + 0x06, 0x00, 0x06, 0x1b, 0x06, 0x06, 0x00, 0x30, 0xc0, 0x00, 0x06, 0x18, + 0x30, 0x03, 0x3c, 0x0e, 0x18, 0x06, 0xc0, 0x00, 0x1c, 0x00, 0x30, 0x00, + 0x06, 0x00, 0x00, 0x03, 0xc0, 0x00, 0x06, 0x18, 0x06, 0x00, 0x06, 0x18, + 0x06, 0x18, 0x06, 0x18, 0x06, 0x0c, 0x06, 0x00, 0x06, 0x00, 0x06, 0x18, + 0x06, 0x18, 0xc0, 0x00, 0x06, 0x18, 0x06, 0x07, 0x06, 0x00, 0x06, 0x18, + 0x06, 0x1e, 0x06, 0x18, 0x06, 0x00, 0x06, 0x1f, 0x06, 0x0c, 0x06, 0x18, + 0xc0, 0x00, 0x06, 0x18, 0xe0, 0x01, 0x1c, 0x0e, 0x1c, 0x0c, 0xc0, 0x00, + 0x0e, 0x00, 0x30, 0x00, 0x06, 0x00, 0x00, 0x03, 0xc0, 0x00, 0x06, 0x18, + 0x0c, 0x00, 0x06, 0x18, 0x06, 0x0c, 0x0c, 0x0c, 0x06, 0x06, 0x06, 0x00, + 0x06, 0x00, 0x0c, 0x0c, 0x06, 0x18, 0xc0, 0x00, 0x0c, 0x0c, 0x06, 0x0e, + 0x06, 0x00, 0x06, 0x18, 0x06, 0x1e, 0x0c, 0x0c, 0x06, 0x00, 0x0c, 0x0e, + 0x06, 0x0c, 0x0e, 0x0c, 0xc0, 0x00, 0x0c, 0x0c, 0xe0, 0x01, 0x1c, 0x0e, + 0x0e, 0x1c, 0xc0, 0x00, 0x06, 0x00, 0x30, 0x00, 0x0c, 0x0c, 0x00, 0x03, + 0xc0, 0x00, 0x0c, 0x0c, 0xfc, 0x07, 0x06, 0x18, 0xfe, 0x0f, 0xfc, 0x0f, + 0xfe, 0x07, 0xfe, 0x1f, 0x06, 0x00, 0xfc, 0x0f, 0x06, 0x18, 0xf0, 0x03, + 0xfc, 0x0f, 0x06, 0x1c, 0xfe, 0x1f, 0x06, 0x18, 0x06, 0x1c, 0xfc, 0x0f, + 0x06, 0x00, 0xfc, 0x1f, 0x06, 0x18, 0xfc, 0x0f, 0xc0, 0x00, 0xfc, 0x0f, + 0xc0, 0x00, 0x0c, 0x0c, 0x06, 0x18, 0xc0, 0x00, 0xfe, 0x1f, 0xf0, 0x03, + 0xfc, 0x0f, 0xf0, 0x03, 0xf0, 0x03, 0xfc, 0x0f, 0xf0, 0x07, 0x06, 0x18, + 0xfe, 0x03, 0xf0, 0x03, 0xfe, 0x01, 0xfe, 0x1f, 0x06, 0x00, 0xf0, 0x03, + 0x06, 0x18, 0xf0, 0x03, 0xf0, 0x03, 0x06, 0x18, 0xfe, 0x1f, 0x06, 0x18, + 0x06, 0x18, 0xf0, 0x03, 0x06, 0x00, 0xf0, 0x1b, 0x06, 0x18, 0xf0, 0x03, + 0xc0, 0x00, 0xf0, 0x03, 0xc0, 0x00, 0x0c, 0x0c, 0x06, 0x18, 0xc0, 0x00, + 0xfe, 0x1f, 0xf0, 0x03, 0xf0, 0x03, 0xf0, 0x03, 0xf0, 0x03, 0xf0, 0x03, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x63, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xe0, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x3c, 0xc0, 0x63, 0x00, 0x00, + 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xe0, 0x7f, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, + 0x00, 0x60, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x18, 0x00, 0x00, 0x03, + 0x00, 0x03, 0x18, 0x00, 0xe0, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x60, 0x60, 0x3c, 0xf8, 0x7f, 0x00, 0x03, 0x00, 0x00, + 0x18, 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x00, + 0x18, 0x00, 0x00, 0x03, 0x00, 0x03, 0x18, 0x00, 0xe0, 0x03, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x60, 0x00, 0x00, 0xf8, 0x7f, + 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, + 0x00, 0x03, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, + 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x78, 0x60, + 0x00, 0x00, 0xf8, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, + 0x00, 0x60, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x03, 0x78, 0x60, 0x00, 0x00, 0xf8, 0x7f, 0x18, 0x60, 0xc0, 0x0f, + 0xd8, 0x0f, 0xc0, 0x0f, 0xc0, 0x6f, 0xc0, 0x0f, 0x00, 0x03, 0xc0, 0x6f, + 0xd8, 0x0f, 0xc0, 0x03, 0xe0, 0x03, 0x18, 0x60, 0x00, 0x03, 0xd8, 0x0e, + 0xd8, 0x0f, 0xc0, 0x0f, 0xd8, 0x0f, 0xc0, 0x6f, 0xd8, 0x0f, 0xe0, 0x3f, + 0xf0, 0x0f, 0x18, 0x60, 0x0c, 0x60, 0x18, 0x60, 0x18, 0x30, 0x18, 0x60, + 0xf8, 0x7f, 0xc0, 0x0f, 0x00, 0x03, 0xf8, 0x60, 0xd8, 0x0f, 0xf8, 0x7f, + 0x18, 0x60, 0xf8, 0x1f, 0xfc, 0x1f, 0xf8, 0x1f, 0xf8, 0x3f, 0xf8, 0x1f, + 0xf0, 0x3f, 0xf8, 0x3f, 0xfc, 0x1f, 0xc0, 0x03, 0xe0, 0x03, 0x0c, 0x38, + 0x80, 0x01, 0xfc, 0x1f, 0xfc, 0x1f, 0xf8, 0x1f, 0xfc, 0x1f, 0xf8, 0x7f, + 0xfc, 0x1f, 0xf8, 0x3f, 0xf8, 0x07, 0x18, 0x60, 0x0c, 0x30, 0x18, 0x60, + 0x1c, 0x38, 0x18, 0x60, 0xfc, 0x3f, 0xf8, 0x1f, 0x00, 0x00, 0xec, 0x30, + 0xfc, 0x1f, 0xfc, 0x3f, 0x0c, 0x30, 0x18, 0x18, 0x1c, 0x18, 0x18, 0x18, + 0x18, 0x38, 0x18, 0x18, 0xf0, 0x3f, 0x18, 0x38, 0x1c, 0x18, 0x00, 0x03, + 0x00, 0x03, 0x0c, 0x1c, 0x80, 0x01, 0xdc, 0x18, 0x1c, 0x18, 0x18, 0x18, + 0x1c, 0x18, 0x18, 0x38, 0x1c, 0x18, 0x1c, 0x30, 0x60, 0x00, 0x0c, 0x30, + 0x18, 0x18, 0x8c, 0x31, 0x38, 0x1c, 0x0c, 0x30, 0x00, 0x1c, 0x18, 0x18, + 0x00, 0x00, 0xcc, 0x30, 0x1c, 0x18, 0xfc, 0x3f, 0x0c, 0x30, 0x00, 0x30, + 0x0c, 0x30, 0x0c, 0x00, 0x0c, 0x30, 0x0c, 0x30, 0x80, 0x01, 0x0c, 0x30, + 0x0c, 0x30, 0x80, 0x01, 0x00, 0x03, 0x0c, 0x0e, 0x80, 0x01, 0x8c, 0x31, + 0x0c, 0x30, 0x0c, 0x30, 0x0c, 0x30, 0x0c, 0x30, 0x0c, 0x30, 0x0c, 0x00, + 0x60, 0x00, 0x0c, 0x30, 0x18, 0x18, 0x8c, 0x31, 0x70, 0x0e, 0x0c, 0x30, + 0x00, 0x0e, 0x0c, 0x00, 0xf8, 0x1f, 0xcc, 0x31, 0x0c, 0x30, 0xfc, 0x3f, + 0x0c, 0x30, 0xf0, 0x37, 0x0c, 0x30, 0x0c, 0x00, 0x0c, 0x30, 0x0c, 0x30, + 0x80, 0x01, 0x0c, 0x30, 0x0c, 0x30, 0x80, 0x01, 0x00, 0x03, 0x0c, 0x07, + 0x80, 0x01, 0x8c, 0x31, 0x0c, 0x30, 0x0c, 0x30, 0x0c, 0x30, 0x0c, 0x30, + 0x0c, 0x30, 0x1c, 0x00, 0x60, 0x00, 0x0c, 0x30, 0x18, 0x0c, 0xcc, 0x33, + 0xe0, 0x07, 0x0c, 0x30, 0x00, 0x07, 0x0c, 0x00, 0xf8, 0x1f, 0x8c, 0x33, + 0x0c, 0x30, 0xfc, 0x3f, 0x0c, 0x30, 0xf8, 0x3f, 0x0c, 0x30, 0x0c, 0x00, + 0x0c, 0x30, 0xfc, 0x3f, 0x80, 0x01, 0x0c, 0x30, 0x0c, 0x30, 0x80, 0x01, + 0x80, 0x01, 0xfc, 0x03, 0x80, 0x01, 0x8c, 0x31, 0x0c, 0x30, 0x0c, 0x30, + 0x0c, 0x30, 0x0c, 0x30, 0x0c, 0x00, 0xf8, 0x0f, 0x60, 0x00, 0x0c, 0x30, + 0x30, 0x0c, 0xcc, 0x33, 0xc0, 0x03, 0x0c, 0x30, 0x80, 0x03, 0x0c, 0x00, + 0x00, 0x00, 0x0c, 0x33, 0x0c, 0x30, 0xfc, 0x3f, 0x06, 0x30, 0x1c, 0x38, + 0x0c, 0x30, 0x0c, 0x00, 0x0c, 0x30, 0xfc, 0x3f, 0x80, 0x01, 0x0c, 0x30, + 0x0c, 0x30, 0x80, 0x01, 0x80, 0x01, 0xfc, 0x03, 0x80, 0x01, 0x8c, 0x31, + 0x0c, 0x30, 0x0c, 0x30, 0x0c, 0x30, 0x0c, 0x30, 0x0c, 0x00, 0xf0, 0x1f, + 0x60, 0x00, 0x0c, 0x30, 0x30, 0x06, 0x7c, 0x1e, 0xc0, 0x03, 0x0c, 0x30, + 0xc0, 0x01, 0x0c, 0x00, 0x00, 0x00, 0x0c, 0x37, 0x0c, 0x30, 0xfc, 0x3f, + 0x06, 0x18, 0x06, 0x18, 0x06, 0x18, 0x06, 0x00, 0x06, 0x18, 0x06, 0x00, + 0xc0, 0x00, 0x06, 0x18, 0x06, 0x18, 0xc0, 0x00, 0x80, 0x01, 0x86, 0x03, + 0xc0, 0x00, 0xc6, 0x18, 0x06, 0x18, 0x06, 0x18, 0x06, 0x18, 0x06, 0x18, + 0x06, 0x00, 0x00, 0x1c, 0x30, 0x00, 0x06, 0x18, 0x30, 0x03, 0x3c, 0x0e, + 0xf0, 0x03, 0x06, 0x18, 0x70, 0x00, 0x06, 0x00, 0xc0, 0x00, 0x06, 0x1f, + 0x06, 0x18, 0xfe, 0x1f, 0x06, 0x18, 0x06, 0x18, 0x06, 0x18, 0x06, 0x00, + 0x06, 0x18, 0x06, 0x00, 0xc0, 0x00, 0x06, 0x18, 0x06, 0x18, 0xc0, 0x00, + 0x80, 0x01, 0x06, 0x07, 0xc0, 0x00, 0xc6, 0x18, 0x06, 0x18, 0x06, 0x18, + 0x06, 0x18, 0x06, 0x18, 0x06, 0x00, 0x00, 0x18, 0x30, 0x00, 0x06, 0x18, + 0xe0, 0x01, 0x1c, 0x0e, 0x38, 0x07, 0x06, 0x18, 0x38, 0x00, 0x06, 0x00, + 0xc0, 0x00, 0x06, 0x1e, 0x06, 0x18, 0xfe, 0x1f, 0x0c, 0x1c, 0x0c, 0x1c, + 0x0e, 0x0c, 0x0c, 0x0c, 0x0c, 0x1c, 0x0c, 0x0c, 0xc0, 0x00, 0x0c, 0x1c, + 0x06, 0x18, 0xc0, 0x00, 0xc0, 0x00, 0x06, 0x0e, 0xc0, 0x00, 0xc6, 0x18, + 0x06, 0x18, 0x0c, 0x0c, 0x0e, 0x0c, 0x0c, 0x1c, 0x06, 0x00, 0x06, 0x1c, + 0x30, 0x00, 0x0c, 0x1c, 0xe0, 0x01, 0x1c, 0x0e, 0x1c, 0x0e, 0x0c, 0x1c, + 0x1c, 0x00, 0x0c, 0x0c, 0xc0, 0x00, 0x06, 0x1e, 0x06, 0x18, 0xfe, 0x1f, + 0xfc, 0x1f, 0xfc, 0x1f, 0xfe, 0x0f, 0xfc, 0x0f, 0xfc, 0x1f, 0xfc, 0x0f, + 0xc0, 0x00, 0xfc, 0x1f, 0x06, 0x18, 0xf0, 0x03, 0xc0, 0x00, 0x06, 0x1c, + 0xf8, 0x07, 0xc6, 0x18, 0x06, 0x18, 0xfc, 0x0f, 0xfe, 0x0f, 0xfc, 0x1f, + 0x06, 0x00, 0xfe, 0x0f, 0xe0, 0x0f, 0xfc, 0x1f, 0xc0, 0x00, 0x0c, 0x0c, + 0x0e, 0x1c, 0xfc, 0x1f, 0xfe, 0x1f, 0xfc, 0x0f, 0x00, 0x00, 0x06, 0x1c, + 0x06, 0x18, 0xfe, 0x1f, 0xf0, 0x1b, 0xf0, 0x1b, 0xf6, 0x03, 0xf0, 0x03, + 0xf0, 0x1b, 0xf0, 0x03, 0xc0, 0x00, 0xf0, 0x1b, 0x06, 0x18, 0xf0, 0x03, + 0xc0, 0x00, 0x06, 0x18, 0xf8, 0x07, 0xc6, 0x18, 0x06, 0x18, 0xf0, 0x03, + 0xf6, 0x03, 0xf0, 0x1b, 0x06, 0x00, 0xfc, 0x07, 0xe0, 0x0f, 0xf0, 0x1b, + 0xc0, 0x00, 0x0c, 0x0c, 0x06, 0x18, 0xf0, 0x1b, 0xfe, 0x1f, 0xf0, 0x03, + 0x00, 0x00, 0x06, 0x18, 0x06, 0x18, 0xfe, 0x1f, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, + 0x00, 0x00, 0x00, 0x00, 0xc3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, + 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0c, 0x0c, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x0c, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x07, 0x00, 0x00, 0x00, 0x00, + 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x07, 0x00, 0x00, 0x70, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x03, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x03, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00 }; diff --git a/src/constants.cpp b/src/constants.cpp new file mode 100644 index 00000000..1c0a4dae --- /dev/null +++ b/src/constants.cpp @@ -0,0 +1,125 @@ +#include "ccextractor.h" + +// RCWT header (11 bytes): +//byte(s) value description (All values below are hex numbers, not +// actual numbers or values +//0-2 CCCCED magic number, for Closed Caption CC Extractor Data +//3 CC Creating program. Legal values: CC = CC Extractor +//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}; + +const unsigned char BROADCAST_HEADER[]={0xff, 0xff, 0xff, 0xff}; +const unsigned char LITTLE_ENDIAN_BOM[]={0xff, 0xfe}; +const unsigned char UTF8_BOM[]={0xef, 0xbb,0xbf}; + +const unsigned char DVD_HEADER[8]={0x00,0x00,0x01,0xb2,0x43,0x43,0x01,0xf8}; +const unsigned char lc1[1]={0x8a}; +const unsigned char lc2[1]={0x8f}; +const unsigned char lc3[2]={0x16,0xfe}; +const unsigned char lc4[2]={0x1e,0xfe}; +const unsigned char lc5[1]={0xff}; +const unsigned char lc6[1]={0xfe}; + +const double framerates_values[16]= +{ + 0, + 24000.0/1001, /* 23.976 */ + 24.0, + 25.0, + 30000.0/1001, /* 29.97 */ + 30.0, + 50.0, + 60000.0/1001, /* 59.94 */ + 60.0, + 0, + 0, + 0, + 0, + 0 +}; + +const char *framerates_types[16]= +{ + "00 - forbidden", + "01 - 23.976", + "02 - 24", + "03 - 25", + "04 - 29.97", + "05 - 30", + "06 - 50", + "07 - 59.94", + "08 - 60", + "09 - reserved", + "10 - reserved", + "11 - reserved", + "12 - reserved", + "13 - reserved", + "14 - reserved", + "15 - reserved" +}; + +const char *aspect_ratio_types[16]= +{ + "00 - forbidden", + "01 - 1:1", + "02 - 4:3", + "03 - 16:9", + "04 - 2.21:1", + "05 - reserved", + "06 - reserved", + "07 - reserved", + "08 - reserved", + "09 - reserved", + "10 - reserved", + "11 - reserved", + "12 - reserved", + "13 - reserved", + "14 - reserved", + "15 - reserved" +}; + + +const char *pict_types[8]= +{ + "00 - ilegal (0)", + "01 - I", + "02 - P", + "03 - B", + "04 - ilegal (D)", + "05 - ilegal (5)", + "06 - ilegal (6)", + "07 - ilegal (7)" +}; + + +const char *slice_types[10]= +{ + "0 - P", + "1 - B", + "2 - I", + "3 - SP", + "4 - SI", + "5 - P", + "6 - B", + "7 - I", + "8 - SP", + "9 - SI" +}; + +const char *cc_types[4] = +{ + "NTSC line 21 field 1 closed captions", + "NTSC line 21 field 2 closed captions", + "DTVCC Channel Packet Data", + "DTVCC Channel Packet Start" +}; + +enum +{ + NTSC_CC_f1 = 0, + NTSC_CC_f2 = 1, + DTVCC_PACKET_DATA = 2, + DTVCC_PACKET_START = 3, +}; diff --git a/src/constants.h b/src/constants.h new file mode 100644 index 00000000..5e8eab93 --- /dev/null +++ b/src/constants.h @@ -0,0 +1,217 @@ +#ifndef CCX_CONSTANTS_H +#define CCX_CONSTANTS_H + +extern const char *framerates_types[16]; +extern const double framerates_values[16]; + +extern const char *aspect_ratio_types[16]; +extern const char *pict_types[8]; +extern const char *slice_types[10]; +extern const char *cc_types[4]; + +extern const unsigned char BROADCAST_HEADER[4]; +extern const unsigned char LITTLE_ENDIAN_BOM[2]; +extern const unsigned char UTF8_BOM[3]; +extern const unsigned char DVD_HEADER[8]; +extern const unsigned char lc1[1]; +extern const unsigned char lc2[1]; +extern const unsigned char lc3[2]; +extern const unsigned char lc4[2]; +extern const unsigned char lc5[1]; +extern const unsigned char lc6[1]; + +extern const unsigned char rcwt_header[11]; + +#define ONEPASS 120 /* Bytes we can always look ahead without going out of limits */ +#define BUFSIZE (2048*1024+ONEPASS) /* 2 Mb plus the safety pass */ +#define MAX_CLOSED_CAPTION_DATA_PER_PICTURE 32 +#define EIA_708_BUFFER_LENGTH 2048 // TODO: Find out what the real limit is +#define TS_PACKET_PAYLOAD_LENGTH 184 // From specs +#define SUBLINESIZE 2048 // Max. length of a .srt line - TODO: Get rid of this +#define STARTBYTESLENGTH (1024*1024) + +#define XMLRPC_CHUNK_SIZE (64*1024) // 64 Kb per chunk, to avoid too many realloc() + +enum ccx_debug_message_types +{ + /* Each debug message now belongs to one of these types. Use bitmaps in case + we want one message to belong to more than one type. */ + CCX_DMT_PARSE=1, // Show information related to parsing the container + CCX_DMT_VIDES=2,// Show video stream related information + CCX_DMT_TIME=4, // Show GOP and PTS timing information + CCX_DMT_VERBOSE=8, // Show lots of debugging output + CCX_DMT_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_CBRAW=0x80, // Caption blocks with FTS timing + CCX_DMT_GENERIC_NOTICES=0x100, // Generic, always displayed even if no debug is selected + CCX_DMT_TELETEXT=0x200, // Show teletext debug? + CCX_DMT_PAT=0x400, // Program Allocation Table dump + CCX_DMT_PMT=0x800, // Program Map Table dump + CCX_DMT_LEVENSHTEIN=0x1000, // Levenshtein distance calculations +}; + +// AVC NAL types +enum ccx_avc_nal_types +{ + CCX_NAL_TYPE_UNSPECIFIED_0 = 0, + CCX_NAL_TYPE_CODED_SLICE_NON_IDR_PICTURE_1 = 1, + CCX_NAL_TYPE_CODED_SLICE_PARTITION_A = 2, + CCX_NAL_TYPE_CODED_SLICE_PARTITION_B = 3, + CCX_NAL_TYPE_CODED_SLICE_PARTITION_C = 4, + CCX_NAL_TYPE_CODED_SLICE_IDR_PICTURE = 5, + CCX_NAL_TYPE_SEI = 6, + CCX_NAL_TYPE_SEQUENCE_PARAMETER_SET_7 = 7, + CCX_NAL_TYPE_PICTURE_PARAMETER_SET = 8, + CCX_NAL_TYPE_ACCESS_UNIT_DELIMITER_9 = 9, + CCX_NAL_TYPE_END_OF_SEQUENCE = 10, + CCX_NAL_TYPE_END_OF_STREAM = 11, + CCX_NAL_TYPE_FILLER_DATA = 12, + CCX_NAL_TYPE_SEQUENCE_PARAMETER_SET_EXTENSION = 13, + CCX_NAL_TYPE_PREFIX_NAL_UNIT = 14, + CCX_NAL_TYPE_SUBSET_SEQUENCE_PARAMETER_SET = 15, + CCX_NAL_TYPE_RESERVED_16 = 16, + 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_RESERVED_21 = 21, + CCX_NAL_TYPE_RESERVED_22 = 22, + CCX_NAL_TYPE_RESERVED_23 = 23, + CCX_NAL_TYPE_UNSPECIFIED_24 = 24, + CCX_NAL_TYPE_UNSPECIFIED_25 = 25, + CCX_NAL_TYPE_UNSPECIFIED_26 = 26, + CCX_NAL_TYPE_UNSPECIFIED_27 = 27, + CCX_NAL_TYPE_UNSPECIFIED_28 = 28, + CCX_NAL_TYPE_UNSPECIFIED_29 = 29, + CCX_NAL_TYPE_UNSPECIFIED_30 = 30, + CCX_NAL_TYPE_UNSPECIFIED_31 = 31 +}; + +// MPEG-2 TS stream types +enum ccx_stream_type +{ + CCX_STREAM_TYPE_UNKNOWNSTREAM = 0, + + CCX_STREAM_TYPE_VIDEO_MPEG1 = 0x01, + CCX_STREAM_TYPE_VIDEO_MPEG2 = 0x02, + CCX_STREAM_TYPE_AUDIO_MPEG1 = 0x03, + CCX_STREAM_TYPE_AUDIO_MPEG2 = 0x04, + CCX_STREAM_TYPE_PRIVATE_TABLE_MPEG2 = 0x05, + CCX_STREAM_TYPE_PRIVATE_MPEG2 = 0x06, + CCX_STREAM_TYPE_MHEG_PACKETS = 0x07, + CCX_STREAM_TYPE_MPEG2_ANNEX_A_DSM_CC = 0x08, + CCX_STREAM_TYPE_ITU_T_H222_1 = 0x09, + CCX_STREAM_TYPE_ISO_IEC_13818_6_TYPE_A = 0x0A, + CCX_STREAM_TYPE_ISO_IEC_13818_6_TYPE_B = 0x0B, + CCX_STREAM_TYPE_ISO_IEC_13818_6_TYPE_C = 0x0C, + CCX_STREAM_TYPE_ISO_IEC_13818_6_TYPE_D = 0x0D, + CCX_STREAM_TYPE_AUDIO_AAC = 0x0f, + CCX_STREAM_TYPE_VIDEO_MPEG4 = 0x10, + CCX_STREAM_TYPE_VIDEO_H264 = 0x1b, + CCX_STREAM_TYPE_PRIVATE_USER_MPEG2=0x80, + CCX_STREAM_TYPE_AUDIO_AC3 = 0x81, + CCX_STREAM_TYPE_AUDIO_HDMV_DTS = 0x82, + CCX_STREAM_TYPE_AUDIO_DTS = 0x8a, +}; + +enum ccx_mpeg_descriptor +{ + 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, +}; + +enum +{ + CCX_MESSAGES_QUIET = 0, + CCX_MESSAGES_STDOUT =1, + CCX_MESSAGES_STDERR =2 +}; + +enum ccx_datasource +{ + CCX_DS_FILE=0, + CCX_DS_STDIN=1, + CCX_DS_NETWORK=2 +}; + +enum ccx_output_format +{ + CCX_OF_RAW = 0, + CCX_OF_SRT = 1, + CCX_OF_SAMI = 2, + CCX_OF_TRANSCRIPT = 3, + CCX_OF_RCWT = 4, + CCX_OF_NULL = 5, + CCX_OF_SMPTETT = 6, + CCX_OF_SPUPNG = 7, + CCX_OF_DVDRAW = 8, // See -d at http://www.geocities.com/mcpoodle43/SCC_TOOLS/DOCS/SCC_TOOLS.HTML#CCExtract +}; + +enum ccx_output_date_format +{ + ODF_NONE = 0, + ODF_HHMMSS = 1, + ODF_SECONDS = 2, + ODF_DATE =3, + ODF_HHMMSSMS = 4, // HH:MM:SS,MILIS (.srt style) +}; + +enum ccx_stream_mode_enum +{ + CCX_SM_ELEMENTARY_OR_NOT_FOUND=0, + CCX_SM_TRANSPORT=1, + CCX_SM_PROGRAM=2, + CCX_SM_ASF=3, + CCX_SM_MCPOODLESRAW = 4, + CCX_SM_RCWT = 5, // Raw Captions With Time, not used yet. + CCX_SM_MYTH = 6, // Use the myth loop + CCX_SM_MP4 = 7, // MP4, ISO- + CCX_SM_HEX_DUMP = 8, // Hexadecimal dump generated by wtvccdump + CCX_SM_WTV = 9, + CCX_SM_AUTODETECT = 16 +}; + +enum ccx_encoding_type +{ + CCX_ENC_UNICODE = 0, + CCX_ENC_LATIN_1 = 1, + CCX_ENC_UTF_8 = 2 +}; + +enum ccx_bufferdata_type +{ + CCX_UNKNOWN = 0, + CCX_PES = 1, + CCX_RAW = 2, + CCX_H264 = 3, + CCX_HAUPPAGE = 4, + CCX_TELETEXT = 5, + CCX_PRIVATE_MPEG2_CC = 6 +}; + +enum ccx_frame_type +{ + CCX_FRAME_TYPE_RESET_OR_UNKNOWN = 0, + CCX_FRAME_TYPE_I_FRAME = 1, + CCX_FRAME_TYPE_P_FRAME = 2, + CCX_FRAME_TYPE_B_FRAME = 3, + CCX_FRAME_TYPE_D_FRAME = 4 +}; + +typedef enum { + YES = 1, + NO = 0, + UNDEF = 0xff +} bool_t; + +#define CCX_TXT_FORBIDDEN 0 // Ignore teletext packets +#define CCX_TXT_AUTO_NOT_YET_FOUND 1 +#define CCX_TXT_IN_USE 2 // Positive autodetected, or forced, etc + + +#endif diff --git a/src/disable_warnings.h b/src/disable_warnings.h new file mode 100644 index 00000000..07ed67bc --- /dev/null +++ b/src/disable_warnings.h @@ -0,0 +1,7 @@ +#ifdef _MSC_VER +#include "stdintmsc.h" +// Don't bug me with strcpy() deprecation warnings +#pragma warning(disable : 4996) +// Or incompatible types from char[] to const char * etc +#pragma warning(disable : 4133) +#endif \ No newline at end of file diff --git a/src/encoding.cpp b/src/encoding.cpp new file mode 100644 index 00000000..b4f44bbe --- /dev/null +++ b/src/encoding.cpp @@ -0,0 +1,835 @@ +#include + +void get_char_in_latin_1 (unsigned char *buffer, unsigned char c) +{ + unsigned char c1='?'; + if (c<0x80) + { + // Regular line-21 character set, mostly ASCII except these exceptions + switch (c) + { + case 0x2a: // lowercase a, acute accent + c1=0xe1; + break; + case 0x5c: // lowercase e, acute accent + c1=0xe9; + break; + case 0x5e: // lowercase i, acute accent + c1=0xed; + break; + case 0x5f: // lowercase o, acute accent + c1=0xf3; + break; + case 0x60: // lowercase u, acute accent + c1=0xfa; + break; + case 0x7b: // lowercase c with cedilla + c1=0xe7; + break; + case 0x7c: // division symbol + c1=0xf7; + break; + case 0x7d: // uppercase N tilde + c1=0xd1; + break; + case 0x7e: // lowercase n tilde + c1=0xf1; + break; + default: + c1=c; + break; + } + *buffer=c1; + return; + } + 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 + case 0x80: // Registered symbol (R) + c1=0xae; + break; + case 0x81: // degree sign + c1=0xb0; + break; + case 0x82: // 1/2 symbol + c1=0xbd; + break; + 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 + c1=0xa2; + break; + case 0x86: // Pounds sterling + c1=0xa3; + break; + case 0x87: // Music note - Not in latin 1, so we use 'pilcrow' + c1=0xb6; + break; + case 0x88: // lowercase a, grave accent + c1=0xe0; + break; + case 0x89: // transparent space, we make it regular + c1=0x20; + break; + case 0x8a: // lowercase e, grave accent + c1=0xe8; + break; + case 0x8b: // lowercase a, circumflex accent + c1=0xe2; + break; + case 0x8c: // lowercase e, circumflex accent + c1=0xea; + break; + case 0x8d: // lowercase i, circumflex accent + c1=0xee; + break; + case 0x8e: // lowercase o, circumflex accent + c1=0xf4; + break; + case 0x8f: // lowercase u, circumflex accent + c1=0xfb; + break; + // THIS BLOCK INCLUDES THE 32 EXTENDED (TWO-BYTE) LINE 21 CHARACTERS + // THAT COME FROM HI BYTE=0x12 AND LOW BETWEEN 0x20 AND 0x3F + case 0x90: // capital letter A with acute + c1=0xc1; + break; + case 0x91: // capital letter E with acute + c1=0xc9; + break; + case 0x92: // capital letter O with acute + c1=0xd3; + break; + case 0x93: // capital letter U with acute + c1=0xda; + break; + case 0x94: // capital letter U with diaresis + c1=0xdc; + break; + case 0x95: // lowercase letter U with diaeresis + c1=0xfc; + break; + case 0x96: // apostrophe + c1=0x27; + break; + case 0x97: // inverted exclamation mark + c1=0xa1; + break; + case 0x98: // asterisk + c1=0x2a; + break; + case 0x99: // apostrophe (yes, duped). See CCADI source code. + c1=0x27; + break; + case 0x9a: // em dash + c1=0x2d; + break; + case 0x9b: // copyright sign + c1=0xa9; + break; + case 0x9c: // Service Mark - not available in latin 1 + break; + case 0x9d: // Full stop (.) + c1=0x2e; + break; + case 0x9e: // Quoatation mark + c1=0x22; + break; + case 0x9f: // Quoatation mark + c1=0x22; + break; + case 0xa0: // uppercase A, grave accent + c1=0xc0; + break; + case 0xa1: // uppercase A, circumflex + c1=0xc2; + break; + case 0xa2: // uppercase C with cedilla + c1=0xc7; + break; + case 0xa3: // uppercase E, grave accent + c1=0xc8; + break; + case 0xa4: // uppercase E, circumflex + c1=0xca; + break; + case 0xa5: // capital letter E with diaresis + c1=0xcb; + break; + case 0xa6: // lowercase letter e with diaresis + c1=0xeb; + break; + case 0xa7: // uppercase I, circumflex + c1=0xce; + break; + case 0xa8: // uppercase I, with diaresis + c1=0xcf; + break; + case 0xa9: // lowercase i, with diaresis + c1=0xef; + break; + case 0xaa: // uppercase O, circumflex + c1=0xd4; + break; + case 0xab: // uppercase U, grave accent + c1=0xd9; + break; + case 0xac: // lowercase u, grave accent + c1=0xf9; + break; + case 0xad: // uppercase U, circumflex + c1=0xdb; + break; + case 0xae: // LEFT-POINTING DOUBLE ANGLE QUOTATION MARK + c1=0xab; + break; + case 0xaf: // RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK + c1=0xbb; + break; + // THIS BLOCK INCLUDES THE 32 EXTENDED (TWO-BYTE) LINE 21 CHARACTERS + // THAT COME FROM HI BYTE=0x13 AND LOW BETWEEN 0x20 AND 0x3F + case 0xb0: // Uppercase A, tilde + c1=0xc3; + break; + case 0xb1: // Lowercase a, tilde + c1=0xe3; + break; + case 0xb2: // Uppercase I, acute accent + c1=0xcd; + break; + case 0xb3: // Uppercase I, grave accent + c1=0xcc; + break; + case 0xb4: // Lowercase i, grave accent + c1=0xec; + break; + case 0xb5: // Uppercase O, grave accent + c1=0xd2; + break; + case 0xb6: // Lowercase o, grave accent + c1=0xf2; + break; + case 0xb7: // Uppercase O, tilde + c1=0xd5; + break; + case 0xb8: // Lowercase o, tilde + c1=0xf5; + break; + case 0xb9: // Open curly brace + c1=0x7b; + break; + case 0xba: // Closing curly brace + c1=0x7d; + break; + case 0xbb: // Backslash + c1=0x5c; + break; + case 0xbc: // Caret + c1=0x5e; + break; + case 0xbd: // Underscore + c1=0x5f; + break; + case 0xbe: // Pipe (broken bar) + c1=0xa6; + break; + case 0xbf: // Tilde + c1=0x7e; + break; + case 0xc0: // Uppercase A, umlaut + c1=0xc4; + break; + case 0xc1: // Lowercase A, umlaut + c1=0xe3; + break; + case 0xc2: // Uppercase O, umlaut + c1=0xd6; + break; + case 0xc3: // Lowercase o, umlaut + c1=0xf6; + break; + case 0xc4: // Esszett (sharp S) + c1=0xdf; + break; + case 0xc5: // Yen symbol + c1=0xa5; + break; + case 0xc6: // Currency symbol + c1=0xa4; + break; + case 0xc7: // Vertical bar + c1=0x7c; + break; + case 0xc8: // Uppercase A, ring + c1=0xc5; + break; + case 0xc9: // Lowercase A, ring + c1=0xe5; + break; + case 0xca: // Uppercase O, slash + c1=0xd8; + break; + case 0xcb: // Lowercase o, slash + c1=0xf8; + break; + case 0xcc: // Upper left corner + case 0xcd: // Upper right corner + case 0xce: // Lower left corner + case 0xcf: // Lower right corner + default: // For those that don't have representation + *buffer='?'; // I'll do it eventually, I promise + break; // This are weird chars anyway + } + *buffer=c1; +} + +void get_char_in_unicode (unsigned char *buffer, unsigned char c) +{ + unsigned char c1,c2; + switch (c) + { + case 0x84: // Trademark symbol (TM) + c2=0x21; + c1=0x22; + break; + case 0x87: // Music note + c2=0x26; + c1=0x6a; + break; + case 0x9c: // Service Mark + c2=0x21; + c1=0x20; + break; + case 0xcc: // Upper left corner + c2=0x23; + c1=0x1c; + break; + case 0xcd: // Upper right corner + c2=0x23; + c1=0x1d; + break; + case 0xce: // Lower left corner + c2=0x23; + c1=0x1e; + break; + case 0xcf: // Lower right corner + c2=0x23; + c1=0x1f; + break; + default: // Everything else, same as latin-1 followed by 00 + get_char_in_latin_1 (&c1,c); + c2=0; + break; + } + *buffer=c1; + *(buffer+1)=c2; +} + +int get_char_in_utf_8 (unsigned char *buffer, unsigned char c) // Returns number of bytes used +{ + if (c<0x80) // Regular line-21 character set, mostly ASCII except these exceptions + { + switch (c) + { + case 0x2a: // lowercase a, acute accent + *buffer=0xc3; + *(buffer+1)=0xa1; + return 2; + case 0x5c: // lowercase e, acute accent + *buffer=0xc3; + *(buffer+1)=0xa9; + return 2; + case 0x5e: // lowercase i, acute accent + *buffer=0xc3; + *(buffer+1)=0xad; + return 2; + case 0x5f: // lowercase o, acute accent + *buffer=0xc3; + *(buffer+1)=0xb3; + return 2; + case 0x60: // lowercase u, acute accent + *buffer=0xc3; + *(buffer+1)=0xba; + return 2; + case 0x7b: // lowercase c with cedilla + *buffer=0xc3; + *(buffer+1)=0xa7; + return 2; + case 0x7c: // division symbol + *buffer=0xc3; + *(buffer+1)=0xb7; + return 2; + case 0x7d: // uppercase N tilde + *buffer=0xc3; + *(buffer+1)=0x91; + return 2; + case 0x7e: // lowercase n tilde + *buffer=0xc3; + *(buffer+1)=0xb1; + return 2; + default: + *buffer=c; + return 1; + } + } + 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 + case 0x80: // Registered symbol (R) + *buffer=0xc2; + *(buffer+1)=0xae; + return 2; + case 0x81: // degree sign + *buffer=0xc2; + *(buffer+1)=0xb0; + return 2; + case 0x82: // 1/2 symbol + *buffer=0xc2; + *(buffer+1)=0xbd; + return 2; + case 0x83: // Inverted (open) question mark + *buffer=0xc2; + *(buffer+1)=0xbf; + return 2; + case 0x84: // Trademark symbol (TM) + *buffer=0xe2; + *(buffer+1)=0x84; + *(buffer+2)=0xa2; + return 3; + case 0x85: // Cents symbol + *buffer=0xc2; + *(buffer+1)=0xa2; + return 2; + case 0x86: // Pounds sterling + *buffer=0xc2; + *(buffer+1)=0xa3; + return 2; + case 0x87: // Music note + *buffer=0xe2; + *(buffer+1)=0x99; + *(buffer+2)=0xaa; + return 3; + case 0x88: // lowercase a, grave accent + *buffer=0xc3; + *(buffer+1)=0xa0; + return 2; + case 0x89: // transparent space, we make it regular + *buffer=0x20; + return 1; + case 0x8a: // lowercase e, grave accent + *buffer=0xc3; + *(buffer+1)=0xa8; + return 2; + case 0x8b: // lowercase a, circumflex accent + *buffer=0xc3; + *(buffer+1)=0xa2; + return 2; + case 0x8c: // lowercase e, circumflex accent + *buffer=0xc3; + *(buffer+1)=0xaa; + return 2; + case 0x8d: // lowercase i, circumflex accent + *buffer=0xc3; + *(buffer+1)=0xae; + return 2; + case 0x8e: // lowercase o, circumflex accent + *buffer=0xc3; + *(buffer+1)=0xb4; + return 2; + case 0x8f: // lowercase u, circumflex accent + *buffer=0xc3; + *(buffer+1)=0xbb; + return 2; + // THIS BLOCK INCLUDES THE 32 EXTENDED (TWO-BYTE) LINE 21 CHARACTERS + // THAT COME FROM HI BYTE=0x12 AND LOW BETWEEN 0x20 AND 0x3F + case 0x90: // capital letter A with acute + *buffer=0xc3; + *(buffer+1)=0x81; + return 2; + case 0x91: // capital letter E with acute + *buffer=0xc3; + *(buffer+1)=0x89; + return 2; + case 0x92: // capital letter O with acute + *buffer=0xc3; + *(buffer+1)=0x93; + return 2; + case 0x93: // capital letter U with acute + *buffer=0xc3; + *(buffer+1)=0x9a; + return 2; + case 0x94: // capital letter U with diaresis + *buffer=0xc3; + *(buffer+1)=0x9c; + return 2; + case 0x95: // lowercase letter U with diaeresis + *buffer=0xc3; + *(buffer+1)=0xbc; + return 2; + case 0x96: // apostrophe + *buffer=0x27; + return 1; + case 0x97: // inverted exclamation mark + *buffer=0xc2; + *(buffer+1)=0xa1; + return 2; + case 0x98: // asterisk + *buffer=0x2a; + return 1; + case 0x99: // Plain single quote + *buffer=0x27; + return 1; + case 0x9a: // em dash + *buffer=0xe2; + *(buffer+1)=0x80; + *(buffer+2)=0x94; + return 3; + case 0x9b: // copyright sign + *buffer=0xc2; + *(buffer+1)=0xa9; + return 2; + case 0x9c: // Service mark + *buffer=0xe2; + *(buffer+1)=0x84; + *(buffer+2)=0xa0; + return 3; + case 0x9d: // Round bullet + *buffer=0xe2; + *(buffer+1)=0x80; + *(buffer+2)=0xa2; + return 3; + case 0x9e: // Opening double quotes + *buffer=0xe2; + *(buffer+1)=0x80; + *(buffer+2)=0x9c; + return 3; + case 0x9f: // Closing double quotes + *buffer=0xe2; + *(buffer+1)=0x80; + *(buffer+2)=0x9d; + return 3; + case 0xa0: // uppercase A, grave accent + *buffer=0xc3; + *(buffer+1)=0x80; + return 2; + case 0xa1: // uppercase A, circumflex + *buffer=0xc3; + *(buffer+1)=0x82; + return 2; + case 0xa2: // uppercase C with cedilla + *buffer=0xc3; + *(buffer+1)=0x87; + return 2; + case 0xa3: // uppercase E, grave accent + *buffer=0xc3; + *(buffer+1)=0x88; + return 2; + case 0xa4: // uppercase E, circumflex + *buffer=0xc3; + *(buffer+1)=0x8a; + return 2; + case 0xa5: // capital letter E with diaresis + *buffer=0xc3; + *(buffer+1)=0x8b; + return 2; + case 0xa6: // lowercase letter e with diaresis + *buffer=0xc3; + *(buffer+1)=0xab; + return 2; + case 0xa7: // uppercase I, circumflex + *buffer=0xc3; + *(buffer+1)=0x8e; + return 2; + case 0xa8: // uppercase I, with diaresis + *buffer=0xc3; + *(buffer+1)=0x8f; + return 2; + case 0xa9: // lowercase i, with diaresis + *buffer=0xc3; + *(buffer+1)=0xaf; + return 2; + case 0xaa: // uppercase O, circumflex + *buffer=0xc3; + *(buffer+1)=0x94; + return 2; + case 0xab: // uppercase U, grave accent + *buffer=0xc3; + *(buffer+1)=0x99; + return 2; + case 0xac: // lowercase u, grave accent + *buffer=0xc3; + *(buffer+1)=0xb9; + return 2; + case 0xad: // uppercase U, circumflex + *buffer=0xc3; + *(buffer+1)=0x9b; + return 2; + case 0xae: // LEFT-POINTING DOUBLE ANGLE QUOTATION MARK + *buffer=0xc2; + *(buffer+1)=0xab; + return 2; + case 0xaf: // RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK + *buffer=0xc2; + *(buffer+1)=0xbb; + return 2; + // THIS BLOCK INCLUDES THE 32 EXTENDED (TWO-BYTE) LINE 21 CHARACTERS + // THAT COME FROM HI BYTE=0x13 AND LOW BETWEEN 0x20 AND 0x3F + case 0xb0: // Uppercase A, tilde + *buffer=0xc3; + *(buffer+1)=0x83; + return 2; + case 0xb1: // Lowercase a, tilde + *buffer=0xc3; + *(buffer+1)=0xa3; + return 2; + case 0xb2: // Uppercase I, acute accent + *buffer=0xc3; + *(buffer+1)=0x8d; + return 2; + case 0xb3: // Uppercase I, grave accent + *buffer=0xc3; + *(buffer+1)=0x8c; + return 2; + case 0xb4: // Lowercase i, grave accent + *buffer=0xc3; + *(buffer+1)=0xac; + return 2; + case 0xb5: // Uppercase O, grave accent + *buffer=0xc3; + *(buffer+1)=0x92; + return 2; + case 0xb6: // Lowercase o, grave accent + *buffer=0xc3; + *(buffer+1)=0xb2; + return 2; + case 0xb7: // Uppercase O, tilde + *buffer=0xc3; + *(buffer+1)=0x95; + return 2; + case 0xb8: // Lowercase o, tilde + *buffer=0xc3; + *(buffer+1)=0xb5; + return 2; + case 0xb9: // Open curly brace + *buffer=0x7b; + return 1; + case 0xba: // Closing curly brace + *buffer=0x7d; + return 1; + case 0xbb: // Backslash + *buffer=0x5c; + return 1; + case 0xbc: // Caret + *buffer=0x5e; + return 1; + case 0xbd: // Underscore + *buffer=0x5f; + return 1; + case 0xbe: // Pipe (broken bar) + *buffer=0xc2; + *(buffer+1)=0xa6; + return 2; + case 0xbf: // Tilde + *buffer=0x7e; // Not sure + return 1; + case 0xc0: // Uppercase A, umlaut + *buffer=0xc3; + *(buffer+1)=0x84; + return 2; + case 0xc1: // Lowercase A, umlaut + *buffer=0xc3; + *(buffer+1)=0xa4; + return 2; + case 0xc2: // Uppercase O, umlaut + *buffer=0xc3; + *(buffer+1)=0x96; + return 2; + case 0xc3: // Lowercase o, umlaut + *buffer=0xc3; + *(buffer+1)=0xb6; + return 2; + case 0xc4: // Esszett (sharp S) + *buffer=0xc3; + *(buffer+1)=0x9f; + return 2; + case 0xc5: // Yen symbol + *buffer=0xc2; + *(buffer+1)=0xa5; + return 2; + case 0xc6: // Currency symbol + *buffer=0xc2; + *(buffer+1)=0xa4; + return 2; + case 0xc7: // Vertical bar + *buffer=0x7c; + return 1; + case 0xc8: // Uppercase A, ring + *buffer=0xc3; + *(buffer+1)=0x85; + return 2; + case 0xc9: // Lowercase A, ring + *buffer=0xc3; + *(buffer+1)=0xa5; + return 2; + case 0xca: // Uppercase O, slash + *buffer=0xc3; + *(buffer+1)=0x98; + return 2; + case 0xcb: // Lowercase o, slash + *buffer=0xc3; + *(buffer+1)=0xb8; + return 2; + case 0xcc: // Top left corner + *buffer=0xe2; + *(buffer+1)=0x8c; + *(buffer+2)=0x9c; + return 3; + case 0xcd: // Top right corner + *buffer=0xe2; + *(buffer+1)=0x8c; + *(buffer+2)=0x9d; + return 3; + case 0xce: // Bottom left corner + *buffer=0xe2; + *(buffer+1)=0x8c; + *(buffer+2)=0x9e; + return 3; + case 0xcf: // Bottom right corner + *buffer=0xe2; + *(buffer+1)=0x8c; + *(buffer+2)=0x9f; + return 3; + default: // + *buffer='?'; // I'll do it eventually, I promise + return 1; // This are weird chars anyway + } +} + +unsigned char cctolower (unsigned char c) +{ + if (c>='A' && c<='Z') + return tolower(c); + switch (c) + { + case 0x7d: // uppercase N tilde + return 0x7e; + case 0x90: // capital letter A with acute + return 0x2a; + case 0x91: // capital letter E with acute + return 0x5c; + case 0x92: // capital letter O with acute + return 0x5f; + case 0x93: // capital letter U with acute + return 0x60; + case 0xa2: // uppercase C with cedilla + return 0x7b; + case 0xa0: // uppercase A, grave accent + return 0x88; + case 0xa3: // uppercase E, grave accent + return 0x8a; + case 0xa1: // uppercase A, circumflex + return 0x8b; + case 0xa4: // uppercase E, circumflex + return 0x8c; + case 0xa7: // uppercase I, circumflex + return 0x8d; + case 0xaa: // uppercase O, circumflex + return 0x8e; + case 0xad: // uppercase U, circumflex + return 0x8f; + case 0x94: // capital letter U with diaresis + return 0x95; + case 0xa5: // capital letter E with diaresis + return 0xa6; + case 0xa8: // uppercase I, with diaresis + return 0xa9; + case 0xab: // uppercase U, grave accent + return 0xac; + case 0xb0: // Uppercase A, tilde + return 0xb1; + case 0xb2: // Uppercase I, acute accent + return 0x5e; + case 0xb3: // Uppercase I, grave accent + return 0xb4; + case 0xb5: // Uppercase O, grave accent + return 0xb6; + case 0xb7: // Uppercase O, tilde + return 0xb8; + case 0xc0: // Uppercase A, umlaut + return 0xc1; + case 0xc2: // Uppercase O, umlaut + return 0xc3; + case 0xc8: // Uppercase A, ring + return 0xc9; + case 0xca: // Uppercase O, slash + return 0xcb; + } + return c; +} + +unsigned char cctoupper (unsigned char c) +{ + if (c>='a' && c<='z') + return toupper(c); + switch (c) + { + case 0x7e: // lowercase n tilde + return 0x7d; + case 0x2a: // lowercase a, acute accent + return 0x90; + case 0x5c: // lowercase e, acute accent + return 0x91; + case 0x5e: // lowercase i, acute accent + return 0xb2; + case 0x5f: // lowercase o, acute accent + return 0x92; + case 0x60: // lowercase u, acute accent + return 0x93; + case 0x7b: // lowercase c with cedilla + return 0xa2; + case 0x88: // lowercase a, grave accent + return 0xa0; + case 0x8a: // lowercase e, grave accent + return 0xa3; + case 0x8b: // lowercase a, circumflex accent + return 0xa1; + case 0x8c: // lowercase e, circumflex accent + return 0xa4; + case 0x8d: // lowercase i, circumflex accent + return 0xa7; + case 0x8e: // lowercase o, circumflex accent + return 0xaa; + case 0x8f: // lowercase u, circumflex accent + return 0xad; + case 0x95: // lowercase letter U with diaeresis + return 0x94; + case 0xa6: // lowercase letter e with diaresis + return 0xa5; + case 0xa9: // lowercase i, with diaresis + return 0xa8; + case 0xac: // lowercase u, grave accent + return 0xab; + case 0xb1: // Lowercase a, tilde + return 0xb0; + case 0xb4: // Lowercase i, grave accent + return 0xb3; + case 0xb6: // Lowercase o, grave accent + return 0xb5; + case 0xb8: // Lowercase o, tilde + return 0xb7; + case 0xc1: // Lowercase A, umlaut + return 0xc0; + case 0xc3: // Lowercase o, umlaut + return 0xc2; + case 0xc9: // Lowercase A, ring + return 0xc8; + case 0xcb: // Lowercase o, slash + return 0xca; + } + return c; +} diff --git a/src/es_functions.cpp b/src/es_functions.cpp new file mode 100644 index 00000000..80ec472e --- /dev/null +++ b/src/es_functions.cpp @@ -0,0 +1,1127 @@ +#include "ccextractor.h" + +// Functions to parse a mpeg-2 data stream, see ISO/IEC 13818-2 6.2 + +static int no_bitstream_error = 0; +static int saw_seqgoppic = 0; +static int in_pic_data = 0; + +static unsigned current_progressive_sequence = 2; +static unsigned current_pulldownfields = 32768; + +static int temporal_reference = 0; +static ccx_frame_type picture_coding_type = CCX_FRAME_TYPE_RESET_OR_UNKNOWN; +static unsigned picture_structure = 0; +unsigned top_field_first = 0; // Needs to be global +static unsigned repeat_first_field = 0; +static unsigned progressive_frame = 0; +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 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 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_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) +{ + if (length<8) // Need to look ahead 8 bytes + return length; + + // Init bitstream + struct bitstream esstream; + init_bitstream(&esstream, data, data+length); + + // Process data. The return value is ignored as esstream.pos holds + // the information how far the parsing progressed. + es_video_sequence(&esstream); + + // This returns how many bytes were processed and can therefore + // be discarded from "buffer". "esstream.pos" points to the next byte + // where processing will continue. + return (LLONG) (esstream.pos - data); +} + + +// Return the next startcode or sequence_error_code if not enough +// data was left in the bitstream. Also set esstream->bitsleft. +// The bitstream pointer shall be moved to the begin of the start +// code if found, or to the position where a search would continue +// would more data be made available. +// This function discards all data until the start code is +// found +static uint8_t search_start_code(struct bitstream *esstream) +{ + make_byte_aligned(esstream); + + // Keep a negative esstream->bitsleft, but correct it. + if (esstream->bitsleft <= 0) + { + dbg_print(CCX_DMT_VERBOSE, "search_start_code: bitsleft <= 0\n"); + esstream->bitsleft -= 8*4; + return 0xB4; + } + + unsigned char *tstr = esstream->pos; + + // Scan for 0x000001xx in header + while(1) + { + // The loop is exited by break only, and if tstr+3 is less than + // esstream->end we found the start code prefix + tstr = (unsigned char *) memchr(tstr, 0x00, + esstream->end - tstr); + if (tstr==NULL) + { + // We don't even have the starting 0x00 + tstr = esstream->end; + esstream->bitsleft = -8*4; + break; + } + else if (tstr+3 >= esstream->end) + { + // Not enough bytes left to check for 0x000001?? + esstream->bitsleft = 8*(esstream->end-(tstr+4)); + break; + } + else if (tstr[1]==0x00 && tstr[2]==0x01) + { + // Found 0x000001?? + esstream->bitsleft = 8*(esstream->end-(tstr+4)); + break; + } + // Keep searching + tstr++; + } + esstream->pos = tstr; + if (esstream->bitsleft < 0) + { + dbg_print(CCX_DMT_VERBOSE, "search_start_code: bitsleft <= 0\n"); + return 0xB4; + } + else + { + dbg_print(CCX_DMT_VERBOSE, "search_start_code: Found %02X\n", tstr[3]); + return tstr[3]; + } +} + + +// Return the next startcode or sequence_error_code if not enough +// data was left in the bitstream. Also set esstream->bitsleft. +// The bitstream pointer shall be moved to the begin of the start +// code if found, or to the position where a search would continue +// would more data be made available. +// Only NULL bytes before the start code are discarded, if a non +// NULL byte is encountered esstream->error is set to TRUE and the +// function returns sequence_error_code with the pointer set after +// that byte. +static uint8_t next_start_code(struct bitstream *esstream) +{ + if (esstream->error || esstream->bitsleft < 0) + { + return 0xB4; + } + + make_byte_aligned(esstream); + + // Only start looking if there is enough data. Adjust bitsleft. + if (esstream->bitsleft < 4*8) + { + dbg_print(CCX_DMT_VERBOSE, "next_start_code: bitsleft %lld < 32\n", esstream->bitsleft); + esstream->bitsleft -= 8*4; + return 0xB4; + } + + uint8_t tmp; + while ((next_u32(esstream)&0x00FFFFFF) != 0x00010000 // LSB 0x000001?? + && esstream->bitsleft > 0) + { + tmp = read_u8(esstream); + if (tmp) + { + dbg_print(CCX_DMT_VERBOSE, "next_start_code: Non zero stuffing\n"); + esstream->error = 1; + return 0xB4; + } + } + + if (esstream->bitsleft < 8) + { + esstream->bitsleft -= 8; + dbg_print(CCX_DMT_VERBOSE, "next_start_code: bitsleft <= 0\n"); + return 0xB4; + } + else + { + dbg_print(CCX_DMT_VERBOSE, "next_start_code: Found %02X\n", *(esstream->pos+3)); + + if ( *(esstream->pos+3) == 0xB4 ) + { + dbg_print(CCX_DMT_VERBOSE, "B4: assume bitstream syntax error!\n"); + esstream->error = 1; + } + + return *(esstream->pos+3); + } +} + + +// Return TRUE if the video sequence was finished, FALSE +// 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) +{ + // Avoid "Skip forward" message on first call and later only + // once per search. + static int noskipmessage = 1; + uint8_t startcode; + + dbg_print(CCX_DMT_VERBOSE, "es_video_sequence()\n"); + + esstream->error = 0; + + // Analyze sequence header ... + if (!no_bitstream_error) + { + // We might start here because of a syntax error. Discard + // all data until a new sequence_header_code or group_start_code + // is found. + + if (!noskipmessage) // Avoid unnecessary output. + mprint("\nSkip forward to the next Sequence or GOP start.\n"); + else + noskipmessage = 0; + + uint8_t startcode; + while(1) + { + // search_start_code() cannot produce esstream->error + startcode = search_start_code(esstream); + if (esstream->bitsleft < 0) + { + noskipmessage = 1; + return 0; + } + + if (startcode == 0xB3 || startcode == 0xB8) // found it + break; + + skip_bits(esstream, 4*8); + } + + no_bitstream_error = 1; + saw_seqgoppic = 0; + in_pic_data = 0; + } + + do + { + startcode = next_start_code(esstream); + + dbg_print(CCX_DMT_VERBOSE, "\nM2V - next start code %02X %d\n", startcode, in_pic_data); + + // Syntax check - also returns on bitsleft < 0 + if (startcode == 0xB4) + { + if (esstream->error) + { + no_bitstream_error = 0; + dbg_print(CCX_DMT_VERBOSE, "es_video_sequence: syntax problem.\n"); + } + + dbg_print(CCX_DMT_VERBOSE, "es_video_sequence: return on B4 startcode.\n"); + + return 0; + } + + // Sequence_end_code + if (startcode == 0xB7) + { + read_u32(esstream); // Advance bitstream + no_bitstream_error = 0; + break; + } + + if (!in_pic_data && startcode == 0xB3) + { + if (!read_seq_info(esstream)) + { + if (esstream->error) + no_bitstream_error = 0; + return 0; + } + saw_seqgoppic = 1; + continue; + } + + if (!in_pic_data && startcode == 0xB8) + { + if (!read_gop_info(esstream)) + { + if (esstream->error) + no_bitstream_error = 0; + return 0; + } + saw_seqgoppic = 2; + continue; + } + + if (!in_pic_data && startcode == 0x00) + { + if (!read_pic_info(esstream)) + { + if (esstream->error) + no_bitstream_error = 0; + return 0; + } + saw_seqgoppic = 3; + in_pic_data = 1; + continue; + } + + // Only looks for extension and user data if we saw sequence, gop + // or picture info before. + // 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 (esstream->error) + no_bitstream_error = 0; + return 0; + } + saw_seqgoppic = 0; + continue; + } + + if (in_pic_data) // See comment in read_pic_data() + { + if (!read_pic_data(esstream)) + { + if (esstream->error) + no_bitstream_error = 0; + return 0; + } + saw_seqgoppic = 0; + in_pic_data = 0; + continue; + } + + // Nothing found - bitstream error + if (startcode == 0xBA) + { + mprint("\nFound PACK header in ES data. Probably wrong stream mode!\n"); + } + else + { + mprint("\nUnexpected startcode: %02X\n", startcode); + } + no_bitstream_error = 0; + return 0; + } + while(1); + + return 1; +} + + +// Return TRUE if all was read. FALSE if a problem occured: +// 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) +{ + 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!"); + + // 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_ext(esstream); + // FIXME: if sequence extension is missing this is not MPEG-2, + // or broken. Set bitstream error. + //extension_and_user_data(esstream); + + if (esstream->error) + return 0; + + if (esstream->bitsleft < 0) + { + init_bitstream(esstream, video_seq_start, esstream->end); + return 0; + } + + dbg_print(CCX_DMT_VERBOSE, "Read Sequence Info - processed\n\n"); + + return 1; +} + + +// 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) +{ + dbg_print(CCX_DMT_VERBOSE, "Sequence header\n"); + + if (esstream->error || esstream->bitsleft <= 0) + return 0; + + // We only get here after seeing that start code + if (read_u32(esstream) != 0xB3010000) // LSB first (0x000001B3) + fatal(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); + + // Discard some information + read_bits(esstream, 18+1+10+1); + + // load_intra_quantiser_matrix + if (read_bits(esstream,1)) + skip_bits(esstream, 8*64); + // load_non_intra_quantiser_matrix + if (read_bits(esstream,1)) + skip_bits(esstream, 8*64); + + if (esstream->bitsleft < 0) + return 0; + + // If we got the whole sequence, process + if (hor_size!=current_hor_size || + vert_size!=current_vert_size || + aspect_ratio!=current_aspect_ratio || + frame_rate!=current_frame_rate) + { + // If horizontal/vertical size, framerate and/or aspect + // ratio are ilegal, we discard the + // whole sequence info. + if (vert_size >= 288 && vert_size <= 1088 && + hor_size >= 352 && hor_size <= 1920 && + hor_size / vert_size >= 352/576 && hor_size / vert_size <= 2 && + frame_rate>0 && frame_rate<9 && + aspect_ratio>0 && aspect_ratio<5) + { + mprint ("\n\nNew video information found"); + if (pts_set==2) + { + unsigned cur_sec = (unsigned) ((current_pts - min_pts) + / MPEG_CLOCK_FREQ); + mprint (" at %02u:%02u",cur_sec/60, cur_sec % 60); + } + mprint ("\n"); + mprint ("[%u * %u] [AR: %s] [FR: %s]", + hor_size,vert_size, + aspect_ratio_types[aspect_ratio], + framerates_types[frame_rate]); + // No newline, force the output of progressive info in picture + // info part. + current_progressive_sequence = 2; + + current_hor_size=hor_size; + current_vert_size=vert_size; + current_aspect_ratio=aspect_ratio; + current_frame_rate=frame_rate; + current_fps = framerates_values[current_frame_rate]; + activity_video_info (hor_size,vert_size, + aspect_ratio_types[aspect_ratio], + framerates_types[frame_rate]); + } + else + { + dbg_print(CCX_DMT_VERBOSE, "\nInvalid sequence header:\n"); + dbg_print(CCX_DMT_VERBOSE, "V: %u H: %u FR: %u AS: %u\n", + vert_size, hor_size, frame_rate, aspect_ratio); + esstream->error = 1; + return 0; + } + } + + // Read complete + return 1; +} + + +// 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_ext(struct bitstream *esstream) +{ + dbg_print(CCX_DMT_VERBOSE, "Sequence extension\n"); + + if (esstream->error || esstream->bitsleft <= 0) + return 0; + + // Syntax check + if (next_start_code(esstream) != 0xB5) + { + dbg_print(CCX_DMT_VERBOSE, "sequence_ext: syntax problem.\n"); + return 0; + } + + read_u32(esstream); // Advance + + // Read extension_start_code_identifier + unsigned extension_id = (unsigned) read_bits(esstream, 4); + if (extension_id != 0x1) // Sequence Extension ID + { + if (esstream->bitsleft >= 0) // When bits left, this is wrong + esstream->error = 1; + + if (esstream->error) + dbg_print(CCX_DMT_VERBOSE, "sequence_ext: syntax problem.\n"); + return 0; + } + + // Discard some information + skip_bits(esstream, 8); + unsigned progressive_sequence = (unsigned) read_bits(esstream,1); + + if (progressive_sequence!=current_progressive_sequence) + { + current_progressive_sequence = progressive_sequence; + mprint(" [progressive: %s]\n\n", + (progressive_sequence ? "yes" : "no")); + } + + skip_bits(esstream, 2+2+2+12+1+8+1+2+5); + + if (esstream->bitsleft < 0) + return 0; + + // Read complete + return 1; +} + + +// Return TRUE if all was read. FALSE if a problem occured: +// 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) +{ + 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!"); + + // 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); + //extension_and_user_data(esstream); + + if (esstream->error) + return 0; + + if (esstream->bitsleft < 0) + { + init_bitstream(esstream, gop_info_start, esstream->end); + return 0; + } + + dbg_print(CCX_DMT_VERBOSE, "Read GOP Info - processed\n\n"); + + return 1; +} + + +// 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) +{ + dbg_print(CCX_DMT_VERBOSE, "GOP header\n"); + + if (esstream->error || esstream->bitsleft <= 0) + return 0; + + // We only get here after seeing that start code + if (read_u32(esstream) != 0xB8010000) // LSB first (0x000001B8) + fatal(EXIT_BUG_BUG, "Impossible!"); + + unsigned drop_frame_flag = (unsigned) read_bits(esstream,1); + struct gop_time_code gtc; + gtc.time_code_hours = (int) read_bits(esstream,5); + gtc.time_code_minutes = (int) read_bits(esstream,6); + skip_bits(esstream,1); // Marker bit + gtc.time_code_seconds = (int) read_bits(esstream,6); + gtc.time_code_pictures = (int) read_bits(esstream,6); + gtc.inited = 1; + calculate_ms_gop_time(>c); + + if (esstream->bitsleft < 0) + return 0; + + if (gop_accepted(>c)) + { + // Do GOP padding during GOP header. The previous GOP and all + // included captions are written. Use the current GOP time to + // do the padding. + + // Flush buffered cc blocks before doing the housekeeping + if (has_ccdata_buffered) + { + process_hdcc(); + } + + // Last GOPs pulldown frames + if ((current_pulldownfields>0) != (pulldownfields>0)) + { + current_pulldownfields = pulldownfields; + dbg_print(CCX_DMT_VERBOSE, "Pulldown: %s", (pulldownfields ? "on" : "off")); + if (pulldownfields) + dbg_print(CCX_DMT_VERBOSE, " - %u fields in last GOP", pulldownfields); + dbg_print(CCX_DMT_VERBOSE, "\n"); + } + pulldownfields = 0; + + // Report synchronization jumps between GOPs. Warn if there + // 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) + || + (gtc.ms - gop_time.ms // or 20% shorter + < 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 + *1000.0/current_fps)), + frames_since_last_gop); + mprint(" != (new) %s\n", + print_mstime(gtc.ms)); + } + + if (first_gop_time.inited == 0) + { + first_gop_time = gtc; + + // It needs to be "+1" because the frame count starts at 0 and we + // need the length of all frames. + if ( total_frames_count == 0 ) + { // If this is the first frame there cannot be an offset + fts_fc_offset = 0; + // first_gop_time.ms stays unchanged + } + else + { + fts_fc_offset = LLONG((total_frames_count+1) + *1000.0/current_fps); + // Compensate for those written before + first_gop_time.ms -= fts_fc_offset; + } + + dbg_print(CCX_DMT_TIME, "\nFirst GOP time: %02u:%02u:%02u:%03u %+lldms\n", + gtc.time_code_hours, + gtc.time_code_minutes, gtc.time_code_seconds, + unsigned(1000.0*gtc.time_code_pictures/current_fps), + fts_fc_offset); + } + + gop_time = gtc; + + frames_since_last_gop=0; + // Indicate that we read a gop header (since last frame number 0) + saw_gop_header = 1; + + // If we use GOP timing, reconstruct the PTS from the GOP + if (ccx_options.use_gop_as_pts==1) + { + current_pts = gtc.ms*(MPEG_CLOCK_FREQ/1000); + if (pts_set==0) + pts_set=1; + current_tref = 0; + frames_since_ref_time = 0; + set_fts(); + fts_at_gop_start = get_fts_max(); + } + else + { + // FIXME: Wrong when PTS are not increasing but are identical + // troughout the GOP and then jump to the next time for the + // next GOP. + // This effect will also lead to captions being one GOP early + // for DVD captions. + fts_at_gop_start = get_fts_max() + LLONG(1000.0/current_fps); + } + + if (ccx_options.debug_mask & CCX_DMT_TIME) + { + dbg_print(CCX_DMT_TIME, "\nNew GOP:\n"); + dbg_print(CCX_DMT_TIME, "\nDrop frame flag: %u:\n", drop_frame_flag); + print_debug_timing(); + } + } + + return 1; +} + + +// Return TRUE if all was read. FALSE if a problem occured: +// 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) +{ + 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!"); + + // 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 *pic_info_start = esstream->pos; + + pic_header(esstream); + pic_coding_ext(esstream); + + if (esstream->error) + return 0; + + if (esstream->bitsleft < 0) + { + init_bitstream(esstream, pic_info_start, esstream->end); + return 0; + } + + // Analyse/use the picture information + static int maxtref; // Use to remember the temporal reference number + + // 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)) +// { + // 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(); + } + anchor_hdcc(temporal_reference); +// } + } + + current_tref = temporal_reference; + current_picture_coding_type = picture_coding_type; + + // We mostly use PTS, but when the GOP mode is enabled do not set + // the FTS time here. + if (ccx_options.use_gop_as_pts!=1) + { + set_fts(); // Initialize fts + } + + dbg_print(CCX_DMT_VIDES, "PTS: %s (%8u) - tref: %2d - %s since tref0/GOP: %2u/%2u", + print_mstime(current_pts/(MPEG_CLOCK_FREQ/1000)), + unsigned(current_pts), temporal_reference, + pict_types[picture_coding_type], + unsigned(frames_since_ref_time), + unsigned(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())); + + // Set min_pts/sync_pts according to the current time stamp. + // Use fts_at_gop_start as reference when a GOP header was seen + // since the last frame 0. If not this is most probably a + // TS without GOP headers but with USER DATA after each picture + // header. Use the current FTS values as reference. + // Note: If a GOP header was present the reference time is from + // the beginning of the GOP, otherwise it is now. + if(temporal_reference == 0) + { + last_gop_length = maxtref + 1; + maxtref = temporal_reference; + + // frames_since_ref_time is used in set_fts() + + if( 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? + } + else + { + // No GOP header, use the current values + fts_at_gop_start=get_fts(); + frames_since_ref_time = 0; + } + + if (ccx_options.debug_mask & CCX_DMT_TIME) + { + dbg_print(CCX_DMT_TIME, "\nNew temporal reference:\n"); + print_debug_timing(); + } + + saw_gop_header = 0; // Reset the value + } + + if ( !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; + } + + // Set maxtref + if( temporal_reference > maxtref ) { + maxtref = temporal_reference; + if (maxtref+1 > max_gop_length) + max_gop_length = maxtref+1; + } + + unsigned extraframe = 0; + if (repeat_first_field) + { + pulldownfields++; + total_pulldownfields++; + if ( current_progressive_sequence || !(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); + } + + total_pulldownframes += extraframe; + total_frames_count += 1+extraframe; + frames_since_last_gop += 1+extraframe; + frames_since_ref_time += 1+extraframe; + + dbg_print(CCX_DMT_VERBOSE, "Read PIC Info - processed\n\n"); + + return 1; +} + + +// 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 pic_header(struct bitstream *esstream) +{ + dbg_print(CCX_DMT_VERBOSE, "PIC header\n"); + + if (esstream->error || esstream->bitsleft <= 0) + return 0; + + // We only get here after seeing that start code + if (read_u32(esstream) != 0x00010000) // LSB first (0x00000100) + fatal(EXIT_BUG_BUG, "Impossible!"); + + temporal_reference = (int) read_bits(esstream,10); + picture_coding_type = (ccx_frame_type) read_bits(esstream,3); + + // Discard vbv_delay + skip_bits(esstream, 16); + + // Discard some information + if (picture_coding_type == 2 || picture_coding_type == 3) + skip_bits(esstream, 4); + if (picture_coding_type == 3) + skip_bits(esstream, 4); + + // extra_information + while( read_bits(esstream,1) == 1 ) + { + skip_bits(esstream, 8); + } + + if (esstream->bitsleft < 0) + return 0; + + if ( !(picture_coding_type==CCX_FRAME_TYPE_I_FRAME + || picture_coding_type==CCX_FRAME_TYPE_P_FRAME + || picture_coding_type==CCX_FRAME_TYPE_B_FRAME)) + { + if (esstream->bitsleft >= 0) // When bits left, this is wrong + esstream->error = 1; + + if (esstream->error) + dbg_print(CCX_DMT_VERBOSE, "pic_header: syntax problem.\n"); + return 0; + } + + return 1; +} + + +// 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 pic_coding_ext(struct bitstream *esstream) +{ + dbg_print(CCX_DMT_VERBOSE, "Picture coding extension %lld\n", esstream->bitsleft); + + if (esstream->error || esstream->bitsleft <= 0) + return 0; + + // Syntax check + if (next_start_code(esstream) != 0xB5) + { + dbg_print(CCX_DMT_VERBOSE, "pic_coding_ext: syntax problem.\n"); + return 0; + } + + read_u32(esstream); // Advance + + // Read extension_start_code_identifier + unsigned extension_id = (unsigned int) read_bits(esstream, 4); + if (extension_id != 0x8) // Picture Coding Extension ID + { + if (esstream->bitsleft >= 0) // When bits left, this is wrong + esstream->error = 1; + + if (esstream->error) + dbg_print(CCX_DMT_VERBOSE, "pic_coding_ext: syntax problem.\n"); + return 0; + } + + // Discard some information + skip_bits(esstream, 4*4+2); + picture_structure = (unsigned int) read_bits(esstream, 2); + top_field_first = (unsigned int) read_bits(esstream, 1); + skip_bits(esstream, 5*1); + repeat_first_field = (unsigned int) read_bits(esstream, 1); + skip_bits(esstream, 1); // chroma + progressive_frame = (unsigned int) read_bits(esstream, 1); + unsigned composite_display = (unsigned int) read_bits(esstream,1); + if (composite_display) + skip_bits(esstream, 1+3+1+7+8); + + if (esstream->bitsleft < 0) + return 0; + + dbg_print(CCX_DMT_VERBOSE, "Picture coding extension - processed\n"); + + // Read complete + return 1; +} + + +// Return TRUE if all was read. FALSE if a problem occured: +// 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) +{ + dbg_print(CCX_DMT_VERBOSE, "Read Extension and User Info\n"); + + // We only get here after seeing that start code + 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!"); + + // 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 (esstream->error) + dbg_print(CCX_DMT_VERBOSE, "\nWarning: Retry while reading Extension and User Data!\n"); + else + dbg_print(CCX_DMT_VERBOSE, "\nBitstream problem while reading Extension and User Data!\n"); + + return 0; + } + + dbg_print(CCX_DMT_VERBOSE, "Read Extension and User Info - processed\n\n"); + + return 1; +} + + +// 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) +{ + dbg_print(CCX_DMT_VERBOSE, "Extension and user data(%d)\n", udtype); + + if (esstream->error || esstream->bitsleft <= 0) + return 0; + + // Remember where to continue + unsigned char *eau_start = esstream->pos; + + uint8_t startcode; + + do + { + startcode = next_start_code(esstream); + + if ( startcode == 0xB2 || startcode == 0xB5 ) + { + read_u32(esstream); // Advance bitstream + unsigned char *dstart = esstream->pos; + + // 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. + uint8_t nextstartcode = search_start_code(esstream); + if (nextstartcode == 0xBA) + { + mprint("\nFound PACK header in ES data. Probably wrong stream mode!\n"); + esstream->error = 1; + return 0; + } + + if (esstream->error) + { + dbg_print(CCX_DMT_VERBOSE, "Extension and user data - syntax problem\n"); + return 0; + } + + if (esstream->bitsleft < 0) + { + dbg_print(CCX_DMT_VERBOSE, "Extension and user data - inclomplete\n"); + // Restore to where we need to continue + init_bitstream(esstream, eau_start, esstream->end); + esstream->bitsleft = -1; // Redundant + return 0; + } + + if (startcode == 0xB2) + { + struct bitstream ustream; + init_bitstream(&ustream, dstart, esstream->pos); + user_data(&ustream, udtype); + } + else + { + dbg_print(CCX_DMT_VERBOSE, "Skip %d bytes extension data.\n", + esstream->pos - dstart); + } + // If we get here esstream points to the end of a block + // of extension or user data. Should we run out of data in + // this loop this is where we want to restart after getting more. + eau_start = esstream->pos; + } + } + while(startcode == 0xB2 || startcode == 0xB5); + + if (esstream->error) + { + dbg_print(CCX_DMT_VERBOSE, "Extension and user data - syntax problem\n"); + return 0; + } + if (esstream->bitsleft < 0) + { + dbg_print(CCX_DMT_VERBOSE, "Extension and user data - inclomplete\n"); + // Restore to where we need to continue + init_bitstream(esstream, eau_start, esstream->end); + esstream->bitsleft = -1; // Redundant + return 0; + } + + dbg_print(CCX_DMT_VERBOSE, "Extension and user data - processed\n"); + + // Read complete + return 1; +} + + +// Return TRUE if all was read. FALSE if a problem occured: +// 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_data(struct bitstream *esstream) +{ + dbg_print(CCX_DMT_VERBOSE, "Read PIC Data\n"); + + uint8_t startcode = next_start_code(esstream); + + // Possibly the last call to this function ended with the last + // bit of the slice? I.e. in_pic_data is still true, but we are + // seeing the next start code. + + // We only get here after seeing that start code + if (startcode < 0x01 || startcode > 0xAF) + { + dbg_print(CCX_DMT_VERBOSE, "Read Pic Data - processed0\n"); + + return 1; + } + + // If we get here esstream points to the start of a slice_start_code + // should we run out of data in esstream this is where we want to restart + // after getting more. + unsigned char *slice_start = esstream->pos; + + do + { + startcode = next_start_code(esstream); + // Syntax check + if (startcode == 0xB4) + { + if (esstream->bitsleft < 0) + init_bitstream(esstream, slice_start, esstream->end); + + if ( esstream->error ) + dbg_print(CCX_DMT_VERBOSE, "read_pic_data: syntax problem.\n"); + else + dbg_print(CCX_DMT_VERBOSE, "read_pic_data: reached end of bitstream.\n"); + + return 0; + } + + slice_start = esstream->pos; // No need to come back + + if ( startcode >= 0x01 && startcode <= 0xAF ) + { + read_u32(esstream); // Advance bitstream + search_start_code(esstream); // Skip this slice + } + } + while(startcode >= 0x01 && startcode <= 0xAF); + + if (esstream->bitsleft < 0) + { + init_bitstream(esstream, slice_start, esstream->end); + return 0; + } + + dbg_print(CCX_DMT_VERBOSE, "Read Pic Data - processed\n"); + + return 1; +} diff --git a/src/es_userdata.cpp b/src/es_userdata.cpp new file mode 100644 index 00000000..659f100a --- /dev/null +++ b/src/es_userdata.cpp @@ -0,0 +1,479 @@ +#include "ccextractor.h" + + +// Parse the user data for captions. The udtype variable denotes +// to which type of data it belongs: +// 0 .. sequence header +// 1 .. GOP header +// 2 .. picture header +// 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) +{ + dbg_print(CCX_DMT_VERBOSE, "user_data(%d)\n", udtype); + + // Shall not happen + if (ustream->error || ustream->bitsleft <= 0) + { + // ustream->error=1; + return 0; // Actually discarded on call. + // CFS: Seen in a Wobble edited file. + // fatal(EXIT_BUG_BUG, "user_data: Impossible!"); + } + + // Do something + 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!"); + } + + // DVD CC header, see + // + if ( !memcmp(ud_header,"\x43\x43", 2 ) ) + { + stat_dvdccheaders++; + + // Probably unneeded, but keep looking for extra caption blocks + int maybeextracb = 1; + + read_bytes(ustream, 4); // "43 43 01 F8" + + unsigned char pattern_flag = (unsigned char) read_bits(ustream,1); + read_bits(ustream,1); + int capcount=(int) read_bits(ustream,5); + int truncate_flag = (int) read_bits(ustream,1); // truncate_flag - one CB extra + + int field1packet = 0; // expect Field 1 first + if (pattern_flag == 0x00) + field1packet=1; // expect Field 1 second + + dbg_print(CCX_DMT_VERBOSE, "Reading %d%s DVD CC segments\n", + capcount, (truncate_flag?"+1":"")); + + capcount += truncate_flag; + + // This data comes before the first frame header, so + // in order to get the correct timing we need to set the + // current time to one frame after the maximum time of the + // last GOP. Only usefull when there are frames before + // the GOP. + if (fts_max > 0) + fts_now = fts_max + LLONG(1000.0/current_fps); + + int rcbcount = 0; + for (int i=0; ibitsleft < 0) + fatal(EXIT_BUG_BUG, "Oops!"); + + // Field_number is either + // 0 .. forbiden + // 1 .. field 1 (odd) + // 2 .. field 2 (even) + // 3 .. repeated, from repeat_first_field, effectively field 1 + if (field_number < 1) + { + // 0 is invalid + cc_data[j*3]=0x00; // Set to invalid + cc_data[j*3+1]=0x00; + cc_data[j*3+2]=0x00; + } + else + { + // Treat field_number 3 as 1 + field_number = (field_number - 1) & 0x01; + // top_field_first also affects to which field the caption + // belongs. + if(!top_field_first) + field_number ^= 0x01; + cc_data[j*3]=0x04|(field_number); + cc_data[j*3+1]=reverse8(cc_data1); + cc_data[j*3+2]=reverse8(cc_data2); + } + } + cc_data[cc_count*3]=0xFF; + store_hdcc(cc_data, cc_count, current_tref, fts_now); + + dbg_print(CCX_DMT_VERBOSE, "Reading SCTE 20 CC blocks - done\n"); + } + // reserved - unspecified + } + // ReplayTV 4000/5000 caption header - parsing information + // derived from CCExtract.bdl + else if ( (ud_header[0] == 0xbb //ReplayTV 4000 + || ud_header[0] == 0x99) //ReplayTV 5000 + && ud_header[1] == 0x02 ) + { + unsigned char data[3]; + if (ud_header[0]==0xbb) + stat_replay4000headers++; + else + 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); + 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); + } + // HDTV - see A/53 Part 4 (Video) + else if ( !memcmp(ud_header,"\x47\x41\x39\x34", 4 ) ) + { + stat_hdtv++; + + read_bytes(ustream, 4); // "47 41 39 34" + + unsigned char type_code = read_u8(ustream); + if (type_code==0x03) // CC data. + { + skip_bits(ustream,1); // reserved + unsigned char process_cc_data = (unsigned char) read_bits(ustream,1); + skip_bits(ustream,1); // additional_data - unused + unsigned char cc_count = (unsigned char) read_bits(ustream,5); + read_bytes(ustream, 1); // "FF" + if (process_cc_data) + { + dbg_print(CCX_DMT_VERBOSE, "Reading %d HDTV CC blocks\n", cc_count); + + 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!"); + + // Check for proper marker - This read makes sure that + // cc_count*3+1 bytes are read and available in cc_data. + if (read_u8(ustream)!=0xFF) + proceed=0; + + if (!proceed) + { + dbg_print(CCX_DMT_VERBOSE, "\rThe following payload is not properly terminated.\n"); + dump (CCX_DMT_VERBOSE, cc_data, cc_count*3+1, 0, 0); + } + dbg_print(CCX_DMT_VERBOSE, "Reading %d HD CC blocks\n", cc_count); + + // B-frames might be (temporal) before or after the anchor + // frame they belong to. Store the buffer until the next anchor + // frame occurs. The buffer will be flushed (sorted) in the + // picture header (or GOP) section when the next anchor occurs. + // Please note we store the current value of the global + // fts_now variable (and not get_fts()) as we are going to + // re-create the timeline in process_hdcc() (Slightly ugly). + store_hdcc(cc_data, cc_count, current_tref, fts_now); + + dbg_print(CCX_DMT_VERBOSE, "Reading HDTV blocks - done\n"); + } + } + // reserved - additional_cc_data + } + // DVB closed caption header for Dish Network (Field 1 only) */ + else if ( !memcmp(ud_header,"\x05\x02", 2 ) ) + { + // Like HDTV (above) Dish Network captions can be stored at each + // frame, but maximal two caption blocks per frame and only one + // field is stored. + // To process this with the HDTV framework we create a "HDTV" caption + // format compatible array. Two times 3 bytes plus one for the 0xFF + // marker at the end. Pre-init to field 1 and set the 0xFF marker. + static unsigned char dishdata[7] = {0x04, 0, 0, 0x04, 0, 0, 0xFF}; + int cc_count; + + dbg_print(CCX_DMT_VERBOSE, "Reading Dish Network user data\n"); + + stat_dishheaders++; + + read_bytes(ustream, 2); // "05 02" + + // The next bytes are like this: + // header[2] : ID: 0x04 (MPEG?), 0x03 (H264?) + // header[3-4]: Two byte counter (counting (sub-)GOPs?) + // header[5-6]: Two bytes, maybe checksum? + // header[7]: Pattern type + // on B-frame: 0x02, 0x04 + // on I-/P-frame: 0x05 + unsigned char id = read_u8(ustream); + unsigned dishcount = read_u16(ustream); + unsigned something = read_u16(ustream); + unsigned char type = read_u8(ustream); + dbg_print(CCX_DMT_PARSE, "DN ID: %02X Count: %5u Unknown: %04X Pattern: %X", + id, dishcount, something, type); + + unsigned char hi; + + // The following block needs 4 to 6 bytes starting from the + // current position + unsigned char *dcd = ustream->pos; // dish caption data + switch (type) + { + case 0x02: + // Two byte caption - always on B-frame + // The following 4 bytes are: + // 0 : 0x09 + // 1-2: caption block + // 3 : REPEAT - 02: two bytes + // - 04: four bytes (repeat first two) + dbg_print(CCX_DMT_PARSE, "\n02 %02X %02X:%02X - R:%02X :", + dcd[0], dcd[1], dcd[2], dcd[3]); + + cc_count = 1; + dishdata[1]=dcd[1]; + dishdata[2]=dcd[2]; + + dbg_print(CCX_DMT_PARSE, "%s", debug_608toASC( dishdata, 0) ); + + type=dcd[3]; // repeater (0x02 or 0x04) + hi = dishdata[1] & 0x7f; // Get only the 7 low bits + if (type==0x04 && hi<32) // repeat (only for non-character pairs) + { + cc_count = 2; + dishdata[3]=0x04; // Field 1 + dishdata[4]=dishdata[1]; + dishdata[5]=dishdata[2]; + + dbg_print(CCX_DMT_PARSE, "%s:\n", debug_608toASC( dishdata+3, 0) ); + } + else + { + dbg_print(CCX_DMT_PARSE, ":\n"); + } + + dishdata[cc_count*3] = 0xFF; // Set end marker + + store_hdcc(dishdata, cc_count, current_tref, fts_now); + + // Ignore 3 (0x0A, followed by two unknown) bytes. + break; + case 0x04: + // Four byte caption - always on B-frame + // The following 5 bytes are: + // 0 : 0x09 + // 1-2: caption block + // 3-4: caption block + dbg_print(CCX_DMT_PARSE, "\n04 %02X %02X:%02X:%02X:%02X :", + dcd[0], dcd[1], dcd[2], dcd[3], dcd[4]); + + cc_count = 2; + dishdata[1]=dcd[1]; + dishdata[2]=dcd[2]; + + dishdata[3]=0x04; // Field 1 + dishdata[4]=dcd[3]; + dishdata[5]=dcd[4]; + dishdata[6] = 0xFF; // Set end marker + + dbg_print(CCX_DMT_PARSE, "%s", debug_608toASC( dishdata, 0) ); + dbg_print(CCX_DMT_PARSE, "%s:\n", debug_608toASC( dishdata+3, 0) ); + + store_hdcc(dishdata, cc_count, current_tref, fts_now); + + // Ignore 4 (0x020A, followed by two unknown) bytes. + break; + case 0x05: + // Buffered caption - always on I-/P-frame + // The following six bytes are: + // 0 : 0x04 + // - the following are from previous 0x05 caption header - + // 1 : prev dcd[2] + // 2-3: prev dcd[3-4] + // 4-5: prev dcd[5-6] + dbg_print(CCX_DMT_PARSE, " - %02X pch: %02X %5u %02X:%02X\n", + dcd[0], dcd[1], + (unsigned)dcd[2]*256+dcd[3], + dcd[4], dcd[5]); + dcd+=6; // Skip these 6 bytes + + // Now one of the "regular" 0x02 or 0x04 captions follows + dbg_print(CCX_DMT_PARSE, "%02X %02X %02X:%02X", + dcd[0], dcd[1], dcd[2], dcd[3]); + + type=dcd[0]; // Number of caption bytes (0x02 or 0x04) + + cc_count = 1; + dishdata[1]=dcd[2]; + dishdata[2]=dcd[3]; + + dcd+=4; // Skip the first 4 bytes. + if (type==0x02) + { + type=dcd[0]; // repeater (0x02 or 0x04) + dcd++; // Skip the repeater byte. + + dbg_print(CCX_DMT_PARSE, " - R:%02X :%s", type, debug_608toASC( dishdata, 0) ); + + hi = dishdata[1] & 0x7f; // Get only the 7 low bits + if (type==0x04 && hi<32) + { + cc_count = 2; + dishdata[3]=0x04; // Field 1 + dishdata[4]=dishdata[1]; + dishdata[5]=dishdata[2]; + dbg_print(CCX_DMT_PARSE, "%s:\n", debug_608toASC( dishdata+3, 0) ); + } + else + { + dbg_print(CCX_DMT_PARSE, ":\n"); + } + dishdata[cc_count*3] = 0xFF; // Set end marker + } + else + { + dbg_print(CCX_DMT_PARSE, ":%02X:%02X ", + dcd[0], dcd[1]); + cc_count = 2; + dishdata[3]=0x04; // Field 1 + dishdata[4]=dcd[0]; + dishdata[5]=dcd[1]; + dishdata[6] = 0xFF; // Set end marker + + 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); + + // Ignore 3 (0x0A, followed by 2 unknown) bytes. + break; + default: + // printf ("Unknown?\n"); + break; + } // switch + + dbg_print(CCX_DMT_VERBOSE, "Reading Dish Network user data - done\n"); + } + // CEA 608 / aka "Divicom standard", see: + // http://www.pixeltools.com/tech_tip_closed_captioning.html + else if ( !memcmp(ud_header,"\x02\x09", 2 ) ) + { + // Either a documentation or more examples are needed. + stat_divicom++; + + unsigned char data[3]; + + read_bytes(ustream, 2); // "02 09" + read_bytes(ustream, 2); // "80 80" ??? + read_bytes(ustream, 2); // "02 0A" ??? + data[0]=0x04; // Field 1 + data[1]=read_u8(ustream); + data[2]=read_u8(ustream); + do_cb(data); + // This is probably incomplete! + } + else + { + // Some other user data + // 06 02 ... Seems to be DirectTV + dbg_print(CCX_DMT_VERBOSE, "Unrecognized user data:\n"); + int udatalen = ustream->end - ustream->pos; + dump (CCX_DMT_VERBOSE, ustream->pos, (udatalen > 128 ? 128 : udatalen),0 ,0); + } + + dbg_print(CCX_DMT_VERBOSE, "User data - processed\n"); + + // Read complete + return 1; +} diff --git a/src/file_functions.cpp b/src/file_functions.cpp new file mode 100644 index 00000000..ba59e1c1 --- /dev/null +++ b/src/file_functions.cpp @@ -0,0 +1,488 @@ +#include "ccextractor.h" + +long FILEBUFFERSIZE = 1024*1024*16; // 16 Mbytes no less. Minimize number of real read calls() +LLONG buffered_read_opt_file (unsigned char *buffer, unsigned int bytes); + +#ifdef _WIN32 + WSADATA wsaData = {0}; + int iResult = 0; +#endif + +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; +} + +LLONG gettotalfilessize (void) // -1 if one or more files failed to open +{ + LLONG ts=0; + int h; + for (int i=0;i0 && ((past+bytesinbuffer) < inputsize) && !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); + } + if (ccx_options.binary_concat) + { + total_past+=inputsize; + past=0; // Reset always or at the end we'll have double the size + } + } + for (;;) + { + current_file++; + if (current_file>=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]); +#ifdef _WIN32 + infd=OPEN (inputfile[current_file],O_RDONLY | O_BINARY); +#else + infd=OPEN (inputfile[current_file],O_RDONLY); +#endif + if (infd == -1) + mprint ("\rWarning: Unable to open input file [%s]\n", inputfile[current_file]); + else + { + activity_input_file_open (inputfile[current_file]); + if (!ccx_options.live_stream) + { + inputsize = getfilesize (infd); + if (!ccx_options.binary_concat) + total_inputsize=inputsize; + } + return 1; // Succeeded + } + } + return 0; +} + +void position_sanity_check () +{ +#ifdef SANITY_CHECK + if (in!=-1) + { + LLONG realpos=LSEEK (in,0,SEEK_CUR); + if (realpos!=past-filebuffer_pos+bytesinbuffer) + { + fatal (EXIT_BUG_BUG, "Position desync, THIS IS A BUG. Real pos =%lld, past=%lld.\n",realpos,past); + } + } +#endif +} + + +int init_file_buffer(void) +{ + filebuffer_start=0; + filebuffer_pos=0; + if (filebuffer==NULL) + { + filebuffer=(unsigned char *) malloc (FILEBUFFERSIZE); + bytesinbuffer=0; + } + if (filebuffer==NULL) + { + fatal (EXIT_NOT_ENOUGH_MEMORY, "Not enough memory\n"); + } + return 0; +} + +void buffered_seek (int offset) +{ + position_sanity_check(); + if (offset<0) + { + filebuffer_pos+=offset; + if (filebuffer_pos<0) + { + // We got into the start buffer (hopefully) + if (startbytes_pos+filebuffer_pos < 0) + { + fatal (EXIT_BUG_BUG, "PANIC: Attempt to seek before buffer start, this is a bug!"); + } + startbytes_pos+=filebuffer_pos; + filebuffer_pos=0; + } + } + else + { + buffered_read_opt (NULL, offset); + position_sanity_check(); + } +} + +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 + // real time input from hardware. + sleep_secs (1); + ccx_options.live_stream=0; + return; + } + + if (ccx_options.live_stream==-1) // Just sleep, no timeout to check + { + sleep_secs (1); + return; + } + if (time(NULL)>start+ccx_options.live_stream) // More than live_stream seconds elapsed. No more live + ccx_options.live_stream=0; + else + sleep_secs(1); +} + +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 + // unnecessary, but we do it in case we intentionally messed with the + // buffer + memcpy (filebuffer, buffer, bytes); + filebuffer_pos=0; + return; + } + if (filebuffer_pos>0) // Discard old bytes, because we may need the space + { + // Non optimal since data is moved later again but we don't care since + // we're never here in ccextractor. + memmove (filebuffer,filebuffer+filebuffer_pos,bytesinbuffer-filebuffer_pos); + bytesinbuffer-=filebuffer_pos; + bytesinbuffer=0; + filebuffer_pos=0; + } + + if (bytesinbuffer + bytes > FILEBUFFERSIZE) + fatal (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 copied=0; + position_sanity_check(); + time_t seconds=0; + if (ccx_options.live_stream>0) + time (&seconds); + if (ccx_options.buffer_input || filebuffer_pos 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); + else + { + socklen_t len = sizeof(cliaddr); + i = recvfrom(infd,(char *) filebuffer+keep,FILEBUFFERSIZE-keep,0,(struct sockaddr *)&cliaddr,&len); + } + 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))) + eof=1; + } + filebuffer_pos=keep; + bytesinbuffer=(int) i+keep; + ready=i; + } + int copy = (int) (ready>=bytes ? bytes:ready); + if (copy) + { + if (buffer!=NULL) + { + memcpy (buffer, filebuffer+filebuffer_pos, copy); + buffer+=copy; + } + filebuffer_pos+=copy; + bytes-=copy; + copied+=copy; + } + } + return copied; + } + else // Read without buffering + { + + 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)))) + { + if( i == -1) + fatal (EXIT_READ_ERROR, "Error reading input file!\n"); + else if (i==0) + sleepandchecktimeout (seconds); + else + { + copied+=i; + bytes-=i; + buffer+=i; + } + } + return copied; + } + // return fread(buffer,1,bytes,in); + //return FSEEK (in,bytes,SEEK_CUR); + while (bytes!=0 && infd!=-1) + { + LLONG op =LSEEK (infd,0,SEEK_CUR); // Get current pos + if (op+bytes<0) // Would mean moving beyond start of file: Not supported + return 0; + LLONG np =LSEEK (infd,bytes,SEEK_CUR); // Pos after moving + copied=copied+(np-op); + bytes=bytes-(unsigned int) copied; + if (copied==0) + { + if (ccx_options.live_stream) + sleepandchecktimeout (seconds); + else + { + if (ccx_options.binary_concat) + switch_to_next_file(0); + else + break; + } + } + } + return copied; + } +} diff --git a/src/general_loop.cpp b/src/general_loop.cpp new file mode 100644 index 00000000..bf5811ea --- /dev/null +++ b/src/general_loop.cpp @@ -0,0 +1,817 @@ +#include "ccextractor.h" +#ifdef _WIN32 +#include +#else +#include +#endif + +#include "708.h" + +// IMPORTED TRASH INFO, REMOVE +extern long num_nal_unit_type_7; +extern long num_vcl_hrd; +extern long num_nal_hrd; +extern long num_jump_in_frames; +extern long num_unexpected_sei_length; + +/* General video information */ + unsigned current_hor_size = 0; +unsigned current_vert_size = 0; +unsigned current_aspect_ratio = 0; +unsigned current_frame_rate = 4; // Assume standard fps +double current_fps = framerates_values[current_frame_rate]; +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 +int ccx_bufferdatatype = CCX_PES; // Can be RAW, PES, H264 or Hauppage + +int current_tref = 0; // Store temporal reference of current frame +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; + +static int non_compliant_DVD = 0; // Found extra captions in DVDs? + +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 + +LLONG process_raw_with_field (void); + + +// Program stream specific data grabber +LLONG ps_getmoredata(void) +{ + int enough = 0; + int payload_read = 0; + + static unsigned vpesnum=0; + + unsigned char nextheader[512]; // Next header in PS + int falsepack=0; + + // Read and return the next video PES payload + do + { + if (BUFSIZE-inbuf<500) + { + mprint("Less than 500 left\n"); + enough=1; // Stop when less than 500 bytes are left in buffer + } + else + { + buffered_read(nextheader,6); + past+=result; + if (result!=6) + { + // Consider this the end of the show. + end_of_file=1; + break; + } + + // 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) ) + { + if( !strangeheader ) + { + mprint ("\nNot a recognized header. Searching for next header.\n"); + dump (CCX_DMT_GENERIC_NOTICES, nextheader,6,0,0); + // Only print the message once per loop / unrecognized header + strangeheader = 1; + } + + unsigned char *newheader; + // The amount of bytes read into nextheader by the buffered_read above + int hlen = 6; + // Find first 0x00 + // If there is a 00 in the first element we need to advance + // one step as clearly bytes 1,2,3 are wrong + newheader = (unsigned char *) memchr (nextheader+1, 0, hlen-1); + if (newheader != NULL ) + { + int atpos = newheader-nextheader; + + memmove (nextheader,newheader,(size_t)(hlen-atpos)); + buffered_read(nextheader+(hlen-atpos),atpos); + past+=result; + if (result!=atpos) + { + end_of_file=1; + break; + } + } + else + { + buffered_read(nextheader,hlen); + past+=result; + if (result!=hlen) + { + end_of_file=1; + break; + } + } + } + if (end_of_file) + { + // No more headers + break; + } + // Found 00-00-01 in nextheader, assume a regular header + strangeheader=0; + + // PACK header + if ( nextheader[3]==0xBA) + { + dbg_print(CCX_DMT_VERBOSE, "PACK header\n"); + buffered_read(nextheader+6,8); + past+=result; + if (result!=8) + { + // Consider this the end of the show. + end_of_file=1; + break; + } + + if ( (nextheader[4]&0xC4)!=0x44 || !(nextheader[6]&0x04) + || !(nextheader[8]&0x04) || !(nextheader[9]&0x01) + || (nextheader[12]&0x03)!=0x03 ) + { + // broken pack header + falsepack=1; + } + // We don't need SCR/SCR_ext + int stufflen=nextheader[13]&0x07; + + if (falsepack) + { + mprint ("Warning: Defective Pack header\n"); + } + + // If not defect, load stuffing + buffered_skip ((int) stufflen); + past+=stufflen; + // fake a result value as something was skipped + result=1; + continue; + } + // Some PES stream + else if (nextheader[3]>=0xBB && nextheader[3]<=0xDF) + { + // System header + // nextheader[3]==0xBB + // 0xBD Private 1 + // 0xBE PAdding + // 0xBF Private 2 + // 0xC0-0DF audio + + unsigned headerlen=nextheader[4]<<8 | nextheader[5]; + + dbg_print(CCX_DMT_VERBOSE, "non Video PES (type 0x%2X) - len %u\n", + nextheader[3], headerlen); + + // The 15000 here is quite arbitrary, the longest packages I + // know of are 12302 bytes (Private 1 data in RTL recording). + if ( headerlen > 15000 ) + { + mprint("Suspicious non Video PES (type 0x%2X) - len %u\n", + nextheader[3], headerlen); + mprint("Do not skip over, search for next.\n"); + headerlen = 2; + } + + // Skip over it + buffered_skip ((int) headerlen); + past+=headerlen; + // fake a result value as something was skipped + result=1; + + continue; + } + // Read the next video PES + else if ((nextheader[3]&0xf0)==0xe0) + { + int hlen; // Dummy variable, unused + int peslen = read_video_pes_header(nextheader, &hlen, 0); + if (peslen < 0) + { + end_of_file=1; + break; + } + + vpesnum++; + dbg_print(CCX_DMT_VERBOSE, "PES video packet #%u\n", vpesnum); + + + int want = (int) ((BUFSIZE-inbuf)>peslen ? peslen : (BUFSIZE-inbuf)); + + if (want != peslen) { + fatal(EXIT_BUFFER_FULL, "Oh Oh, PES longer than remaining buffer space\n"); + } + if (want == 0) // Found package with header but without payload + { + continue; + } + + buffered_read (buffer+inbuf,want); + past=past+result; + if (result>0) { + payload_read+=(int) result; + } + inbuf+=result; + + if (result!=want) { // Not complete - EOF + end_of_file=1; + break; + } + enough = 1; // We got one PES + + } else { + // If we are here this is an unknown header type + mprint("Unknown header %02X\n", nextheader[3]); + strangeheader=1; + } + } + } + while (result!=0 && !enough && BUFSIZE!=inbuf); + + dbg_print(CCX_DMT_VERBOSE, "PES data read: %d\n", payload_read); + + return payload_read; +} + + +// Returns number of bytes read, or zero for EOF +LLONG general_getmoredata(void) +{ + int bytesread = 0; + int want; + + do + { + want = (int) (BUFSIZE-inbuf); + buffered_read (buffer+inbuf,want); // This is a macro. + // 'result' HAS the number of bytes read + past=past+result; + inbuf+=result; + bytesread+=(int) result; + } while (result!=0 && result!=want); + return bytesread; +} + +// Hexadecimal dump process +void processhex (char *filename) +{ + size_t max=(size_t) 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"); + unsigned char *bytes=NULL; + unsigned byte_count=0; + int warning_shown=0; + while(fgets(line, max-1, fr) != NULL) + { + char *c1, *c2=NULL; // Positions for first and second colons + int len; + long timing; + if (line[0]==';') // Skip comments + continue; + c1=strchr (line,':'); + if (c1) c2=strchr (c1+1,':'); + if (!c2) // Line doesn't contain what we want + continue; + *c1=0; + *c2=0; + len=atoi (line); + timing=atol (c1+2)*(MPEG_CLOCK_FREQ/1000); + current_pts=timing; + if (pts_set==0) + pts_set=1; + set_fts(); + c2++; +/* + if (strlen (c2)==8) + { + unsigned char high1=c2[1]; + unsigned char low1=c2[2]; + int value1=hex2int (high1,low1); + unsigned char high2=c2[4]; + unsigned char low2=c2[5]; + int value2=hex2int (high2,low2); + buffer[0]=value1; + buffer[1]=value2; + inbuf=2; + process_raw(); + continue; + } + if (strlen (c2)<=(strlen (mpeg_header)+1)) + continue; */ + c2++; // Skip blank + /* + if (strncmp (c2,mpeg_header,strlen (mpeg_header))) // No idea how to deal with this. + { + if (!warning_shown) + { + warning_shown=1; + mprint ("\nWarning: This file contains data I can't process: Please submit .hex for analysis!\n"); + } + continue; + } + // OK, seems like a decent chunk of CCdata. + c2+=strlen (mpeg_header); + */ + byte_count=strlen (c2)/3; + /* + if (atoi (line)!=byte_count+strlen (mpeg_header)/3) // Number of bytes reported don't match actual contents + continue; + */ + if (atoi (line)!=byte_count) // Number of bytes reported don't match actual contents + continue; + if (!byte_count) // Nothing to get from this line except timing info, already done. + continue; + 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;i3 && bytes[0]==0x03 && + bytes[2]==0xff && bytes[byte_count-1]==0xff) + { + ok=1; + for (unsigned i=3; i>1)&0x1F); + unsigned filler=((magic>>6)&1); + unsigned pattern=((magic>>7)&1); + int always_ff=1; + 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; + for (unsigned i=1; i0 + memmove (buffer,buffer+pos,(size_t) (inbuf-pos)); + inbuf-=pos; + } + pos = 0; + + // GET MORE DATA IN BUFFER + LLONG i; + position_sanity_check(); + switch (stream_mode) + { + case CCX_SM_ELEMENTARY_OR_NOT_FOUND: + i = general_getmoredata(); + break; + case CCX_SM_TRANSPORT: + i = ts_getmoredata(); + break; + case CCX_SM_PROGRAM: + i = ps_getmoredata(); + break; + case CCX_SM_ASF: + i = asf_getmoredata(); + break; + case CCX_SM_WTV: + i = wtv_getmoredata(); + break; + default: + fatal(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 (i==0) + { + end_of_file = 1; + memset (buffer+inbuf, 0, (size_t) (BUFSIZE-inbuf)); /* Clear buffer at the end */ + } + + if (inbuf == 0) + { + /* Done: Get outta here */ + break; + } + + LLONG got; // Means 'consumed' from buffer actually + + static LLONG last_pts = 0x01FFFFFFFFLL; + + if (ccx_options.hauppauge_mode) + { + got = process_raw_with_field(); + if (pts_set) + set_fts(); // Try to fix timing from TS data + } + else if (ccx_bufferdatatype == CCX_PES) + { + got = process_m2v (buffer, inbuf); + } + else if (ccx_bufferdatatype == CCX_TELETEXT) + { + // Dispatch to Petr Kutalek 's telxcc. + tlt_process_pes_packet (buffer, (uint16_t) inbuf); + got = inbuf; + } + else if (ccx_bufferdatatype == CCX_PRIVATE_MPEG2_CC) + { + got = inbuf; // Do nothing. Still don't know how to process it + } + else if (ccx_bufferdatatype == CCX_RAW) // Raw two byte 608 data from DVR-MS/ASF + { + // The asf_getmoredata() loop sets current_pts when possible + if (pts_set == 0) + { + mprint("DVR-MS/ASF file without useful time stamps - count blocks.\n"); + // Otherwise rely on counting blocks + current_pts = 12345; // Pick a valid PTS time + pts_set = 1; + } + + if (current_pts != last_pts) + { + // Only initialize the FTS values and reset the cb + // counters when the PTS is different. This happens frequently + // with ASF files. + + if (min_pts==0x01FFFFFFFFLL) + { + // First call + fts_at_gop_start = 0; + } + else + fts_at_gop_start = get_fts(); + + frames_since_ref_time = 0; + set_fts(); + + last_pts = current_pts; + } + + dbg_print(CCX_DMT_VIDES, "PTS: %s (%8u)", + print_mstime(current_pts/(MPEG_CLOCK_FREQ/1000)), + unsigned(current_pts)); + dbg_print(CCX_DMT_VIDES, " FTS: %s\n", print_mstime(get_fts())); + + got = process_raw(); + } + else if (ccx_bufferdatatype == CCX_H264) // H.264 data from TS file + { + got = process_avc(buffer, inbuf); + } + else + fatal(EXIT_BUG_BUG, "Unknown data type!"); + + if (got>inbuf) + { + mprint ("BUG BUG\n"); + } + pos+=got; + + if (ccx_options.live_stream) + { + int cur_sec = (int) (get_fts() / 1000); + int th=cur_sec/10; + if (last_reported_progress!=th) + { + activity_progress (-1,cur_sec/60, cur_sec%60); + last_reported_progress = th; + } + } + else + { + if (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) + { + LLONG t=get_fts(); + if (!t && global_timestamp_inited) + t=global_timestamp-min_global_timestamp; + int cur_sec = (int) (t / 1000); + activity_progress(progress, cur_sec/60, cur_sec%60); + last_reported_progress = progress; + } + } + } + position_sanity_check(); + } + // Flush remaining HD captions + if (has_ccdata_buffered) + process_hdcc(); + + if (total_past!=total_inputsize && ccx_options.binary_concat && !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); + } + mprint ("\nNumber of NAL_type_7: %ld\n",num_nal_unit_type_7); + mprint ("Number of VCL_HRD: %ld\n",num_vcl_hrd); + mprint ("Number of NAL HRD: %ld\n",num_nal_hrd); + mprint ("Number of jump-in-frames: %ld\n",num_jump_in_frames); + mprint ("Number of num_unexpected_sei_length: %ld", num_unexpected_sei_length); +} + +// Raw caption with FTS file process +void rcwt_loop( void ) +{ + // 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"); + + // Generic buffer to hold some data + static unsigned char *parsebuf = (unsigned char*)malloc(1024); + static long parsebufsize = 1024; + + LLONG currfts; + uint16_t cbcount = 0; + + int bread = 0; // Bytes read + + buffered_read(parsebuf,11); + past+=result; + bread+=(int) result; + if (result!=11) + { + mprint("Premature end of file!\n"); + end_of_file=1; + return; + } + + // Expecting RCWT header + if( !memcmp(parsebuf, "\xCC\xCC\xED", 3 ) ) + { + dbg_print(CCX_DMT_PARSE, "\nRCWT header\n"); + 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"); + } + + // Initialize first time. As RCWT files come with the correct FTS the + // initial (minimal) time needs to be set to 0. + current_pts = 0; + pts_set=1; + set_fts(); // Now set the FTS related variables + + // Loop until no more data is found + while(1) + { + // Read the data header + buffered_read(parsebuf,10); + past+=result; + bread+=(int) result; + + if (result!=10) + { + if (result!=0) + mprint("Premature end of file!\n"); + + // We are done + end_of_file=1; + break; + } + currfts = *((LLONG*)(parsebuf)); + cbcount = *((uint16_t*)(parsebuf+8)); + + dbg_print(CCX_DMT_PARSE, "RCWT data header FTS: %s blocks: %u\n", + print_mstime(currfts), cbcount); + + if ( cbcount > 0 ) + { + if ( cbcount*3 > parsebufsize) { + parsebuf = (unsigned char*)realloc(parsebuf, cbcount*3); + if (!parsebuf) + fatal(EXIT_NOT_ENOUGH_MEMORY, "Out of memory"); + parsebufsize = cbcount*3; + } + buffered_read(parsebuf,cbcount*3); + past+=result; + bread+=(int) result; + if (result!=cbcount*3) + { + mprint("Premature end of file!\n"); + end_of_file=1; + break; + } + + // Process the data + current_pts = currfts*(MPEG_CLOCK_FREQ/1000); + if (pts_set==0) + pts_set=1; + set_fts(); // Now set the FTS related variables + + dbg_print(CCX_DMT_VIDES, "PTS: %s (%8u)", + print_mstime(current_pts/(MPEG_CLOCK_FREQ/1000)), + unsigned(current_pts)); + dbg_print(CCX_DMT_VIDES, " FTS: %s\n", print_mstime(get_fts())); + + for (int j=0; j +#include +#include +#include +#include + +GF_EXPORT +const char *gf_m4v_get_profile_name(u8 video_pl) +{ + switch (video_pl) { + case 0x00: return "Reserved (0x00) Profile"; + case 0x01: return "Simple Profile @ Level 1"; + case 0x02: return "Simple Profile @ Level 2"; + case 0x03: return "Simple Profile @ Level 3"; + case 0x08: return "Simple Profile @ Level 0"; + case 0x10: return "Simple Scalable Profile @ Level 0"; + case 0x11: return "Simple Scalable Profile @ Level 1"; + case 0x12: return "Simple Scalable Profile @ Level 2"; + case 0x15: return "AVC/H264 Profile"; + case 0x21: return "Core Profile @ Level 1"; + case 0x22: return "Core Profile @ Level 2"; + case 0x32: return "Main Profile @ Level 2"; + case 0x33: return "Main Profile @ Level 3"; + case 0x34: return "Main Profile @ Level 4"; + case 0x42: return "N-bit Profile @ Level 2"; + case 0x51: return "Scalable Texture Profile @ Level 1"; + case 0x61: return "Simple Face Animation Profile @ Level 1"; + case 0x62: return "Simple Face Animation Profile @ Level 2"; + case 0x63: return "Simple FBA Profile @ Level 1"; + case 0x64: return "Simple FBA Profile @ Level 2"; + case 0x71: return "Basic Animated Texture Profile @ Level 1"; + case 0x72: return "Basic Animated Texture Profile @ Level 2"; + case 0x81: return "Hybrid Profile @ Level 1"; + case 0x82: return "Hybrid Profile @ Level 2"; + case 0x91: return "Advanced Real Time Simple Profile @ Level 1"; + case 0x92: return "Advanced Real Time Simple Profile @ Level 2"; + case 0x93: return "Advanced Real Time Simple Profile @ Level 3"; + case 0x94: return "Advanced Real Time Simple Profile @ Level 4"; + case 0xA1: return "Core Scalable Profile @ Level1"; + case 0xA2: return "Core Scalable Profile @ Level2"; + case 0xA3: return "Core Scalable Profile @ Level3"; + case 0xB1: return "Advanced Coding Efficiency Profile @ Level 1"; + case 0xB2: return "Advanced Coding Efficiency Profile @ Level 2"; + case 0xB3: return "Advanced Coding Efficiency Profile @ Level 3"; + case 0xB4: return "Advanced Coding Efficiency Profile @ Level 4"; + case 0xC1: return "Advanced Core Profile @ Level 1"; + case 0xC2: return "Advanced Core Profile @ Level 2"; + case 0xD1: return "Advanced Scalable Texture @ Level1"; + case 0xD2: return "Advanced Scalable Texture @ Level2"; + case 0xE1: return "Simple Studio Profile @ Level 1"; + case 0xE2: return "Simple Studio Profile @ Level 2"; + case 0xE3: return "Simple Studio Profile @ Level 3"; + case 0xE4: return "Simple Studio Profile @ Level 4"; + case 0xE5: return "Core Studio Profile @ Level 1"; + case 0xE6: return "Core Studio Profile @ Level 2"; + case 0xE7: return "Core Studio Profile @ Level 3"; + case 0xE8: return "Core Studio Profile @ Level 4"; + case 0xF0: return "Advanced Simple Profile @ Level 0"; + case 0xF1: return "Advanced Simple Profile @ Level 1"; + case 0xF2: return "Advanced Simple Profile @ Level 2"; + case 0xF3: return "Advanced Simple Profile @ Level 3"; + case 0xF4: return "Advanced Simple Profile @ Level 4"; + case 0xF5: return "Advanced Simple Profile @ Level 5"; + case 0xF7: return "Advanced Simple Profile @ Level 3b"; + case 0xF8: return "Fine Granularity Scalable Profile @ Level 0"; + case 0xF9: return "Fine Granularity Scalable Profile @ Level 1"; + case 0xFA: return "Fine Granularity Scalable Profile @ Level 2"; + case 0xFB: return "Fine Granularity Scalable Profile @ Level 3"; + case 0xFC: return "Fine Granularity Scalable Profile @ Level 4"; + case 0xFD: return "Fine Granularity Scalable Profile @ Level 5"; + case 0xFE: return "Not part of MPEG-4 Visual profiles"; + case 0xFF: return "No visual capability required"; + default: return "ISO Reserved Profile"; + } +} + + +#ifndef GPAC_DISABLE_AV_PARSERS + +#define MPEG12_START_CODE_PREFIX 0x000001 +#define MPEG12_PICTURE_START_CODE 0x00000100 +#define MPEG12_SLICE_MIN_START 0x00000101 +#define MPEG12_SLICE_MAX_START 0x000001af +#define MPEG12_USER_DATA_START_CODE 0x000001b2 +#define MPEG12_SEQUENCE_START_CODE 0x000001b3 +#define MPEG12_SEQUENCE_ERR_START_CODE 0x000001b4 +#define MPEG12_EXT_START_CODE 0x000001b5 +#define MPEG12_SEQUENCE_END_START_CODE 0x000001b7 +#define MPEG12_GOP_START_CODE 0x000001b8 + +s32 gf_mv12_next_start_code(unsigned char *pbuffer, u32 buflen, u32 *optr, u32 *scode) +{ + u32 value; + u32 offset; + + if (buflen < 4) return -1; + for (offset = 0; offset < buflen - 3; offset++, pbuffer++) { +#ifdef GPAC_BIG_ENDIAN + value = *(u32 *)pbuffer >> 8; +#else + value = (pbuffer[0] << 16) | (pbuffer[1] << 8) | (pbuffer[2] << 0); +#endif + + if (value == MPEG12_START_CODE_PREFIX) { + *optr = offset; + *scode = (value << 8) | pbuffer[3]; + return 0; + } + } + return -1; +} + +s32 gf_mv12_next_slice_start(unsigned char *pbuffer, u32 startoffset, u32 buflen, u32 *slice_offset) +{ + u32 slicestart, code; + while (gf_mv12_next_start_code(pbuffer + startoffset, buflen - startoffset, &slicestart, &code) >= 0) { + if ((code >= MPEG12_SLICE_MIN_START) && (code <= MPEG12_SLICE_MAX_START)) { + *slice_offset = slicestart + startoffset; + return 0; + } + startoffset += slicestart + 4; + } + return -1; +} + + +/* + MPEG-4 video (14496-2) +*/ + +#define M4V_VO_START_CODE 0x00 +#define M4V_VOL_START_CODE 0x20 +#define M4V_VOP_START_CODE 0xB6 +#define M4V_VISOBJ_START_CODE 0xB5 +#define M4V_VOS_START_CODE 0xB0 +#define M4V_GOV_START_CODE 0xB3 +#define M4V_UDTA_START_CODE 0xB2 + + +#define M2V_PIC_START_CODE 0x00 +#define M2V_SEQ_START_CODE 0xB3 +#define M2V_EXT_START_CODE 0xB5 +#define M2V_GOP_START_CODE 0xB8 + +struct __tag_m4v_parser +{ + GF_BitStream *bs; + Bool mpeg12; + u32 current_object_type; + u64 current_object_start; + u32 tc_dec, prev_tc_dec, tc_disp, prev_tc_disp; +}; + +GF_EXPORT +GF_M4VParser *gf_m4v_parser_new(char *data, u64 data_size, Bool mpeg12video) +{ + GF_M4VParser *tmp; + if (!data || !data_size) return NULL; + GF_SAFEALLOC(tmp, GF_M4VParser); + tmp->bs = gf_bs_new(data, data_size, GF_BITSTREAM_READ); + tmp->mpeg12 = mpeg12video; + return tmp; +} + +GF_M4VParser *gf_m4v_parser_bs_new(GF_BitStream *bs, Bool mpeg12video) +{ + GF_M4VParser *tmp; + GF_SAFEALLOC(tmp, GF_M4VParser); + tmp->bs = bs; + tmp->mpeg12 = mpeg12video; + return tmp; +} + +GF_EXPORT +void gf_m4v_parser_del(GF_M4VParser *m4v) +{ + gf_bs_del(m4v->bs); + gf_free(m4v); +} + + +#define M4V_CACHE_SIZE 4096 +s32 M4V_LoadObject(GF_M4VParser *m4v) +{ + u32 v, bpos, found; + char m4v_cache[M4V_CACHE_SIZE]; + u64 end, cache_start, load_size; + if (!m4v) return 0; + bpos = 0; + found = 0; + load_size = 0; + end = 0; + cache_start = 0; + v = 0xffffffff; + while (!end) { + /*refill cache*/ + if (bpos == (u32) load_size) { + if (!gf_bs_available(m4v->bs)) break; + load_size = gf_bs_available(m4v->bs); + if (load_size>M4V_CACHE_SIZE) load_size=M4V_CACHE_SIZE; + bpos = 0; + cache_start = gf_bs_get_position(m4v->bs); + gf_bs_read_data(m4v->bs, m4v_cache, (u32) load_size); + } + v = ( (v<<8) & 0xFFFFFF00) | ((u8) m4v_cache[bpos]); + bpos++; + if ((v & 0xFFFFFF00) == 0x00000100) { + end = cache_start+bpos-4; + found = 1; + break; + } + } + if (!found) return -1; + m4v->current_object_start = end; + gf_bs_seek(m4v->bs, end+3); + m4v->current_object_type = gf_bs_read_u8(m4v->bs); + return (s32) m4v->current_object_type; +} + + +GF_EXPORT +void gf_m4v_rewrite_pl(char **o_data, u32 *o_dataLen, u8 PL) +{ + u32 pos = 0; + unsigned char *data = (unsigned char *)*o_data; + u32 dataLen = *o_dataLen; + + while (pos+4bs, start); + assert(start < 1<<31); + m4v->current_object_start = (u32) start; + m4v->current_object_type = 0; + return GF_OK; +} + + +static GF_Err gf_m4v_parse_config_mpeg12(GF_M4VParser *m4v, GF_M4VDecSpecInfo *dsi) +{ + char p[4]; + u32 ext_type; + s32 o_type; + u8 go, par; + + if (!m4v || !dsi) return GF_BAD_PARAM; + + memset(dsi, 0, sizeof(GF_M4VDecSpecInfo)); + dsi->VideoPL = 0; + + go = 1; + while (go) { + o_type = M4V_LoadObject(m4v); + switch (o_type) { + case M2V_SEQ_START_CODE: + dsi->RAP_stream = 1; + gf_bs_read_data(m4v->bs, p, 4); + dsi->width = (p[0] << 4) | ((p[1] >> 4) & 0xf); + dsi->height = ((p[1] & 0xf) << 8) | p[2]; + + dsi->VideoPL = GPAC_OTI_VIDEO_MPEG1; + par = (p[3] >> 4) & 0xf; + switch (par) { + case 2: dsi->par_num = dsi->height/3; dsi->par_den = dsi->width/4; break; + case 3: dsi->par_num = dsi->height/9; dsi->par_den = dsi->width/16; break; + case 4: dsi->par_num = dsi->height/2; dsi->par_den = dsi->width/21; break; + default: dsi->par_den = dsi->par_num = 0; break; + } + switch (p[3] & 0xf) { + case 0: break; + case 1: dsi->fps = 24000.0/1001.0; break; + case 2: dsi->fps = 24.0; break; + case 3: dsi->fps = 25.0; break; + case 4: dsi->fps = 30000.0/1001.0; break; + case 5: dsi->fps = 30.0; break; + case 6: dsi->fps = 50.0; break; + case 7: dsi->fps = ((60.0*1000.0)/1001.0); break; + case 8: dsi->fps = 60.0; break; + case 9: dsi->fps = 1; break; + case 10: dsi->fps = 5; break; + case 11: dsi->fps = 10; break; + case 12: dsi->fps = 12; break; + case 13: dsi->fps = 15; break; + } + break; + case M2V_EXT_START_CODE: + gf_bs_read_data(m4v->bs, p, 4); + ext_type = ((p[0] >> 4) & 0xf); + if (ext_type == 1) { + dsi->VideoPL = 0x65; + dsi->height = ((p[1] & 0x1) << 13) | ((p[2] & 0x80) << 5) | (dsi->height & 0x0fff); + dsi->width = (((p[2] >> 5) & 0x3) << 12) | (dsi->width & 0x0fff); + } + break; + case M2V_PIC_START_CODE: + if (dsi->width) go = 0; + break; + default: + break; + /*EOS*/ + case -1: + go = 0; + m4v->current_object_start = gf_bs_get_position(m4v->bs); + break; + } + } + M4V_Reset(m4v, 0); + return GF_OK; +} + + +static const struct { u32 w, h; } m4v_sar[6] = { { 0, 0 }, { 1, 1 }, { 12, 11 }, { 10, 11 }, { 16, 11 }, { 40, 33 } }; + +static u8 m4v_get_sar_idx(u32 w, u32 h) +{ + u32 i; + for (i=0; i<6; i++) { + if ((m4v_sar[i].w==w) && (m4v_sar[i].h==h)) return i; + } + return 0xF; +} + +static GF_Err gf_m4v_parse_config_mpeg4(GF_M4VParser *m4v, GF_M4VDecSpecInfo *dsi) +{ + s32 o_type; + u8 go, verid, par; + s32 clock_rate; + + if (!m4v || !dsi) return GF_BAD_PARAM; + + memset(dsi, 0, sizeof(GF_M4VDecSpecInfo)); + + go = 1; + while (go) { + o_type = M4V_LoadObject(m4v); + switch (o_type) { + /*vosh*/ + case M4V_VOS_START_CODE: + dsi->VideoPL = (u8) gf_bs_read_u8(m4v->bs); + break; + + case M4V_VOL_START_CODE: + verid = 0; + dsi->RAP_stream = gf_bs_read_int(m4v->bs, 1); + dsi->objectType = gf_bs_read_int(m4v->bs, 8); + if (gf_bs_read_int(m4v->bs, 1)) { + verid = gf_bs_read_int(m4v->bs, 4); + gf_bs_read_int(m4v->bs, 3); + } + par = gf_bs_read_int(m4v->bs, 4); + if (par == 0xF) { + dsi->par_num = gf_bs_read_int(m4v->bs, 8); + dsi->par_den = gf_bs_read_int(m4v->bs, 8); + } else if (par<6) { + dsi->par_num = m4v_sar[par].w; + dsi->par_den = m4v_sar[par].h; + } + if (gf_bs_read_int(m4v->bs, 1)) { + gf_bs_read_int(m4v->bs, 3); + if (gf_bs_read_int(m4v->bs, 1)) gf_bs_read_int(m4v->bs, 79); + } + dsi->has_shape = gf_bs_read_int(m4v->bs, 2); + if (dsi->has_shape && (verid!=1) ) gf_bs_read_int(m4v->bs, 4); + gf_bs_read_int(m4v->bs, 1); + /*clock rate*/ + dsi->clock_rate = gf_bs_read_int(m4v->bs, 16); + /*marker*/ + gf_bs_read_int(m4v->bs, 1); + + clock_rate = dsi->clock_rate-1; + if (clock_rate >= 65536) clock_rate = 65535; + if (clock_rate > 0) { + for (dsi->NumBitsTimeIncrement = 1; dsi->NumBitsTimeIncrement < 16; dsi->NumBitsTimeIncrement++) { + if (clock_rate == 1) break; + clock_rate = (clock_rate >> 1); + } + } else { + /*fix from vivien for divX*/ + dsi->NumBitsTimeIncrement = 1; + } + /*fixed FPS stream*/ + dsi->time_increment = 0; + if (gf_bs_read_int(m4v->bs, 1)) { + dsi->time_increment = gf_bs_read_int(m4v->bs, dsi->NumBitsTimeIncrement); + } + if (!dsi->has_shape) { + gf_bs_read_int(m4v->bs, 1); + dsi->width = gf_bs_read_int(m4v->bs, 13); + gf_bs_read_int(m4v->bs, 1); + dsi->height = gf_bs_read_int(m4v->bs, 13); + } else { + dsi->width = dsi->height = 0; + } + /*shape will be done later*/ + gf_bs_align(m4v->bs); + break; + + case M4V_VOP_START_CODE: + case M4V_GOV_START_CODE: + go = 0; + break; + /*EOS*/ + case -1: + go = 0; + m4v->current_object_start = gf_bs_get_position(m4v->bs); + break; + /*don't interest us*/ + case M4V_UDTA_START_CODE: + default: + break; + } + } + return GF_OK; +} + +GF_EXPORT +GF_Err gf_m4v_parse_config(GF_M4VParser *m4v, GF_M4VDecSpecInfo *dsi) +{ + if (m4v->mpeg12) { + return gf_m4v_parse_config_mpeg12(m4v, dsi); + } else { + return gf_m4v_parse_config_mpeg4(m4v, dsi); + } +} + +static GF_Err gf_m4v_parse_frame_mpeg12(GF_M4VParser *m4v, GF_M4VDecSpecInfo dsi, u8 *frame_type, u32 *time_inc, u64 *size, u64 *start, Bool *is_coded) +{ + u8 go, hasVOP, firstObj, val; + s32 o_type; + + if (!m4v || !size || !start || !frame_type) return GF_BAD_PARAM; + + *size = 0; + firstObj = 1; + hasVOP = 0; + *is_coded = 0; + m4v->current_object_type = (u32) -1; + *frame_type = 0; + + M4V_Reset(m4v, m4v->current_object_start); + go = 1; + + while (go) { + o_type = M4V_LoadObject(m4v); + switch (o_type) { + case M2V_PIC_START_CODE: + /*done*/ + if (hasVOP) { + go = 0; + break; + } + if (firstObj) { + *start = m4v->current_object_start; + firstObj = 0; + } + hasVOP = 1; + *is_coded = 1; + + val = gf_bs_read_u8(m4v->bs); + val = gf_bs_read_u8(m4v->bs); + *frame_type = ( (val >> 3) & 0x7 ) - 1; + break; + case M2V_GOP_START_CODE: + if (firstObj) { + *start = m4v->current_object_start; + firstObj = 0; + } + if (hasVOP) go = 0; + break; + + case M2V_SEQ_START_CODE: + if (firstObj) { + *start = m4v->current_object_start; + firstObj = 0; + } + if (hasVOP) { + go = 0; + break; + } + + /**/ + break; + + default: + break; + + case -1: + *size = gf_bs_get_position(m4v->bs) - *start; + return GF_EOS; + } + } + *size = m4v->current_object_start - *start; + return GF_OK; +} + +static GF_Err gf_m4v_parse_frame_mpeg4(GF_M4VParser *m4v, GF_M4VDecSpecInfo dsi, u8 *frame_type, u32 *time_inc, u64 *size, u64 *start, Bool *is_coded) +{ + u8 go, hasVOP, firstObj, secs; + s32 o_type; + u32 vop_inc = 0; + + if (!m4v || !size || !start || !frame_type) return GF_BAD_PARAM; + + *size = 0; + firstObj = 1; + hasVOP = 0; + *is_coded = 0; + m4v->current_object_type = (u32) -1; + *frame_type = 0; + + M4V_Reset(m4v, m4v->current_object_start); + go = 1; + while (go) { + o_type = M4V_LoadObject(m4v); + switch (o_type) { + case M4V_VOP_START_CODE: + /*done*/ + if (hasVOP) { + go = 0; + break; + } + if (firstObj) { + *start = m4v->current_object_start; + firstObj = 0; + } + hasVOP = 1; + + /*coding type*/ + *frame_type = gf_bs_read_int(m4v->bs, 2); + /*modulo time base*/ + secs = 0; + while (gf_bs_read_int(m4v->bs, 1) != 0) + secs ++; + /*no support for B frames in parsing*/ + secs += (dsi.enh_layer || *frame_type!=2) ? m4v->tc_dec : m4v->tc_disp; + /*marker*/ + gf_bs_read_int(m4v->bs, 1); + /*vop_time_inc*/ + if (dsi.NumBitsTimeIncrement) + vop_inc = gf_bs_read_int(m4v->bs, dsi.NumBitsTimeIncrement); + + m4v->prev_tc_dec = m4v->tc_dec; + m4v->prev_tc_disp = m4v->tc_disp; + if (dsi.enh_layer || *frame_type!=2) { + m4v->tc_disp = m4v->tc_dec; + m4v->tc_dec = secs; + } + *time_inc = secs * dsi.clock_rate + vop_inc; + /*marker*/ + gf_bs_read_int(m4v->bs, 1); + /*coded*/ + *is_coded = gf_bs_read_int(m4v->bs, 1); + gf_bs_align(m4v->bs); + break; + case M4V_GOV_START_CODE: + if (firstObj) { + *start = m4v->current_object_start; + firstObj = 0; + } + if (hasVOP) go = 0; + break; + + case M4V_VOS_START_CODE: + case M4V_VOL_START_CODE: + if (hasVOP) { + go = 0; + } else if (firstObj) { + *start = m4v->current_object_start; + firstObj = 0; + } + break; + + case M4V_VO_START_CODE: + default: + break; + + case -1: + *size = gf_bs_get_position(m4v->bs) - *start; + return GF_EOS; + } + } + *size = m4v->current_object_start - *start; + return GF_OK; +} + +GF_EXPORT +GF_Err gf_m4v_parse_frame(GF_M4VParser *m4v, GF_M4VDecSpecInfo dsi, u8 *frame_type, u32 *time_inc, u64 *size, u64 *start, Bool *is_coded) +{ + if (m4v->mpeg12) { + return gf_m4v_parse_frame_mpeg12(m4v, dsi, frame_type, time_inc, size, start, is_coded); + } else { + return gf_m4v_parse_frame_mpeg4(m4v, dsi, frame_type, time_inc, size, start, is_coded); + } +} + +GF_Err gf_m4v_rewrite_par(char **o_data, u32 *o_dataLen, s32 par_n, s32 par_d) +{ + u64 start, end, size; + GF_BitStream *mod; + GF_M4VParser *m4v; + Bool go = 1; + + m4v = gf_m4v_parser_new(*o_data, *o_dataLen, 0); + mod = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); + + end = start = 0; + while (go) { + u32 type = M4V_LoadObject(m4v); + + end = gf_bs_get_position(m4v->bs) - 4; + size = end - start; + /*store previous object*/ + if (size) { + assert (size < 1<<31); + if (size) gf_bs_write_data(mod, *o_data + start, (u32) size); + start = end; + } + + switch (type) { + case M4V_VOL_START_CODE: + gf_bs_write_int(mod, 0, 8); + gf_bs_write_int(mod, 0, 8); + gf_bs_write_int(mod, 1, 8); + gf_bs_write_int(mod, M4V_VOL_START_CODE, 8); + gf_bs_write_int(mod, gf_bs_read_int(m4v->bs, 1), 1); + gf_bs_write_int(mod, gf_bs_read_int(m4v->bs, 8), 8); + start = gf_bs_read_int(m4v->bs, 1); + gf_bs_write_int(mod, (u32) start, 1); + if (start) { + gf_bs_write_int(mod, gf_bs_read_int(m4v->bs, 7), 7); + } + start = gf_bs_read_int(m4v->bs, 4); + if (start == 0xF) { + gf_bs_read_int(m4v->bs, 8); + gf_bs_read_int(m4v->bs, 8); + } + if ((par_n>=0) && (par_d>=0)) { + u8 par = m4v_get_sar_idx(par_n, par_d); + gf_bs_write_int(mod, par, 4); + if (par==0xF) { + gf_bs_write_int(mod, par_n, 8); + gf_bs_write_int(mod, par_d, 8); + } + } else { + gf_bs_write_int(mod, 0x0, 4); + } + case -1: + go = 0; + break; + default: + break; + } + } + while (gf_bs_bits_available(m4v->bs)) { + u32 b = gf_bs_read_int(m4v->bs, 1); + gf_bs_write_int(mod, b, 1); + } + + gf_m4v_parser_del(m4v); + gf_free(*o_data); + gf_bs_get_content(mod, o_data, o_dataLen); + gf_bs_del(mod); + return GF_OK; +} + +GF_EXPORT +u64 gf_m4v_get_object_start(GF_M4VParser *m4v) +{ + return m4v->current_object_start; +} + +GF_EXPORT +Bool gf_m4v_is_valid_object_type(GF_M4VParser *m4v) +{ + return ((s32) m4v->current_object_type==-1) ? 0 : 1; +} + + +GF_EXPORT +GF_Err gf_m4v_get_config(char *rawdsi, u32 rawdsi_size, GF_M4VDecSpecInfo *dsi) +{ + GF_Err e; + GF_M4VParser *vparse; + if (!rawdsi || !rawdsi_size) return GF_NON_COMPLIANT_BITSTREAM; + vparse = gf_m4v_parser_new(rawdsi, rawdsi_size, 0); + e = gf_m4v_parse_config(vparse, dsi); + gf_m4v_parser_del(vparse); + return e; +} +#endif + + +/* + AAC parser +*/ + +GF_EXPORT +const char *gf_m4a_object_type_name(u32 objectType) +{ + switch (objectType) { + case 0: return "MPEG-4 Audio Reserved"; + case 1: return "MPEG-4 Audio AAC Main"; + case 2: return "MPEG-4 Audio AAC LC"; + case 3: return "MPEG-4 Audio AAC SSR"; + case 4: return "MPEG-4 Audio AAC LTP"; + case 5: return "MPEG-4 Audio SBR"; + case 6: return "MPEG-4 Audio AAC Scalable"; + case 7: return "MPEG-4 Audio TwinVQ"; + case 8: return "MPEG-4 Audio CELP"; + case 9: return "MPEG-4 Audio HVXC"; + case 10: return "MPEG-4 Audio Reserved"; + case 11: return "MPEG-4 Audio Reserved"; + case 12: return "MPEG-4 Audio TTSI"; + case 13: return "MPEG-4 Audio Main synthetic"; + case 14: return "MPEG-4 Audio Wavetable synthesis"; + case 15: return "MPEG-4 Audio General MIDI"; + case 16: return "MPEG-4 Audio Algorithmic Synthesis and Audio FX"; + case 17: return "MPEG-4 Audio ER AAC LC"; + case 18: return "MPEG-4 Audio Reserved"; + case 19: return "MPEG-4 Audio ER AAC LTP"; + case 20: return "MPEG-4 Audio ER AAC scalable"; + case 21: return "MPEG-4 Audio ER TwinVQ"; + case 22: return "MPEG-4 Audio ER BSAC"; + case 23: return "MPEG-4 Audio ER AAC LD"; + case 24: return "MPEG-4 Audio ER CELP"; + case 25: return "MPEG-4 Audio ER HVXC"; + case 26: return "MPEG-4 Audio ER HILN"; + case 27: return "MPEG-4 Audio ER Parametric"; + case 28: return "MPEG-4 Audio SSC"; + case 29: return "MPEG-4 Audio ParametricStereo"; + case 30: return "MPEG-4 Audio Reserved"; + case 31: return "MPEG-4 Audio Reserved"; + case 32: return "MPEG-1 Audio Layer-1"; + case 33: return "MPEG-1 Audio Layer-2"; + case 34: return "MPEG-1 Audio Layer-3"; + case 35: return "MPEG-4 Audio DST"; + case 36: return "MPEG-4 Audio ALS"; + default: return "MPEG-4 Audio Unknown"; + } +} + +GF_EXPORT +const char *gf_m4a_get_profile_name(u8 audio_pl) +{ + switch (audio_pl) { + case 0x00: return "ISO Reserved (0x00)"; + case 0x01: return "Main Audio Profile @ Level 1"; + case 0x02: return "Main Audio Profile @ Level 2"; + case 0x03: return "Main Audio Profile @ Level 3"; + case 0x04: return "Main Audio Profile @ Level 4"; + case 0x05: return "Scalable Audio Profile @ Level 1"; + case 0x06: return "Scalable Audio Profile @ Level 2"; + case 0x07: return "Scalable Audio Profile @ Level 3"; + case 0x08: return "Scalable Audio Profile @ Level 4"; + case 0x09: return "Speech Audio Profile @ Level 1"; + case 0x0A: return "Speech Audio Profile @ Level 2"; + case 0x0B: return "Synthetic Audio Profile @ Level 1"; + case 0x0C: return "Synthetic Audio Profile @ Level 2"; + case 0x0D: return "Synthetic Audio Profile @ Level 3"; + case 0x0E: return "High Quality Audio Profile @ Level 1"; + case 0x0F: return "High Quality Audio Profile @ Level 2"; + case 0x10: return "High Quality Audio Profile @ Level 3"; + case 0x11: return "High Quality Audio Profile @ Level 4"; + case 0x12: return "High Quality Audio Profile @ Level 5"; + case 0x13: return "High Quality Audio Profile @ Level 6"; + case 0x14: return "High Quality Audio Profile @ Level 7"; + case 0x15: return "High Quality Audio Profile @ Level 8"; + case 0x16: return "Low Delay Audio Profile @ Level 1"; + case 0x17: return "Low Delay Audio Profile @ Level 2"; + case 0x18: return "Low Delay Audio Profile @ Level 3"; + case 0x19: return "Low Delay Audio Profile @ Level 4"; + case 0x1A: return "Low Delay Audio Profile @ Level 5"; + case 0x1B: return "Low Delay Audio Profile @ Level 6"; + case 0x1C: return "Low Delay Audio Profile @ Level 7"; + case 0x1D: return "Low Delay Audio Profile @ Level 8"; + case 0x1E: return "Natural Audio Profile @ Level 1"; + case 0x1F: return "Natural Audio Profile @ Level 2"; + case 0x20: return "Natural Audio Profile @ Level 3"; + case 0x21: return "Natural Audio Profile @ Level 4"; + case 0x22: return "Mobile Audio Internetworking Profile @ Level 1"; + case 0x23: return "Mobile Audio Internetworking Profile @ Level 2"; + case 0x24: return "Mobile Audio Internetworking Profile @ Level 3"; + case 0x25: return "Mobile Audio Internetworking Profile @ Level 4"; + case 0x26: return "Mobile Audio Internetworking Profile @ Level 5"; + case 0x27: return "Mobile Audio Internetworking Profile @ Level 6"; + case 0x28: return "AAC Profile @ Level 1"; + case 0x29: return "AAC Profile @ Level 2"; + case 0x2A: return "AAC Profile @ Level 4"; + case 0x2B: return "AAC Profile @ Level 5"; + case 0x2C: return "High Efficiency AAC Profile @ Level 2"; + case 0x2D: return "High Efficiency AAC Profile @ Level 3"; + case 0x2E: return "High Efficiency AAC Profile @ Level 4"; + case 0x2F: return "High Efficiency AAC Profile @ Level 5"; + case 0xFE: return "Not part of MPEG-4 audio profiles"; + case 0xFF: return "No audio capability required"; + default: return "ISO Reserved / User Private"; + } +} + +#ifndef GPAC_DISABLE_AV_PARSERS + +u32 gf_m4a_get_profile(GF_M4ADecSpecInfo *cfg) +{ + switch (cfg->base_object_type) { + case 2: /*AAC LC*/ + if (cfg->nb_chan<=2) return (cfg->base_sr<=24000) ? 0x28 : 0x29; /*LC@L1 or LC@L2*/ + return (cfg->base_sr<=48000) ? 0x2A : 0x2B; /*LC@L4 or LC@L5*/ + case 5: /*HE-AAC - SBR*/ + case 29: /*HE-AAC - SBR+PS*/ + if (cfg->nb_chan<=2) return (cfg->base_sr<=24000) ? 0x2C : 0x2D; /*HE@L2 or HE@L3*/ + return (cfg->base_sr<=48000) ? 0x2E : 0x2F; /*HE@L4 or HE@L5*/ + /*default to HQ*/ + default: + if (cfg->nb_chan<=2) return (cfg->base_sr<24000) ? 0x0E : 0x0F; /*HQ@L1 or HQ@L2*/ + return 0x10; /*HQ@L3*/ + } +} + + + +GF_EXPORT +GF_Err gf_m4a_parse_config(GF_BitStream *bs, GF_M4ADecSpecInfo *cfg, Bool size_known) +{ + memset(cfg, 0, sizeof(GF_M4ADecSpecInfo)); + cfg->base_object_type = gf_bs_read_int(bs, 5); + /*extended object type*/ + if (cfg->base_object_type==31) { + cfg->base_object_type = 32 + gf_bs_read_int(bs, 6); + } + cfg->base_sr_index = gf_bs_read_int(bs, 4); + if (cfg->base_sr_index == 0x0F) { + cfg->base_sr = gf_bs_read_int(bs, 24); + } else { + cfg->base_sr = GF_M4ASampleRates[cfg->base_sr_index]; + } + cfg->nb_chan = gf_bs_read_int(bs, 4); + /*this is 7+1 channels*/ + if (cfg->nb_chan==7) cfg->nb_chan=8; + + if (cfg->base_object_type==5 || cfg->base_object_type==29) { + if (cfg->base_object_type==29) { + cfg->has_ps = 1; + cfg->nb_chan = 1; + } + cfg->has_sbr = 1; + cfg->sbr_sr_index = gf_bs_read_int(bs, 4); + if (cfg->sbr_sr_index == 0x0F) { + cfg->sbr_sr = gf_bs_read_int(bs, 24); + } else { + cfg->sbr_sr = GF_M4ASampleRates[cfg->sbr_sr_index]; + } + cfg->sbr_object_type = gf_bs_read_int(bs, 5); + } + + /*object cfg*/ + switch (cfg->base_object_type) { + case 1: + case 2: + case 3: + case 4: + case 6: + case 7: + case 17: + case 19: + case 20: + case 21: + case 22: + case 23: + { + Bool ext_flag; + /*frame length flag*/ + /*fl_flag = */gf_bs_read_int(bs, 1); + /*depends on core coder*/ + if (gf_bs_read_int(bs, 1)) + /*delay = */gf_bs_read_int(bs, 14); + ext_flag = gf_bs_read_int(bs, 1); + if (!cfg->nb_chan) { + } + if ((cfg->base_object_type == 6) || (cfg->base_object_type == 20)) { + gf_bs_read_int(bs, 3); + } + if (ext_flag) { + if (cfg->base_object_type == 22) { + gf_bs_read_int(bs, 5); + gf_bs_read_int(bs, 11); + } + if ((cfg->base_object_type == 17) + || (cfg->base_object_type == 19) + || (cfg->base_object_type == 20) + || (cfg->base_object_type == 23) + ) { + gf_bs_read_int(bs, 1); + gf_bs_read_int(bs, 1); + gf_bs_read_int(bs, 1); + } + ext_flag = gf_bs_read_int(bs, 1); + } + } + break; + } + /*ER cfg*/ + switch (cfg->base_object_type) { + case 17: + case 19: + case 20: + case 21: + case 22: + case 23: + case 24: + case 25: + case 26: + case 27: + { + u32 epConfig = gf_bs_read_int(bs, 2); + if ((epConfig == 2) || (epConfig == 3) ) { + } + if (epConfig == 3) { + gf_bs_read_int(bs, 1); + } + } + break; + } + + if (size_known && (cfg->base_object_type != 5) && (cfg->base_object_type != 29) ) { + while (gf_bs_available(bs)>=2) { + u32 sync = gf_bs_peek_bits(bs, 11, 0); + if (sync==0x2b7) { + gf_bs_read_int(bs, 11); + cfg->sbr_object_type = gf_bs_read_int(bs, 5); + cfg->has_sbr = gf_bs_read_int(bs, 1); + if (cfg->has_sbr) { + cfg->sbr_sr_index = gf_bs_read_int(bs, 4); + if (cfg->sbr_sr_index == 0x0F) { + cfg->sbr_sr = gf_bs_read_int(bs, 24); + } else { + cfg->sbr_sr = GF_M4ASampleRates[cfg->sbr_sr_index]; + } + } + } else if (sync == 0x548) { + gf_bs_read_int(bs, 11); + cfg->has_ps = gf_bs_read_int(bs, 1); + if (cfg->has_ps) + cfg->nb_chan = 1; + } else { + break; + } + } + } + cfg->audioPL = gf_m4a_get_profile(cfg); + return GF_OK; +} + +GF_EXPORT +GF_Err gf_m4a_get_config(char *dsi, u32 dsi_size, GF_M4ADecSpecInfo *cfg) +{ + GF_BitStream *bs; + if (!dsi || !dsi_size || (dsi_size<2) ) return GF_NON_COMPLIANT_BITSTREAM; + bs = gf_bs_new(dsi, dsi_size, GF_BITSTREAM_READ); + gf_m4a_parse_config(bs, cfg, 1); + gf_bs_del(bs); + return GF_OK; +} + +GF_EXPORT +GF_Err gf_m4a_write_config_bs(GF_BitStream *bs, GF_M4ADecSpecInfo *cfg) +{ + if (!cfg->base_sr_index) { + if (!cfg->base_sr) return GF_BAD_PARAM; + while (GF_M4ASampleRates[cfg->base_sr_index]) { + if (GF_M4ASampleRates[cfg->base_sr_index]==cfg->base_sr) + break; + cfg->base_sr_index++; + } + } + if (cfg->sbr_sr && !cfg->sbr_sr_index) { + while (GF_M4ASampleRates[cfg->sbr_sr_index]) { + if (GF_M4ASampleRates[cfg->sbr_sr_index]==cfg->sbr_sr) + break; + cfg->sbr_sr_index++; + } + } + /*extended object type*/ + if (cfg->base_object_type>=32) { + gf_bs_write_int(bs, 31, 5); + gf_bs_write_int(bs, cfg->base_object_type-32, 6); + } else { + gf_bs_write_int(bs, cfg->base_object_type, 5); + } + gf_bs_write_int(bs, cfg->base_sr_index, 4); + if (cfg->base_sr_index == 0x0F) { + gf_bs_write_int(bs, cfg->base_sr, 24); + } + if (cfg->nb_chan == 8) { + gf_bs_write_int(bs, 7, 4); + } else { + gf_bs_write_int(bs, cfg->nb_chan, 4); + } + + if (cfg->base_object_type==5 || cfg->base_object_type==29) { + if (cfg->base_object_type == 29) { + cfg->has_ps = 1; + cfg->nb_chan = 1; + } + cfg->has_sbr = 1; + gf_bs_write_int(bs, cfg->sbr_sr_index, 4); + if (cfg->sbr_sr_index == 0x0F) { + gf_bs_write_int(bs, cfg->sbr_sr, 24); + } + gf_bs_write_int(bs, cfg->sbr_object_type, 5); + } + + /*object cfg*/ + switch (cfg->base_object_type) { + case 1: + case 2: + case 3: + case 4: + case 6: + case 7: + case 17: + case 19: + case 20: + case 21: + case 22: + case 23: + { + /*frame length flag*/ + gf_bs_write_int(bs, 0, 1); + /*depends on core coder*/ + gf_bs_write_int(bs, 0, 1); + /*ext flag*/ + gf_bs_write_int(bs, 0, 1); + if ((cfg->base_object_type == 6) || (cfg->base_object_type == 20)) { + gf_bs_write_int(bs, 0, 3); + } + } + break; + } + /*ER cfg - not supported*/ + + /*implicit sbr - not used yet*/ + if (0 && (cfg->base_object_type != 5) && (cfg->base_object_type != 29) ) { + gf_bs_write_int(bs, 0x2b7, 11); + cfg->sbr_object_type = gf_bs_read_int(bs, 5); + cfg->has_sbr = gf_bs_read_int(bs, 1); + if (cfg->has_sbr) { + cfg->sbr_sr_index = gf_bs_read_int(bs, 4); + if (cfg->sbr_sr_index == 0x0F) { + cfg->sbr_sr = gf_bs_read_int(bs, 24); + } else { + cfg->sbr_sr = GF_M4ASampleRates[cfg->sbr_sr_index]; + } + } + } + return GF_OK; +} + +GF_EXPORT +GF_Err gf_m4a_write_config(GF_M4ADecSpecInfo *cfg, char **dsi, u32 *dsi_size) +{ + GF_BitStream *bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); + gf_m4a_write_config_bs(bs, cfg); + gf_bs_get_content(bs, dsi, dsi_size); + gf_bs_del(bs); + return GF_OK; +} + +#endif /*GPAC_DISABLE_AV_PARSERS*/ + +GF_EXPORT +u8 gf_mp3_version(u32 hdr) +{ + return ((hdr >> 19) & 0x3); +} + +GF_EXPORT +const char *gf_mp3_version_name(u32 hdr) +{ + u32 v = gf_mp3_version(hdr); + switch (v) { + case 0: return "MPEG-2.5"; + case 1: return "Reserved"; + case 2: return "MPEG-2"; + case 3: return "MPEG-1"; + default: return "Unknown"; + } +} + +#ifndef GPAC_DISABLE_AV_PARSERS + + +GF_EXPORT +u8 gf_mp3_layer(u32 hdr) +{ + return 4 - (((hdr >> 17) & 0x3)); +} + +GF_EXPORT +u8 gf_mp3_num_channels(u32 hdr) +{ + if (((hdr >> 6) & 0x3) == 3) return 1; + return 2; +} + +GF_EXPORT +u16 gf_mp3_sampling_rate(u32 hdr) +{ + u16 res; + /* extract the necessary fields from the MP3 header */ + u8 version = gf_mp3_version(hdr); + u8 sampleRateIndex = (hdr >> 10) & 0x3; + + switch (sampleRateIndex) { + case 0: + res = 44100; + break; + case 1: + res = 48000; + break; + case 2: + res = 32000; + break; + default: + GF_LOG(GF_LOG_ERROR, GF_LOG_CODING, ("[MPEG-1/2 Audio] Samplerate index not valid\n")); + return 0; + } + /*reserved or MPEG-1*/ + if (version & 1) return res; + + /*MPEG-2*/ + res /= 2; + /*MPEG-2.5*/ + if (version == 0) res /= 2; + return res; +} + +GF_EXPORT +u16 gf_mp3_window_size(u32 hdr) +{ + u8 version = gf_mp3_version(hdr); + u8 layer = gf_mp3_layer(hdr); + + if (layer == 3) { + if (version == 3) return 1152; + return 576; + } + if (layer == 2) return 1152; + return 384; +} + +GF_EXPORT +u8 gf_mp3_object_type_indication(u32 hdr) +{ + switch (gf_mp3_version(hdr)) { + case 3: + return GPAC_OTI_AUDIO_MPEG1; + case 2: + case 0: + return GPAC_OTI_AUDIO_MPEG2_PART3; + default: + return 0x00; + } +} + +/*aligned bitrate parsing with libMAD*/ + +static +u32 const bitrate_table[5][15] = { + /* MPEG-1 */ + { 0, 32000, 64000, 96000, 128000, 160000, 192000, 224000, /* Layer I */ + 256000, 288000, 320000, 352000, 384000, 416000, 448000 }, + { 0, 32000, 48000, 56000, 64000, 80000, 96000, 112000, /* Layer II */ + 128000, 160000, 192000, 224000, 256000, 320000, 384000 }, + { 0, 32000, 40000, 48000, 56000, 64000, 80000, 96000, /* Layer III */ + 112000, 128000, 160000, 192000, 224000, 256000, 320000 }, + + /* MPEG-2 LSF */ + { 0, 32000, 48000, 56000, 64000, 80000, 96000, 112000, /* Layer I */ + 128000, 144000, 160000, 176000, 192000, 224000, 256000 }, + { 0, 8000, 16000, 24000, 32000, 40000, 48000, 56000, /* Layers */ + 64000, 80000, 96000, 112000, 128000, 144000, 160000 } /* II & III */ +}; + + +u32 gf_mp3_bit_rate(u32 hdr) +{ + u8 version = gf_mp3_version(hdr); + u8 layer = gf_mp3_layer(hdr); + u8 bitRateIndex = (hdr >> 12) & 0xF; + + if (bitRateIndex == 15) { + GF_LOG(GF_LOG_ERROR, GF_LOG_CODING, ("[MPEG-1/2 Audio] Bitrate index not valid\n")); + return 0; + } + + /*MPEG-1*/ + if (version & 1) + return bitrate_table[layer - 1][bitRateIndex]; + /*MPEG-2/2.5*/ + else + return bitrate_table[3 + (layer >> 1)][bitRateIndex]; +} + + + +GF_EXPORT +u16 gf_mp3_frame_size(u32 hdr) +{ + u8 version = gf_mp3_version(hdr); + u8 layer = gf_mp3_layer(hdr); + u32 pad = ( (hdr >> 9) & 0x1) ? 1 : 0; + u32 bitrate = gf_mp3_bit_rate(hdr); + u32 samplerate = gf_mp3_sampling_rate(hdr); + + u32 frameSize = 0; + if (!samplerate || !bitrate) return 0; + + if (layer==1) { + frameSize = (( 12 * bitrate / samplerate) + pad) * 4; + } else { + u32 slots_per_frame = 144; + if ((layer == 3) && !(version & 1)) slots_per_frame = 72; + frameSize = (slots_per_frame * bitrate / samplerate) + pad; + } + return (u16) frameSize; +} + + +GF_EXPORT +u32 gf_mp3_get_next_header(FILE* in) +{ + u8 b, state = 0; + u32 dropped = 0; + unsigned char bytes[4]; + bytes[0] = bytes[1] = bytes[2] = bytes[3] = 0; + + while (1) { + if (fread(&b, 1, 1, in) == 0) return 0; + + if (state==3) { + bytes[state] = b; + return GF_4CC(bytes[0], bytes[1], bytes[2], bytes[3]); + } + if (state==2) { + if (((b & 0xF0) == 0) || ((b & 0xF0) == 0xF0) || ((b & 0x0C) == 0x0C)) { + if (bytes[1] == 0xFF) state = 1; + else state = 0; + } else { + bytes[state] = b; + state = 3; + } + } + if (state==1) { + if (((b & 0xE0) == 0xE0) && ((b & 0x18) != 0x08) && ((b & 0x06) != 0)) { + bytes[state] = b; + state = 2; + } else { + state = 0; + } + } + + if (state==0) { + if (b == 0xFF) { + bytes[state] = b; + state = 1; + } else { + if ((dropped == 0) && ((b & 0xE0) == 0xE0) && ((b & 0x18) != 0x08) && ((b & 0x06) != 0)) { + bytes[0] = (u8) 0xFF; + bytes[1] = b; + state = 2; + } else { + dropped++; + } + } + } + } + return 0; +} + +GF_EXPORT +u32 gf_mp3_get_next_header_mem(const char *buffer, u32 size, u32 *pos) +{ + u32 cur; + u8 b, state = 0; + u32 dropped = 0; + unsigned char bytes[4]; + bytes[0] = bytes[1] = bytes[2] = bytes[3] = 0; + + cur = 0; + *pos = 0; + while (cur>1)); + return (v + 1) >> 1; +} + +u32 AVC_IsStartCode(GF_BitStream *bs) +{ + u8 s1, s2, s3, s4; + Bool is_sc = 0; + u64 pos = gf_bs_get_position(bs); + s1 = gf_bs_read_int(bs, 8); + s2 = gf_bs_read_int(bs, 8); + if (!s1 && !s2) { + s3 = gf_bs_read_int(bs, 8); + if (s3==0x01) is_sc = 3; + else if (!s3) { + s4 = gf_bs_read_int(bs, 8); + if (s4==0x01) is_sc = 4; + } + } + gf_bs_seek(bs, pos+is_sc); + return is_sc; +} + +/*read that amount of data at each IO access rather than fetching byte by byte...*/ +#define AVC_CACHE_SIZE 4096 +u32 AVC_NextStartCode(GF_BitStream *bs) +{ + u32 v, bpos; + char avc_cache[AVC_CACHE_SIZE]; + u64 end, cache_start, load_size; + u64 start = gf_bs_get_position(bs); + if (start<3) return 0; + + load_size = 0; + bpos = 0; + cache_start = 0; + end = 0; + v = 0xffffffff; + while (!end) { + /*refill cache*/ + if (bpos == (u32) load_size) { + if (!gf_bs_available(bs)) break; + load_size = gf_bs_available(bs); + if (load_size>AVC_CACHE_SIZE) load_size=AVC_CACHE_SIZE; + bpos = 0; + cache_start = gf_bs_get_position(bs); + gf_bs_read_data(bs, avc_cache, (u32) load_size); + } + v = ( (v<<8) & 0xFFFFFF00) | ((u32) avc_cache[bpos]); + bpos++; + if (v == 0x00000001) end = cache_start+bpos-4; + else if ( (v & 0x00FFFFFF) == 0x00000001) end = cache_start+bpos-3; + } + gf_bs_seek(bs, start); + if (!end) end = gf_bs_get_size(bs); + return (u32) (end-start); +} + +Bool AVC_SliceIsIDR(AVCState *avc) +{ + if (avc->sei.recovery_point.valid) + { + avc->sei.recovery_point.valid = 0; + return 1; + } + if (avc->s_info.nal_unit_type != GF_AVC_NALU_IDR_SLICE) return 0; + switch (avc->s_info.slice_type) { + case GF_AVC_TYPE_I: + case GF_AVC_TYPE2_I: + case GF_AVC_TYPE_SI: + case GF_AVC_TYPE2_SI: + return 1; + default: + return 0; + } +} + +static const struct { u32 w, h; } avc_sar[14] = +{ + { 0, 0 }, { 1, 1 }, { 12, 11 }, { 10, 11 }, + { 16, 11 }, { 40, 33 }, { 24, 11 }, { 20, 11 }, + { 32, 11 }, { 80, 33 }, { 18, 11 }, { 15, 11 }, + { 64, 33 }, { 160,99 }, +}; + +/*ISO 14496-10 (N11084) E.1.2*/ +static void avc_parse_hrd_parameters(GF_BitStream *bs, AVC_HRD *hrd) +{ + int i, cpb_cnt_minus1; + + cpb_cnt_minus1 = avc_get_ue(bs); /*cpb_cnt_minus1*/ + gf_bs_read_int(bs, 4); /*bit_rate_scale*/ + gf_bs_read_int(bs, 4); /*cpb_size_scale*/ + + /*for( SchedSelIdx = 0; SchedSelIdx <= cpb_cnt_minus1; SchedSelIdx++ ) {*/ + for (i=0; i<=cpb_cnt_minus1; i++) { + avc_get_ue(bs); /*bit_rate_value_minus1[ SchedSelIdx ]*/ + avc_get_ue(bs); /*cpb_size_value_minus1[ SchedSelIdx ]*/ + gf_bs_read_int(bs, 1); /*cbr_flag[ SchedSelIdx ]*/ + } + gf_bs_read_int(bs, 5); /*initial_cpb_removal_delay_length_minus1*/ + hrd->cpb_removal_delay_length_minus1 = gf_bs_read_int(bs, 5); /*cpb_removal_delay_length_minus1*/ + hrd->dpb_output_delay_length_minus1 = gf_bs_read_int(bs, 5); /*dpb_output_delay_length_minus1*/ + hrd->time_offset_length = gf_bs_read_int(bs, 5); /*time_offset_length*/ + + return; +} + +/*returns the nal_size without emulation prevention bytes*/ +static u32 avc_emulation_bytes_add_count(unsigned char *buffer, u32 nal_size) +{ + u32 i = 0, emulation_bytes_count = 0; + u8 num_zero = 0; + + while (i < nal_size) { + /*ISO 14496-10: "Within the NAL unit, any four-byte sequence that starts with 0x000003 + other than the following sequences shall not occur at any byte-aligned position: + – 0x00000300 + – 0x00000301 + – 0x00000302 + – 0x00000303" + */ + if (num_zero == 2 && buffer[i] < 0x04) { + /*emulation code found*/ + num_zero = 0; + emulation_bytes_count++; + } else { + if (!buffer[i]) + num_zero++; + else + num_zero = 0; + } + i++; + } + return emulation_bytes_count; +} + +static u32 avc_add_emulation_bytes(const unsigned char *buffer_src, unsigned char *buffer_dst, u32 nal_size) +{ + + u32 i = 0, emulation_bytes_count = 0; + u8 num_zero = 0; + + while (i < nal_size) { + /*ISO 14496-10: "Within the NAL unit, any four-byte sequence that starts with 0x000003 + other than the following sequences shall not occur at any byte-aligned position: + 0x00000300 + 0x00000301 + 0x00000302 + 0x00000303" + */ + if (num_zero == 2 && buffer_src[i] < 0x04) { + /*add emulation code*/ + num_zero = 0; + buffer_dst[i+emulation_bytes_count] = 0x03; + emulation_bytes_count++; + } else { + if (!buffer_src[i]) + num_zero++; + else + num_zero = 0; + } + buffer_dst[i+emulation_bytes_count] = buffer_src[i]; + i++; + } + return nal_size+emulation_bytes_count; +} +#ifdef GPAC_UNUSED_FUNC +/*returns the nal_size without emulation prevention bytes*/ +static u32 avc_emulation_bytes_remove_count(unsigned char *buffer, u32 nal_size) +{ + u32 i = 0, emulation_bytes_count = 0; + u8 num_zero = 0; + + while (i < nal_size) + { + /*ISO 14496-10: "Within the NAL unit, any four-byte sequence that starts with 0x000003 + other than the following sequences shall not occur at any byte-aligned position: + – 0x00000300 + – 0x00000301 + – 0x00000302 + – 0x00000303" + */ + if (num_zero == 2 + && buffer[i] == 0x03 + && i+1 < nal_size /*next byte is readable*/ + && buffer[i+1] < 0x04) + { + /*emulation code found*/ + num_zero = 0; + emulation_bytes_count++; + i++; + } + + if (!buffer[i]) + num_zero++; + else + num_zero = 0; + + i++; + } + + return emulation_bytes_count; +} +#endif /*GPAC_UNUSED_FUNC*/ + +/*nal_size is updated to allow better error detection*/ + u32 avc_remove_emulation_bytes(const unsigned char *buffer_src, unsigned char *buffer_dst, u32 nal_size) +{ + u32 i = 0, emulation_bytes_count = 0; + u8 num_zero = 0; + + while (i < nal_size) + { + /*ISO 14496-10: "Within the NAL unit, any four-byte sequence that starts with 0x000003 + other than the following sequences shall not occur at any byte-aligned position: + 0x00000300 + 0x00000301 + 0x00000302 + 0x00000303" + */ + if (num_zero == 2 + && buffer_src[i] == 0x03 + && i+1 < nal_size /*next byte is readable*/ + && buffer_src[i+1] < 0x04) + { + /*emulation code found*/ + num_zero = 0; + emulation_bytes_count++; + i++; + } + + buffer_dst[i-emulation_bytes_count] = buffer_src[i]; + + if (!buffer_src[i]) + num_zero++; + else + num_zero = 0; + + i++; + } + + return nal_size-emulation_bytes_count; +} + +s32 AVC_ReadSeqInfo(char *sps_data, u32 sps_size, AVCState *avc, u32 subseq_sps, u32 *vui_flag_pos) +{ + AVC_SPS *sps; + u32 ChromaArrayType = 0; + s32 mb_width, mb_height, sps_id = -1; + u32 profile_idc, level_idc, pcomp, i, chroma_format_idc, cl, cr, ct, cb; + GF_BitStream *bs; + char *sps_data_without_emulation_bytes = NULL; + u32 sps_data_without_emulation_bytes_size = 0; + + /*SPS still contains emulation bytes*/ + sps_data_without_emulation_bytes = (char *) gf_malloc(sps_size*sizeof(char)); + sps_data_without_emulation_bytes_size = avc_remove_emulation_bytes((unsigned char *) sps_data, (unsigned char *) sps_data_without_emulation_bytes, sps_size); + bs = gf_bs_new(sps_data_without_emulation_bytes, sps_data_without_emulation_bytes_size, GF_BITSTREAM_READ); + + if (vui_flag_pos) *vui_flag_pos = 0; + + profile_idc = gf_bs_read_int(bs, 8); + + pcomp = gf_bs_read_int(bs, 8); + /*sanity checks*/ + if (pcomp & 0x3) + goto exit; + + level_idc = gf_bs_read_int(bs, 8); + + /*SubsetSps is used to be sure that AVC SPS are not going to be scratched + by subset SPS. According to the SVC standard, subset SPS can have the same sps_id + than its base layer, but it does not refer to the same SPS. */ + sps_id = avc_get_ue(bs) + GF_SVC_SSPS_ID_SHIFT * subseq_sps; + + sps = &avc->sps[sps_id]; + sps->state |= subseq_sps ? AVC_SUBSPS_PARSED : AVC_SPS_PARSED; + + /*High Profile and SVC*/ + switch (profile_idc) { + case 100: + case 110: + case 122: + case 244: + case 44: + /*sanity checks: note1 from 7.4.2.1.1 of iso/iec 14496-10-N11084*/ + if (pcomp & 0xE0) + goto exit; + case 83: + case 86: + case 118: + case 128: + chroma_format_idc = avc_get_ue(bs); + ChromaArrayType = chroma_format_idc; + if (chroma_format_idc == 3) { + u8 separate_colour_plane_flag = gf_bs_read_int(bs, 1); + /* + Depending on the value of separate_colour_plane_flag, the value of the variable ChromaArrayType is assigned as follows. + – If separate_colour_plane_flag is equal to 0, ChromaArrayType is set equal to chroma_format_idc. + – Otherwise (separate_colour_plane_flag is equal to 1), ChromaArrayType is set equal to 0. + */ + if (separate_colour_plane_flag) ChromaArrayType = 0; + } + /*bit_depth_luma_minus8 = */ avc_get_ue(bs); + /*bit_depth_chroma_minus8 = */ avc_get_ue(bs); + /*qpprime_y_zero_transform_bypass_flag = */ gf_bs_read_int(bs, 1); + /*seq_scaling_matrix_present_flag*/ + if (gf_bs_read_int(bs, 1)) { + u32 k; + for (k=0; k<8; k++) { + if (gf_bs_read_int(bs, 1)) { + u32 z, last = 8, next = 8; + u32 sl = k<6 ? 16 : 64; + for (z=0; zprofile_idc = profile_idc; + sps->level_idc = level_idc; + sps->prof_compat = pcomp; + sps->log2_max_frame_num = avc_get_ue(bs) + 4; + sps->poc_type = avc_get_ue(bs); + + if (sps->poc_type == 0) { + sps->log2_max_poc_lsb = avc_get_ue(bs) + 4; + } else if(sps->poc_type == 1) { + sps->delta_pic_order_always_zero_flag = gf_bs_read_int(bs, 1); + sps->offset_for_non_ref_pic = avc_get_se(bs); + sps->offset_for_top_to_bottom_field = avc_get_se(bs); + sps->poc_cycle_length = avc_get_ue(bs); + for(i=0; ipoc_cycle_length; i++) sps->offset_for_ref_frame[i] = avc_get_se(bs); + } + if (sps->poc_type > 2) { + sps_id = -1; + goto exit; + } + avc_get_ue(bs); /*ref_frame_count*/ + gf_bs_read_int(bs, 1); /*gaps_in_frame_num_allowed_flag*/ + mb_width = avc_get_ue(bs) + 1; + mb_height= avc_get_ue(bs) + 1; + + sps->frame_mbs_only_flag = gf_bs_read_int(bs, 1); + + sps->width = mb_width * 16; + sps->height = (2-sps->frame_mbs_only_flag) * mb_height * 16; + + /*mb_adaptive_frame_field_flag*/ + if (!sps->frame_mbs_only_flag) gf_bs_read_int(bs, 1); + gf_bs_read_int(bs, 1); /*direct_8x8_inference_flag*/ + cl = cr = ct = cb = 0; + if (gf_bs_read_int(bs, 1)) /*crop*/ { + cl = avc_get_ue(bs); /*crop_left*/ + cr = avc_get_ue(bs); /*crop_right*/ + ct = avc_get_ue(bs); /*crop_top*/ + cb = avc_get_ue(bs); /*crop_bottom*/ + + sps->width = 16*mb_width - 2*(cl + cr); + sps->height -= (2-sps->frame_mbs_only_flag)*2*(ct + cb); + } + + if (vui_flag_pos) { + *vui_flag_pos = (u32) gf_bs_get_bit_offset(bs); + } + /*vui_parameters_present_flag*/ + if (gf_bs_read_int(bs, 1)) { + /*aspect_ratio_info_present_flag*/ + if (gf_bs_read_int(bs, 1)) { + s32 aspect_ratio_idc = gf_bs_read_int(bs, 8); + if (aspect_ratio_idc == 255) { + sps->vui.par_num = gf_bs_read_int(bs, 16); /*AR num*/ + sps->vui.par_den = gf_bs_read_int(bs, 16); /*AR den*/ + } else if (aspect_ratio_idc<14) { + sps->vui.par_num = avc_sar[aspect_ratio_idc].w; + sps->vui.par_den = avc_sar[aspect_ratio_idc].h; + } + } + if(gf_bs_read_int(bs, 1)) /* overscan_info_present_flag */ + gf_bs_read_int(bs, 1); /* overscan_appropriate_flag */ + + if (gf_bs_read_int(bs, 1)){ /* video_signal_type_present_flag */ + gf_bs_read_int(bs, 3); /* video_format */ + gf_bs_read_int(bs, 1); /* video_full_range_flag */ + if (gf_bs_read_int(bs, 1)){ /* colour_description_present_flag */ + gf_bs_read_int(bs, 8); /* colour_primaries */ + gf_bs_read_int(bs, 8); /* transfer_characteristics */ + gf_bs_read_int(bs, 8); /* matrix_coefficients */ + } + } + + if (gf_bs_read_int(bs, 1)) { /* chroma_location_info_present_flag */ + avc_get_ue(bs); /* chroma_sample_location_type_top_field */ + avc_get_ue(bs); /* chroma_sample_location_type_bottom_field */ + } + + sps->vui.timing_info_present_flag = gf_bs_read_int(bs, 1); + if (sps->vui.timing_info_present_flag) { + sps->vui.num_units_in_tick = gf_bs_read_int(bs, 32); + sps->vui.time_scale = gf_bs_read_int(bs, 32); + sps->vui.fixed_frame_rate_flag = gf_bs_read_int(bs, 1); + } + + sps->vui.nal_hrd_parameters_present_flag = gf_bs_read_int(bs, 1); + if (sps->vui.nal_hrd_parameters_present_flag) + avc_parse_hrd_parameters(bs, &sps->vui.hrd); + + sps->vui.vcl_hrd_parameters_present_flag = gf_bs_read_int(bs, 1); + if (sps->vui.vcl_hrd_parameters_present_flag) + avc_parse_hrd_parameters(bs, &sps->vui.hrd); + + if (sps->vui.nal_hrd_parameters_present_flag || sps->vui.vcl_hrd_parameters_present_flag) + gf_bs_read_int(bs, 1); /*low_delay_hrd_flag*/ + + sps->vui.pic_struct_present_flag = gf_bs_read_int(bs, 1); + } + /*end of seq_parameter_set_data*/ + + if (subseq_sps) { + if ((profile_idc==83) || (profile_idc==86)) { + u8 extended_spatial_scalability_idc; + /*parsing seq_parameter_set_svc_extension*/ + + /*inter_layer_deblocking_filter_control_present_flag=*/ gf_bs_read_int(bs, 1); + extended_spatial_scalability_idc = gf_bs_read_int(bs, 2); + if (ChromaArrayType == 1 || ChromaArrayType == 2) { + /*chroma_phase_x_plus1_flag*/ gf_bs_read_int(bs, 1); + } + if( ChromaArrayType == 1 ) { + /*chroma_phase_y_plus1*/ gf_bs_read_int(bs, 2); + } + if (extended_spatial_scalability_idc == 1) { + if( ChromaArrayType > 0 ) { + /*seq_ref_layer_chroma_phase_x_plus1_flag*/gf_bs_read_int(bs, 1); + /*seq_ref_layer_chroma_phase_y_plus1*/gf_bs_read_int(bs, 2); + } + /*seq_scaled_ref_layer_left_offset*/ avc_get_se(bs); + /*seq_scaled_ref_layer_top_offset*/avc_get_se(bs); + /*seq_scaled_ref_layer_right_offset*/avc_get_se(bs); + /*seq_scaled_ref_layer_bottom_offset*/avc_get_se(bs); + } + if (/*seq_tcoeff_level_prediction_flag*/gf_bs_read_int(bs, 1)) { + /*adaptive_tcoeff_level_prediction_flag*/ gf_bs_read_int(bs, 1); + } + /*slice_header_restriction_flag*/gf_bs_read_int(bs, 1); + + /*svc_vui_parameters_present*/ + if (gf_bs_read_int(bs, 1)) { + u32 i, vui_ext_num_entries_minus1; + vui_ext_num_entries_minus1 = avc_get_ue(bs); + + for (i=0; i <= vui_ext_num_entries_minus1; i++) { + u8 vui_ext_nal_hrd_parameters_present_flag, vui_ext_vcl_hrd_parameters_present_flag, vui_ext_timing_info_present_flag; + /*u8 vui_ext_dependency_id = */gf_bs_read_int(bs, 3); + /*u8 vui_ext_quality_id = */gf_bs_read_int(bs, 4); + /*u8 vui_ext_temporal_id = */gf_bs_read_int(bs, 3); + vui_ext_timing_info_present_flag = gf_bs_read_int(bs, 1); + if (vui_ext_timing_info_present_flag) { + /*u32 vui_ext_num_units_in_tick = */gf_bs_read_int(bs, 32); + /*u32 vui_ext_time_scale = */gf_bs_read_int(bs, 32); + /*u8 vui_ext_fixed_frame_rate_flag = */gf_bs_read_int(bs, 1); + } + vui_ext_nal_hrd_parameters_present_flag = gf_bs_read_int(bs, 1); + if (vui_ext_nal_hrd_parameters_present_flag) { + //hrd_parameters( ) + } + vui_ext_vcl_hrd_parameters_present_flag = gf_bs_read_int(bs, 1); + if (vui_ext_vcl_hrd_parameters_present_flag) { + //hrd_parameters( ) + } + if ( vui_ext_nal_hrd_parameters_present_flag || vui_ext_vcl_hrd_parameters_present_flag) { + /*vui_ext_low_delay_hrd_flag*/gf_bs_read_int(bs, 1); + } + /*vui_ext_pic_struct_present_flag*/gf_bs_read_int(bs, 1); + } + } + } + else if ((profile_idc==118) || (profile_idc==128)) { + GF_LOG(GF_LOG_WARNING, GF_LOG_CODING, ("[avc-h264] MVC not supported - skipping parsing end of Subset SPS\n")); + goto exit; + } + + if (gf_bs_read_int(bs, 1)) { + GF_LOG(GF_LOG_WARNING, GF_LOG_CODING, ("[avc-h264] skipping parsing end of Subset SPS (additional_extension2)\n")); + goto exit; + } + } + +exit: + gf_bs_del(bs); + gf_free(sps_data_without_emulation_bytes); + return sps_id; +} + +s32 AVC_ReadPictParamSet(char *pps_data, u32 pps_size, AVCState *avc) +{ + GF_BitStream *bs; + char *pps_data_without_emulation_bytes = NULL; + u32 pps_data_without_emulation_bytes_size = 0; + s32 pps_id; + AVC_PPS *pps; + + /*PPS still contains emulation bytes*/ + pps_data_without_emulation_bytes = (char *) gf_malloc(pps_size*sizeof(char)); + pps_data_without_emulation_bytes_size = avc_remove_emulation_bytes((unsigned char *) pps_data, (unsigned char *) pps_data_without_emulation_bytes, pps_size); + bs = gf_bs_new(pps_data_without_emulation_bytes, pps_data_without_emulation_bytes_size, GF_BITSTREAM_READ); + pps_id = avc_get_ue(bs); + pps = &avc->pps[pps_id]; + + if (!pps->status) pps->status = 1; + pps->sps_id = avc_get_ue(bs); + /*sps_id may be refer to regular SPS or subseq sps, depending on the coded slice refering to the pps*/ + if (!avc->sps[pps->sps_id].state && !avc->sps[pps->sps_id + GF_SVC_SSPS_ID_SHIFT].state) { + pps_id = -1; + goto exit; + } + avc->sps_active_idx = pps->sps_id; /*set active sps*/ + /*pps->cabac = */gf_bs_read_int(bs, 1); + pps->pic_order_present= gf_bs_read_int(bs, 1); + pps->slice_group_count= avc_get_ue(bs) + 1; + if (pps->slice_group_count > 1 ) /*pps->mb_slice_group_map_type = */avc_get_ue(bs); + /*pps->ref_count[0]= */avc_get_ue(bs) /*+ 1*/; + /*pps->ref_count[1]= */avc_get_ue(bs) /*+ 1*/; + /* + if ((pps->ref_count[0] > 32) || (pps->ref_count[1] > 32)) goto exit; + */ + + /*pps->weighted_pred = */gf_bs_read_int(bs, 1); + /*pps->weighted_bipred_idc = */gf_bs_read_int(bs, 2); + /*pps->init_qp = */avc_get_se(bs) /*+ 26*/; + /*pps->init_qs= */avc_get_se(bs) /*+ 26*/; + /*pps->chroma_qp_index_offset = */avc_get_se(bs); + /*pps->deblocking_filter_parameters_present = */gf_bs_read_int(bs, 1); + /*pps->constrained_intra_pred = */gf_bs_read_int(bs, 1); + pps->redundant_pic_cnt_present = gf_bs_read_int(bs, 1); + +exit: + gf_bs_del(bs); + gf_free(pps_data_without_emulation_bytes); + return pps_id; +} + +static s32 SVC_ReadNal_header_extension(GF_BitStream *bs, SVC_NALUHeader *NalHeader) +{ + gf_bs_read_int(bs, 1); //reserved_one_bits + NalHeader->idr_pic_flag = gf_bs_read_int(bs, 1); //idr_flag + NalHeader->priority_id = gf_bs_read_int(bs, 6); //priority_id + gf_bs_read_int(bs, 1); //no_inter_layer_pred_flag + NalHeader->dependency_id = gf_bs_read_int(bs, 3); //DependencyId + NalHeader->quality_id = gf_bs_read_int(bs, 4); //quality_id + NalHeader->temporal_id = gf_bs_read_int(bs, 3); //temporal_id + gf_bs_read_int(bs, 1); //use_ref_base_pic_flag + gf_bs_read_int(bs, 1); //discardable_flag + gf_bs_read_int(bs, 1); //output_flag + gf_bs_read_int(bs, 2); //reserved_three_2bits + return 1; +} + +static s32 avc_parse_slice(GF_BitStream *bs, AVCState *avc, Bool svc_idr_flag, AVCSliceInfo *si) +{ + s32 pps_id; + + /*s->current_picture.reference= h->nal_ref_idc != 0;*/ + /*first_mb_in_slice = */avc_get_ue(bs); + si->slice_type = avc_get_ue(bs); + if (si->slice_type > 9) return -1; + + pps_id = avc_get_ue(bs); + if (pps_id>255) return -1; + si->pps = &avc->pps[pps_id]; + if (!si->pps->slice_group_count) return -2; + si->sps = &avc->sps[si->pps->sps_id]; + if (!si->sps->log2_max_frame_num) return -2; + + si->frame_num = gf_bs_read_int(bs, si->sps->log2_max_frame_num); + + si->field_pic_flag = 0; + si->bottom_field_flag = 0; + if (!si->sps->frame_mbs_only_flag) { + si->field_pic_flag = gf_bs_read_int(bs, 1); + if (si->field_pic_flag) + si->bottom_field_flag = gf_bs_read_int(bs, 1); + } + if ((si->nal_unit_type==GF_AVC_NALU_IDR_SLICE) || svc_idr_flag) + si->idr_pic_id = avc_get_ue(bs); + + if (si->sps->poc_type==0) { + si->poc_lsb = gf_bs_read_int(bs, si->sps->log2_max_poc_lsb); + if (si->pps->pic_order_present && !si->field_pic_flag) { + si->delta_poc_bottom = avc_get_se(bs); + } + } else if ((si->sps->poc_type==1) && !si->sps->delta_pic_order_always_zero_flag) { + si->delta_poc[0] = avc_get_se(bs); + if ((si->pps->pic_order_present==1) && !si->field_pic_flag) + si->delta_poc[1] = avc_get_se(bs); + } + if (si->pps->redundant_pic_cnt_present) { + si->redundant_pic_cnt = avc_get_ue(bs); + } + return 0; +} + + +static s32 svc_parse_slice(GF_BitStream *bs, AVCState *avc, AVCSliceInfo *si) +{ + s32 pps_id; + + /*s->current_picture.reference= h->nal_ref_idc != 0;*/ + /*first_mb_in_slice = */avc_get_ue(bs); + si->slice_type = avc_get_ue(bs); + if (si->slice_type > 9) return -1; + + pps_id = avc_get_ue(bs); + if (pps_id>255) + return -1; + si->pps = &avc->pps[pps_id]; + si->pps->id = pps_id; + if (!si->pps->slice_group_count) + return -2; + si->sps = &avc->sps[si->pps->sps_id + GF_SVC_SSPS_ID_SHIFT]; + if (!si->sps->log2_max_frame_num) + return -2; + + si->frame_num = gf_bs_read_int(bs, si->sps->log2_max_frame_num); + + si->field_pic_flag = 0; + if (si->sps->frame_mbs_only_flag) { + /*s->picture_structure= PICT_FRAME;*/ + } else { + si->field_pic_flag = gf_bs_read_int(bs, 1); + if (si->field_pic_flag) si->bottom_field_flag = gf_bs_read_int(bs, 1); + } + if (si->nal_unit_type == GF_AVC_NALU_IDR_SLICE || si ->NalHeader.idr_pic_flag) + si->idr_pic_id = avc_get_ue(bs); + + if (si->sps->poc_type==0) { + si->poc_lsb = gf_bs_read_int(bs, si->sps->log2_max_poc_lsb); + if (si->pps->pic_order_present && !si->field_pic_flag) { + si->delta_poc_bottom = avc_get_se(bs); + } + } else if ((si->sps->poc_type==1) && !si->sps->delta_pic_order_always_zero_flag) { + si->delta_poc[0] = avc_get_se(bs); + if ((si->pps->pic_order_present==1) && !si->field_pic_flag) + si->delta_poc[1] = avc_get_se(bs); + } + if (si->pps->redundant_pic_cnt_present) { + si->redundant_pic_cnt = avc_get_ue(bs); + } + return 0; +} + + +static s32 avc_parse_recovery_point_sei(GF_BitStream *bs, AVCState *avc) +{ + AVCSeiRecoveryPoint *rp = &avc->sei.recovery_point; + + rp->frame_cnt = avc_get_ue(bs); + rp->exact_match_flag = gf_bs_read_int(bs, 1); + rp->broken_link_flag = gf_bs_read_int(bs, 1); + rp->changing_slice_group_idc = gf_bs_read_int(bs, 2); + rp->valid = 1; + + return 0; +} + +/*for interpretation see ISO 14496-10 N.11084, table D-1*/ +static s32 avc_parse_pic_timing_sei(GF_BitStream *bs, AVCState *avc) +{ + int i; + int sps_id = avc->sps_active_idx; + const char NumClockTS[] = {1, 1, 1, 2, 2, 3, 3, 2, 3}; + AVCSeiPicTiming *pt = &avc->sei.pic_timing; + + if (sps_id < 0) { + /*sps_active_idx equals -1 when no sps has been detected. In this case SEI should not be decoded.*/ + assert(0); + return 1; + } + if (avc->sps[sps_id].vui.nal_hrd_parameters_present_flag || avc->sps[sps_id].vui.vcl_hrd_parameters_present_flag) { /*CpbDpbDelaysPresentFlag, see 14496-10(2003) E.11*/ + gf_bs_read_int(bs, 1+avc->sps[sps_id].vui.hrd.cpb_removal_delay_length_minus1); /*cpb_removal_delay*/ + gf_bs_read_int(bs, 1+avc->sps[sps_id].vui.hrd.dpb_output_delay_length_minus1); /*dpb_output_delay*/ + } + + /*ISO 14496-10 (2003), D.8.2: we need to get pic_struct in order to know if we display top field first or bottom field first*/ + if (avc->sps[sps_id].vui.pic_struct_present_flag) { + pt->pic_struct = gf_bs_read_int(bs, 4); + if (pt->pic_struct > 8) { + GF_LOG(GF_LOG_ERROR, GF_LOG_CODING, ("[avc-h264] invalid pic_struct value %d\n", pt->pic_struct)); + return 1; + } + + for (i=0; ipic_struct]; i++) { + if (gf_bs_read_int(bs, 1)) {/*clock_timestamp_flag[i]*/ + Bool full_timestamp_flag; + gf_bs_read_int(bs, 2); /*ct_type*/ + gf_bs_read_int(bs, 1); /*nuit_field_based_flag*/ + gf_bs_read_int(bs, 5); /*counting_type*/ + full_timestamp_flag = gf_bs_read_int(bs, 1);/*full_timestamp_flag*/ + gf_bs_read_int(bs, 1); /*discontinuity_flag*/ + gf_bs_read_int(bs, 1); /*cnt_dropped_flag*/ + gf_bs_read_int(bs, 8); /*n_frames*/ + if (full_timestamp_flag) { + gf_bs_read_int(bs, 6); /*seconds_value*/ + gf_bs_read_int(bs, 6); /*minutes_value*/ + gf_bs_read_int(bs, 5); /*hours_value*/ + } else { + if (gf_bs_read_int(bs, 1)) { /*seconds_flag*/ + gf_bs_read_int(bs, 6); /*seconds_value*/ + if (gf_bs_read_int(bs, 1)) { /*minutes_flag*/ + gf_bs_read_int(bs, 6); /*minutes_value*/ + if (gf_bs_read_int(bs, 1)) { /*hours_flag*/ + gf_bs_read_int(bs, 5); /*hours_value*/ + } + } + } + if (avc->sps[sps_id].vui.hrd.time_offset_length > 0) + gf_bs_read_int(bs, avc->sps[sps_id].vui.hrd.time_offset_length); /*time_offset*/ + } + } + } + } + + return 0; +} + + +static void avc_compute_poc(AVCSliceInfo *si) +{ + enum { + AVC_PIC_FRAME, + AVC_PIC_FIELD_TOP, + AVC_PIC_FIELD_BOTTOM, + } pic_type; + s32 field_poc[2] = {0,0}; + s32 max_frame_num = 1 << (si->sps->log2_max_frame_num); + + /* picture type */ + if (si->sps->frame_mbs_only_flag || !si->field_pic_flag) pic_type = AVC_PIC_FRAME; + else if (si->bottom_field_flag) pic_type = AVC_PIC_FIELD_BOTTOM; + else pic_type = AVC_PIC_FIELD_TOP; + + /* frame_num_offset */ + if (si->nal_unit_type == GF_AVC_NALU_IDR_SLICE) { + si->poc_lsb_prev = 0; + si->poc_msb_prev = 0; + si->frame_num_offset = 0; + } else { + if (si->frame_num < si->frame_num_prev) + si->frame_num_offset = si->frame_num_offset_prev + max_frame_num; + else + si->frame_num_offset = si->frame_num_offset_prev; + } + + /*ISO 14496-10 N.11084 8.2.1.1*/ + if (si->sps->poc_type==0) + { + const u32 max_poc_lsb = 1 << (si->sps->log2_max_poc_lsb); + + /*ISO 14496-10 N.11084 eq (8-3)*/ + if ((si->poc_lsb < si->poc_lsb_prev) && + (si->poc_lsb_prev - si->poc_lsb >= max_poc_lsb / 2) ) + si->poc_msb = si->poc_msb_prev + max_poc_lsb; + else if ((si->poc_lsb > si->poc_lsb_prev) && + (si->poc_lsb - si->poc_lsb_prev > max_poc_lsb / 2)) + si->poc_msb = si->poc_msb_prev - max_poc_lsb; + else + si->poc_msb = si->poc_msb_prev; + + /*ISO 14496-10 N.11084 eq (8-4)*/ + if (pic_type != AVC_PIC_FIELD_BOTTOM) + field_poc[0] = si->poc_msb + si->poc_lsb; + + /*ISO 14496-10 N.11084 eq (8-5)*/ + if (pic_type != AVC_PIC_FIELD_TOP) { + if (!si->field_pic_flag) + field_poc[1] = field_poc[0] + si->delta_poc_bottom; + else + field_poc[1] = si->poc_msb + si->poc_lsb; + } + } + /*ISO 14496-10 N.11084 8.2.1.2*/ + else if (si->sps->poc_type==1) + { + u32 i; + s32 abs_frame_num, expected_delta_per_poc_cycle, expected_poc; + + if (si->sps->poc_cycle_length) + abs_frame_num = si->frame_num_offset + si->frame_num; + else + abs_frame_num = 0; + + if (!si->nal_ref_idc && (abs_frame_num > 0)) abs_frame_num--; + + expected_delta_per_poc_cycle = 0; + for (i=0; i < si->sps->poc_cycle_length; i++) + expected_delta_per_poc_cycle += si->sps->offset_for_ref_frame[i]; + + if (abs_frame_num > 0) { + const u32 poc_cycle_cnt = ( abs_frame_num - 1 ) / si->sps->poc_cycle_length; + const u32 frame_num_in_poc_cycle = ( abs_frame_num - 1 ) % si->sps->poc_cycle_length; + + expected_poc = poc_cycle_cnt * expected_delta_per_poc_cycle; + for (i = 0; i<=frame_num_in_poc_cycle; i++) + expected_poc += si->sps->offset_for_ref_frame[i]; + } else { + expected_poc = 0; + } + + if (!si->nal_ref_idc) expected_poc += si->sps->offset_for_non_ref_pic; + + field_poc[0] = expected_poc + si->delta_poc[0]; + field_poc[1] = field_poc[0] + si->sps->offset_for_top_to_bottom_field; + if (pic_type == AVC_PIC_FRAME) field_poc[1] += si->delta_poc[1]; + } + /*ISO 14496-10 N.11084 8.2.1.3*/ + else if (si->sps->poc_type== 2) + { + int poc; + if (si->nal_unit_type == GF_AVC_NALU_IDR_SLICE) { + poc = 0; + } else { + const int abs_frame_num = si->frame_num_offset + si->frame_num; + poc = 2 * abs_frame_num; + if (!si->nal_ref_idc) poc -= 1; + } + field_poc[0] = poc; + field_poc[1] = poc; + } + + /*ISO 14496-10 N.11084 eq (8-1)*/ + if (pic_type == AVC_PIC_FRAME) + si->poc = MIN(field_poc[0], field_poc[1] ); + else if (pic_type == AVC_PIC_FIELD_TOP) + si->poc = field_poc[0]; + else + si->poc = field_poc[1]; +} + +s32 AVC_ParseNALU(GF_BitStream *bs, u32 nal_hdr, AVCState *avc) +{ + u8 idr_flag; + s32 slice, ret; + AVCSliceInfo n_state; + + slice = 0; + memcpy(&n_state, &avc->s_info, sizeof(AVCSliceInfo)); + n_state.nal_unit_type = nal_hdr & 0x1F; + n_state.nal_ref_idc = (nal_hdr>>5) & 0x3; + + idr_flag = 0; + ret = 0; + switch (n_state.nal_unit_type) { + case GF_AVC_NALU_ACCESS_UNIT: + case GF_AVC_NALU_END_OF_SEQ: + case GF_AVC_NALU_END_OF_STREAM: + ret = 1; + break; + + case GF_AVC_NALU_SVC_SLICE: + SVC_ReadNal_header_extension(bs, &n_state.NalHeader); + slice = 1; + // slice buffer - read the info and compare. + ret = svc_parse_slice(bs, avc, &n_state); + if (avc->s_info.nal_ref_idc) { + n_state.poc_lsb_prev = avc->s_info.poc_lsb; + n_state.poc_msb_prev = avc->s_info.poc_msb; + } + if (slice) + avc_compute_poc(&n_state); + + if (avc->s_info.poc != n_state.poc) { + memcpy(&avc -> s_info, &n_state, sizeof(AVCSliceInfo)); + return 1; + } + memcpy(&avc -> s_info, &n_state, sizeof(AVCSliceInfo)); + return 0; + + case GF_AVC_NALU_SVC_PREFIX_NALU: + SVC_ReadNal_header_extension(bs, &n_state.NalHeader); + return 0; + + case GF_AVC_NALU_NON_IDR_SLICE: + case GF_AVC_NALU_DP_A_SLICE: + case GF_AVC_NALU_DP_B_SLICE: + case GF_AVC_NALU_DP_C_SLICE: + case GF_AVC_NALU_IDR_SLICE: + slice = 1; + /* slice buffer - read the info and compare.*/ + ret = avc_parse_slice(bs, avc, idr_flag, &n_state); + if (ret<0) return ret; + ret = 0; + if ( + ((avc->s_info.nal_unit_type > GF_AVC_NALU_IDR_SLICE) || (avc->s_info.nal_unit_type < GF_AVC_NALU_NON_IDR_SLICE)) + && (avc->s_info.nal_unit_type != GF_AVC_NALU_SVC_SLICE) + ) { + break; + } + if (avc->s_info.frame_num != n_state.frame_num) { ret = 1; break; } + + if (avc->s_info.field_pic_flag != n_state.field_pic_flag) { ret = 1; break; } + if ((avc->s_info.nal_ref_idc != n_state.nal_ref_idc) && + (!avc->s_info.nal_ref_idc || !n_state.nal_ref_idc)) { + ret = 1; + break; + } + assert(avc->s_info.sps); + + if (avc->s_info.sps->poc_type == n_state.sps->poc_type) { + if (!avc->s_info.sps->poc_type) { + if (!n_state.bottom_field_flag && (avc->s_info.poc_lsb != n_state.poc_lsb)) { + ret = 1; + break; + } + if (avc->s_info.delta_poc_bottom != n_state.delta_poc_bottom) { + ret = 1; + break; + } + } else if (avc->s_info.sps->poc_type==1) { + if (avc->s_info.delta_poc[0] != n_state.delta_poc[0]) { + ret =1; + break; + } + if (avc->s_info.delta_poc[1] != n_state.delta_poc[1]) { + ret = 1; + break; + } + } + } + + if (n_state.nal_unit_type == GF_AVC_NALU_IDR_SLICE) { + if (avc->s_info.nal_unit_type != GF_AVC_NALU_IDR_SLICE) { /*IdrPicFlag differs in value*/ + ret = 1; + break; + } + else if (avc->s_info.idr_pic_id != n_state.idr_pic_id) { /*both IDR and idr_pic_id differs*/ + ret = 1; + break; + } + } + break; + case GF_AVC_NALU_SEQ_PARAM: + case GF_AVC_NALU_PIC_PARAM: + return 0; + default: + if (avc->s_info.nal_unit_type <= GF_AVC_NALU_IDR_SLICE) ret = 1; + //To detect change of AU when multiple sps and pps in stream + else if ((nal_hdr & 0x1F) == GF_AVC_NALU_SEI && avc -> s_info .nal_unit_type == GF_AVC_NALU_SVC_SLICE) + ret = 1; + else if ((nal_hdr & 0x1F) == GF_AVC_NALU_SEQ_PARAM && avc -> s_info .nal_unit_type == GF_AVC_NALU_SVC_SLICE) + ret = 1; + else + ret = 0; + break; + } + + /* save _prev values */ + if (ret && avc->s_info.sps) { + n_state.frame_num_offset_prev = avc->s_info.frame_num_offset; + if ((avc->s_info.sps->poc_type != 2) || (avc->s_info.nal_ref_idc != 0)) + n_state.frame_num_prev = avc->s_info.frame_num; + if (avc->s_info.nal_ref_idc) { + n_state.poc_lsb_prev = avc->s_info.poc_lsb; + n_state.poc_msb_prev = avc->s_info.poc_msb; + } + } + if (slice) avc_compute_poc(&n_state); + memcpy(&avc->s_info, &n_state, sizeof(AVCSliceInfo)); + return ret; +} + +u32 AVC_ReformatSEI_NALU(char *buffer, u32 nal_size, AVCState *avc) +{ + u32 ptype, psize, hdr, written, var; + u64 start; + char *new_buffer; + GF_BitStream *bs; + char *sei_without_emulation_bytes = NULL; + u32 sei_without_emulation_bytes_size = 0; + + hdr = buffer[0]; + if ((hdr & 0x1F) != GF_AVC_NALU_SEI) return 0; + + /*PPS still contains emulation bytes*/ + sei_without_emulation_bytes = (char *) gf_malloc(nal_size*sizeof(char)); + sei_without_emulation_bytes_size = avc_remove_emulation_bytes((unsigned char *) buffer, (unsigned char *) sei_without_emulation_bytes, nal_size); + + bs = gf_bs_new(sei_without_emulation_bytes, sei_without_emulation_bytes_size, GF_BITSTREAM_READ); + gf_bs_read_int(bs, 8); + + new_buffer = (char*)gf_malloc(sizeof(char)*nal_size); + new_buffer[0] = (char) hdr; + written = 1; + + /*parse SEI*/ + while (gf_bs_available(bs)) { + Bool do_copy; + ptype = 0; + while (gf_bs_peek_bits(bs, 8, 0)==0xFF) { + gf_bs_read_int(bs, 8); + ptype += 255; + } + ptype += gf_bs_read_int(bs, 8); + psize = 0; + while (gf_bs_peek_bits(bs, 8, 0)==0xFF) { + gf_bs_read_int(bs, 8); + psize += 255; + } + psize += gf_bs_read_int(bs, 8); + + start = gf_bs_get_position(bs); + do_copy = 1; + switch (ptype) { + /*remove SEI messages forbidden in MP4*/ + case 3: /*filler data*/ + case 10: /*sub_seq info*/ + case 11: /*sub_seq_layer char*/ + case 12: /*sub_seq char*/ + do_copy = 0; + break; + case 5: /*user unregistered */ + { + u8 prev = sei_without_emulation_bytes[start+2+psize]; + sei_without_emulation_bytes[start+2+psize] = 0; + GF_LOG(GF_LOG_DEBUG, GF_LOG_CODING, ("[avc-h264] SEI user message %s\n", sei_without_emulation_bytes+start+2)); + sei_without_emulation_bytes[start+2+psize] = prev; + } + break; + + case 6: /*recovery point*/ + { + GF_BitStream *rp_bs = gf_bs_new(sei_without_emulation_bytes + start, psize, GF_BITSTREAM_READ); + avc_parse_recovery_point_sei(rp_bs, avc); + gf_bs_del(rp_bs); + } + break; + + case 1: /*pic_timing*/ + { + GF_BitStream *pt_bs = gf_bs_new(sei_without_emulation_bytes + start, psize, GF_BITSTREAM_READ); + avc_parse_pic_timing_sei(pt_bs, avc); + gf_bs_del(pt_bs); + } + break; + + case 0: /*buffering period*/ + case 2: /*pan scan rect*/ + case 4: /*user registered ITU t35*/ + case 7: /*def_rec_pic_marking_repetition*/ + case 8: /*spare_pic*/ + case 9: /*scene info*/ + case 13: /*full frame freeze*/ + case 14: /*full frame freeze release*/ + case 15: /*full frame snapshot*/ + case 16: /*progressive refinement segment start*/ + case 17: /*progressive refinement segment end*/ + case 18: /*motion constrained slice group*/ + default: /*reserved*/ + break; + } + + if (do_copy) { + var = ptype; + while (var>=255) { + new_buffer[written] = (char) 0xff; + written++; + var-=255; + } + new_buffer[written] = (char) var; + written++; + var = psize; + while (var>=255) { + new_buffer[written] = (char) 0xff; + written++; var-=255; + } + new_buffer[written] = (char) var; + written++; + memcpy(new_buffer+written, sei_without_emulation_bytes+start, sizeof(char) * psize); + written += psize; + } + + gf_bs_skip_bytes(bs, (u64) psize); + gf_bs_align(bs); + if (gf_bs_available(bs)<=2) { + if (gf_bs_peek_bits(bs, 8, 0)==0x80) { + new_buffer[written] = (char) 0x80; + written += 1; + } + break; + } + } + gf_bs_del(bs); + gf_free(sei_without_emulation_bytes); + + if (written) { + var = avc_emulation_bytes_add_count((unsigned char *) new_buffer, written); + if (var) { + assert(written+var<=nal_size); + written = avc_add_emulation_bytes((unsigned char *) new_buffer, (unsigned char *) buffer, written); + } else { + assert(written<=nal_size); + memcpy(buffer, new_buffer, sizeof(char)*written); + } + } + gf_free(new_buffer); + + /*if only hdr written ignore*/ + return (written>1) ? written : 0; +} + +static u8 avc_get_sar_idx(u32 w, u32 h) +{ + u32 i; + for (i=0; i<14; i++) { + if ((avc_sar[i].w==w) && (avc_sar[i].h==h)) return i; + } + return 0xFF; +} + +#ifndef GPAC_DISABLE_ISOM +GF_Err AVC_ChangePAR(GF_AVCConfig *avcc, s32 ar_n, s32 ar_d) +{ + GF_BitStream *orig, *mod; + AVCState avc; + u32 i, bit_offset, flag; + s32 idx; + GF_AVCConfigSlot *slc; + orig = NULL; + + memset(&avc, 0, sizeof(AVCState)); + avc.sps_active_idx = -1; + + i=0; + while ((slc = (GF_AVCConfigSlot *)gf_list_enum(avcc->sequenceParameterSets, &i))) { + char *no_emulation_buf = NULL; + u32 no_emulation_buf_size = 0, emulation_bytes = 0; + idx = AVC_ReadSeqInfo(slc->data+1/*skip NALU type*/, slc->size-1, &avc, 0, &bit_offset); + if (idx<0) { + if ( orig ) + gf_bs_del(orig); + continue; + } + + /*SPS still contains emulation bytes*/ + no_emulation_buf = (char *) gf_malloc((slc->size-1)*sizeof(char)); + no_emulation_buf_size = avc_remove_emulation_bytes((unsigned char *) slc->data+1, (unsigned char *) no_emulation_buf, slc->size-1); + + orig = gf_bs_new(no_emulation_buf, no_emulation_buf_size, GF_BITSTREAM_READ); + gf_bs_read_data(orig, no_emulation_buf, no_emulation_buf_size); + gf_bs_seek(orig, 0); + mod = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); + + /*copy over till vui flag*/ + while (bit_offset) { + flag = gf_bs_read_int(orig, 1); + gf_bs_write_int(mod, flag, 1); + bit_offset--; + } + /*check VUI*/ + flag = gf_bs_read_int(orig, 1); + gf_bs_write_int(mod, 1, 1); + if (flag) { + /*aspect_ratio_info_present_flag*/ + if (gf_bs_read_int(orig, 1)) { + s32 aspect_ratio_idc = gf_bs_read_int(orig, 8); + if (aspect_ratio_idc == 255) { + gf_bs_read_int(orig, 16); /*AR num*/ + gf_bs_read_int(orig, 16); /*AR den*/ + } + } + } + if ((ar_d<0) || (ar_n<0)) { + /*no AR signaled*/ + gf_bs_write_int(mod, 0, 1); + } else { + u32 sarx; + gf_bs_write_int(mod, 1, 1); + sarx = avc_get_sar_idx((u32) ar_n, (u32) ar_d); + gf_bs_write_int(mod, sarx, 8); + if (sarx==0xFF) { + gf_bs_write_int(mod, ar_n, 16); + gf_bs_write_int(mod, ar_d, 16); + } + } + /*no VUI in input bitstream, set all vui flags to 0*/ + if (!flag) { + gf_bs_write_int(mod, 0, 1); /*overscan_info_present_flag */ + gf_bs_write_int(mod, 0, 1); /*video_signal_type_present_flag */ + gf_bs_write_int(mod, 0, 1); /*chroma_location_info_present_flag */ + gf_bs_write_int(mod, 0, 1); /*timing_info_present_flag*/ + gf_bs_write_int(mod, 0, 1); /*nal_hrd_parameters_present*/ + gf_bs_write_int(mod, 0, 1); /*vcl_hrd_parameters_present*/ + gf_bs_write_int(mod, 0, 1); /*pic_struct_present*/ + gf_bs_write_int(mod, 0, 1); /*bitstream_restriction*/ + } + + /*finally copy over remaining*/ + while (gf_bs_bits_available(orig)) { + flag = gf_bs_read_int(orig, 1); + gf_bs_write_int(mod, flag, 1); + } + gf_bs_del(orig); + orig = NULL; + gf_free(no_emulation_buf); + + /*set anti-emulation*/ + gf_bs_get_content(mod, (char **) &no_emulation_buf, &flag); + emulation_bytes = avc_emulation_bytes_add_count((unsigned char *) no_emulation_buf, flag); + if (flag+emulation_bytes+1>slc->size) + slc->data = (char*)gf_realloc(slc->data, flag+emulation_bytes); + slc->size = avc_add_emulation_bytes((unsigned char *) no_emulation_buf, (unsigned char *) slc->data+1, flag)+1; + + gf_bs_del(mod); + gf_free(no_emulation_buf); + } + return GF_OK; +} +#endif + +GF_EXPORT +GF_Err gf_avc_get_sps_info(char *sps_data, u32 sps_size, u32 *sps_id, u32 *width, u32 *height, s32 *par_n, s32 *par_d) +{ + AVCState avc; + s32 idx; + memset(&avc, 0, sizeof(AVCState)); + avc.sps_active_idx = -1; + + idx = AVC_ReadSeqInfo(sps_data+1/*skip NALU type*/, sps_size-1, &avc, 0, NULL); + if (idx<0) { + return GF_NON_COMPLIANT_BITSTREAM; + } + if (sps_id) *sps_id = idx; + + if (width) *width = avc.sps[idx].width; + if (height) *height = avc.sps[idx].height; + if (par_n) *par_n = avc.sps[idx].vui.par_num ? avc.sps[idx].vui.par_num : (u32) -1; + if (par_d) *par_d = avc.sps[idx].vui.par_den ? avc.sps[idx].vui.par_den : (u32) -1; + + return GF_OK; +} + +#endif /*GPAC_DISABLE_AV_PARSERS*/ + + + +#ifndef GPAC_DISABLE_AV_PARSERS + +static u32 AC3_FindSyncCode(u8 *buf, u32 buflen) +{ + u32 end = buflen - 6; + u32 offset = 0; + while (offset <= end) { + if (buf[offset] == 0x0b && buf[offset + 1] == 0x77) { + return offset; + } + offset++; + } + return buflen; +} + + +static Bool AC3_FindSyncCodeBS(GF_BitStream *bs) +{ + u8 b1; + u64 pos = gf_bs_get_position(bs); + u64 end = gf_bs_get_size(bs) - 6; + + pos += 1; + b1 = gf_bs_read_u8(bs); + while (pos <= end) { + u8 b2 = gf_bs_read_u8(bs); + if ((b1 == 0x0b) && (b2==0x77)) { + gf_bs_seek(bs, pos-1); + return 1; + } + pos++; + } + return 0; +} + +static const u32 ac3_sizecod_to_bitrate[] = { + 32000, 40000, 48000, 56000, 64000, 80000, 96000, + 112000, 128000, 160000, 192000, 224000, 256000, + 320000, 384000, 448000, 512000, 576000, 640000 +}; + +static const u32 ac3_sizecod2_to_framesize[] = { + 96, 120, 144, 168, 192, 240, 288, 336, 384, 480, 576, 672, + 768, 960, 1152, 1344, 1536, 1728, 1920 +}; + +static const u32 ac3_sizecod1_to_framesize[] = { + 69, 87, 104, 121, 139, 174, 208, 243, 278, 348, 417, 487, + 557, 696, 835, 975, 1114, 1253, 1393 +}; +static const u32 ac3_sizecod0_to_framesize[] = { + 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384, 448, + 512, 640, 768, 896, 1024, 1152, 1280 +}; + +static const u32 ac3_mod_to_chans[] = { + 2, 1, 2, 3, 3, 4, 4, 5 +}; + +u32 gf_ac3_get_channels(u32 acmod) +{ + u32 nb_ch; + nb_ch = ac3_mod_to_chans[acmod]; + return nb_ch; +} + +u32 gf_ac3_get_bitrate(u32 brcode) +{ + return ac3_sizecod_to_bitrate[brcode]; +} + +Bool gf_ac3_parser(u8 *buf, u32 buflen, u32 *pos, GF_AC3Header *hdr, Bool full_parse) +{ + u32 fscod, frmsizecod, bsid, ac3_mod, freq, framesize; + if (buflen < 6) return 0; + (*pos) = AC3_FindSyncCode(buf, buflen); + if (*pos >= buflen) return 0; + + buf += (*pos); + fscod = (buf[4] >> 6) & 0x3; + frmsizecod = (buf[4] & 0x3f); + bsid = (buf[5] >> 3) & 0x1f; + ac3_mod = (buf[6] >> 5) & 0x7; + if (bsid >= 12) return 0; + + if (full_parse && hdr) memset(hdr, 0, sizeof(GF_AC3Header)); + + if (hdr) { + hdr->bitrate = ac3_sizecod_to_bitrate[frmsizecod / 2]; + if (bsid > 8) hdr->bitrate = hdr->bitrate >> (bsid - 8); + } + switch (fscod) { + case 0: + freq = 48000; + framesize = ac3_sizecod0_to_framesize[frmsizecod / 2] * 2; + break; + case 1: + freq = 44100; + framesize = (ac3_sizecod1_to_framesize[frmsizecod / 2] + (frmsizecod & 0x1)) * 2; + break; + case 2: + freq = 32000; + framesize = ac3_sizecod2_to_framesize[frmsizecod / 2] * 2; + break; + default: + return 0; + } + if (hdr) { + u16 maskbit, b67; + hdr->sample_rate = freq; + hdr->framesize = framesize; + + hdr->channels = ac3_mod_to_chans[ac3_mod]; + maskbit = 0x100; + if ((ac3_mod & 0x1) && (ac3_mod != 1)) maskbit >>= 2; + if (ac3_mod & 0x4) maskbit >>= 2; + if (ac3_mod == 0x2) maskbit += 2; + b67 = (buf[6] << 8) | buf[7]; + if ((b67 & maskbit) != 0) hdr->channels += 1; + } + return 1; +} + + +Bool gf_ac3_parser_bs(GF_BitStream *bs, GF_AC3Header *hdr, Bool full_parse) +{ + u32 fscod, frmsizecod, bsid, ac3_mod, freq, framesize, bsmod; + u64 pos; + if (!hdr || (gf_bs_available(bs) < 6)) return 0; + if (!AC3_FindSyncCodeBS(bs)) return 0; + + pos = gf_bs_get_position(bs); + gf_bs_read_u32(bs); + fscod = gf_bs_read_int(bs, 2); + frmsizecod = gf_bs_read_int(bs, 6); + bsid = gf_bs_read_int(bs, 5); + bsmod = gf_bs_read_int(bs, 3); + ac3_mod = gf_bs_read_int(bs, 3); + + if (bsid >= 12) return 0; + + //memset(hdr, 0, sizeof(GF_AC3Header)); + + hdr->bitrate = ac3_sizecod_to_bitrate[frmsizecod / 2]; + if (bsid > 8) hdr->bitrate = hdr->bitrate >> (bsid - 8); + + switch (fscod) { + case 0: + freq = 48000; + framesize = ac3_sizecod0_to_framesize[frmsizecod / 2] * 2; + break; + case 1: + freq = 44100; + framesize = (ac3_sizecod1_to_framesize[frmsizecod / 2] + (frmsizecod & 0x1)) * 2; + break; + case 2: + freq = 32000; + framesize = ac3_sizecod2_to_framesize[frmsizecod / 2] * 2; + break; + default: + return 0; + } + hdr->sample_rate = freq; + hdr->framesize = framesize; + + if (full_parse) { + hdr->bsid = bsid; + hdr->bsmod = bsmod; + hdr->acmod = ac3_mod; + hdr->lfon = 0; + hdr->fscod = fscod; + hdr->brcode = frmsizecod / 2; + } + + hdr->channels = ac3_mod_to_chans[ac3_mod]; + if ((ac3_mod & 0x1) && (ac3_mod != 1)) gf_bs_read_int(bs, 2); + if (ac3_mod & 0x4) gf_bs_read_int(bs, 2); + if (ac3_mod == 0x2) gf_bs_read_int(bs, 2); + /*LFEon*/ + if (gf_bs_read_int(bs, 1)) { + hdr->channels += 1; + hdr->lfon = 1; + } + + + gf_bs_seek(bs, pos); + return 1; +} + +#endif /*GPAC_DISABLE_AV_PARSERS*/ + + +#if !defined(GPAC_DISABLE_AV_PARSERS) && !defined (GPAC_DISABLE_OGG) + +/* + Vorbis parser +*/ + +static u32 vorbis_book_maptype1_quantvals(u32 entries, u32 dim) +{ + u32 vals = (u32) floor(pow(entries, 1.0/dim)); + while(1) { + u32 acc=1; + u32 acc1=1; + u32 i; + for (i=0;ientries) return (vals); + else { + if (acc>entries) vals--; + else vals++; + } + } +} + +u32 _ilog_(u32 v) +{ + u32 ret=0; + while(v) { + ret++; + v>>=1; + } + return(ret); +} + +static u32 ilog(u32 v) +{ + u32 ret=0; + if(v) --v; + while(v) { + ret++; + v>>=1; + } + return (ret); +} + +static u32 icount(u32 v) +{ + u32 ret=0; + while(v) { + ret += v&1; + v>>=1; + } + return(ret); +} + + +GF_EXPORT +Bool gf_vorbis_parse_header(GF_VorbisParser *vp, char *data, u32 data_len) +{ + u32 pack_type, i, j, k, times, nb_part, nb_books, nb_modes; + char szNAME[8]; + oggpack_buffer opb; + + oggpack_readinit(&opb, (u8*)data, data_len); + pack_type = oggpack_read(&opb, 8); + i=0; + while (i<6) { szNAME[i] = oggpack_read(&opb, 8); i++;} + szNAME[i] = 0; + if (strcmp(szNAME, "vorbis")) return vp->is_init = 0; + + switch (pack_type) { + case 0x01: + vp->version = oggpack_read(&opb, 32); + if (vp->version!=0) return 0; + vp->channels = oggpack_read(&opb, 8); + vp->sample_rate = oggpack_read(&opb, 32); + vp->max_r = oggpack_read(&opb, 32); + vp->avg_r = oggpack_read(&opb, 32); + vp->low_r = oggpack_read(&opb, 32); + + vp->min_block = 1<max_block = 1<sample_rate < 1) return vp->is_init = 0; + if (vp->channels < 1) return vp->is_init = 0; + if (vp->min_block<8) return vp->is_init = 0; + if (vp->max_block < vp->min_block) return vp->is_init = 0; + if (oggpack_read(&opb, 1) != 1) return vp->is_init = 0; + vp->is_init = 1; + return 1; + case 0x03: + /*trash comments*/ + vp->is_init ++; + return 1; + case 0x05: + /*need at least bitstream header to make sure we're parsing the right thing*/ + if (!vp->is_init) return 0; + break; + default: + vp->is_init = 0; + return 0; + } + /*OK parse codebook*/ + nb_books = oggpack_read(&opb, 8) + 1; + /*skip vorbis static books*/ + for (i=0; ichannels)); + oggpack_read(&opb, ilog(vp->channels)); + } + } + oggpack_read(&opb, 2); + if (sub_maps>1) { + for(j=0; jchannels; j++) oggpack_read(&opb, 4); + } + for (j=0;jmode_flag[i] = oggpack_read(&opb, 1); + oggpack_read(&opb, 16); + oggpack_read(&opb, 16); + oggpack_read(&opb, 8); + } + + vp->modebits = 0; + j = nb_modes; + while(j>1) { + vp->modebits++; + j>>=1; + } + return 1; +} + +GF_EXPORT +u32 gf_vorbis_check_frame(GF_VorbisParser *vp, char *data, u32 data_length) +{ + s32 block_size; + oggpack_buffer opb; + if (!vp->is_init) return 0; + oggpack_readinit(&opb, (unsigned char*)data, data_length); + /*not audio*/ + if (oggpack_read(&opb, 1) !=0) return 0; + block_size = oggpack_read(&opb, vp->modebits); + if (block_size == -1) return 0; + return ((vp->mode_flag[block_size]) ? vp->max_block : vp->min_block) / (2); +} + +#endif /*!defined(GPAC_DISABLE_AV_PARSERS) && !defined (GPAC_DISABLE_OGG)*/ + diff --git a/src/gpacmp4/avc_ext.c b/src/gpacmp4/avc_ext.c new file mode 100644 index 00000000..61192656 --- /dev/null +++ b/src/gpacmp4/avc_ext.c @@ -0,0 +1,552 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / ISO Media File Format sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include +#include + +#ifndef GPAC_DISABLE_ISOM + +static GF_AVCConfig *AVC_DuplicateConfig(GF_AVCConfig *cfg) +{ + u32 i, count; + GF_AVCConfigSlot *p1, *p2; + GF_AVCConfig *cfg_new = gf_odf_avc_cfg_new(); + cfg_new->AVCLevelIndication = cfg->AVCLevelIndication; + cfg_new->AVCProfileIndication = cfg->AVCProfileIndication; + cfg_new->configurationVersion = cfg->configurationVersion; + cfg_new->nal_unit_size = cfg->nal_unit_size; + cfg_new->profile_compatibility = cfg->profile_compatibility; + cfg_new->complete_representation = cfg->complete_representation; + + count = gf_list_count(cfg->sequenceParameterSets); + for (i=0; isequenceParameterSets, i); + p2 = (GF_AVCConfigSlot*)gf_malloc(sizeof(GF_AVCConfigSlot)); + p2->size = p1->size; + p2->data = (char *)gf_malloc(sizeof(char)*p1->size); + memcpy(p2->data, p1->data, sizeof(char)*p1->size); + gf_list_add(cfg_new->sequenceParameterSets, p2); + } + + count = gf_list_count(cfg->pictureParameterSets); + for (i=0; ipictureParameterSets, i); + p2 = (GF_AVCConfigSlot*)gf_malloc(sizeof(GF_AVCConfigSlot)); + p2->size = p1->size; + p2->data = (char*)gf_malloc(sizeof(char)*p1->size); + memcpy(p2->data, p1->data, sizeof(char)*p1->size); + gf_list_add(cfg_new->pictureParameterSets, p2); + } + return cfg_new; +} + + +void AVC_RewriteESDescriptor(GF_MPEGVisualSampleEntryBox *avc) +{ + GF_AVCConfig *avcc, *svcc; + if (avc->emul_esd) gf_odf_desc_del((GF_Descriptor *)avc->emul_esd); + avc->emul_esd = gf_odf_desc_esd_new(2); + avc->emul_esd->decoderConfig->streamType = GF_STREAM_VISUAL; + /*AVC OTI is 0x21, AVC parameter set stream OTI (not supported in gpac) is 0x22*/ + avc->emul_esd->decoderConfig->objectTypeIndication = GPAC_OTI_VIDEO_AVC; + if (avc->bitrate) { + avc->emul_esd->decoderConfig->bufferSizeDB = avc->bitrate->bufferSizeDB; + avc->emul_esd->decoderConfig->avgBitrate = avc->bitrate->avgBitrate; + avc->emul_esd->decoderConfig->maxBitrate = avc->bitrate->maxBitrate; + } + if (avc->descr) { + u32 i=0; + GF_Descriptor *desc,*clone; + i=0; + while ((desc = (GF_Descriptor *)gf_list_enum(avc->descr->descriptors, &i))) { + clone = NULL; + gf_odf_desc_copy(desc, &clone); + if (gf_odf_desc_add_desc((GF_Descriptor *)avc->emul_esd, clone) != GF_OK) + gf_odf_desc_del(clone); + } + } + if (avc->avc_config) { + avcc = avc->avc_config->config ? AVC_DuplicateConfig(avc->avc_config->config) : NULL; + /*merge SVC config*/ + if (avc->svc_config) { + svcc = AVC_DuplicateConfig(avc->svc_config->config); + while (gf_list_count(svcc->sequenceParameterSets)) { + GF_AVCConfigSlot *p = (GF_AVCConfigSlot*)gf_list_get(svcc->sequenceParameterSets, 0); + gf_list_rem(svcc->sequenceParameterSets, 0); + gf_list_add(avcc->sequenceParameterSets, p); + } + while (gf_list_count(svcc->pictureParameterSets)) { + GF_AVCConfigSlot *p = (GF_AVCConfigSlot*)gf_list_get(svcc->pictureParameterSets, 0); + gf_list_rem(svcc->pictureParameterSets, 0); + gf_list_add(avcc->pictureParameterSets, p); + } + gf_odf_avc_cfg_del(svcc); + } + if (avcc) { + gf_odf_avc_cfg_write(avcc, &avc->emul_esd->decoderConfig->decoderSpecificInfo->data, &avc->emul_esd->decoderConfig->decoderSpecificInfo->dataLength); + gf_odf_avc_cfg_del(avcc); + } + } else { + svcc = AVC_DuplicateConfig(avc->svc_config->config); + gf_odf_avc_cfg_write(svcc, &avc->emul_esd->decoderConfig->decoderSpecificInfo->data, &avc->emul_esd->decoderConfig->decoderSpecificInfo->dataLength); + gf_odf_avc_cfg_del(svcc); + } +} + +GF_Err AVC_UpdateESD(GF_MPEGVisualSampleEntryBox *avc, GF_ESD *esd) +{ + if (!avc->bitrate) avc->bitrate = (GF_MPEG4BitRateBox*)gf_isom_box_new(GF_ISOM_BOX_TYPE_BTRT); + if (avc->descr) gf_isom_box_del((GF_Box *) avc->descr); + avc->descr = NULL; + avc->bitrate->avgBitrate = esd->decoderConfig->avgBitrate; + avc->bitrate->maxBitrate = esd->decoderConfig->maxBitrate; + avc->bitrate->bufferSizeDB = esd->decoderConfig->bufferSizeDB; + + if (gf_list_count(esd->IPIDataSet) + || gf_list_count(esd->IPMPDescriptorPointers) + || esd->langDesc + || gf_list_count(esd->extensionDescriptors) + || esd->ipiPtr || esd->qos || esd->RegDescriptor) { + + avc->descr = (GF_MPEG4ExtensionDescriptorsBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_M4DS); + if (esd->RegDescriptor) { gf_list_add(avc->descr->descriptors, esd->RegDescriptor); esd->RegDescriptor = NULL; } + if (esd->qos) { gf_list_add(avc->descr->descriptors, esd->qos); esd->qos = NULL; } + if (esd->ipiPtr) { gf_list_add(avc->descr->descriptors, esd->ipiPtr); esd->ipiPtr= NULL; } + + while (gf_list_count(esd->IPIDataSet)) { + GF_Descriptor *desc = (GF_Descriptor *)gf_list_get(esd->IPIDataSet, 0); + gf_list_rem(esd->IPIDataSet, 0); + gf_list_add(avc->descr->descriptors, desc); + } + while (gf_list_count(esd->IPMPDescriptorPointers)) { + GF_Descriptor *desc = (GF_Descriptor *)gf_list_get(esd->IPMPDescriptorPointers, 0); + gf_list_rem(esd->IPMPDescriptorPointers, 0); + gf_list_add(avc->descr->descriptors, desc); + } + if (esd->langDesc) { + gf_list_add(avc->descr->descriptors, esd->langDesc); + esd->langDesc = NULL; + } + while (gf_list_count(esd->extensionDescriptors)) { + GF_Descriptor *desc = (GF_Descriptor *)gf_list_get(esd->extensionDescriptors, 0); + gf_list_rem(esd->extensionDescriptors, 0); + gf_list_add(avc->descr->descriptors, desc); + } + } + + /*update GF_AVCConfig*/ + if (!avc->svc_config) { + if (!avc->avc_config) avc->avc_config = (GF_AVCConfigurationBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_AVCC); + if (esd->decoderConfig->decoderSpecificInfo && esd->decoderConfig->decoderSpecificInfo->data) { + if (avc->avc_config->config) gf_odf_avc_cfg_del(avc->avc_config->config); + avc->avc_config->config = gf_odf_avc_cfg_read(esd->decoderConfig->decoderSpecificInfo->data, esd->decoderConfig->decoderSpecificInfo->dataLength); + } + gf_odf_desc_del((GF_Descriptor *)esd); + } + AVC_RewriteESDescriptor(avc); + return GF_OK; +} + + +#ifndef GPAC_DISABLE_ISOM_WRITE +GF_Err gf_isom_avc_config_new(GF_ISOFile *the_file, u32 trackNumber, GF_AVCConfig *cfg, char *URLname, char *URNname, u32 *outDescriptionIndex) +{ + GF_TrackBox *trak; + GF_Err e; + u32 dataRefIndex; + GF_MPEGVisualSampleEntryBox *entry; + + e = CanAccessMovie(the_file, GF_ISOM_OPEN_WRITE); + if (e) return e; + + trak = gf_isom_get_track_from_file(the_file, trackNumber); + if (!trak || !trak->Media || !cfg) return GF_BAD_PARAM; + + //get or create the data ref + e = Media_FindDataRef(trak->Media->information->dataInformation->dref, URLname, URNname, &dataRefIndex); + if (e) return e; + if (!dataRefIndex) { + e = Media_CreateDataRef(trak->Media->information->dataInformation->dref, URLname, URNname, &dataRefIndex); + if (e) return e; + } + trak->Media->mediaHeader->modificationTime = gf_isom_get_mp4time(); + + //create a new entry + entry = (GF_MPEGVisualSampleEntryBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_AVC1); + if (!entry) return GF_OUT_OF_MEM; + entry->avc_config = (GF_AVCConfigurationBox*)gf_isom_box_new(GF_ISOM_BOX_TYPE_AVCC); + entry->avc_config->config = AVC_DuplicateConfig(cfg); + entry->dataReferenceIndex = dataRefIndex; + e = gf_list_add(trak->Media->information->sampleTable->SampleDescription->boxList, entry); + *outDescriptionIndex = gf_list_count(trak->Media->information->sampleTable->SampleDescription->boxList); + AVC_RewriteESDescriptor(entry); + return e; +} + +static GF_Err gf_isom_avc_config_update_ex(GF_ISOFile *the_file, u32 trackNumber, u32 DescriptionIndex, GF_AVCConfig *cfg, u32 op_type) +{ + GF_TrackBox *trak; + GF_Err e; + GF_MPEGVisualSampleEntryBox *entry; + + e = CanAccessMovie(the_file, GF_ISOM_OPEN_WRITE); + if (e) return e; + trak = gf_isom_get_track_from_file(the_file, trackNumber); + if (!trak || !trak->Media || !cfg || !DescriptionIndex) return GF_BAD_PARAM; + entry = (GF_MPEGVisualSampleEntryBox *)gf_list_get(trak->Media->information->sampleTable->SampleDescription->boxList, DescriptionIndex-1); + if (!entry) return GF_BAD_PARAM; + switch (entry->type) { + case GF_ISOM_BOX_TYPE_AVC1: + case GF_ISOM_BOX_TYPE_AVC2: + case GF_ISOM_BOX_TYPE_SVC1: + break; + default: + return GF_BAD_PARAM; + } + + switch (op_type) { + /*AVCC replacement*/ + case 0: + if (!entry->avc_config) entry->avc_config = (GF_AVCConfigurationBox*)gf_isom_box_new(GF_ISOM_BOX_TYPE_AVCC); + if (entry->avc_config->config) gf_odf_avc_cfg_del(entry->avc_config->config); + entry->avc_config->config = AVC_DuplicateConfig(cfg); + entry->type = GF_ISOM_BOX_TYPE_AVC1; + break; + /*SVCC replacement*/ + case 1: + if (!entry->svc_config) entry->svc_config = (GF_AVCConfigurationBox*)gf_isom_box_new(GF_ISOM_BOX_TYPE_SVCC); + if (entry->svc_config->config) gf_odf_avc_cfg_del(entry->svc_config->config); + entry->svc_config->config = AVC_DuplicateConfig(cfg); + entry->type = GF_ISOM_BOX_TYPE_AVC1; + break; + /*SVCC replacement and AVC removal*/ + case 2: + if (entry->avc_config) { + gf_isom_box_del((GF_Box*)entry->avc_config); + entry->avc_config = NULL; + } + if (!entry->svc_config) entry->svc_config = (GF_AVCConfigurationBox*)gf_isom_box_new(GF_ISOM_BOX_TYPE_SVCC); + if (entry->svc_config->config) gf_odf_avc_cfg_del(entry->svc_config->config); + entry->svc_config->config = AVC_DuplicateConfig(cfg); + entry->type = GF_ISOM_BOX_TYPE_SVC1; + break; + } + AVC_RewriteESDescriptor(entry); + return GF_OK; +} + +GF_Err gf_isom_avc_config_update(GF_ISOFile *the_file, u32 trackNumber, u32 DescriptionIndex, GF_AVCConfig *cfg) +{ + return gf_isom_avc_config_update_ex(the_file, trackNumber, DescriptionIndex, cfg, 0); +} + +GF_Err gf_isom_svc_config_update(GF_ISOFile *the_file, u32 trackNumber, u32 DescriptionIndex, GF_AVCConfig *cfg, Bool is_add) +{ + return gf_isom_avc_config_update_ex(the_file, trackNumber, DescriptionIndex, cfg, is_add ? 1 : 2); +} + +GF_Err gf_isom_set_ipod_compatible(GF_ISOFile *the_file, u32 trackNumber) +{ + static const u8 ipod_ext[][16] = { { 0x6B, 0x68, 0x40, 0xF2, 0x5F, 0x24, 0x4F, 0xC5, 0xBA, 0x39, 0xA5, 0x1B, 0xCF, 0x03, 0x23, 0xF3} }; + GF_TrackBox *trak; + GF_Err e; + GF_MPEGVisualSampleEntryBox *entry; + + e = CanAccessMovie(the_file, GF_ISOM_OPEN_WRITE); + if (e) return e; + trak = gf_isom_get_track_from_file(the_file, trackNumber); + if (!trak || !trak->Media) return GF_BAD_PARAM; + entry = (GF_MPEGVisualSampleEntryBox*)gf_list_get(trak->Media->information->sampleTable->SampleDescription->boxList, 0); + if (!entry) return GF_OK; + switch (entry->type) { + case GF_ISOM_BOX_TYPE_AVC1: + case GF_ISOM_BOX_TYPE_AVC2: + case GF_ISOM_BOX_TYPE_SVC1: + break; + default: + return GF_OK; + } + + if (!entry->ipod_ext) entry->ipod_ext = (GF_UnknownUUIDBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_UUID); + memcpy(entry->ipod_ext->uuid, ipod_ext, sizeof(u8)*16); + entry->ipod_ext->dataSize = 0; + return GF_OK; +} + +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + +GF_EXPORT +GF_AVCConfig *gf_isom_avc_config_get(GF_ISOFile *the_file, u32 trackNumber, u32 DescriptionIndex) +{ + GF_TrackBox *trak; + GF_MPEGVisualSampleEntryBox *entry; + trak = gf_isom_get_track_from_file(the_file, trackNumber); + if (!trak || !trak->Media || !DescriptionIndex) return NULL; + entry = (GF_MPEGVisualSampleEntryBox*)gf_list_get(trak->Media->information->sampleTable->SampleDescription->boxList, DescriptionIndex-1); + if (!entry) return NULL; + //if (entry->type != GF_ISOM_BOX_TYPE_AVC1) return NULL; + if (!entry->avc_config) return NULL; + return AVC_DuplicateConfig(entry->avc_config->config); +} + +GF_EXPORT +GF_AVCConfig *gf_isom_svc_config_get(GF_ISOFile *the_file, u32 trackNumber, u32 DescriptionIndex) +{ + GF_TrackBox *trak; + GF_MPEGVisualSampleEntryBox *entry; + trak = gf_isom_get_track_from_file(the_file, trackNumber); + if (!trak || !trak->Media || !DescriptionIndex) return NULL; + entry = (GF_MPEGVisualSampleEntryBox*)gf_list_get(trak->Media->information->sampleTable->SampleDescription->boxList, DescriptionIndex-1); + if (!entry) return NULL; + if (!entry->svc_config) return NULL; + return AVC_DuplicateConfig(entry->svc_config->config); +} + +void btrt_del(GF_Box *s) +{ + GF_MPEG4BitRateBox *ptr = (GF_MPEG4BitRateBox *)s; + if (ptr) gf_free(ptr); +} +GF_Err btrt_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_MPEG4BitRateBox *ptr = (GF_MPEG4BitRateBox *)s; + ptr->bufferSizeDB = gf_bs_read_u32(bs); + ptr->maxBitrate = gf_bs_read_u32(bs); + ptr->avgBitrate = gf_bs_read_u32(bs); + return GF_OK; +} +GF_Box *btrt_New() +{ + GF_MPEG4BitRateBox *tmp = (GF_MPEG4BitRateBox *) gf_malloc(sizeof(GF_MPEG4BitRateBox)); + if (tmp == NULL) return NULL; + memset(tmp, 0, sizeof(GF_MPEG4BitRateBox)); + tmp->type = GF_ISOM_BOX_TYPE_BTRT; + return (GF_Box *)tmp; +} + +#ifndef GPAC_DISABLE_ISOM_WRITE +GF_Err btrt_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_MPEG4BitRateBox *ptr = (GF_MPEG4BitRateBox *) s; + if (!s) return GF_BAD_PARAM; + e = gf_isom_box_write_header(s, bs); + if (e) return e; + gf_bs_write_u32(bs, ptr->bufferSizeDB); + gf_bs_write_u32(bs, ptr->maxBitrate); + gf_bs_write_u32(bs, ptr->avgBitrate); + return GF_OK; +} +GF_Err btrt_Size(GF_Box *s) +{ + GF_Err e; + GF_MPEG4BitRateBox *ptr = (GF_MPEG4BitRateBox *)s; + e = gf_isom_box_get_size(s); + ptr->size += 12; + return e; +} +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + + + +void m4ds_del(GF_Box *s) +{ + GF_MPEG4ExtensionDescriptorsBox *ptr = (GF_MPEG4ExtensionDescriptorsBox *)s; + gf_odf_desc_list_del(ptr->descriptors); + gf_list_del(ptr->descriptors); + gf_free(ptr); +} +GF_Err m4ds_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + char *enc_od; + GF_MPEG4ExtensionDescriptorsBox *ptr = (GF_MPEG4ExtensionDescriptorsBox *)s; + u32 od_size = (u32) ptr->size; + if (!od_size) return GF_OK; + enc_od = (char *)gf_malloc(sizeof(char) * od_size); + gf_bs_read_data(bs, enc_od, od_size); + e = gf_odf_desc_list_read((char *)enc_od, od_size, ptr->descriptors); + gf_free(enc_od); + return e; +} +GF_Box *m4ds_New() +{ + GF_MPEG4ExtensionDescriptorsBox *tmp = (GF_MPEG4ExtensionDescriptorsBox *) gf_malloc(sizeof(GF_MPEG4ExtensionDescriptorsBox)); + if (tmp == NULL) return NULL; + memset(tmp, 0, sizeof(GF_MPEG4ExtensionDescriptorsBox)); + tmp->type = GF_ISOM_BOX_TYPE_M4DS; + tmp->descriptors = gf_list_new(); + return (GF_Box *)tmp; +} + +#ifndef GPAC_DISABLE_ISOM_WRITE +GF_Err m4ds_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + char *enc_ods; + u32 enc_od_size; + GF_MPEG4ExtensionDescriptorsBox *ptr = (GF_MPEG4ExtensionDescriptorsBox *) s; + if (!s) return GF_BAD_PARAM; + e = gf_isom_box_write_header(s, bs); + if (e) return e; + enc_ods = NULL; + enc_od_size = 0; + e = gf_odf_desc_list_write(ptr->descriptors, &enc_ods, &enc_od_size); + if (e) return e; + if (enc_od_size) { + gf_bs_write_data(bs, enc_ods, enc_od_size); + gf_free(enc_ods); + } + return GF_OK; +} +GF_Err m4ds_Size(GF_Box *s) +{ + GF_Err e; + u32 descSize; + GF_MPEG4ExtensionDescriptorsBox *ptr = (GF_MPEG4ExtensionDescriptorsBox *)s; + e = gf_isom_box_get_size(s); + if (!e) e = gf_odf_desc_list_size(ptr->descriptors, &descSize); + ptr->size += descSize; + return e; +} +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + + + +void avcc_del(GF_Box *s) +{ + GF_AVCConfigurationBox *ptr = (GF_AVCConfigurationBox *)s; + if (ptr->config) gf_odf_avc_cfg_del(ptr->config); + gf_free(ptr); +} +GF_Err avcc_Read(GF_Box *s, GF_BitStream *bs) +{ + u32 i, count; + GF_AVCConfigurationBox *ptr = (GF_AVCConfigurationBox *)s; + + if (ptr->config) gf_odf_avc_cfg_del(ptr->config); + ptr->config = gf_odf_avc_cfg_new(); + ptr->config->configurationVersion = gf_bs_read_u8(bs); + ptr->config->AVCProfileIndication = gf_bs_read_u8(bs); + ptr->config->profile_compatibility = gf_bs_read_u8(bs); + ptr->config->AVCLevelIndication = gf_bs_read_u8(bs); + if (ptr->type==GF_ISOM_BOX_TYPE_AVCC) { + gf_bs_read_int(bs, 6); + } else { + ptr->config->complete_representation = gf_bs_read_int(bs, 1); + gf_bs_read_int(bs, 5); + } + ptr->config->nal_unit_size = 1 + gf_bs_read_int(bs, 2); + gf_bs_read_int(bs, 3); + count = gf_bs_read_int(bs, 5); + + for (i=0; isize = gf_bs_read_u16(bs); + sl->data = (char *)gf_malloc(sizeof(char) * sl->size); + gf_bs_read_data(bs, sl->data, sl->size); + gf_list_add(ptr->config->sequenceParameterSets, sl); + } + + count = gf_bs_read_u8(bs); + for (i=0; isize = gf_bs_read_u16(bs); + sl->data = (char *)gf_malloc(sizeof(char) * sl->size); + gf_bs_read_data(bs, sl->data, sl->size); + gf_list_add(ptr->config->pictureParameterSets, sl); + } + return GF_OK; +} +GF_Box *avcc_New() +{ + GF_AVCConfigurationBox *tmp = (GF_AVCConfigurationBox *) gf_malloc(sizeof(GF_AVCConfigurationBox)); + if (tmp == NULL) return NULL; + memset(tmp, 0, sizeof(GF_AVCConfigurationBox)); + tmp->type = GF_ISOM_BOX_TYPE_AVCC; + return (GF_Box *)tmp; +} + +#ifndef GPAC_DISABLE_ISOM_WRITE +GF_Err avcc_Write(GF_Box *s, GF_BitStream *bs) +{ + u32 i, count; + GF_Err e; + GF_AVCConfigurationBox *ptr = (GF_AVCConfigurationBox *) s; + if (!s) return GF_BAD_PARAM; + if (!ptr->config) return GF_OK; + e = gf_isom_box_write_header(s, bs); + if (e) return e; + + gf_bs_write_u8(bs, ptr->config->configurationVersion); + gf_bs_write_u8(bs, ptr->config->AVCProfileIndication); + gf_bs_write_u8(bs, ptr->config->profile_compatibility); + gf_bs_write_u8(bs, ptr->config->AVCLevelIndication); + if (ptr->type==GF_ISOM_BOX_TYPE_AVCC) { + gf_bs_write_int(bs, 0x3F, 6); + } else { + gf_bs_write_int(bs, ptr->config->complete_representation, 1); + gf_bs_write_int(bs, 0x1F, 5); + } + gf_bs_write_int(bs, ptr->config->nal_unit_size - 1, 2); + gf_bs_write_int(bs, 0x7, 3); + count = gf_list_count(ptr->config->sequenceParameterSets); + gf_bs_write_int(bs, count, 5); + for (i=0; iconfig->sequenceParameterSets, i); + gf_bs_write_u16(bs, sl->size); + gf_bs_write_data(bs, sl->data, sl->size); + } + + count = gf_list_count(ptr->config->pictureParameterSets); + gf_bs_write_u8(bs, count); + for (i=0; iconfig->pictureParameterSets, i); + gf_bs_write_u16(bs, sl->size); + gf_bs_write_data(bs, sl->data, sl->size); + } + return GF_OK; +} +GF_Err avcc_Size(GF_Box *s) +{ + GF_Err e; + u32 i, count; + GF_AVCConfigurationBox *ptr = (GF_AVCConfigurationBox *)s; + e = gf_isom_box_get_size(s); + if (e) return e; + if (!ptr->config) { + ptr->size = 0; + return e; + } + ptr->size += 7; + count = gf_list_count(ptr->config->sequenceParameterSets); + for (i=0; isize += 2 + ((GF_AVCConfigSlot *)gf_list_get(ptr->config->sequenceParameterSets, i))->size; + count = gf_list_count(ptr->config->pictureParameterSets); + for (i=0; isize += 2 + ((GF_AVCConfigSlot *)gf_list_get(ptr->config->pictureParameterSets, i))->size; + return GF_OK; +} +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + + +#endif /*GPAC_DISABLE_ISOM*/ diff --git a/src/gpacmp4/avilib.c b/src/gpacmp4/avilib.c new file mode 100644 index 00000000..1e7f9de5 --- /dev/null +++ b/src/gpacmp4/avilib.c @@ -0,0 +1,2976 @@ +/* + * avilib.c + * + * Copyright (C) Thomas Östreich - June 2001 + * multiple audio track support Copyright (C) 2002 Thomas Östreich + * + * Original code: + * Copyright (C) 1999 Rainer Johanni + * + * This file is part of transcode, a linux video stream processing tool + * + * transcode is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * transcode 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include "../disable_warnings.h" +#include + +#ifndef GPAC_DISABLE_AVILIB + +#include + + +#define PACKAGE "GPAC/avilib" +#define VERSION GPAC_FULL_VERSION + +#define INFO_LIST + +// add a new riff chunk after XX MB +//#define NEW_RIFF_THRES (1900*1024*1024) +#define NEW_RIFF_THRES (1900*1024*1024) +//#define NEW_RIFF_THRES (10*1024*1024) + +// Maximum number of indices per stream +#define NR_IXNN_CHUNKS 32 + + +#define DEBUG_ODML +#undef DEBUG_ODML + +/* The following variable indicates the kind of error */ + +long AVI_errno = 0; + +#define MAX_INFO_STRLEN 64 +static char id_str[MAX_INFO_STRLEN]; + +#define FRAME_RATE_SCALE 1000000 + +/******************************************************************* + * * + * Utilities for writing an AVI File * + * * + *******************************************************************/ + +static u32 avi_read(FILE *fd, char *buf, u32 len) +{ + s32 n = 0; + u32 r = 0; + + while (r < len) { + n = fread(buf + r, 1, len - r, fd); + if (n == 0) break; + if (n < 0) return r; + r += n; + } + + return r; +} + +static u32 avi_write (FILE *fd, char *buf, u32 len) +{ + s32 n = 0; + u32 r = 0; + + while (r < len) { + n = fwrite (buf + r, 1, len - r, fd); + if (n < 0) + return n; + + r += n; + } + return r; +} + +/* HEADERBYTES: The number of bytes to reserve for the header */ + +#define HEADERBYTES 2048 + +/* AVI_MAX_LEN: The maximum length of an AVI file, we stay a bit below + the 2GB limit (Remember: 2*10^9 is smaller than 2 GB) */ + +#define AVI_MAX_LEN (UINT_MAX-(1<<20)*16-HEADERBYTES) + +#define PAD_EVEN(x) ( ((x)+1) & ~1 ) + + +/* Copy n into dst as a 4 or 2 byte, little endian number. + Should also work on big endian machines */ + +static void long2str(unsigned char *dst, s32 n) +{ + dst[0] = (n )&0xff; + dst[1] = (n>> 8)&0xff; + dst[2] = (n>>16)&0xff; + dst[3] = (n>>24)&0xff; +} + +#ifdef WORDS_BIGENDIAN +static void short2str(unsigned char *dst, s32 n) +{ + dst[0] = (n )&0xff; + dst[1] = (n>> 8)&0xff; +} +#endif + +/* Convert a string of 4 or 2 bytes to a number, + also working on big endian machines */ + +static u64 str2ullong(unsigned char *str) +{ + u64 r = (str[0] | (str[1]<<8) | (str[2]<<16) | (str[3]<<24)); + u64 s = (str[4] | (str[5]<<8) | (str[6]<<16) | (str[7]<<24)); +#ifdef __GNUC__ + return ((s<<32)&0xffffffff00000000ULL)|(r&0xffffffff); +#else + return ((s<<32)&0xffffffff00000000)|(r&0xffffffff); +#endif +} + +static u32 str2ulong(unsigned char *str) +{ + return ( str[0] | (str[1]<<8) | (str[2]<<16) | (str[3]<<24) ); +} +static u32 str2ushort(unsigned char *str) +{ + return ( str[0] | (str[1]<<8) ); +} + +// bit 31 denotes a keyframe +static u32 str2ulong_len (unsigned char *str) +{ + return str2ulong(str) & 0x7fffffff; +} + + +// if bit 31 is 0, its a keyframe +static u32 str2ulong_key (unsigned char *str) +{ + u32 c = str2ulong(str); + c &= 0x80000000; + if (c == 0) return 0x10; + else return 0; +} + +/* Calculate audio sample size from number of bits and number of channels. + This may have to be adjusted for eg. 12 bits and stereo */ + +static int avi_sampsize(avi_t *AVI, int j) +{ + int s; + s = ((AVI->track[j].a_bits+7)/8)*AVI->track[j].a_chans; + // if(s==0) s=1; /* avoid possible zero divisions */ + if(s<4) s=4; /* avoid possible zero divisions */ + return s; +} + +/* Add a chunk (=tag and data) to the AVI file, + returns -1 on write error, 0 on success */ + +static int avi_add_chunk(avi_t *AVI, unsigned char *tag, unsigned char *data, u32 length) +{ + unsigned char c[8]; + char p=0; + + /* Copy tag and length int c, so that we need only 1 write system call + for these two values */ + + memcpy(c,tag,4); + long2str(c+4,length); + + /* Output tag, length and data, restore previous position + if the write fails */ + + if( avi_write(AVI->fdes,(char *)c,8) != 8 || + avi_write(AVI->fdes,(char *)data,length) != length || + avi_write(AVI->fdes,&p,length&1) != (length&1)) // if len is uneven, write a pad byte + { + gf_f64_seek(AVI->fdes,AVI->pos,SEEK_SET); + AVI_errno = AVI_ERR_WRITE; + return -1; + } + + /* Update file position */ + + AVI->pos += 8 + PAD_EVEN(length); + + //GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[avilib] pos=%lu %s\n", AVI->pos, tag)); + + return 0; +} + +#define OUTD(n) long2str((unsigned char*) (ix00+bl),(s32)n); bl+=4 +#define OUTW(n) ix00[bl] = (n)&0xff; ix00[bl+1] = (n>>8)&0xff; bl+=2 +#define OUTC(n) ix00[bl] = (n)&0xff; bl+=1 +#define OUTS(s) memcpy(ix00+bl,s,4); bl+=4 + +// this does the physical writeout of the ix## structure +static int avi_ixnn_entry(avi_t *AVI, avistdindex_chunk *ch, avisuperindex_entry *en) +{ + int bl; + u32 k; + unsigned int max = ch->nEntriesInUse * sizeof (u32) * ch->wLongsPerEntry + 24; // header + char *ix00 = (char *)gf_malloc (max); + char dfcc[5]; + memcpy (dfcc, ch->fcc, 4); + dfcc[4] = 0; + + bl = 0; + + if (en) { + en->qwOffset = AVI->pos; + en->dwSize = max; + //en->dwDuration = ch->nEntriesInUse -1; // NUMBER OF stream ticks == frames for video/samples for audio + } + +#ifdef DEBUG_ODML + //GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[avilib] ODML Write %s: Entries %ld size %d \n", dfcc, ch->nEntriesInUse, max)); +#endif + + //OUTS(ch->fcc); + //OUTD(max); + OUTW(ch->wLongsPerEntry); + OUTC(ch->bIndexSubType); + OUTC(ch->bIndexType); + OUTD(ch->nEntriesInUse); + OUTS(ch->dwChunkId); + OUTD(ch->qwBaseOffset&0xffffffff); + OUTD((ch->qwBaseOffset>>32)&0xffffffff); + OUTD(ch->dwReserved3); + + for (k = 0; k < ch->nEntriesInUse; k++) { + OUTD(ch->aIndex[k].dwOffset); + OUTD(ch->aIndex[k].dwSize); + + } + avi_add_chunk (AVI, (unsigned char*)ch->fcc, (unsigned char*)ix00, max); + + gf_free(ix00); + + return 0; +} +#undef OUTS +#undef OUTW +#undef OUTD +#undef OUTC + +// inits a super index structure including its enclosed stdindex +static int avi_init_super_index(avi_t *AVI, unsigned char *idxtag, avisuperindex_chunk **si) +{ + int k; + + avisuperindex_chunk *sil = NULL; + + if ((sil = (avisuperindex_chunk *) gf_malloc (sizeof (avisuperindex_chunk))) == NULL) { + AVI_errno = AVI_ERR_NO_MEM; + return -1; + } + memcpy (sil->fcc, "indx", 4); + sil->dwSize = 0; // size of this chunk + sil->wLongsPerEntry = 4; + sil->bIndexSubType = 0; + sil->bIndexType = AVI_INDEX_OF_INDEXES; + sil->nEntriesInUse = 0; // none are in use + memcpy (sil->dwChunkId, idxtag, 4); + memset (sil->dwReserved, 0, sizeof (sil->dwReserved)); + + // NR_IXNN_CHUNKS == allow 32 indices which means 32 GB files -- arbitrary + sil->aIndex = (avisuperindex_entry *) gf_malloc (sil->wLongsPerEntry * NR_IXNN_CHUNKS * sizeof (void*)); + if (!sil->aIndex) { + AVI_errno = AVI_ERR_NO_MEM; + return -1; + } + memset (sil->aIndex, 0, sil->wLongsPerEntry * NR_IXNN_CHUNKS * sizeof (u32)); + + sil->stdindex = (avistdindex_chunk **)gf_malloc (NR_IXNN_CHUNKS * sizeof (avistdindex_chunk *)); + if (!sil->stdindex) { + AVI_errno = AVI_ERR_NO_MEM; + return -1; + } + for (k = 0; k < NR_IXNN_CHUNKS; k++) { + sil->stdindex[k] = (avistdindex_chunk *) gf_malloc (sizeof (avistdindex_chunk)); + // gets rewritten later + sil->stdindex[k]->qwBaseOffset = (u64)k * NEW_RIFF_THRES; + sil->stdindex[k]->aIndex = NULL; + } + + *si = sil; + + return 0; +} + +// fills an alloc'ed stdindex structure and mallocs some entries for the actual chunks +static int avi_add_std_index(avi_t *AVI, unsigned char *idxtag, unsigned char *strtag, + avistdindex_chunk *stdil) +{ + + memcpy (stdil->fcc, idxtag, 4); + stdil->dwSize = 4096; + stdil->wLongsPerEntry = 2; //sizeof(avistdindex_entry)/sizeof(u32); + stdil->bIndexSubType = 0; + stdil->bIndexType = AVI_INDEX_OF_CHUNKS; + stdil->nEntriesInUse = 0; + + // cp 00db ChunkId + memcpy(stdil->dwChunkId, strtag, 4); + + //stdil->qwBaseOffset = AVI->video_superindex->aIndex[ cur_std_idx ]->qwOffset; + + stdil->aIndex = (avistdindex_entry *)gf_malloc(stdil->dwSize * sizeof (u32) * stdil->wLongsPerEntry); + + if (!stdil->aIndex) { + AVI_errno = AVI_ERR_NO_MEM; + return -1; + } + + + return 0; +} + +static int avi_add_odml_index_entry_core(avi_t *AVI, long flags, u64 pos, unsigned long len, avistdindex_chunk *si) +{ + u32 cur_chunk_idx; + // put new chunk into index + si->nEntriesInUse++; + cur_chunk_idx = si->nEntriesInUse-1; + + // need to fetch more memory + if (cur_chunk_idx >= si->dwSize) { + si->dwSize += 4096; + si->aIndex = (avistdindex_entry *)gf_realloc ( si->aIndex, si->dwSize * sizeof (u32) * si->wLongsPerEntry); + } + + if(len>AVI->max_len) AVI->max_len=len; + + // if bit 31 is set, it is NOT a keyframe + if (flags != 0x10) { + len |= 0x80000000; + } + + si->aIndex [ cur_chunk_idx ].dwSize = len; + si->aIndex [ cur_chunk_idx ].dwOffset = (u32) (pos - si->qwBaseOffset + 8); + + //GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[avilib] ODML: POS: 0x%lX\n", si->aIndex [ cur_chunk_idx ].dwOffset)); + + return 0; +} + +static int avi_add_odml_index_entry(avi_t *AVI, unsigned char *tag, long flags, u64 pos, unsigned long len) +{ + char fcc[5]; + + int audio = (strchr ((char*)tag, 'w')?1:0); + int video = !audio; + + unsigned int cur_std_idx; + u32 audtr; + s64 towrite = 0; + + if (video) { + + if (!AVI->video_superindex) { + if (avi_init_super_index(AVI, (unsigned char *)"ix00", &AVI->video_superindex) < 0) return -1; + AVI->video_superindex->nEntriesInUse++; + cur_std_idx = AVI->video_superindex->nEntriesInUse-1; + + if (avi_add_std_index (AVI, (unsigned char *)"ix00", (unsigned char *)"00db", AVI->video_superindex->stdindex[ cur_std_idx ]) < 0) + return -1; + } // init + + } // video + + if (audio) { + + fcc[0] = 'i'; fcc[1] = 'x'; fcc[2] = tag[0]; fcc[3] = tag[1]; fcc[4] = '\0'; + if (!AVI->track[AVI->aptr].audio_superindex) { + +#ifdef DEBUG_ODML + GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[avilib] ODML: fcc = %s\n", fcc)); +#endif + if (avi_init_super_index(AVI, (unsigned char *)fcc, &AVI->track[AVI->aptr].audio_superindex) < 0) return -1; + + + AVI->track[AVI->aptr].audio_superindex->nEntriesInUse++; + + sprintf(fcc, "ix%02d", AVI->aptr+1); + if (avi_add_std_index (AVI, (unsigned char *)fcc, tag, AVI->track[AVI->aptr].audio_superindex->stdindex[ + AVI->track[AVI->aptr].audio_superindex->nEntriesInUse - 1 ]) < 0 + ) return -1; + } // init + + } + + towrite = 0; + if (AVI->video_superindex) { + + cur_std_idx = AVI->video_superindex->nEntriesInUse-1; + towrite += AVI->video_superindex->stdindex[cur_std_idx]->nEntriesInUse*8 + + 4+4+2+1+1+4+4+8+4; + if (cur_std_idx == 0) { + towrite += AVI->n_idx*16 + 8; + towrite += HEADERBYTES; + } + } + + for (audtr=0; audtranum; audtr++) { + if (AVI->track[audtr].audio_superindex) { + cur_std_idx = AVI->track[audtr].audio_superindex->nEntriesInUse-1; + towrite += AVI->track[audtr].audio_superindex->stdindex[cur_std_idx]->nEntriesInUse*8 + + 4+4+2+1+1+4+4+8+4; + } + } + towrite += len + (len&1) + 8; + + //GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[avilib] ODML: towrite = 0x%llX = %"LLD"\n", towrite, towrite)); + + if (AVI->video_superindex && + (s64)(AVI->pos+towrite) > (s64)((s64)NEW_RIFF_THRES*AVI->video_superindex->nEntriesInUse)) { + + GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[avilib] Adding a new RIFF chunk: %d\n", AVI->video_superindex->nEntriesInUse)); + + // rotate ALL indices + AVI->video_superindex->nEntriesInUse++; + cur_std_idx = AVI->video_superindex->nEntriesInUse-1; + + if (AVI->video_superindex->nEntriesInUse > NR_IXNN_CHUNKS) { + GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[avilib] Internal error in avilib - redefine NR_IXNN_CHUNKS\n")); + GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[avilib] cur_std_idx=%d NR_IXNN_CHUNKS=%d" + "POS=%"LLD" towrite=%"LLD"\n", + cur_std_idx,NR_IXNN_CHUNKS, AVI->pos, towrite)); + return -1; + } + + if (avi_add_std_index (AVI, (unsigned char *)"ix00", (unsigned char *)"00db", AVI->video_superindex->stdindex[ cur_std_idx ]) < 0) + return -1; + + for (audtr = 0; audtr < AVI->anum; audtr++) { + char aud[5]; + if (!AVI->track[audtr].audio_superindex) { + // not initialized -> no index + continue; + } + AVI->track[audtr].audio_superindex->nEntriesInUse++; + + sprintf(fcc, "ix%02d", audtr+1); + sprintf(aud, "0%01dwb", audtr+1); + if (avi_add_std_index (AVI, (unsigned char *)fcc, (unsigned char *)aud, AVI->track[audtr].audio_superindex->stdindex[ + AVI->track[audtr].audio_superindex->nEntriesInUse - 1 ]) < 0 + ) return -1; + } + + // write the new riff; + if (cur_std_idx > 0) { + + // dump the _previous_ == already finished index + avi_ixnn_entry (AVI, AVI->video_superindex->stdindex[cur_std_idx - 1], + &AVI->video_superindex->aIndex[cur_std_idx - 1]); + AVI->video_superindex->aIndex[cur_std_idx - 1].dwDuration = + AVI->video_superindex->stdindex[cur_std_idx - 1]->nEntriesInUse - 1; + + for (audtr = 0; audtr < AVI->anum; audtr++) { + + if (!AVI->track[audtr].audio_superindex) { + // not initialized -> no index + continue; + } + avi_ixnn_entry (AVI, AVI->track[audtr].audio_superindex->stdindex[cur_std_idx - 1], + &AVI->track[audtr].audio_superindex->aIndex[cur_std_idx - 1]); + + AVI->track[audtr].audio_superindex->aIndex[cur_std_idx - 1].dwDuration = + AVI->track[audtr].audio_superindex->stdindex[cur_std_idx - 1]->nEntriesInUse - 1; + if (AVI->track[audtr].a_fmt == 0x1) { + AVI->track[audtr].audio_superindex->aIndex[cur_std_idx - 1].dwDuration *= + AVI->track[audtr].a_bits*AVI->track[audtr].a_rate*AVI->track[audtr].a_chans/800; + } + } + + // XXX: dump idx1 structure + if (cur_std_idx == 1) { + avi_add_chunk(AVI, (unsigned char *)"idx1", (unsigned char *)AVI->idx, AVI->n_idx*16); + // qwBaseOffset will contain the start of the second riff chunk + } + // Fix the Offsets later at closing time + avi_add_chunk(AVI, (unsigned char *)"RIFF", (unsigned char *)"AVIXLIST\0\0\0\0movi", 16); + + AVI->video_superindex->stdindex[ cur_std_idx ]->qwBaseOffset = AVI->pos -16 -8; +#ifdef DEBUG_ODML + GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[avilib] ODML: RIFF No.%02d at Offset 0x%llX\n", cur_std_idx, AVI->pos -16 -8)); +#endif + + for (audtr = 0; audtr < AVI->anum; audtr++) { + if (AVI->track[audtr].audio_superindex) + AVI->track[audtr].audio_superindex->stdindex[ cur_std_idx ]->qwBaseOffset = + AVI->pos -16 -8; + + } + + // now we can be sure + AVI->is_opendml++; + } + + } + + + if (video) { + avi_add_odml_index_entry_core(AVI, flags, AVI->pos, len, + AVI->video_superindex->stdindex[ AVI->video_superindex->nEntriesInUse-1 ]); + + AVI->total_frames++; + } // video + + if (audio) { + avi_add_odml_index_entry_core(AVI, flags, AVI->pos, len, + AVI->track[AVI->aptr].audio_superindex->stdindex[ + AVI->track[AVI->aptr].audio_superindex->nEntriesInUse-1 ]); + } + + + return 0; +} + +// #undef NR_IXNN_CHUNKS + +static int avi_add_index_entry(avi_t *AVI, unsigned char *tag, long flags, u64 pos, u64 len) +{ + void *ptr; + + if(AVI->n_idx>=AVI->max_idx) { + ptr = gf_realloc((void *)AVI->idx,(AVI->max_idx+4096)*16); + + if(ptr == 0) { + AVI_errno = AVI_ERR_NO_MEM; + return -1; + } + AVI->max_idx += 4096; + AVI->idx = (unsigned char((*)[16]) ) ptr; + } + + /* Add index entry */ + + // GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[avilib] INDEX %s %ld %lu %lu\n", tag, flags, pos, len)); + + memcpy(AVI->idx[AVI->n_idx],tag,4); + long2str(AVI->idx[AVI->n_idx]+ 4,flags); + long2str(AVI->idx[AVI->n_idx]+ 8, (s32) pos); + long2str(AVI->idx[AVI->n_idx]+12, (s32) len); + + /* Update counter */ + + AVI->n_idx++; + + if(len>AVI->max_len) AVI->max_len=(u32) len; + + return 0; +} + +/* Returns 1 if more audio is in that video junk */ +int AVI_can_read_audio(avi_t *AVI) +{ + if(AVI->mode==AVI_MODE_WRITE) { return -1; } + if(!AVI->video_index) { return -1; } + if(!AVI->track[AVI->aptr].audio_index) { return -1; } + + // is it -1? the last ones got left out --tibit + //if (AVI->track[AVI->aptr].audio_posc>=AVI->track[AVI->aptr].audio_chunks-1) { + if (AVI->track[AVI->aptr].audio_posc>=AVI->track[AVI->aptr].audio_chunks) { + return 0; + } + + if (AVI->video_pos >= AVI->video_frames) return 1; + + if (AVI->track[AVI->aptr].audio_index[AVI->track[AVI->aptr].audio_posc].pos < AVI->video_index[AVI->video_pos].pos) return 1; + else return 0; +} +/* + AVI_open_output_file: Open an AVI File and write a bunch + of zero bytes as space for the header. + + returns a pointer to avi_t on success, a zero pointer on error +*/ + +GF_EXPORT +avi_t* AVI_open_output_file(char * filename) +{ + avi_t *AVI; + int i; + + unsigned char AVI_header[HEADERBYTES]; + + /* Allocate the avi_t struct and zero it */ + + AVI = (avi_t *) gf_malloc(sizeof(avi_t)); + if(AVI==0) + { + AVI_errno = AVI_ERR_NO_MEM; + return 0; + } + memset((void *)AVI,0,sizeof(avi_t)); + + AVI->fdes = gf_f64_open(filename, "w+b"); + if (!AVI->fdes ) + { + AVI_errno = AVI_ERR_OPEN; + gf_free(AVI); + return 0; + } + + /* Write out HEADERBYTES bytes, the header will go here + when we are finished with writing */ + + for (i=0;ifdes,(char *)AVI_header,HEADERBYTES); + if (i != HEADERBYTES) + { + fclose(AVI->fdes); + AVI_errno = AVI_ERR_WRITE; + gf_free(AVI); + return 0; + } + + AVI->pos = HEADERBYTES; + AVI->mode = AVI_MODE_WRITE; /* open for writing */ + + //init + AVI->anum = 0; + AVI->aptr = 0; + + return AVI; +} + +GF_EXPORT +void AVI_set_video(avi_t *AVI, int width, int height, double fps, char *compressor) +{ + /* may only be called if file is open for writing */ + + if(AVI->mode==AVI_MODE_READ) return; + + AVI->width = width; + AVI->height = height; + AVI->fps = fps; + + if(strncmp(compressor, "RGB", 3)==0) { + memset(AVI->compressor, 0, 4); + } else { + memcpy(AVI->compressor,compressor,4); + } + + AVI->compressor[4] = 0; + + avi_update_header(AVI); +} + +void AVI_set_audio(avi_t *AVI, int channels, long rate, int bits, int format, long mp3rate) +{ + /* may only be called if file is open for writing */ + + if(AVI->mode==AVI_MODE_READ) return; + + //inc audio tracks + AVI->aptr=AVI->anum; + ++AVI->anum; + + if(AVI->anum > AVI_MAX_TRACKS) { + GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[avilib] error - only %d audio tracks supported\n", AVI_MAX_TRACKS)); + exit(1); + } + + AVI->track[AVI->aptr].a_chans = channels; + AVI->track[AVI->aptr].a_rate = rate; + AVI->track[AVI->aptr].a_bits = bits; + AVI->track[AVI->aptr].a_fmt = format; + AVI->track[AVI->aptr].mp3rate = mp3rate; + + avi_update_header(AVI); +} + +#define OUT4CC(s) \ + if(nhb<=HEADERBYTES-4) memcpy(AVI_header+nhb,s,4); nhb += 4 + +#define OUTLONG(n) \ + if(nhb<=HEADERBYTES-4) long2str(AVI_header+nhb, (s32)(n)); nhb += 4 + +#define OUTSHRT(n) \ + if(nhb<=HEADERBYTES-2) { \ + AVI_header[nhb ] = (u8) ((n )&0xff); \ + AVI_header[nhb+1] = (u8) ((n>>8)&0xff); \ + } \ + nhb += 2 + +#define OUTCHR(n) \ + if(nhb<=HEADERBYTES-1) { \ + AVI_header[nhb ] = (n )&0xff; \ + } \ + nhb += 1 + +#define OUTMEM(d, s) \ + { \ + u32 s_ = (u32) (s); \ + if(nhb + s_ <= HEADERBYTES) \ + memcpy(AVI_header+nhb, (d), s_); \ + nhb += s_; \ + } + + +//ThOe write preliminary AVI file header: 0 frames, max vid/aud size +int avi_update_header(avi_t *AVI) +{ + int njunk, sampsize, hasIndex, ms_per_frame, frate, flag; + int movi_len, hdrl_start, strl_start; + u32 j; + unsigned char AVI_header[HEADERBYTES]; + u32 nhb; + unsigned long xd_size, xd_size_align2; + + //assume max size + movi_len = AVI_MAX_LEN - HEADERBYTES + 4; + + //assume index will be written + hasIndex=1; + + if(AVI->fps < 0.001) { + frate=0; + ms_per_frame=0; + } else { + frate = (int) (FRAME_RATE_SCALE*AVI->fps + 0.5); + ms_per_frame=(int) (1000000/AVI->fps + 0.5); + } + + /* Prepare the file header */ + + nhb = 0; + + /* The RIFF header */ + + OUT4CC ("RIFF"); + OUTLONG(movi_len); // assume max size + OUT4CC ("AVI "); + + /* Start the header list */ + + OUT4CC ("LIST"); + OUTLONG(0); /* Length of list in bytes, don't know yet */ + hdrl_start = nhb; /* Store start position */ + OUT4CC ("hdrl"); + + /* The main AVI header */ + + /* The Flags in AVI File header */ + +#define AVIF_HASINDEX 0x00000010 /* Index at end of file */ +#define AVIF_MUSTUSEINDEX 0x00000020 +#define AVIF_ISINTERLEAVED 0x00000100 +#define AVIF_TRUSTCKTYPE 0x00000800 /* Use CKType to find key frames */ +#define AVIF_WASCAPTUREFILE 0x00010000 +#define AVIF_COPYRIGHTED 0x00020000 + + OUT4CC ("avih"); + OUTLONG(56); /* # of bytes to follow */ + OUTLONG(ms_per_frame); /* Microseconds per frame */ + //ThOe ->0 + // OUTLONG(10000000); /* MaxBytesPerSec, I hope this will never be used */ + OUTLONG(0); + OUTLONG(0); /* PaddingGranularity (whatever that might be) */ + /* Other sources call it 'reserved' */ + flag = AVIF_ISINTERLEAVED; + if(hasIndex) flag |= AVIF_HASINDEX; + if(hasIndex && AVI->must_use_index) flag |= AVIF_MUSTUSEINDEX; + OUTLONG(flag); /* Flags */ + OUTLONG(0); // no frames yet + OUTLONG(0); /* InitialFrames */ + + OUTLONG(AVI->anum+1); + + OUTLONG(0); /* SuggestedBufferSize */ + OUTLONG(AVI->width); /* Width */ + OUTLONG(AVI->height); /* Height */ + /* MS calls the following 'reserved': */ + OUTLONG(0); /* TimeScale: Unit used to measure time */ + OUTLONG(0); /* DataRate: Data rate of playback */ + OUTLONG(0); /* StartTime: Starting time of AVI data */ + OUTLONG(0); /* DataLength: Size of AVI data chunk */ + + + /* Start the video stream list ---------------------------------- */ + + OUT4CC ("LIST"); + OUTLONG(0); /* Length of list in bytes, don't know yet */ + strl_start = nhb; /* Store start position */ + OUT4CC ("strl"); + + /* The video stream header */ + + OUT4CC ("strh"); + OUTLONG(56); /* # of bytes to follow */ + OUT4CC ("vids"); /* Type */ + OUT4CC (AVI->compressor); /* Handler */ + OUTLONG(0); /* Flags */ + OUTLONG(0); /* Reserved, MS says: wPriority, wLanguage */ + OUTLONG(0); /* InitialFrames */ + OUTLONG(FRAME_RATE_SCALE); /* Scale */ + OUTLONG(frate); /* Rate: Rate/Scale == samples/second */ + OUTLONG(0); /* Start */ + OUTLONG(0); // no frames yet + OUTLONG(0); /* SuggestedBufferSize */ + OUTLONG(-1); /* Quality */ + OUTLONG(0); /* SampleSize */ + OUTLONG(0); /* Frame */ + OUTLONG(0); /* Frame */ + // OUTLONG(0); /* Frame */ + //OUTLONG(0); /* Frame */ + + /* The video stream format */ + + xd_size = AVI->extradata_size; + xd_size_align2 = (AVI->extradata_size+1) & ~1; + + OUT4CC ("strf"); + OUTLONG(40 + xd_size_align2);/* # of bytes to follow */ + OUTLONG(40 + xd_size); /* Size */ + OUTLONG(AVI->width); /* Width */ + OUTLONG(AVI->height); /* Height */ + OUTSHRT(1); OUTSHRT(24); /* Planes, Count */ + OUT4CC (AVI->compressor); /* Compression */ + // ThOe (*3) + OUTLONG(AVI->width*AVI->height*3); /* SizeImage (in bytes?) */ + OUTLONG(0); /* XPelsPerMeter */ + OUTLONG(0); /* YPelsPerMeter */ + OUTLONG(0); /* ClrUsed: Number of colors used */ + OUTLONG(0); /* ClrImportant: Number of colors important */ + + // write extradata + if (xd_size > 0 && AVI->extradata) { + OUTMEM(AVI->extradata, xd_size); + if (xd_size != xd_size_align2) { + OUTCHR(0); + } + } + + /* Finish stream list, i.e. put number of bytes in the list to proper pos */ + + long2str(AVI_header+strl_start-4,nhb-strl_start); + + + /* Start the audio stream list ---------------------------------- */ + + for(j=0; janum; ++j) { + + sampsize = avi_sampsize(AVI, j); + + OUT4CC ("LIST"); + OUTLONG(0); /* Length of list in bytes, don't know yet */ + strl_start = nhb; /* Store start position */ + OUT4CC ("strl"); + + /* The audio stream header */ + + OUT4CC ("strh"); + OUTLONG(56); /* # of bytes to follow */ + OUT4CC ("auds"); + + // ----------- + // ThOe + OUTLONG(0); /* Format (Optionally) */ + // ----------- + + OUTLONG(0); /* Flags */ + OUTLONG(0); /* Reserved, MS says: wPriority, wLanguage */ + OUTLONG(0); /* InitialFrames */ + + // ThOe /4 + OUTLONG(sampsize/4); /* Scale */ + OUTLONG(1000*AVI->track[j].mp3rate/8); + OUTLONG(0); /* Start */ + OUTLONG(4*AVI->track[j].audio_bytes/sampsize); /* Length */ + OUTLONG(0); /* SuggestedBufferSize */ + OUTLONG(-1); /* Quality */ + + // ThOe /4 + OUTLONG(sampsize/4); /* SampleSize */ + + OUTLONG(0); /* Frame */ + OUTLONG(0); /* Frame */ + // OUTLONG(0); /* Frame */ + //OUTLONG(0); /* Frame */ + + /* The audio stream format */ + + OUT4CC ("strf"); + OUTLONG(16); /* # of bytes to follow */ + OUTSHRT(AVI->track[j].a_fmt); /* Format */ + OUTSHRT(AVI->track[j].a_chans); /* Number of channels */ + OUTLONG(AVI->track[j].a_rate); /* SamplesPerSec */ + // ThOe + OUTLONG(1000*AVI->track[j].mp3rate/8); + //ThOe (/4) + + OUTSHRT(sampsize/4); /* BlockAlign */ + + + OUTSHRT(AVI->track[j].a_bits); /* BitsPerSample */ + + /* Finish stream list, i.e. put number of bytes in the list to proper pos */ + + long2str(AVI_header+strl_start-4,nhb-strl_start); + } + + /* Finish header list */ + + long2str(AVI_header+hdrl_start-4,nhb-hdrl_start); + + + /* Calculate the needed amount of junk bytes, output junk */ + + njunk = HEADERBYTES - nhb - 8 - 12; + + /* Safety first: if njunk <= 0, somebody has played with + HEADERBYTES without knowing what (s)he did. + This is a fatal error */ + + if(njunk<=0) + { + GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[avilib] AVI_close_output_file: # of header bytes too small\n")); + exit(1); + } + + OUT4CC ("JUNK"); + OUTLONG(njunk); + memset(AVI_header+nhb,0,njunk); + + nhb += njunk; + + /* Start the movi list */ + + OUT4CC ("LIST"); + OUTLONG(movi_len); /* Length of list in bytes */ + OUT4CC ("movi"); + + /* Output the header, truncate the file to the number of bytes + actually written, report an error if someting goes wrong */ + + if ( (gf_f64_seek(AVI->fdes, 0, SEEK_SET) ==(u64)-1) || + avi_write(AVI->fdes,(char *)AVI_header,HEADERBYTES)!=HEADERBYTES || + (gf_f64_seek(AVI->fdes,AVI->pos,SEEK_SET)==(u64)-1) + ) { + AVI_errno = AVI_ERR_CLOSE; + return -1; + } + + return 0; +} + + +//SLM +#ifndef S_IRUSR +#define S_IRWXU 00700 /* read, write, execute: owner */ +#define S_IRUSR 00400 /* read permission: owner */ +#define S_IWUSR 00200 /* write permission: owner */ +#define S_IXUSR 00100 /* execute permission: owner */ +#define S_IRWXG 00070 /* read, write, execute: group */ +#define S_IRGRP 00040 /* read permission: group */ +#define S_IWGRP 00020 /* write permission: group */ +#define S_IXGRP 00010 /* execute permission: group */ +#define S_IRWXO 00007 /* read, write, execute: other */ +#define S_IROTH 00004 /* read permission: other */ +#define S_IWOTH 00002 /* write permission: other */ +#define S_IXOTH 00001 /* execute permission: other */ +#endif + +/* + Write the header of an AVI file and close it. + returns 0 on success, -1 on write error. +*/ + +static int avi_close_output_file(avi_t *AVI) +{ + int ret, njunk, sampsize, hasIndex, ms_per_frame, frate, idxerror, flag; + u64 movi_len; + int hdrl_start, strl_start; + u32 j; + unsigned char AVI_header[HEADERBYTES]; + long nhb; + unsigned long xd_size, xd_size_align2; + +#ifdef INFO_LIST + long info_len; + long id_len, real_id_len; + long info_start_pos; +// time_t calptr; +#endif + + /* Calculate length of movi list */ + + // dump the rest of the index + if (AVI->is_opendml) { + int cur_std_idx = AVI->video_superindex->nEntriesInUse-1; + u32 audtr; + +#ifdef DEBUG_ODML + GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[avilib] ODML dump the rest indices\n")); +#endif + avi_ixnn_entry (AVI, AVI->video_superindex->stdindex[cur_std_idx], + &AVI->video_superindex->aIndex[cur_std_idx]); + + AVI->video_superindex->aIndex[cur_std_idx].dwDuration = + AVI->video_superindex->stdindex[cur_std_idx]->nEntriesInUse - 1; + + for (audtr = 0; audtr < AVI->anum; audtr++) { + if (!AVI->track[audtr].audio_superindex) { + // not initialized -> no index + continue; + } + avi_ixnn_entry (AVI, AVI->track[audtr].audio_superindex->stdindex[cur_std_idx], + &AVI->track[audtr].audio_superindex->aIndex[cur_std_idx]); + AVI->track[audtr].audio_superindex->aIndex[cur_std_idx].dwDuration = + AVI->track[audtr].audio_superindex->stdindex[cur_std_idx]->nEntriesInUse - 1; + if (AVI->track[audtr].a_fmt == 0x1) { + AVI->track[audtr].audio_superindex->aIndex[cur_std_idx].dwDuration *= + AVI->track[audtr].a_bits*AVI->track[audtr].a_rate*AVI->track[audtr].a_chans/800; + } + } + // The AVI->video_superindex->nEntriesInUse contains the offset + AVI->video_superindex->stdindex[ cur_std_idx+1 ]->qwBaseOffset = AVI->pos; + } + + if (AVI->is_opendml) { + // Correct! + movi_len = AVI->video_superindex->stdindex[ 1 ]->qwBaseOffset - HEADERBYTES+4 - AVI->n_idx*16 - 8; + } else { + movi_len = AVI->pos - HEADERBYTES + 4; + } + + + /* Try to ouput the index entries. This may fail e.g. if no space + is left on device. We will report this as an error, but we still + try to write the header correctly (so that the file still may be + readable in the most cases */ + + idxerror = 0; + hasIndex = 1; + if (!AVI->is_opendml) { + // GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[avilib] pos=%lu, index_len=%ld \n", AVI->pos, AVI->n_idx*16)); + ret = avi_add_chunk(AVI, (unsigned char *)"idx1", (unsigned char *)AVI->idx, AVI->n_idx*16); + hasIndex = (ret==0); + //GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[avilib] pos=%lu, index_len=%d\n", AVI->pos, hasIndex)); + + if(ret) { + idxerror = 1; + AVI_errno = AVI_ERR_WRITE_INDEX; + } + } + + /* Calculate Microseconds per frame */ + + if(AVI->fps < 0.001) { + frate=0; + ms_per_frame=0; + } else { + frate = (int) (FRAME_RATE_SCALE*AVI->fps + 0.5); + ms_per_frame=(int) (1000000/AVI->fps + 0.5); + } + + /* Prepare the file header */ + + nhb = 0; + + /* The RIFF header */ + + OUT4CC ("RIFF"); + if (AVI->is_opendml) { + OUTLONG(AVI->video_superindex->stdindex[ 1 ]->qwBaseOffset - 8); /* # of bytes to follow */ + } else { + OUTLONG(AVI->pos - 8); /* # of bytes to follow */ + } + + OUT4CC ("AVI "); + + /* Start the header list */ + + OUT4CC ("LIST"); + OUTLONG(0); /* Length of list in bytes, don't know yet */ + hdrl_start = nhb; /* Store start position */ + OUT4CC ("hdrl"); + + /* The main AVI header */ + + /* The Flags in AVI File header */ + +#define AVIF_HASINDEX 0x00000010 /* Index at end of file */ +#define AVIF_MUSTUSEINDEX 0x00000020 +#define AVIF_ISINTERLEAVED 0x00000100 +#define AVIF_TRUSTCKTYPE 0x00000800 /* Use CKType to find key frames */ +#define AVIF_WASCAPTUREFILE 0x00010000 +#define AVIF_COPYRIGHTED 0x00020000 + + OUT4CC ("avih"); + OUTLONG(56); /* # of bytes to follow */ + OUTLONG(ms_per_frame); /* Microseconds per frame */ + //ThOe ->0 + // OUTLONG(10000000); /* MaxBytesPerSec, I hope this will never be used */ + OUTLONG(0); + OUTLONG(0); /* PaddingGranularity (whatever that might be) */ + /* Other sources call it 'reserved' */ + flag = AVIF_ISINTERLEAVED; + if(hasIndex) flag |= AVIF_HASINDEX; + if(hasIndex && AVI->must_use_index) flag |= AVIF_MUSTUSEINDEX; + OUTLONG(flag); /* Flags */ + OUTLONG(AVI->video_frames); /* TotalFrames */ + OUTLONG(0); /* InitialFrames */ + + OUTLONG(AVI->anum+1); +// if (AVI->track[0].audio_bytes) +// { OUTLONG(2); } /* Streams */ +// else +// { OUTLONG(1); } /* Streams */ + + OUTLONG(0); /* SuggestedBufferSize */ + OUTLONG(AVI->width); /* Width */ + OUTLONG(AVI->height); /* Height */ + /* MS calls the following 'reserved': */ + OUTLONG(0); /* TimeScale: Unit used to measure time */ + OUTLONG(0); /* DataRate: Data rate of playback */ + OUTLONG(0); /* StartTime: Starting time of AVI data */ + OUTLONG(0); /* DataLength: Size of AVI data chunk */ + + + /* Start the video stream list ---------------------------------- */ + + OUT4CC ("LIST"); + OUTLONG(0); /* Length of list in bytes, don't know yet */ + strl_start = nhb; /* Store start position */ + OUT4CC ("strl"); + + /* The video stream header */ + + OUT4CC ("strh"); + OUTLONG(56); /* # of bytes to follow */ + OUT4CC ("vids"); /* Type */ + OUT4CC (AVI->compressor); /* Handler */ + OUTLONG(0); /* Flags */ + OUTLONG(0); /* Reserved, MS says: wPriority, wLanguage */ + OUTLONG(0); /* InitialFrames */ + OUTLONG(FRAME_RATE_SCALE); /* Scale */ + OUTLONG(frate); /* Rate: Rate/Scale == samples/second */ + OUTLONG(0); /* Start */ + OUTLONG(AVI->video_frames); /* Length */ + OUTLONG(AVI->max_len); /* SuggestedBufferSize */ + OUTLONG(0); /* Quality */ + OUTLONG(0); /* SampleSize */ + OUTLONG(0); /* Frame */ + OUTLONG(0); /* Frame */ + //OUTLONG(0); /* Frame */ + //OUTLONG(0); /* Frame */ + + /* The video stream format */ + + xd_size = AVI->extradata_size; + xd_size_align2 = (AVI->extradata_size+1) & ~1; + + OUT4CC ("strf"); + OUTLONG(40 + xd_size_align2);/* # of bytes to follow */ + OUTLONG(40 + xd_size); /* Size */ + OUTLONG(AVI->width); /* Width */ + OUTLONG(AVI->height); /* Height */ + OUTSHRT(1); OUTSHRT(24); /* Planes, Count */ + OUT4CC (AVI->compressor); /* Compression */ + // ThOe (*3) + OUTLONG(AVI->width*AVI->height*3); /* SizeImage (in bytes?) */ + OUTLONG(0); /* XPelsPerMeter */ + OUTLONG(0); /* YPelsPerMeter */ + OUTLONG(0); /* ClrUsed: Number of colors used */ + OUTLONG(0); /* ClrImportant: Number of colors important */ + + // write extradata if present + if (xd_size > 0 && AVI->extradata) { + OUTMEM(AVI->extradata, xd_size); + if (xd_size != xd_size_align2) { + OUTCHR(0); + } + } + + // dump index of indices for audio + if (AVI->is_opendml) { + u32 k; + + OUT4CC(AVI->video_superindex->fcc); + OUTLONG(2+1+1+4+4+3*4 + AVI->video_superindex->nEntriesInUse * (8+4+4)); + OUTSHRT(AVI->video_superindex->wLongsPerEntry); + OUTCHR(AVI->video_superindex->bIndexSubType); + OUTCHR(AVI->video_superindex->bIndexType); + OUTLONG(AVI->video_superindex->nEntriesInUse); + OUT4CC(AVI->video_superindex->dwChunkId); + OUTLONG(0); + OUTLONG(0); + OUTLONG(0); + + + for (k = 0; k < AVI->video_superindex->nEntriesInUse; k++) { + u32 r = (u32) ((AVI->video_superindex->aIndex[k].qwOffset >> 32) & 0xffffffff); + u32 s = (u32) ((AVI->video_superindex->aIndex[k].qwOffset) & 0xffffffff); + + OUTLONG(s); + OUTLONG(r); + OUTLONG(AVI->video_superindex->aIndex[k].dwSize); + OUTLONG(AVI->video_superindex->aIndex[k].dwDuration); + } + + } + + /* Finish stream list, i.e. put number of bytes in the list to proper pos */ + + long2str(AVI_header+strl_start-4,nhb-strl_start); + + /* Start the audio stream list ---------------------------------- */ + + for(j=0; janum; ++j) { + + //if (AVI->track[j].a_chans && AVI->track[j].audio_bytes) + { + unsigned long nBlockAlign = 0; + unsigned long avgbsec = 0; + unsigned long scalerate = 0; + + sampsize = avi_sampsize(AVI, j); + sampsize = AVI->track[j].a_fmt==0x1?sampsize*4:sampsize; + + nBlockAlign = (AVI->track[j].a_rate<32000)?576:1152; + /* + GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[avilib] XXX sampsize (%d) block (%ld) rate (%ld) audio_bytes (%ld) mp3rate(%ld,%ld)\n", + sampsize, nBlockAlign, AVI->track[j].a_rate, + (long int)AVI->track[j].audio_bytes, + 1000*AVI->track[j].mp3rate/8, AVI->track[j].mp3rate)); + */ + + if (AVI->track[j].a_fmt==0x1) { + sampsize = (AVI->track[j].a_chans<2)?sampsize/2:sampsize; + avgbsec = AVI->track[j].a_rate*sampsize/4; + scalerate = AVI->track[j].a_rate*sampsize/4; + } else { + avgbsec = 1000*AVI->track[j].mp3rate/8; + scalerate = 1000*AVI->track[j].mp3rate/8; + } + + OUT4CC ("LIST"); + OUTLONG(0); /* Length of list in bytes, don't know yet */ + strl_start = nhb; /* Store start position */ + OUT4CC ("strl"); + + /* The audio stream header */ + + OUT4CC ("strh"); + OUTLONG(56); /* # of bytes to follow */ + OUT4CC ("auds"); + + // ----------- + // ThOe + OUTLONG(0); /* Format (Optionally) */ + // ----------- + + OUTLONG(0); /* Flags */ + OUTLONG(0); /* Reserved, MS says: wPriority, wLanguage */ + OUTLONG(0); /* InitialFrames */ + + // VBR + if (AVI->track[j].a_fmt == 0x55 && AVI->track[j].a_vbr) { + OUTLONG(nBlockAlign); /* Scale */ + OUTLONG(AVI->track[j].a_rate); /* Rate */ + OUTLONG(0); /* Start */ + OUTLONG(AVI->track[j].audio_chunks); /* Length */ + OUTLONG(0); /* SuggestedBufferSize */ + OUTLONG(0); /* Quality */ + OUTLONG(0); /* SampleSize */ + OUTLONG(0); /* Frame */ + OUTLONG(0); /* Frame */ + } else { + OUTLONG(sampsize/4); /* Scale */ + OUTLONG(scalerate); /* Rate */ + OUTLONG(0); /* Start */ + OUTLONG(4*AVI->track[j].audio_bytes/sampsize); /* Length */ + OUTLONG(0); /* SuggestedBufferSize */ + OUTLONG(0xffffffff); /* Quality */ + OUTLONG(sampsize/4); /* SampleSize */ + OUTLONG(0); /* Frame */ + OUTLONG(0); /* Frame */ + } + + /* The audio stream format */ + + OUT4CC ("strf"); + + if (AVI->track[j].a_fmt == 0x55 && AVI->track[j].a_vbr) { + + OUTLONG(30); /* # of bytes to follow */ // mplayer writes 28 + OUTSHRT(AVI->track[j].a_fmt); /* Format */ // 2 + OUTSHRT(AVI->track[j].a_chans); /* Number of channels */ // 2 + OUTLONG(AVI->track[j].a_rate); /* SamplesPerSec */ // 4 + //ThOe/tibit + OUTLONG(1000*AVI->track[j].mp3rate/8); /* maybe we should write an avg. */ // 4 + OUTSHRT(nBlockAlign); /* BlockAlign */ // 2 + OUTSHRT(AVI->track[j].a_bits); /* BitsPerSample */ // 2 + + OUTSHRT(12); /* cbSize */ // 2 + OUTSHRT(1); /* wID */ // 2 + OUTLONG(2); /* fdwFlags */ // 4 + OUTSHRT(nBlockAlign); /* nBlockSize */ // 2 + OUTSHRT(1); /* nFramesPerBlock */ // 2 + OUTSHRT(0); /* nCodecDelay */ // 2 + + } else if (AVI->track[j].a_fmt == 0x55 && !AVI->track[j].a_vbr) { + + OUTLONG(30); /* # of bytes to follow */ + OUTSHRT(AVI->track[j].a_fmt); /* Format */ + OUTSHRT(AVI->track[j].a_chans); /* Number of channels */ + OUTLONG(AVI->track[j].a_rate); /* SamplesPerSec */ + //ThOe/tibit + OUTLONG(1000*AVI->track[j].mp3rate/8); + OUTSHRT(sampsize/4); /* BlockAlign */ + OUTSHRT(AVI->track[j].a_bits); /* BitsPerSample */ + + OUTSHRT(12); /* cbSize */ + OUTSHRT(1); /* wID */ + OUTLONG(2); /* fdwFlags */ + OUTSHRT(nBlockAlign); /* nBlockSize */ + OUTSHRT(1); /* nFramesPerBlock */ + OUTSHRT(0); /* nCodecDelay */ + + } else { + + OUTLONG(18); /* # of bytes to follow */ + OUTSHRT(AVI->track[j].a_fmt); /* Format */ + OUTSHRT(AVI->track[j].a_chans); /* Number of channels */ + OUTLONG(AVI->track[j].a_rate); /* SamplesPerSec */ + //ThOe/tibit + OUTLONG(avgbsec); /* Avg bytes/sec */ + OUTSHRT(sampsize/4); /* BlockAlign */ + OUTSHRT(AVI->track[j].a_bits); /* BitsPerSample */ + OUTSHRT(0); /* cbSize */ + + } + } + if (AVI->is_opendml) { + u32 k; + + if (!AVI->track[j].audio_superindex) { + // not initialized -> no index + continue; + } + + OUT4CC(AVI->track[j].audio_superindex->fcc); /* "indx" */ + OUTLONG(2+1+1+4+4+3*4 + AVI->track[j].audio_superindex->nEntriesInUse * (8+4+4)); + OUTSHRT(AVI->track[j].audio_superindex->wLongsPerEntry); + OUTCHR(AVI->track[j].audio_superindex->bIndexSubType); + OUTCHR(AVI->track[j].audio_superindex->bIndexType); + OUTLONG(AVI->track[j].audio_superindex->nEntriesInUse); + OUT4CC(AVI->track[j].audio_superindex->dwChunkId); + OUTLONG(0); OUTLONG(0); OUTLONG(0); + + for (k = 0; k < AVI->track[j].audio_superindex->nEntriesInUse; k++) { + u32 r = (u32) ((AVI->track[j].audio_superindex->aIndex[k].qwOffset >> 32) & 0xffffffff); + u32 s = (u32) ((AVI->track[j].audio_superindex->aIndex[k].qwOffset) & 0xffffffff); + + /* + GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[avilib] AUD[%d] NrEntries %d/%ld (%c%c%c%c) |0x%llX|%ld|%ld| \n", j, k, + AVI->track[j].audio_superindex->nEntriesInUse, + AVI->track[j].audio_superindex->dwChunkId[0], AVI->track[j].audio_superindex->dwChunkId[1], + AVI->track[j].audio_superindex->dwChunkId[2], AVI->track[j].audio_superindex->dwChunkId[3], + AVI->track[j].audio_superindex->aIndex[k].qwOffset, + AVI->track[j].audio_superindex->aIndex[k].dwSize, + AVI->track[j].audio_superindex->aIndex[k].dwDuration + )); + */ + + OUTLONG(s); + OUTLONG(r); + OUTLONG(AVI->track[j].audio_superindex->aIndex[k].dwSize); + OUTLONG(AVI->track[j].audio_superindex->aIndex[k].dwDuration); + } + } + /* Finish stream list, i.e. put number of bytes in the list to proper pos */ + long2str(AVI_header+strl_start-4,nhb-strl_start); + } + + if (AVI->is_opendml) { + OUT4CC("LIST"); + OUTLONG(16); + OUT4CC("odml"); + OUT4CC("dmlh"); + OUTLONG(4); + OUTLONG(AVI->total_frames); + } + + /* Finish header list */ + + long2str(AVI_header+hdrl_start-4,nhb-hdrl_start); + + + // add INFO list --- (0.6.0pre4) + +#ifdef INFO_LIST + OUT4CC ("LIST"); + + info_start_pos = nhb; + info_len = MAX_INFO_STRLEN + 12; + OUTLONG(info_len); // rewritten later + OUT4CC ("INFO"); + + OUT4CC ("ISFT"); + //OUTLONG(MAX_INFO_STRLEN); + memset(id_str, 0, MAX_INFO_STRLEN); + + sprintf(id_str, "%s-%s", PACKAGE, VERSION); + real_id_len = id_len = strlen(id_str)+1; + if (id_len&1) id_len++; + + OUTLONG(real_id_len); + + memset(AVI_header+nhb, 0, id_len); + memcpy(AVI_header+nhb, id_str, id_len); + nhb += id_len; + + info_len = 0; + + // write correct len + long2str(AVI_header+info_start_pos, info_len + id_len + 4+4+4); + + nhb += info_len; + +// OUT4CC ("ICMT"); +// OUTLONG(MAX_INFO_STRLEN); + +// calptr=time(NULL); +// sprintf(id_str, "\t%s %s", ctime(&calptr), ""); +// memset(AVI_header+nhb, 0, MAX_INFO_STRLEN); +// memcpy(AVI_header+nhb, id_str, 25); +// nhb += MAX_INFO_STRLEN; +#endif + + // ---------------------------- + + /* Calculate the needed amount of junk bytes, output junk */ + + njunk = HEADERBYTES - nhb - 8 - 12; + + /* Safety first: if njunk <= 0, somebody has played with + HEADERBYTES without knowing what (s)he did. + This is a fatal error */ + + if(njunk<=0) + { + GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[avilib] AVI_close_output_file: # of header bytes too small\n")); + exit(1); + } + + OUT4CC ("JUNK"); + OUTLONG(njunk); + memset(AVI_header+nhb,0,njunk); + + nhb += njunk; + + /* Start the movi list */ + + OUT4CC ("LIST"); + OUTLONG(movi_len); /* Length of list in bytes */ + OUT4CC ("movi"); + + /* Output the header, truncate the file to the number of bytes + actually written, report an error if someting goes wrong */ + + if ( (gf_f64_seek(AVI->fdes,0,SEEK_SET)==(u64)-1) || + avi_write(AVI->fdes,(char *)AVI_header,HEADERBYTES)!=HEADERBYTES +// || ftruncate(AVI->fdes,AVI->pos)<0 + ) + { + AVI_errno = AVI_ERR_CLOSE; + return -1; + } + + + // Fix up the empty additional RIFF and LIST chunks + if (AVI->is_opendml) { + u32 k; + char f[4]; + u32 len; + + for (k=1; kvideo_superindex->nEntriesInUse; k++) { + // the len of the RIFF Chunk + gf_f64_seek(AVI->fdes, AVI->video_superindex->stdindex[k]->qwBaseOffset+4, SEEK_SET); + len = (u32) (AVI->video_superindex->stdindex[k+1]->qwBaseOffset - AVI->video_superindex->stdindex[k]->qwBaseOffset - 8); + long2str((unsigned char *)f, len); + avi_write(AVI->fdes, f, 4); + + // len of the LIST/movi chunk + gf_f64_seek(AVI->fdes, 8, SEEK_CUR); + len -= 12; + long2str((unsigned char *)f, len); + avi_write(AVI->fdes, f, 4); + } + } + + + if(idxerror) return -1; + + return 0; +} + +/* + AVI_write_data: + Add video or audio data to the file; + + Return values: + 0 No error; + -1 Error, AVI_errno is set appropriatly; + +*/ + +static int avi_write_data(avi_t *AVI, char *data, unsigned long length, int audio, int keyframe) +{ + int n = 0; + + unsigned char astr[5]; + + // transcode core itself checks for the size -- unneeded and + // does harm to xvid 2pass encodes where the first pass can get + // _very_ large -- tibit. + +#if 0 + /* Check for maximum file length */ + + if ( (AVI->pos + 8 + length + 8 + (AVI->n_idx+1)*16) > AVI_MAX_LEN ) { + AVI_errno = AVI_ERR_SIZELIM; + return -1; + } +#endif + + /* Add index entry */ + + //set tag for current audio track + sprintf((char *)astr, "0%1dwb", (int)(AVI->aptr+1)); + + if(audio) { + if (!AVI->is_opendml) n = avi_add_index_entry(AVI,astr,0x10,AVI->pos,length); + n += avi_add_odml_index_entry(AVI,astr,0x10,AVI->pos,length); + } else { + if (!AVI->is_opendml) n = avi_add_index_entry(AVI,(unsigned char *)"00db",((keyframe)?0x10:0x0),AVI->pos,length); + n += avi_add_odml_index_entry(AVI,(unsigned char *)"00db",((keyframe)?0x10:0x0),AVI->pos,length); + } + + if(n) return -1; + + /* Output tag and data */ + + if(audio) + n = avi_add_chunk(AVI,(unsigned char *)astr, (unsigned char *)data, length); + else + n = avi_add_chunk(AVI,(unsigned char *)"00db", (unsigned char *)data, length); + + if (n) return -1; + + return 0; +} + +GF_EXPORT +int AVI_write_frame(avi_t *AVI, char *data, long bytes, int keyframe) +{ + s64 pos; + + if(AVI->mode==AVI_MODE_READ) { AVI_errno = AVI_ERR_NOT_PERM; return -1; } + + pos = AVI->pos; + + if(avi_write_data(AVI,data,bytes,0,keyframe)) return -1; + + AVI->last_pos = pos; + AVI->last_len = bytes; + AVI->video_frames++; + return 0; +} + +int AVI_dup_frame(avi_t *AVI) +{ + if(AVI->mode==AVI_MODE_READ) { AVI_errno = AVI_ERR_NOT_PERM; return -1; } + + if(AVI->last_pos==0) return 0; /* No previous real frame */ + if(avi_add_index_entry(AVI,(unsigned char *)"00db",0x10,AVI->last_pos,AVI->last_len)) return -1; + AVI->video_frames++; + AVI->must_use_index = 1; + return 0; +} + +int AVI_write_audio(avi_t *AVI, char *data, long bytes) +{ + if(AVI->mode==AVI_MODE_READ) { AVI_errno = AVI_ERR_NOT_PERM; return -1; } + + if( avi_write_data(AVI,data,bytes,1,0) ) return -1; + AVI->track[AVI->aptr].audio_bytes += bytes; + AVI->track[AVI->aptr].audio_chunks++; + return 0; +} + + +int AVI_append_audio(avi_t *AVI, char *data, long bytes) +{ + + // won't work for >2gb + long i, length, pos; + unsigned char c[4]; + + if(AVI->mode==AVI_MODE_READ) { AVI_errno = AVI_ERR_NOT_PERM; return -1; } + + // update last index entry: + + --AVI->n_idx; + length = str2ulong(AVI->idx[AVI->n_idx]+12); + pos = str2ulong(AVI->idx[AVI->n_idx]+8); + + //update; + long2str(AVI->idx[AVI->n_idx]+12,length+bytes); + + ++AVI->n_idx; + + AVI->track[AVI->aptr].audio_bytes += bytes; + + //update chunk header + gf_f64_seek(AVI->fdes, pos+4, SEEK_SET); + long2str(c, length+bytes); + avi_write(AVI->fdes, (char *)c, 4); + + gf_f64_seek(AVI->fdes, pos+8+length, SEEK_SET); + + i=PAD_EVEN(length + bytes); + + bytes = i - length; + avi_write(AVI->fdes, data, bytes); + AVI->pos = pos + 8 + i; + + return 0; +} + + +u64 AVI_bytes_remain(avi_t *AVI) +{ + if(AVI->mode==AVI_MODE_READ) return 0; + + return ( AVI_MAX_LEN - (AVI->pos + 8 + 16*AVI->n_idx)); +} + +u64 AVI_bytes_written(avi_t *AVI) +{ + if(AVI->mode==AVI_MODE_READ) return 0; + + return (AVI->pos + 8 + 16*AVI->n_idx); +} + +int AVI_set_audio_track(avi_t *AVI, u32 track) +{ + + if (track + 1 > AVI->anum) return(-1); + + //this info is not written to file anyway + AVI->aptr=track; + return 0; +} + +int AVI_get_audio_track(avi_t *AVI) +{ + return(AVI->aptr); +} + +void AVI_set_audio_vbr(avi_t *AVI, long is_vbr) +{ + AVI->track[AVI->aptr].a_vbr = is_vbr; +} + +long AVI_get_audio_vbr(avi_t *AVI) +{ + return(AVI->track[AVI->aptr].a_vbr); +} + + +/******************************************************************* + * * + * Utilities for reading video and audio from an AVI File * + * * + *******************************************************************/ + +GF_EXPORT +int AVI_close(avi_t *AVI) +{ + int ret; + u32 j; + + /* If the file was open for writing, the header and index still have + to be written */ + + if(AVI->mode == AVI_MODE_WRITE) + ret = avi_close_output_file(AVI); + else + ret = 0; + + /* Even if there happened an error, we first clean up */ + + fclose(AVI->fdes); + if(AVI->idx) gf_free(AVI->idx); + if(AVI->video_index) gf_free(AVI->video_index); + if(AVI->video_superindex) { + if(AVI->video_superindex->aIndex) gf_free(AVI->video_superindex->aIndex); + if (AVI->video_superindex->stdindex) { + for (j=0; j < NR_IXNN_CHUNKS; j++) { + if (AVI->video_superindex->stdindex[j]->aIndex) + gf_free(AVI->video_superindex->stdindex[j]->aIndex); + gf_free(AVI->video_superindex->stdindex[j]); + } + gf_free(AVI->video_superindex->stdindex); + } + gf_free(AVI->video_superindex); + } + + for (j=0; janum; j++) + { + if(AVI->track[j].audio_index) gf_free(AVI->track[j].audio_index); + if(AVI->track[j].audio_superindex) { + if(AVI->track[j].audio_superindex->aIndex) gf_free(AVI->track[j].audio_superindex->aIndex); + gf_free(AVI->track[j].audio_superindex); + } + } + + if (AVI->bitmap_info_header) + gf_free(AVI->bitmap_info_header); + for (j = 0; j < AVI->anum; j++) + if (AVI->wave_format_ex[j]) + gf_free(AVI->wave_format_ex[j]); + + gf_free(AVI); + AVI=NULL; + + return ret; +} + + +#define ERR_EXIT(x) \ +{ \ + AVI_close(AVI); \ + AVI_errno = x; \ + return 0; \ +} + + +avi_t *AVI_open_input_file(char *filename, int getIndex) +{ + avi_t *AVI=NULL; + + /* Create avi_t structure */ + + AVI = (avi_t *) gf_malloc(sizeof(avi_t)); + if(AVI==NULL) + { + AVI_errno = AVI_ERR_NO_MEM; + return 0; + } + memset((void *)AVI,0,sizeof(avi_t)); + + AVI->mode = AVI_MODE_READ; /* open for reading */ + + /* Open the file */ + + AVI->fdes = gf_f64_open(filename,"rb"); + if(!AVI->fdes ) + { + AVI_errno = AVI_ERR_OPEN; + gf_free(AVI); + return 0; + } + + AVI_errno = 0; + avi_parse_input_file(AVI, getIndex); + + if (AVI != NULL && !AVI_errno) { + AVI->aptr=0; //reset + } + + if (AVI_errno) return NULL; + + return AVI; +} + +avi_t *AVI_open_fd(FILE *fd, int getIndex) +{ + avi_t *AVI=NULL; + + /* Create avi_t structure */ + + AVI = (avi_t *) gf_malloc(sizeof(avi_t)); + if(AVI==NULL) + { + AVI_errno = AVI_ERR_NO_MEM; + return 0; + } + memset((void *)AVI,0,sizeof(avi_t)); + + AVI->mode = AVI_MODE_READ; /* open for reading */ + + // file alread open + AVI->fdes = fd; + + AVI_errno = 0; + avi_parse_input_file(AVI, getIndex); + + if (AVI != NULL && !AVI_errno) { + AVI->aptr=0; //reset + } + + if (AVI_errno) + return AVI=NULL; + else + return AVI; +} + +int avi_parse_input_file(avi_t *AVI, int getIndex) +{ + long i, rate, scale, idx_type; + s64 n; + unsigned char *hdrl_data; + u64 header_offset=0; + long hdrl_len=0; + long nvi, nai[AVI_MAX_TRACKS], ioff; + u64 tot[AVI_MAX_TRACKS]; + u32 j; + int lasttag = 0; + int vids_strh_seen = 0; + int vids_strf_seen = 0; + int auds_strh_seen = 0; + // int auds_strf_seen = 0; + int num_stream = 0; + char data[256]; + s64 oldpos=-1, newpos=-1; + + long aud_chunks = 0; + /* Read first 12 bytes and check that this is an AVI file */ + + if( avi_read(AVI->fdes,data,12) != 12 ) ERR_EXIT(AVI_ERR_READ) + + if( strnicmp(data ,"RIFF",4) !=0 || + strnicmp(data+8,"AVI ",4) !=0 ) ERR_EXIT(AVI_ERR_NO_AVI) + + /* Go through the AVI file and extract the header list, + the start position of the 'movi' list and an optionally + present idx1 tag */ + + hdrl_data = 0; + + + while(1) + { + if( avi_read(AVI->fdes,data,8) != 8 ) break; /* We assume it's EOF */ + newpos = gf_f64_tell(AVI->fdes); + if(oldpos==newpos) { + /* This is a broken AVI stream... */ + return -1; + } + oldpos=newpos; + + n = str2ulong((unsigned char *)data+4); + n = PAD_EVEN(n); + + if(strnicmp(data,"LIST",4) == 0) + { + if( avi_read(AVI->fdes,data,4) != 4 ) ERR_EXIT(AVI_ERR_READ) + n -= 4; + if(strnicmp(data,"hdrl",4) == 0) + { + hdrl_len = (u32) n; + hdrl_data = (unsigned char *) gf_malloc((u32)n); + if(hdrl_data==0) ERR_EXIT(AVI_ERR_NO_MEM); + + // offset of header + + header_offset = gf_f64_tell(AVI->fdes); + + if( avi_read(AVI->fdes,(char *)hdrl_data, (u32) n) != n ) ERR_EXIT(AVI_ERR_READ) + } + else if(strnicmp(data,"movi",4) == 0) + { + AVI->movi_start = gf_f64_tell(AVI->fdes); + if (gf_f64_seek(AVI->fdes,n,SEEK_CUR)==(u64)-1) break; + } + else + if (gf_f64_seek(AVI->fdes,n,SEEK_CUR)==(u64)-1) break; + } + else if(strnicmp(data,"idx1",4) == 0) + { + /* n must be a multiple of 16, but the reading does not + break if this is not the case */ + + AVI->n_idx = AVI->max_idx = (u32) (n/16); + AVI->idx = (unsigned char((*)[16]) ) gf_malloc((u32)n); + if(AVI->idx==0) ERR_EXIT(AVI_ERR_NO_MEM) + if(avi_read(AVI->fdes, (char *) AVI->idx, (u32) n) != n ) { + gf_free( AVI->idx); AVI->idx=NULL; + AVI->n_idx = 0; + } + } + else + gf_f64_seek(AVI->fdes,n,SEEK_CUR); + } + + if(!hdrl_data ) ERR_EXIT(AVI_ERR_NO_HDRL) + if(!AVI->movi_start) ERR_EXIT(AVI_ERR_NO_MOVI) + + /* Interpret the header list */ + + for(i=0;icompressor,hdrl_data+i+4,4); + AVI->compressor[4] = 0; + + // ThOe + AVI->v_codech_off = header_offset + i+4; + + scale = str2ulong(hdrl_data+i+20); + rate = str2ulong(hdrl_data+i+24); + if(scale!=0) AVI->fps = (double)rate/(double)scale; + AVI->video_frames = str2ulong(hdrl_data+i+32); + AVI->video_strn = num_stream; + AVI->max_len = 0; + vids_strh_seen = 1; + lasttag = 1; /* vids */ + memcpy(&AVI->video_stream_header, hdrl_data + i, + sizeof(alAVISTREAMHEADER)); + } + else if (strnicmp ((char *)hdrl_data+i,"auds",4) ==0 && ! auds_strh_seen) + { + + //inc audio tracks + AVI->aptr=AVI->anum; + ++AVI->anum; + + if(AVI->anum > AVI_MAX_TRACKS) { + GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[avilib] error - only %d audio tracks supported\n", AVI_MAX_TRACKS)); + return(-1); + } + + AVI->track[AVI->aptr].audio_bytes = str2ulong(hdrl_data+i+32)*avi_sampsize(AVI, 0); + AVI->track[AVI->aptr].audio_strn = num_stream; + + // if samplesize==0 -> vbr + AVI->track[AVI->aptr].a_vbr = !str2ulong(hdrl_data+i+44); + + AVI->track[AVI->aptr].padrate = str2ulong(hdrl_data+i+24); + memcpy(&AVI->stream_headers[AVI->aptr], hdrl_data + i, + sizeof(alAVISTREAMHEADER)); + + // auds_strh_seen = 1; + lasttag = 2; /* auds */ + + // ThOe + AVI->track[AVI->aptr].a_codech_off = header_offset + i; + + } + else if (strnicmp ((char*)hdrl_data+i,"iavs",4) ==0 && ! auds_strh_seen) { + GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[avilib] AVILIB: error - DV AVI Type 1 no supported\n")); + return (-1); + } + else + lasttag = 0; + num_stream++; + } + else if(strnicmp((char*)hdrl_data+i,"dmlh",4) == 0) { + AVI->total_frames = str2ulong(hdrl_data+i+8); +#ifdef DEBUG_ODML + GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[avilib] real number of frames %d\n", AVI->total_frames)); +#endif + i += 8; + } + else if(strnicmp((char *)hdrl_data+i,"strf",4)==0) + { + i += 8; + if(lasttag == 1) + { + alBITMAPINFOHEADER bih; + + memcpy(&bih, hdrl_data + i, sizeof(alBITMAPINFOHEADER)); + AVI->bitmap_info_header = (alBITMAPINFOHEADER *) + gf_malloc(str2ulong((unsigned char *)&bih.bi_size)); + if (AVI->bitmap_info_header != NULL) + memcpy(AVI->bitmap_info_header, hdrl_data + i, + str2ulong((unsigned char *)&bih.bi_size)); + + AVI->width = str2ulong(hdrl_data+i+4); + AVI->height = str2ulong(hdrl_data+i+8); + vids_strf_seen = 1; + //ThOe + AVI->v_codecf_off = header_offset + i+16; + + memcpy(AVI->compressor2, hdrl_data+i+16, 4); + AVI->compressor2[4] = 0; + + } + else if(lasttag == 2) + { + alWAVEFORMATEX *wfe; + char *nwfe; + int wfes; + + if ((u32) (hdrl_len - i) < sizeof(alWAVEFORMATEX)) + wfes = hdrl_len - i; + else + wfes = sizeof(alWAVEFORMATEX); + wfe = (alWAVEFORMATEX *)gf_malloc(sizeof(alWAVEFORMATEX)); + if (wfe != NULL) { + memset(wfe, 0, sizeof(alWAVEFORMATEX)); + memcpy(wfe, hdrl_data + i, wfes); + if (str2ushort((unsigned char *)&wfe->cb_size) != 0) { + nwfe = (char *) + gf_realloc(wfe, sizeof(alWAVEFORMATEX) + + str2ushort((unsigned char *)&wfe->cb_size)); + if (nwfe != 0) { + s64 lpos = gf_f64_tell(AVI->fdes); + gf_f64_seek(AVI->fdes, header_offset + i + sizeof(alWAVEFORMATEX), + SEEK_SET); + wfe = (alWAVEFORMATEX *)nwfe; + nwfe = &nwfe[sizeof(alWAVEFORMATEX)]; + avi_read(AVI->fdes, nwfe, + str2ushort((unsigned char *)&wfe->cb_size)); + gf_f64_seek(AVI->fdes, lpos, SEEK_SET); + } + } + AVI->wave_format_ex[AVI->aptr] = wfe; + } + + AVI->track[AVI->aptr].a_fmt = str2ushort(hdrl_data+i ); + + //ThOe + AVI->track[AVI->aptr].a_codecf_off = header_offset + i; + + AVI->track[AVI->aptr].a_chans = str2ushort(hdrl_data+i+2); + AVI->track[AVI->aptr].a_rate = str2ulong (hdrl_data+i+4); + //ThOe: read mp3bitrate + AVI->track[AVI->aptr].mp3rate = 8*str2ulong(hdrl_data+i+8)/1000; + //:ThOe + AVI->track[AVI->aptr].a_bits = str2ushort(hdrl_data+i+14); + // auds_strf_seen = 1; + } + } + else if(strnicmp((char*)hdrl_data+i,"indx",4) == 0) { + char *a; + + if(lasttag == 1) // V I D E O + { + + a = (char*)hdrl_data+i; + + AVI->video_superindex = (avisuperindex_chunk *) gf_malloc (sizeof (avisuperindex_chunk)); + memset(AVI->video_superindex, 0, sizeof (avisuperindex_chunk)); + memcpy (AVI->video_superindex->fcc, a, 4); a += 4; + AVI->video_superindex->dwSize = str2ulong((unsigned char *)a); a += 4; + AVI->video_superindex->wLongsPerEntry = str2ushort((unsigned char *)a); a += 2; + AVI->video_superindex->bIndexSubType = *a; a += 1; + AVI->video_superindex->bIndexType = *a; a += 1; + AVI->video_superindex->nEntriesInUse = str2ulong((unsigned char *)a); a += 4; + memcpy (AVI->video_superindex->dwChunkId, a, 4); a += 4; + + // 3 * reserved + a += 4; a += 4; a += 4; + + if (AVI->video_superindex->bIndexSubType != 0) { + GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[avilib] Invalid Header, bIndexSubType != 0\n")); + } + + AVI->video_superindex->aIndex = (avisuperindex_entry*) + gf_malloc (AVI->video_superindex->wLongsPerEntry * AVI->video_superindex->nEntriesInUse * sizeof (u32)); + + // position of ix## chunks + for (j=0; jvideo_superindex->nEntriesInUse; ++j) { + AVI->video_superindex->aIndex[j].qwOffset = str2ullong ((unsigned char*)a); a += 8; + AVI->video_superindex->aIndex[j].dwSize = str2ulong ((unsigned char*)a); a += 4; + AVI->video_superindex->aIndex[j].dwDuration = str2ulong ((unsigned char*)a); a += 4; + +#ifdef DEBUG_ODML + GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[avilib] [%d] 0x%llx 0x%lx %lu\n", j, + (unsigned long long)AVI->video_superindex->aIndex[j].qwOffset, + (unsigned long)AVI->video_superindex->aIndex[j].dwSize, + (unsigned long)AVI->video_superindex->aIndex[j].dwDuration)); +#endif + } + + +#ifdef DEBUG_ODML + GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[avilib] FOURCC \"%c%c%c%c\"\n", AVI->video_superindex->fcc[0], AVI->video_superindex->fcc[1], + AVI->video_superindex->fcc[2], AVI->video_superindex->fcc[3])); + GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[avilib] LEN \"%ld\"\n", (long)AVI->video_superindex->dwSize)); + GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[avilib] wLongsPerEntry \"%d\"\n", AVI->video_superindex->wLongsPerEntry)); + GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[avilib] bIndexSubType \"%d\"\n", AVI->video_superindex->bIndexSubType)); + GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[avilib] bIndexType \"%d\"\n", AVI->video_superindex->bIndexType)); + GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[avilib] nEntriesInUse \"%ld\"\n", (long)AVI->video_superindex->nEntriesInUse)); + GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[avilib] dwChunkId \"%c%c%c%c\"\n", AVI->video_superindex->dwChunkId[0], AVI->video_superindex->dwChunkId[1], + AVI->video_superindex->dwChunkId[2], AVI->video_superindex->dwChunkId[3])); +#endif + + AVI->is_opendml = 1; + + } + else if(lasttag == 2) // A U D I O + { + + a = (char*) hdrl_data+i; + + AVI->track[AVI->aptr].audio_superindex = (avisuperindex_chunk *) gf_malloc (sizeof (avisuperindex_chunk)); + memcpy (AVI->track[AVI->aptr].audio_superindex->fcc, a, 4); a += 4; + AVI->track[AVI->aptr].audio_superindex->dwSize = str2ulong((unsigned char*)a); a += 4; + AVI->track[AVI->aptr].audio_superindex->wLongsPerEntry = str2ushort((unsigned char*)a); a += 2; + AVI->track[AVI->aptr].audio_superindex->bIndexSubType = *a; a += 1; + AVI->track[AVI->aptr].audio_superindex->bIndexType = *a; a += 1; + AVI->track[AVI->aptr].audio_superindex->nEntriesInUse = str2ulong((unsigned char*)a); a += 4; + memcpy (AVI->track[AVI->aptr].audio_superindex->dwChunkId, a, 4); a += 4; + + // 3 * reserved + a += 4; a += 4; a += 4; + + if (AVI->track[AVI->aptr].audio_superindex->bIndexSubType != 0) { + GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[avilib] Invalid Header, bIndexSubType != 0\n")); + } + + AVI->track[AVI->aptr].audio_superindex->aIndex = (avisuperindex_entry*) + gf_malloc (AVI->track[AVI->aptr].audio_superindex->wLongsPerEntry * + AVI->track[AVI->aptr].audio_superindex->nEntriesInUse * sizeof (u32)); + + // position of ix## chunks + for (j=0; jtrack[AVI->aptr].audio_superindex->nEntriesInUse; ++j) { + AVI->track[AVI->aptr].audio_superindex->aIndex[j].qwOffset = str2ullong ((unsigned char*)a); a += 8; + AVI->track[AVI->aptr].audio_superindex->aIndex[j].dwSize = str2ulong ((unsigned char*)a); a += 4; + AVI->track[AVI->aptr].audio_superindex->aIndex[j].dwDuration = str2ulong ((unsigned char*)a); a += 4; + +#ifdef DEBUG_ODML + GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[avilib] [%d] 0x%llx 0x%lx %lu\n", j, + (unsigned long long)AVI->track[AVI->aptr].audio_superindex->aIndex[j].qwOffset, + (unsigned long)AVI->track[AVI->aptr].audio_superindex->aIndex[j].dwSize, + (unsigned long)AVI->track[AVI->aptr].audio_superindex->aIndex[j].dwDuration)); +#endif + } + +#ifdef DEBUG_ODML + GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[avilib] FOURCC \"%.4s\"\n", AVI->track[AVI->aptr].audio_superindex->fcc)); + GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[avilib] LEN \"%ld\"\n", (long)AVI->track[AVI->aptr].audio_superindex->dwSize)); + GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[avilib] wLongsPerEntry \"%d\"\n", AVI->track[AVI->aptr].audio_superindex->wLongsPerEntry)); + GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[avilib] bIndexSubType \"%d\"\n", AVI->track[AVI->aptr].audio_superindex->bIndexSubType)); + GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[avilib] bIndexType \"%d\"\n", AVI->track[AVI->aptr].audio_superindex->bIndexType)); + GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[avilib] nEntriesInUse \"%ld\"\n", (long)AVI->track[AVI->aptr].audio_superindex->nEntriesInUse)); + GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[avilib] dwChunkId \"%.4s\"\n", AVI->track[AVI->aptr].audio_superindex->dwChunkId[0])); +#endif + + } + i += 8; + } + else if((strnicmp((char*)hdrl_data+i,"JUNK",4) == 0) || + (strnicmp((char*)hdrl_data+i,"strn",4) == 0) || + (strnicmp((char*)hdrl_data+i,"vprp",4) == 0)){ + i += 8; + // do not reset lasttag + } else + { + i += 8; + lasttag = 0; + } + //GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[avilib] adding %ld bytes\n", (long int)n)); + + i += (u32) n; + } + + gf_free(hdrl_data); + + if(!vids_strh_seen || !vids_strf_seen) ERR_EXIT(AVI_ERR_NO_VIDS) + + AVI->video_tag[0] = AVI->video_strn/10 + '0'; + AVI->video_tag[1] = AVI->video_strn%10 + '0'; + AVI->video_tag[2] = 'd'; + AVI->video_tag[3] = 'b'; + + /* Audio tag is set to "99wb" if no audio present */ + if(!AVI->track[0].a_chans) AVI->track[0].audio_strn = 99; + + { + int i=0; + for(j=0; janum+1; ++j) { + if (j == AVI->video_strn) continue; + AVI->track[i].audio_tag[0] = j/10 + '0'; + AVI->track[i].audio_tag[1] = j%10 + '0'; + AVI->track[i].audio_tag[2] = 'w'; + AVI->track[i].audio_tag[3] = 'b'; + ++i; + } + } + + gf_f64_seek(AVI->fdes,AVI->movi_start,SEEK_SET); + + if(!getIndex) return(0); + + /* if the file has an idx1, check if this is relative + to the start of the file or to the start of the movi list */ + + idx_type = 0; + + if(AVI->idx) + { + s64 pos, len; + + /* Search the first videoframe in the idx1 and look where + it is in the file */ + + for(i=0;in_idx;i++) + if( strnicmp((char *)AVI->idx[i],(char *)AVI->video_tag,3)==0 ) break; + if(i>=AVI->n_idx) ERR_EXIT(AVI_ERR_NO_VIDS) + + pos = str2ulong(AVI->idx[i]+ 8); + len = str2ulong(AVI->idx[i]+12); + + gf_f64_seek(AVI->fdes,pos,SEEK_SET); + if(avi_read(AVI->fdes,data,8)!=8) ERR_EXIT(AVI_ERR_READ) + if( strnicmp(data,(char *)AVI->idx[i],4)==0 && str2ulong((unsigned char *)data+4)==len ) + { + idx_type = 1; /* Index from start of file */ + } + else + { + gf_f64_seek(AVI->fdes,pos+AVI->movi_start-4,SEEK_SET); + if(avi_read(AVI->fdes,data,8)!=8) ERR_EXIT(AVI_ERR_READ) + if( strnicmp(data,(char *)AVI->idx[i],4)==0 && str2ulong((unsigned char *)data+4)==len ) + { + idx_type = 2; /* Index from start of movi list */ + } + } + /* idx_type remains 0 if neither of the two tests above succeeds */ + } + + + if(idx_type == 0 && !AVI->is_opendml && !AVI->total_frames) + { + /* we must search through the file to get the index */ + + gf_f64_seek(AVI->fdes, AVI->movi_start, SEEK_SET); + + AVI->n_idx = 0; + + while(1) + { + if( avi_read(AVI->fdes,data,8) != 8 ) break; + n = str2ulong((unsigned char *)data+4); + + /* The movi list may contain sub-lists, ignore them */ + + if(strnicmp(data,"LIST",4)==0) + { + gf_f64_seek(AVI->fdes,4,SEEK_CUR); + continue; + } + + /* Check if we got a tag ##db, ##dc or ##wb */ + + if( ( (data[2]=='d' || data[2]=='D') && + (data[3]=='b' || data[3]=='B' || data[3]=='c' || data[3]=='C') ) + || ( (data[2]=='w' || data[2]=='W') && + (data[3]=='b' || data[3]=='B') ) ) + { + u64 __pos = gf_f64_tell(AVI->fdes) - 8; + avi_add_index_entry(AVI,(unsigned char *)data,0,__pos,n); + } + + gf_f64_seek(AVI->fdes,PAD_EVEN(n),SEEK_CUR); + } + idx_type = 1; + } + + // ************************ + // OPENDML + // ************************ + + // read extended index chunks + if (AVI->is_opendml) { + u64 offset = 0; + int hdrl_len = 4+4+2+1+1+4+4+8+4; + char *en, *chunk_start; + int k = 0; + u32 audtr = 0; + u32 nrEntries = 0; + + AVI->video_index = NULL; + + nvi = 0; + for(audtr=0; audtranum; ++audtr) { nai[audtr] = 0; tot[audtr] = 0; } + + // ************************ + // VIDEO + // ************************ + + for (j=0; jvideo_superindex->nEntriesInUse; j++) { + + // read from file + chunk_start = en = (char*) gf_malloc ((u32) (AVI->video_superindex->aIndex[j].dwSize+hdrl_len) ); + + if (gf_f64_seek(AVI->fdes, AVI->video_superindex->aIndex[j].qwOffset, SEEK_SET) == (u64)-1) { + gf_free(chunk_start); + continue; + } + + if (avi_read(AVI->fdes, en, (u32) (AVI->video_superindex->aIndex[j].dwSize+hdrl_len) ) <= 0) { + gf_free(chunk_start); + continue; + } + + nrEntries = str2ulong((unsigned char*)en + 12); +#ifdef DEBUG_ODML + //GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[avilib] [%d:0] Video nrEntries %ld\n", j, nrEntries)); +#endif + offset = str2ullong((unsigned char*)en + 20); + + // skip header + en += hdrl_len; + nvi += nrEntries; + AVI->video_index = (video_index_entry *) gf_realloc (AVI->video_index, nvi * sizeof (video_index_entry)); + if (!AVI->video_index) { + GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[avilib] out of mem (size = %ld)\n", nvi * sizeof (video_index_entry))); + exit(1); + } + + while (k < nvi) { + + AVI->video_index[k].pos = offset + str2ulong((unsigned char*)en); en += 4; + AVI->video_index[k].len = str2ulong_len((unsigned char*)en); + AVI->video_index[k].key = str2ulong_key((unsigned char*)en); en += 4; + + // completely empty chunk + if (AVI->video_index[k].pos-offset == 0 && AVI->video_index[k].len == 0) { + k--; + nvi--; + } + +#ifdef DEBUG_ODML + /* + GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[avilib] [%d] POS 0x%llX len=%d key=%s offset (%llx) (%ld)\n", k, + AVI->video_index[k].pos, + (int)AVI->video_index[k].len, + AVI->video_index[k].key?"yes":"no ", offset, + AVI->video_superindex->aIndex[j].dwSize)); + */ +#endif + + k++; + } + + gf_free(chunk_start); + } + + AVI->video_frames = nvi; + // this should deal with broken 'rec ' odml files. + if (AVI->video_frames == 0) { + AVI->is_opendml=0; + goto multiple_riff; + } + + // ************************ + // AUDIO + // ************************ + + for(audtr=0; audtranum; ++audtr) { + + k = 0; + if (!AVI->track[audtr].audio_superindex) { + GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[avilib] (%s) cannot read audio index for track %d\n", __FILE__, audtr)); + continue; + } + for (j=0; jtrack[audtr].audio_superindex->nEntriesInUse; j++) { + + // read from file + chunk_start = en = (char*)gf_malloc ((u32) (AVI->track[audtr].audio_superindex->aIndex[j].dwSize+hdrl_len)); + + if (gf_f64_seek(AVI->fdes, AVI->track[audtr].audio_superindex->aIndex[j].qwOffset, SEEK_SET) == (u64)-1) { + gf_free(chunk_start); + continue; + } + + if (avi_read(AVI->fdes, en, (u32) (AVI->track[audtr].audio_superindex->aIndex[j].dwSize+hdrl_len)) <= 0) { + gf_free(chunk_start); + continue; + } + + nrEntries = str2ulong((unsigned char*)en + 12); + //if (nrEntries > 50) nrEntries = 2; // XXX +#ifdef DEBUG_ODML + //GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[avilib] [%d:%d] Audio nrEntries %ld\n", j, audtr, nrEntries)); +#endif + offset = str2ullong((unsigned char*)en + 20); + + // skip header + en += hdrl_len; + nai[audtr] += nrEntries; + AVI->track[audtr].audio_index = (audio_index_entry *) gf_realloc (AVI->track[audtr].audio_index, nai[audtr] * sizeof (audio_index_entry)); + + while (k < nai[audtr]) { + + AVI->track[audtr].audio_index[k].pos = offset + str2ulong((unsigned char*)en); en += 4; + AVI->track[audtr].audio_index[k].len = str2ulong_len((unsigned char*)en); en += 4; + AVI->track[audtr].audio_index[k].tot = tot[audtr]; + tot[audtr] += AVI->track[audtr].audio_index[k].len; + +#ifdef DEBUG_ODML + /* + GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[avilib] [%d:%d] POS 0x%llX len=%d offset (%llx) (%ld)\n", k, audtr, + AVI->track[audtr].audio_index[k].pos, + (int)AVI->track[audtr].audio_index[k].len, + offset, AVI->track[audtr].audio_superindex->aIndex[j].dwSize)); + */ +#endif + + ++k; + } + + gf_free(chunk_start); + } + + AVI->track[audtr].audio_chunks = nai[audtr]; + AVI->track[audtr].audio_bytes = tot[audtr]; + } + } // is opendml + + else if (AVI->total_frames && !AVI->is_opendml && idx_type==0) { + + // ********************* + // MULTIPLE RIFF CHUNKS (and no index) + // ********************* + +multiple_riff: + + gf_f64_seek(AVI->fdes, AVI->movi_start, SEEK_SET); + + AVI->n_idx = 0; + + GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[avilib] Reconstructing index...")); + + // Number of frames; only one audio track supported + nvi = AVI->video_frames = AVI->total_frames; + nai[0] = AVI->track[0].audio_chunks = AVI->total_frames; + for(j=1; janum; ++j) AVI->track[j].audio_chunks = 0; + + AVI->video_index = (video_index_entry *) gf_malloc(nvi*sizeof(video_index_entry)); + + if(AVI->video_index==0) ERR_EXIT(AVI_ERR_NO_MEM); + + for(j=0; janum; ++j) { + if(AVI->track[j].audio_chunks) { + AVI->track[j].audio_index = (audio_index_entry *) gf_malloc((nai[j]+1)*sizeof(audio_index_entry)); + memset(AVI->track[j].audio_index, 0, (nai[j]+1)*(sizeof(audio_index_entry))); + if(AVI->track[j].audio_index==0) ERR_EXIT(AVI_ERR_NO_MEM); + } + } + + nvi = 0; + for(j=0; janum; ++j) {nai[j] = 0; tot[j] = 0;} + + aud_chunks = AVI->total_frames; + + while(1) + { + if (nvi >= AVI->total_frames) break; + + if( avi_read(AVI->fdes,data,8) != 8 ) break; + n = str2ulong((unsigned char *)data+4); + + + j=0; + + if (aud_chunks - nai[j] -1 <= 0) { + aud_chunks += AVI->total_frames; + AVI->track[j].audio_index = (audio_index_entry *) + gf_realloc( AVI->track[j].audio_index, (aud_chunks+1)*sizeof(audio_index_entry)); + if (!AVI->track[j].audio_index) { + GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[avilib] Internal error in avilib -- no mem\n")); + AVI_errno = AVI_ERR_NO_MEM; + return -1; + } + } + + /* Check if we got a tag ##db, ##dc or ##wb */ + + // VIDEO + if( + (data[0]=='0' || data[1]=='0') && + (data[2]=='d' || data[2]=='D') && + (data[3]=='b' || data[3]=='B' || data[3]=='c' || data[3]=='C') ) { + + AVI->video_index[nvi].key = 0x0; + AVI->video_index[nvi].pos = gf_f64_tell(AVI->fdes); + AVI->video_index[nvi].len = (u32) n; + + /* + GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[avilib] Frame %ld pos %"LLD" len %"LLD" key %ld\n", + nvi, AVI->video_index[nvi].pos, AVI->video_index[nvi].len, (long)AVI->video_index[nvi].key)); + */ + nvi++; + gf_f64_seek(AVI->fdes,PAD_EVEN(n),SEEK_CUR); + } + + //AUDIO + else if( + (data[0]=='0' || data[1]=='1') && + (data[2]=='w' || data[2]=='W') && + (data[3]=='b' || data[3]=='B') ) { + + + AVI->track[j].audio_index[nai[j]].pos = gf_f64_tell(AVI->fdes); + AVI->track[j].audio_index[nai[j]].len = (u32) n; + AVI->track[j].audio_index[nai[j]].tot = tot[j]; + tot[j] += AVI->track[j].audio_index[nai[j]].len; + nai[j]++; + + gf_f64_seek(AVI->fdes,PAD_EVEN(n),SEEK_CUR); + } + else { + gf_f64_seek(AVI->fdes,-4,SEEK_CUR); + } + + } + if (nvi < AVI->total_frames) { + GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[avilib] Uh? Some frames seems missing (%ld/%d)\n", + nvi, AVI->total_frames)); + } + + + AVI->video_frames = nvi; + AVI->track[0].audio_chunks = nai[0]; + + for(j=0; janum; ++j) AVI->track[j].audio_bytes = tot[j]; + idx_type = 1; + GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[avilib] done. nvi=%ld nai=%ld tot=%ld\n", nvi, nai[0], tot[0])); + + } // total_frames but no indx chunk (xawtv does this) + + else + + { + // ****************** + // NO OPENDML + // ****************** + + /* Now generate the video index and audio index arrays */ + + nvi = 0; + for(j=0; janum; ++j) nai[j] = 0; + + for(i=0;in_idx;i++) { + + if(strnicmp((char *)AVI->idx[i],AVI->video_tag,3) == 0) nvi++; + + for(j=0; janum; ++j) if(strnicmp((char *)AVI->idx[i], AVI->track[j].audio_tag,4) == 0) nai[j]++; + } + + AVI->video_frames = nvi; + for(j=0; janum; ++j) AVI->track[j].audio_chunks = nai[j]; + + + if(AVI->video_frames==0) ERR_EXIT(AVI_ERR_NO_VIDS); + AVI->video_index = (video_index_entry *) gf_malloc(nvi*sizeof(video_index_entry)); + if(AVI->video_index==0) ERR_EXIT(AVI_ERR_NO_MEM); + + for(j=0; janum; ++j) { + if(AVI->track[j].audio_chunks) { + AVI->track[j].audio_index = (audio_index_entry *) gf_malloc((nai[j]+1)*sizeof(audio_index_entry)); + memset(AVI->track[j].audio_index, 0, (nai[j]+1)*(sizeof(audio_index_entry))); + if(AVI->track[j].audio_index==0) ERR_EXIT(AVI_ERR_NO_MEM); + } + } + + nvi = 0; + for(j=0; janum; ++j) {nai[j] = 0; tot[j] = 0;} + + ioff = idx_type == 1 ? 8 : (u32)AVI->movi_start+4; + + for(i=0;in_idx;i++) { + + //video + if(strnicmp((char *)AVI->idx[i],AVI->video_tag,3) == 0) { + AVI->video_index[nvi].key = str2ulong(AVI->idx[i]+ 4); + AVI->video_index[nvi].pos = str2ulong(AVI->idx[i]+ 8)+ioff; + AVI->video_index[nvi].len = str2ulong(AVI->idx[i]+12); + nvi++; + } + + //audio + for(j=0; janum; ++j) { + + if(strnicmp((char *)AVI->idx[i],AVI->track[j].audio_tag,4) == 0) { + AVI->track[j].audio_index[nai[j]].pos = str2ulong(AVI->idx[i]+ 8)+ioff; + AVI->track[j].audio_index[nai[j]].len = str2ulong(AVI->idx[i]+12); + AVI->track[j].audio_index[nai[j]].tot = tot[j]; + tot[j] += AVI->track[j].audio_index[nai[j]].len; + nai[j]++; + } + } + } + + + for(j=0; janum; ++j) AVI->track[j].audio_bytes = tot[j]; + + } // is no opendml + + /* Reposition the file */ + + gf_f64_seek(AVI->fdes,AVI->movi_start,SEEK_SET); + AVI->video_pos = 0; + + return(0); +} + +long AVI_video_frames(avi_t *AVI) +{ + return AVI->video_frames; +} +int AVI_video_width(avi_t *AVI) +{ + return AVI->width; +} +int AVI_video_height(avi_t *AVI) +{ + return AVI->height; +} +double AVI_frame_rate(avi_t *AVI) +{ + return AVI->fps; +} +char* AVI_video_compressor(avi_t *AVI) +{ + return AVI->compressor2; +} + +long AVI_max_video_chunk(avi_t *AVI) +{ + return AVI->max_len; +} + +int AVI_audio_tracks(avi_t *AVI) +{ + return(AVI->anum); +} + +int AVI_audio_channels(avi_t *AVI) +{ + return AVI->track[AVI->aptr].a_chans; +} + +long AVI_audio_mp3rate(avi_t *AVI) +{ + return AVI->track[AVI->aptr].mp3rate; +} + +long AVI_audio_padrate(avi_t *AVI) +{ + return AVI->track[AVI->aptr].padrate; +} + +int AVI_audio_bits(avi_t *AVI) +{ + return AVI->track[AVI->aptr].a_bits; +} + +int AVI_audio_format(avi_t *AVI) +{ + return AVI->track[AVI->aptr].a_fmt; +} + +long AVI_audio_rate(avi_t *AVI) +{ + return AVI->track[AVI->aptr].a_rate; +} + + +long AVI_frame_size(avi_t *AVI, long frame) +{ + if(AVI->mode==AVI_MODE_WRITE) { AVI_errno = AVI_ERR_NOT_PERM; return -1; } + if(!AVI->video_index) { AVI_errno = AVI_ERR_NO_IDX; return -1; } + + if(frame < 0 || frame >= AVI->video_frames) return 0; + return (u32) (AVI->video_index[frame].len); +} + +long AVI_audio_size(avi_t *AVI, long frame) +{ + if(AVI->mode==AVI_MODE_WRITE) { AVI_errno = AVI_ERR_NOT_PERM; return -1; } + if(!AVI->track[AVI->aptr].audio_index) { AVI_errno = AVI_ERR_NO_IDX; return -1; } + + if(frame < 0 || frame >= AVI->track[AVI->aptr].audio_chunks) return -1; + return (u32) (AVI->track[AVI->aptr].audio_index[frame].len); +} + +u64 AVI_get_video_position(avi_t *AVI, long frame) +{ + if(AVI->mode==AVI_MODE_WRITE) { AVI_errno = AVI_ERR_NOT_PERM; return (u64) -1; } + if(!AVI->video_index) { AVI_errno = AVI_ERR_NO_IDX; return (u64) -1; } + + if(frame < 0 || frame >= AVI->video_frames) return 0; + return(AVI->video_index[frame].pos); +} + + +int AVI_seek_start(avi_t *AVI) +{ + if(AVI->mode==AVI_MODE_WRITE) { AVI_errno = AVI_ERR_NOT_PERM; return -1; } + + gf_f64_seek(AVI->fdes,AVI->movi_start,SEEK_SET); + AVI->video_pos = 0; + return 0; +} + +int AVI_set_video_position(avi_t *AVI, long frame) +{ + if(AVI->mode==AVI_MODE_WRITE) { AVI_errno = AVI_ERR_NOT_PERM; return -1; } + if(!AVI->video_index) { AVI_errno = AVI_ERR_NO_IDX; return -1; } + + if (frame < 0 ) frame = 0; + AVI->video_pos = frame; + return 0; +} + +int AVI_set_audio_bitrate(avi_t *AVI, long bitrate) +{ + if(AVI->mode==AVI_MODE_READ) { AVI_errno = AVI_ERR_NOT_PERM; return -1; } + + AVI->track[AVI->aptr].mp3rate = bitrate; + return 0; +} + + +long AVI_read_frame(avi_t *AVI, char *vidbuf, int *keyframe) +{ + long n; + + if(AVI->mode==AVI_MODE_WRITE) { AVI_errno = AVI_ERR_NOT_PERM; return -1; } + if(!AVI->video_index) { AVI_errno = AVI_ERR_NO_IDX; return -1; } + + if(AVI->video_pos < 0 || AVI->video_pos >= AVI->video_frames) return -1; + n = (u32) AVI->video_index[AVI->video_pos].len; + + *keyframe = (AVI->video_index[AVI->video_pos].key==0x10) ? 1:0; + + if (vidbuf == NULL) { + AVI->video_pos++; + return n; + } + + gf_f64_seek(AVI->fdes, AVI->video_index[AVI->video_pos].pos, SEEK_SET); + + if (avi_read(AVI->fdes,vidbuf,n) != (u32) n) + { + AVI_errno = AVI_ERR_READ; + return -1; + } + + AVI->video_pos++; + + return n; +} + +long AVI_get_audio_position_index(avi_t *AVI) +{ + if(AVI->mode==AVI_MODE_WRITE) { AVI_errno = AVI_ERR_NOT_PERM; return -1; } + if(!AVI->track[AVI->aptr].audio_index) { AVI_errno = AVI_ERR_NO_IDX; return -1; } + + return (AVI->track[AVI->aptr].audio_posc); +} + +int AVI_set_audio_position_index(avi_t *AVI, long indexpos) +{ + if(AVI->mode==AVI_MODE_WRITE) { AVI_errno = AVI_ERR_NOT_PERM; return -1; } + if(!AVI->track[AVI->aptr].audio_index) { AVI_errno = AVI_ERR_NO_IDX; return -1; } + if(indexpos > AVI->track[AVI->aptr].audio_chunks) { AVI_errno = AVI_ERR_NO_IDX; return -1; } + + AVI->track[AVI->aptr].audio_posc = indexpos; + AVI->track[AVI->aptr].audio_posb = 0; + + return 0; +} + + +int AVI_set_audio_position(avi_t *AVI, long byte) +{ + long n0, n1, n; + + if(AVI->mode==AVI_MODE_WRITE) { AVI_errno = AVI_ERR_NOT_PERM; return -1; } + if(!AVI->track[AVI->aptr].audio_index) { AVI_errno = AVI_ERR_NO_IDX; return -1; } + + if(byte < 0) byte = 0; + + /* Binary search in the audio chunks */ + + n0 = 0; + n1 = AVI->track[AVI->aptr].audio_chunks; + + while(n0track[AVI->aptr].audio_index[n].tot>(u32) byte) + n1 = n; + else + n0 = n; + } + + AVI->track[AVI->aptr].audio_posc = n0; + AVI->track[AVI->aptr].audio_posb = (u32) (byte - AVI->track[AVI->aptr].audio_index[n0].tot); + + return 0; +} + +long AVI_read_audio(avi_t *AVI, char *audbuf, long bytes, int *continuous) +{ + long nr, left, todo; + s64 pos; + + if(AVI->mode==AVI_MODE_WRITE) { AVI_errno = AVI_ERR_NOT_PERM; return -1; } + if(!AVI->track[AVI->aptr].audio_index) { AVI_errno = AVI_ERR_NO_IDX; return -1; } + + nr = 0; /* total number of bytes read */ + + if (bytes==0) { + AVI->track[AVI->aptr].audio_posc++; + AVI->track[AVI->aptr].audio_posb = 0; + } + + *continuous = 1; + while(bytes>0) + { + s64 ret; + left = (long) (AVI->track[AVI->aptr].audio_index[AVI->track[AVI->aptr].audio_posc].len - AVI->track[AVI->aptr].audio_posb); + if(left==0) + { + if(AVI->track[AVI->aptr].audio_posc>=AVI->track[AVI->aptr].audio_chunks-1) return nr; + AVI->track[AVI->aptr].audio_posc++; + AVI->track[AVI->aptr].audio_posb = 0; + *continuous = 0; + continue; + } + if(bytestrack[AVI->aptr].audio_index[AVI->track[AVI->aptr].audio_posc].pos + AVI->track[AVI->aptr].audio_posb; + gf_f64_seek(AVI->fdes, pos, SEEK_SET); + if ( (ret = avi_read(AVI->fdes,audbuf+nr,todo)) != todo) + { + GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[avilib] XXX pos = %"LLD", ret = %"LLD", todo = %ld\n", pos, ret, todo)); + AVI_errno = AVI_ERR_READ; + return -1; + } + bytes -= todo; + nr += todo; + AVI->track[AVI->aptr].audio_posb += todo; + } + + return nr; +} + + +/* AVI_read_data: Special routine for reading the next audio or video chunk + without having an index of the file. */ + +int AVI_read_data(avi_t *AVI, char *vidbuf, long max_vidbuf, + char *audbuf, long max_audbuf, + long *len) +{ + +/* + * Return codes: + * + * 1 = video data read + * 2 = audio data read + * 0 = reached EOF + * -1 = video buffer too small + * -2 = audio buffer too small + */ + + s64 n; + char data[8]; + + if(AVI->mode==AVI_MODE_WRITE) return 0; + + while(1) + { + /* Read tag and length */ + + if( avi_read(AVI->fdes,data,8) != 8 ) return 0; + + /* if we got a list tag, ignore it */ + + if(strnicmp(data,"LIST",4) == 0) + { + gf_f64_seek(AVI->fdes,4,SEEK_CUR); + continue; + } + + n = PAD_EVEN(str2ulong((unsigned char *)data+4)); + + if(strnicmp(data,AVI->video_tag,3) == 0) + { + *len = (u32) n; + AVI->video_pos++; + if(n>max_vidbuf) + { + gf_f64_seek(AVI->fdes,n,SEEK_CUR); + return -1; + } + if(avi_read(AVI->fdes,vidbuf, (u32) n) != n ) return 0; + return 1; + } + else if(strnicmp(data,AVI->track[AVI->aptr].audio_tag,4) == 0) + { + *len = (u32) n; + if(n>max_audbuf) + { + gf_f64_seek(AVI->fdes,n,SEEK_CUR); + return -2; + } + if(avi_read(AVI->fdes,audbuf, (u32) n) != n ) return 0; + return 2; + break; + } + else + if(gf_f64_seek(AVI->fdes,n,SEEK_CUR) == (u64) -1) return 0; + } +} + +u64 AVI_max_size(void) +{ + return((u64) AVI_MAX_LEN); +} + + +#endif /*GPAC_DISABLE_AVILIB*/ diff --git a/src/gpacmp4/base_encoding.c b/src/gpacmp4/base_encoding.c new file mode 100644 index 00000000..44eafff6 --- /dev/null +++ b/src/gpacmp4/base_encoding.c @@ -0,0 +1,280 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / common tools sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include +#include + +static const char base_64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + +GF_EXPORT +u32 gf_base64_encode(char *_in, u32 inSize, char *_out, u32 outSize) +{ + s32 padding; + u32 i = 0, j = 0; + unsigned char *in = (unsigned char *)_in; + unsigned char *out = (unsigned char *)_out; + + if (outSize < (inSize * 4 / 3)) return 0; + + while (i < inSize) { + padding = 3 - (inSize - i); + if (padding == 2) { + out[j] = base_64[in[i]>>2]; + out[j+1] = base_64[(in[i] & 0x03) << 4]; + out[j+2] = '='; + out[j+3] = '='; + } else if (padding == 1) { + out[j] = base_64[in[i]>>2]; + out[j+1] = base_64[((in[i] & 0x03) << 4) | ((in[i+1] & 0xf0) >> 4)]; + out[j+2] = base_64[(in[i+1] & 0x0f) << 2]; + out[j+3] = '='; + } else{ + out[j] = base_64[in[i]>>2]; + out[j+1] = base_64[((in[i] & 0x03) << 4) | ((in[i+1] & 0xf0) >> 4)]; + out[j+2] = base_64[((in[i+1] & 0x0f) << 2) | ((in[i+2] & 0xc0) >> 6)]; + out[j+3] = base_64[in[i+2] & 0x3f]; + } + i += 3; + j += 4; + } + return j; +} + +static const unsigned char index_64[128] = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 62, 0xff, 0xff, 0xff, 63, + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 0xff, 0xff, 0xff, 0xff, 0xff +}; + +#define char64(c) ((c > 127) ? (char) 0xff : index_64[(c)]) + +/*denoise input*/ +u32 load_block(char *in, u32 size, u32 pos, char *out) +{ + u32 i, len; + u8 c; + len = i = 0; + while ((len<4) && ((pos+i)='A') && (c<='Z')) + || ((c>='a') && (c<='z')) + || ((c>='0') && (c<='9')) + || (c=='=') || (c=='+') || (c=='/') + ) { + out[len] = c; + len++; + } + i++; + } + while (len<4) { out[len] = (char) 0xFF; len++; } + return pos+i; +} + +GF_EXPORT +u32 gf_base64_decode(char *in_buf, u32 inSize, char *out, u32 outSize) +{ + u32 i = 0, j = 0, padding; + unsigned char c[4], in[4]; + + if (outSize < (inSize * 3 / 4)) return 0; + + while ((i + 3) < inSize) { + padding = 0; + i = load_block(in_buf, inSize, i, (char*)in); + c[0] = char64(in[0]); + padding += (c[0] == 0xff); + c[1] = char64(in[1]); + padding += (c[1] == 0xff); + c[2] = char64(in[2]); + padding += (c[2] == 0xff); + c[3] = char64(in[3]); + padding += (c[3] == 0xff); + if (padding == 2) { + out[j++] = (c[0] << 2) | ((c[1] & 0x30) >> 4); + out[j] = (c[1] & 0x0f) << 4; + } else if (padding == 1) { + out[j++] = (c[0] << 2) | ((c[1] & 0x30) >> 4); + out[j++] = ((c[1] & 0x0f) << 4) | ((c[2] & 0x3c) >> 2); + out[j] = (c[2] & 0x03) << 6; + } else { + out[j++] = (c[0] << 2) | ((c[1] & 0x30) >> 4); + out[j++] = ((c[1] & 0x0f) << 4) | ((c[2] & 0x3c) >> 2); + out[j++] = ((c[2] & 0x03) << 6) | (c[3] & 0x3f); + } + //i += 4; + } + return j; +} + + +/* + * Copyright (c) ENST 2004 - Philippe de Cuetos + */ + +static const char base_16[] = "0123456789abcdef"; + +GF_EXPORT +u32 gf_base16_encode(char *_in, u32 inSize, char *_out, u32 outSize) +{ + u32 i = 0; + unsigned char *in = (unsigned char *)_in; + unsigned char *out = (unsigned char *)_out; + + if (outSize < (inSize * 2)+1) return 0; + + for (i=0;i> 4)]; + out[2*i+1] = base_16[(in[i] & 0x0f)]; + } + out[(inSize * 2)] = 0; + + return i; +} + +#define char16(nb) (((nb) < 97) ? ((nb)-48) : ((nb)-87)) + +GF_EXPORT +u32 gf_base16_decode(char *in, u32 inSize, char *out, u32 outSize) +{ + u32 j=0; + u32 c[2] = {0,0}; + + if (outSize < (inSize / 2)) return 0; + if ((inSize % 2) != 0) return 0; + + for (j=0;j + +#define ZLIB_COMPRESS_SAFE 4 + +#define HAVE_ZLIB 0 + +GF_Err gf_gz_compress_payload(char **data, u32 data_len, u32 *max_size) +{ +#if HAVE_ZLIB + z_stream stream; + int err; + char *dest = (char *)gf_malloc(sizeof(char)*data_len*ZLIB_COMPRESS_SAFE); + stream.next_in = (Bytef*)(*data) ; + stream.avail_in = (uInt)data_len ; + stream.next_out = ( Bytef*)dest; + stream.avail_out = (uInt)data_len*ZLIB_COMPRESS_SAFE; + stream.zalloc = (alloc_func)NULL; + stream.zfree = (free_func)NULL; + stream.opaque = (voidpf)NULL; + + err = deflateInit(&stream, 9); + if (err != Z_OK) { + gf_free(dest); + return GF_IO_ERR; + } + + err = deflate(&stream, Z_FINISH); + if (err != Z_STREAM_END) { + deflateEnd(&stream); + gf_free(dest); + return GF_IO_ERR; + } + if (data_len + +/*the default size for new streams allocation...*/ +#define BS_MEM_BLOCK_ALLOC_SIZE 250 + +/*private types*/ +enum +{ + GF_BITSTREAM_FILE_READ = GF_BITSTREAM_WRITE + 1, + GF_BITSTREAM_FILE_WRITE, + /*private mode if we own the buffer*/ + GF_BITSTREAM_WRITE_DYN +}; + +struct __tag_bitstream +{ + /*original stream data*/ + FILE *stream; + + /*original data*/ + char *original; + /*the size of our buffer in bytes*/ + u64 size; + /*current position in BYTES*/ + u64 position; + /*the byte readen/written*/ + u32 current; + /*the number of bits in the current byte*/ + u32 nbBits; + /*the bitstream mode*/ + u32 bsmode; + + void (*EndOfStream)(void *par); + void *par; +}; + + +GF_EXPORT +GF_BitStream *gf_bs_new(const char *buffer, u64 BufferSize, u32 mode) +{ + GF_BitStream *tmp; + if ( (buffer && ! BufferSize)) return NULL; + + tmp = (GF_BitStream *)gf_malloc(sizeof(GF_BitStream)); + if (!tmp) return NULL; + memset(tmp, 0, sizeof(GF_BitStream)); + + tmp->original = (char*)buffer; + tmp->size = BufferSize; + + tmp->position = 0; + tmp->current = 0; + tmp->bsmode = mode; + tmp->stream = NULL; + + switch (tmp->bsmode) { + case GF_BITSTREAM_READ: + tmp->nbBits = 8; + tmp->current = 0; + break; + case GF_BITSTREAM_WRITE: + tmp->nbBits = 0; + if (! buffer) { + /*if BufferSize is specified, use it. This is typically used when AvgSize of + some buffers is known, but some exceed it.*/ + if (BufferSize) { + tmp->size = BufferSize; + } else { + tmp->size = BS_MEM_BLOCK_ALLOC_SIZE; + } + tmp->original = (char *) gf_malloc(sizeof(char) * ((u32) tmp->size)); + if (! tmp->original) { + gf_free(tmp); + return NULL; + } + tmp->bsmode = GF_BITSTREAM_WRITE_DYN; + } else { + tmp->original = (char*)buffer; + tmp->size = BufferSize; + } + break; + default: + /*the stream constructor is not the same...*/ + gf_free(tmp); + return NULL; + } + return tmp; +} + +GF_EXPORT +GF_BitStream *gf_bs_from_file(FILE *f, u32 mode) +{ + GF_BitStream *tmp; + if (!f) return NULL; + + tmp = (GF_BitStream *)gf_malloc(sizeof(GF_BitStream)); + if (!tmp) return NULL; + memset(tmp, 0, sizeof(GF_BitStream)); + /*switch to internal mode*/ + mode = (mode==GF_BITSTREAM_READ) ? GF_BITSTREAM_FILE_READ : GF_BITSTREAM_FILE_WRITE; + tmp->bsmode = mode; + tmp->current = 0; + tmp->nbBits = (mode == GF_BITSTREAM_FILE_READ) ? 8 : 0; + tmp->original = NULL; + tmp->position = 0; + tmp->stream = f; + + /*get the size of this file (for read streams)*/ + tmp->position = gf_f64_tell(f); + gf_f64_seek(f, 0, SEEK_END); + tmp->size = gf_f64_tell(f); + gf_f64_seek(f, tmp->position, SEEK_SET); + return tmp; +} + +GF_EXPORT +void gf_bs_del(GF_BitStream *bs) +{ + if (!bs) return; + /*if we are in dynamic mode (alloc done by the bitstream), free the buffer if still present*/ + if ((bs->bsmode == GF_BITSTREAM_WRITE_DYN) && bs->original) gf_free(bs->original); + gf_free(bs); +} + + +/*returns 1 if aligned wrt current mode, 0 otherwise*/ +static Bool BS_IsAlign(GF_BitStream *bs) +{ + switch (bs->bsmode) { + case GF_BITSTREAM_READ: + case GF_BITSTREAM_FILE_READ: + return ( (8 == bs->nbBits) ? 1 : 0); + default: + return !bs->nbBits; + } +} + + +/*fetch a new byte in the bitstream switch between packets*/ +static u8 BS_ReadByte(GF_BitStream *bs) +{ + if (bs->bsmode == GF_BITSTREAM_READ) { + if (bs->position >= bs->size) { + if (bs->EndOfStream) bs->EndOfStream(bs->par); + return 0; + } + return (u32) bs->original[bs->position++]; + } + /*we are in FILE mode, test for end of file*/ + if (!feof(bs->stream)) { + bs->position++; + return (u32) fgetc(bs->stream); + } + if (bs->EndOfStream) bs->EndOfStream(bs->par); + return 0; +} + +#define NO_OPTS + +#ifndef NO_OPTS +static u32 bit_mask[] = {0x80, 0x40, 0x20, 0x10, 0x8, 0x4, 0x2, 0x1}; +static u32 bits_mask[] = {0x0, 0x1, 0x3, 0x7, 0xF, 0x1F, 0x3F, 0x7F}; +#endif + +GF_EXPORT +u8 gf_bs_read_bit(GF_BitStream *bs) +{ + if (bs->nbBits == 8) { + bs->current = BS_ReadByte(bs); + bs->nbBits = 0; + } +#ifdef NO_OPTS + { + s32 ret; + bs->current <<= 1; + bs->nbBits++; + ret = (bs->current & 0x100) >> 8; + return (u8) ret; + } +#else + return (u8) (bs->current & bit_mask[bs->nbBits++]) ? 1 : 0; +#endif + +} + +GF_EXPORT +u32 gf_bs_read_int(GF_BitStream *bs, u32 nBits) +{ + u32 ret; + +#ifndef NO_OPTS + if (nBits + bs->nbBits <= 8) { + bs->nbBits += nBits; + ret = (bs->current >> (8 - bs->nbBits) ) & bits_mask[nBits]; + return ret; + } +#endif + ret = 0; + while (nBits-- > 0) { + ret <<= 1; + ret |= gf_bs_read_bit(bs); + } + return ret; +} + +GF_EXPORT +u32 gf_bs_read_u8(GF_BitStream *bs) +{ + assert(bs->nbBits==8); + return (u32) BS_ReadByte(bs); +} + +GF_EXPORT +u32 gf_bs_read_u16(GF_BitStream *bs) +{ + u32 ret; + assert(bs->nbBits==8); + ret = BS_ReadByte(bs); ret<<=8; + ret |= BS_ReadByte(bs); + return ret; +} + + +GF_EXPORT +u32 gf_bs_read_u24(GF_BitStream *bs) +{ + u32 ret; + assert(bs->nbBits==8); + ret = BS_ReadByte(bs); ret<<=8; + ret |= BS_ReadByte(bs); ret<<=8; + ret |= BS_ReadByte(bs); + return ret; +} + +GF_EXPORT +u32 gf_bs_read_u32(GF_BitStream *bs) +{ + u32 ret; + assert(bs->nbBits==8); + ret = BS_ReadByte(bs); ret<<=8; + ret |= BS_ReadByte(bs); ret<<=8; + ret |= BS_ReadByte(bs); ret<<=8; + ret |= BS_ReadByte(bs); + return ret; +} + +GF_EXPORT +u64 gf_bs_read_u64(GF_BitStream *bs) +{ + u64 ret; + ret = gf_bs_read_u32(bs); ret<<=32; + ret |= gf_bs_read_u32(bs); + return ret; +} + +GF_EXPORT +u64 gf_bs_read_long_int(GF_BitStream *bs, u32 nBits) +{ + u64 ret = 0; + if (nBits>64) { + gf_bs_read_long_int(bs, nBits-64); + ret = gf_bs_read_long_int(bs, 64); + } else { + while (nBits-- > 0) { + ret <<= 1; + ret |= gf_bs_read_bit(bs); + } + } + return ret; +} + + +GF_EXPORT +Float gf_bs_read_float(GF_BitStream *bs) +{ + char buf [4] = "\0\0\0"; +#ifdef NO_OPTS + s32 i; + for (i = 0; i < 32; i++) + buf[3-i/8] |= gf_bs_read_bit(bs) << (7 - i%8); +#else + buf[3] = gf_bs_read_int(bs, 8); + buf[2] = gf_bs_read_int(bs, 8); + buf[1] = gf_bs_read_int(bs, 8); + buf[0] = gf_bs_read_int(bs, 8); +#endif + return (* (Float *) buf); +} + +GF_EXPORT +Double gf_bs_read_double(GF_BitStream *bs) +{ + char buf [8] = "\0\0\0\0\0\0\0"; + s32 i; + for (i = 0; i < 64; i++) + buf[7-i/8] |= gf_bs_read_bit(bs) << (7 - i%8); + return (* (Double *) buf); +} + +GF_EXPORT +u32 gf_bs_read_data(GF_BitStream *bs, char *data, u32 nbBytes) +{ + u64 orig = bs->position; + + if (bs->position+nbBytes > bs->size) return 0; + + if (BS_IsAlign(bs)) { + switch (bs->bsmode) { + case GF_BITSTREAM_READ: + memcpy(data, bs->original + bs->position, nbBytes); + bs->position += nbBytes; + return nbBytes; + case GF_BITSTREAM_FILE_READ: + case GF_BITSTREAM_FILE_WRITE: + nbBytes = fread(data, 1, nbBytes, bs->stream); + bs->position += nbBytes; + return nbBytes; + default: + return 0; + } + } + + while (nbBytes-- > 0) { + *data++ = gf_bs_read_int(bs, 8); + } + return (u32) (bs->position - orig); + +} + + + +static void BS_WriteByte(GF_BitStream *bs, u8 val) +{ + /*we don't allow write on READ buffers*/ + if ( (bs->bsmode == GF_BITSTREAM_READ) || (bs->bsmode == GF_BITSTREAM_FILE_READ) ) return; + if (!bs->original && !bs->stream) return; + + /*we are in MEM mode*/ + if ( (bs->bsmode == GF_BITSTREAM_WRITE) || (bs->bsmode == GF_BITSTREAM_WRITE_DYN) ) { + if (bs->position == bs->size) { + /*no more space...*/ + if (bs->bsmode != GF_BITSTREAM_WRITE_DYN) return; + /*gf_realloc if enough space...*/ + if (bs->size > 0xFFFFFFFF) return; + bs->original = (char*)gf_realloc(bs->original, (u32) (bs->size + BS_MEM_BLOCK_ALLOC_SIZE)); + if (!bs->original) return; + bs->size += BS_MEM_BLOCK_ALLOC_SIZE; + } + bs->original[bs->position] = val; + bs->position++; + return; + } + /*we are in FILE mode, no pb for any gf_realloc...*/ + fputc(val, bs->stream); + /*check we didn't rewind the stream*/ + if (bs->size == bs->position) bs->size++; + bs->position += 1; +} + +static void BS_WriteBit(GF_BitStream *bs, u32 bit) +{ + bs->current <<= 1; + bs->current |= bit; + if (++ bs->nbBits == 8) { + bs->nbBits = 0; + BS_WriteByte(bs, (u8) bs->current); + bs->current = 0; + } +} + +GF_EXPORT +void gf_bs_write_int(GF_BitStream *bs, s32 value, s32 nBits) +{ + value <<= sizeof (s32) * 8 - nBits; + while (--nBits >= 0) { + BS_WriteBit (bs, value < 0); + value <<= 1; + } +} + +GF_EXPORT +void gf_bs_write_long_int(GF_BitStream *bs, s64 value, s32 nBits) +{ + if (nBits>64) { + gf_bs_write_int(bs, 0, nBits-64); + gf_bs_write_long_int(bs, value, 64); + } else { + value <<= sizeof (s64) * 8 - nBits; + while (--nBits >= 0) { + BS_WriteBit (bs, value < 0); + value <<= 1; + } + } +} + +GF_EXPORT +void gf_bs_write_u8(GF_BitStream *bs, u32 value) +{ + assert(!bs->nbBits); + BS_WriteByte(bs, (u8) value); +} + +GF_EXPORT +void gf_bs_write_u16(GF_BitStream *bs, u32 value) +{ + assert(!bs->nbBits); + BS_WriteByte(bs, (u8) ((value>>8)&0xff)); + BS_WriteByte(bs, (u8) ((value)&0xff)); +} + +GF_EXPORT +void gf_bs_write_u24(GF_BitStream *bs, u32 value) +{ + assert(!bs->nbBits); + BS_WriteByte(bs, (u8) ((value>>16)&0xff)); + BS_WriteByte(bs, (u8) ((value>>8)&0xff)); + BS_WriteByte(bs, (u8) ((value)&0xff)); +} + +GF_EXPORT +void gf_bs_write_u32(GF_BitStream *bs, u32 value) +{ + assert(!bs->nbBits); + BS_WriteByte(bs, (u8) ((value>>24)&0xff)); + BS_WriteByte(bs, (u8) ((value>>16)&0xff)); + BS_WriteByte(bs, (u8) ((value>>8)&0xff)); + BS_WriteByte(bs, (u8) ((value)&0xff)); +} + +GF_EXPORT +void gf_bs_write_u64(GF_BitStream *bs, u64 value) +{ + assert(!bs->nbBits); + gf_bs_write_u32(bs, (u32) ((value>>32)&0xffffffff)); + gf_bs_write_u32(bs, (u32) (value&0xffffffff)); +} + +GF_EXPORT +u32 gf_bs_write_byte(GF_BitStream *bs, u8 byte, u32 repeat_count) +{ + if (!BS_IsAlign(bs)) { + u32 count = 0; + while (countbsmode) { + case GF_BITSTREAM_WRITE: + if (bs->position + repeat_count > bs->size) + return 0; + memset(bs->original + bs->position, byte, repeat_count); + bs->position += repeat_count; + return repeat_count; + case GF_BITSTREAM_WRITE_DYN: + /*need to gf_realloc ...*/ + if (bs->position+repeat_count> bs->size) { + if (bs->size + repeat_count > 0xFFFFFFFF) + return 0; + bs->original = (char*)gf_realloc(bs->original, sizeof(u32)*((u32) bs->size + repeat_count)); + if (!bs->original) + return 0; + bs->size += repeat_count; + } + memset(bs->original + bs->position, byte, repeat_count); + bs->position += repeat_count; + return repeat_count; + case GF_BITSTREAM_FILE_READ: + case GF_BITSTREAM_FILE_WRITE: + if (fwrite(&byte, 1, repeat_count, bs->stream) != repeat_count) return 0; + if (bs->size == bs->position) bs->size += repeat_count; + bs->position += repeat_count; + return repeat_count; + default: + return 0; + } +} + + + +GF_EXPORT +void gf_bs_write_float(GF_BitStream *bs, Float value) +{ + u32 i; + union + { float f; + char sz [4]; + } float_value; + float_value.f = value; + + for (i = 0; i < 32; i++) + BS_WriteBit(bs, (float_value.sz [3 - i / 8] & 1 << (7 - i % 8)) != 0); + +} + +GF_EXPORT +void gf_bs_write_double (GF_BitStream *bs, Double value) +{ + u32 i; + union + { Double d; + char sz [8]; + } double_value; + double_value.d = value; + for (i = 0; i < 64; i++) { + BS_WriteBit(bs, (double_value.sz [7 - i / 8] & 1 << (7 - i % 8)) != 0); + } +} + + +GF_EXPORT +u32 gf_bs_write_data(GF_BitStream *bs, const char *data, u32 nbBytes) +{ + /*we need some feedback for this guy...*/ + u64 begin = bs->position; + if (!nbBytes) return 0; + + if (BS_IsAlign(bs)) { + switch (bs->bsmode) { + case GF_BITSTREAM_WRITE: + if (bs->position+nbBytes > bs->size) + return 0; + memcpy(bs->original + bs->position, data, nbBytes); + bs->position += nbBytes; + return nbBytes; + case GF_BITSTREAM_WRITE_DYN: + /*need to gf_realloc ...*/ + if (bs->position+nbBytes > bs->size) { + if (bs->size + nbBytes > 0xFFFFFFFF) + return 0; + bs->original = (char*)gf_realloc(bs->original, sizeof(u32)*((u32) bs->size + nbBytes)); + if (!bs->original) + return 0; + bs->size += nbBytes; + } + memcpy(bs->original + bs->position, data, nbBytes); + bs->position += nbBytes; + return nbBytes; + case GF_BITSTREAM_FILE_READ: + case GF_BITSTREAM_FILE_WRITE: + if (fwrite(data, nbBytes, 1, bs->stream) != 1) return 0; + if (bs->size == bs->position) bs->size += nbBytes; + bs->position += nbBytes; + return nbBytes; + default: + return 0; + } + } + + while (nbBytes) { + gf_bs_write_int(bs, (s32) *data, 8); + data++; + nbBytes--; + } + return (u32) (bs->position - begin); +} + +/*align return the num of bits read in READ mode, 0 in WRITE*/ +GF_EXPORT +u8 gf_bs_align(GF_BitStream *bs) +{ + u8 res = 8 - bs->nbBits; + if ( (bs->bsmode == GF_BITSTREAM_READ) || (bs->bsmode == GF_BITSTREAM_FILE_READ)) { + if (res > 0) { + gf_bs_read_int(bs, res); + } + return res; + } + if (bs->nbBits > 0) { + gf_bs_write_int (bs, 0, res); + return res; + } + return 0; +} + + +/*size available in the bitstream*/ +GF_EXPORT +u64 gf_bs_available(GF_BitStream *bs) +{ + s64 cur, end; + + /*in WRITE mode only, this should not be called, but return something big in case ...*/ + if ( (bs->bsmode == GF_BITSTREAM_WRITE) + || (bs->bsmode == GF_BITSTREAM_WRITE_DYN) + ) + return (u64) -1; + + /*we are in MEM mode*/ + if (bs->bsmode == GF_BITSTREAM_READ) { + if ((s64)bs->size - (s64)bs->position < 0) + return 0; + else + return (bs->size - bs->position); + } + /*FILE READ: assume size hasn't changed, otherwise the user shall call gf_bs_get_refreshed_size*/ + if (bs->bsmode==GF_BITSTREAM_FILE_READ) return (bs->size - bs->position); + + cur = gf_f64_tell(bs->stream); + gf_f64_seek(bs->stream, 0, SEEK_END); + end = gf_f64_tell(bs->stream); + gf_f64_seek(bs->stream, cur, SEEK_SET); + return (u64) (end - cur); +} + +/*call this funct to set the buffer size to the nb of bytes written +Used only in WRITE mode, as we don't know the real size during allocation... +return -1 for bad param or gf_malloc failed +return nbBytes cut*/ +static s32 BS_CutBuffer(GF_BitStream *bs) +{ + s32 nbBytes; + if ( (bs->bsmode != GF_BITSTREAM_WRITE_DYN) && (bs->bsmode != GF_BITSTREAM_WRITE)) return (u32) -1; + /*Align our buffer or we're dead!*/ + gf_bs_align(bs); + + nbBytes = (u32) (bs->size - bs->position); + if (!nbBytes || (nbBytes == 0xFFFFFFFF) || (bs->position >= 0xFFFFFFFF)) return 0; + bs->original = (char*)gf_realloc(bs->original, (u32) bs->position); + if (! bs->original) return (u32) -1; + /*just in case, re-adjust..*/ + bs->size = bs->position; + return nbBytes; +} + +/*For DYN mode, this gets the content out*/ +GF_EXPORT +void gf_bs_get_content(GF_BitStream *bs, char **output, u32 *outSize) +{ + /*only in WRITE MEM mode*/ + if (bs->bsmode != GF_BITSTREAM_WRITE_DYN) return; + if (!bs->position && !bs->nbBits) { + *output = NULL; + *outSize = 0; + gf_free(bs->original); + } else { + s32 copy = BS_CutBuffer(bs); + if (copy < 0){ + *output = NULL; + } else + *output = bs->original; + *outSize = (u32) bs->size; + } + bs->original = NULL; + bs->size = 0; + bs->position = 0; +} + +/* Skip nbytes. + Align + If READ (MEM or FILE) mode, just read n times 8 bit + If WRITE (MEM or FILE) mode, write n times 0 on 8 bit +*/ +GF_EXPORT +void gf_bs_skip_bytes(GF_BitStream *bs, u64 nbBytes) +{ + if (!bs || !nbBytes) return; + + gf_bs_align(bs); + + /*special case for file skipping...*/ + if ((bs->bsmode == GF_BITSTREAM_FILE_WRITE) || (bs->bsmode == GF_BITSTREAM_FILE_READ)) { + gf_f64_seek(bs->stream, nbBytes, SEEK_CUR); + bs->position += nbBytes; + return; + } + + /*special case for reading*/ + if (bs->bsmode == GF_BITSTREAM_READ) { + bs->position += nbBytes; + return; + } + /*for writing we must do it this way, otherwise pb in dynamic buffers*/ + while (nbBytes) { + gf_bs_write_int(bs, 0, 8); + nbBytes--; + } +} + +/*Only valid for READ MEMORY*/ +GF_EXPORT +void gf_bs_rewind_bits(GF_BitStream *bs, u64 nbBits) +{ + u64 nbBytes; + if (bs->bsmode != GF_BITSTREAM_READ) return; + + nbBits -= (bs->nbBits); + nbBytes = (nbBits+8)>>3; + nbBits = nbBytes*8 - nbBits; + gf_bs_align(bs); + assert(bs->position >= nbBytes); + bs->position -= nbBytes + 1; + gf_bs_read_int(bs, (u32)nbBits); + return; +} + +/*seek from begining of stream: use internally even when non aligned!*/ +static GF_Err BS_SeekIntern(GF_BitStream *bs, u64 offset) +{ + u32 i; + /*if mem, do it */ + if ((bs->bsmode == GF_BITSTREAM_READ) || (bs->bsmode == GF_BITSTREAM_WRITE) || (bs->bsmode == GF_BITSTREAM_WRITE_DYN)) { + if (offset > 0xFFFFFFFF) return GF_IO_ERR; + /*0 for write, read will be done automatically*/ + if (offset >= bs->size) { + if ( (bs->bsmode == GF_BITSTREAM_READ) || (bs->bsmode == GF_BITSTREAM_WRITE) ) return GF_BAD_PARAM; + /*in DYN, gf_realloc ...*/ + bs->original = (char*)gf_realloc(bs->original, (u32) (offset + 1)); + for (i = 0; i < (u32) (offset + 1 - bs->size); i++) { + bs->original[bs->size + i] = 0; + } + bs->size = offset + 1; + } + bs->current = bs->original[offset]; + bs->position = offset; + bs->nbBits = (bs->bsmode == GF_BITSTREAM_READ) ? 8 : 0; + return GF_OK; + } + + gf_f64_seek(bs->stream, offset, SEEK_SET); + + bs->position = offset; + bs->current = 0; + /*setup NbBits so that next acccess to the buffer will trigger read/write*/ + bs->nbBits = (bs->bsmode == GF_BITSTREAM_FILE_READ) ? 8 : 0; + return GF_OK; +} + +/*seek from begining of stream: align before anything else*/ +GF_EXPORT +GF_Err gf_bs_seek(GF_BitStream *bs, u64 offset) +{ + /*warning: we allow offset = bs->size for WRITE buffers*/ + if (offset > bs->size) return GF_BAD_PARAM; + + gf_bs_align(bs); + return BS_SeekIntern(bs, offset); +} + +/*peek bits (as int!!) from orig position (ON BYTE BOUNDARIES, from 0) - only for read ...*/ +GF_EXPORT +u32 gf_bs_peek_bits(GF_BitStream *bs, u32 numBits, u32 byte_offset) +{ + u64 curPos; + u32 curBits, ret, current; + + if ( (bs->bsmode != GF_BITSTREAM_READ) && (bs->bsmode != GF_BITSTREAM_FILE_READ)) return 0; + if (!numBits || (bs->size < bs->position + byte_offset)) return 0; + + /*store our state*/ + curPos = bs->position; + curBits = bs->nbBits; + current = bs->current; + + if (byte_offset) gf_bs_seek(bs, bs->position + byte_offset); + ret = gf_bs_read_int(bs, numBits); + + /*restore our cache - position*/ + gf_bs_seek(bs, curPos); + /*to avoid re-reading our bits ...*/ + bs->nbBits = curBits; + bs->current = current; + return ret; +} + +GF_EXPORT +u64 gf_bs_get_refreshed_size(GF_BitStream *bs) +{ + s64 offset; + + switch (bs->bsmode) { + case GF_BITSTREAM_READ: + case GF_BITSTREAM_WRITE: + return bs->size; + + default: + offset = gf_f64_tell(bs->stream); + gf_f64_seek(bs->stream, 0, SEEK_END); + bs->size = gf_f64_tell(bs->stream); + gf_f64_seek(bs->stream, offset, SEEK_SET); + return bs->size; + } +} + +GF_EXPORT +u64 gf_bs_get_size(GF_BitStream *bs) +{ + return bs->size; +} + +GF_EXPORT +u64 gf_bs_get_position(GF_BitStream *bs) +{ + return bs->position; +} + +GF_EXPORT +u8 gf_bs_bits_available(GF_BitStream *bs) +{ + if (bs->size > bs->position) return 8; + if (bs->nbBits < 8) return (8-bs->nbBits); + return 0; +} + +GF_EXPORT +void gf_bs_set_eos_callback(GF_BitStream *bs, void (*EndOfStream)(void *par), void *par) +{ + bs->EndOfStream = EndOfStream; + bs->par = par; +} + + +GF_EXPORT +u32 gf_bs_read_u32_le(GF_BitStream *bs) +{ + u32 ret, v; + ret = gf_bs_read_int(bs, 8); + v = gf_bs_read_int(bs, 8); v<<=8; ret |= v; + v = gf_bs_read_int(bs, 8); v<<=16; ret |= v; + v = gf_bs_read_int(bs, 8); v<<=24; ret |= v; + return ret; +} + +GF_EXPORT +u16 gf_bs_read_u16_le(GF_BitStream *bs) +{ + u32 ret, v; + ret = gf_bs_read_int(bs, 8); + v = gf_bs_read_int(bs, 8); v<<=8; ret |= v; + return ret; +} + +GF_EXPORT +void gf_bs_write_u32_le(GF_BitStream *bs, u32 val) +{ + gf_bs_write_int(bs, val & 0xFF, 8); + gf_bs_write_int(bs, val>>8, 8); + gf_bs_write_int(bs, val>>16, 8); + gf_bs_write_int(bs, val>>24, 8); +} + +GF_EXPORT +void gf_bs_write_u16_le(GF_BitStream *bs, u32 val) +{ + gf_bs_write_int(bs, val & 0xFF, 8); + gf_bs_write_int(bs, val>>8, 8); +} + +GF_EXPORT +u32 gf_bs_get_bit_offset(GF_BitStream *bs) +{ + if (bs->stream) return 0; + if (bs->bsmode==GF_BITSTREAM_READ) return (u32) ( (bs->position - 1) * 8 + bs->nbBits); + return (u32) ( (bs->position ) * 8 + bs->nbBits); +} + +GF_EXPORT +u32 gf_bs_get_bit_position(GF_BitStream *bs) +{ + if (bs->stream) return 0; + return bs->nbBits; +} + +u32 gf_bs_read_vluimsbf5(GF_BitStream *bs) +{ + u32 nb_words = 0; + while (gf_bs_read_int(bs, 1)) nb_words++; + nb_words++; + return gf_bs_read_int(bs, 4*nb_words); +} + +GF_EXPORT +void gf_bs_truncate(GF_BitStream *bs) +{ + bs->size = bs->position; + if (bs->stream) return; +} diff --git a/src/gpacmp4/box_code_3gpp.c b/src/gpacmp4/box_code_3gpp.c new file mode 100644 index 00000000..2f34b33a --- /dev/null +++ b/src/gpacmp4/box_code_3gpp.c @@ -0,0 +1,1364 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / ISO Media File Format sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ +#include "../disable_warnings.h" +#include + +#ifndef GPAC_DISABLE_ISOM + +void gppa_del(GF_Box *s) +{ + GF_3GPPAudioSampleEntryBox *ptr = (GF_3GPPAudioSampleEntryBox *)s; + if (ptr == NULL) return; + if (ptr->info) gf_isom_box_del((GF_Box *)ptr->info); + if (ptr->protection_info) gf_isom_box_del((GF_Box *)ptr->protection_info); + gf_free(ptr); +} + + +GF_Err gppa_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_3GPPAudioSampleEntryBox *ptr = (GF_3GPPAudioSampleEntryBox *)s; + e = gf_isom_audio_sample_entry_read((GF_AudioSampleEntryBox*)s, bs); + if (e) return e; + e = gf_isom_parse_box((GF_Box **)&ptr->info, bs); + if (e) return e; + ptr->info->cfg.type = ptr->type; + return GF_OK; +} + +GF_Box *gppa_New(u32 type) +{ + GF_3GPPAudioSampleEntryBox *tmp; + GF_SAFEALLOC(tmp, GF_3GPPAudioSampleEntryBox); + if (tmp == NULL) return NULL; + gf_isom_audio_sample_entry_init((GF_AudioSampleEntryBox*)tmp); + tmp->type = type; + return (GF_Box *)tmp; +} + +#ifndef GPAC_DISABLE_ISOM_WRITE + +GF_Err gppa_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_3GPPAudioSampleEntryBox *ptr = (GF_3GPPAudioSampleEntryBox *)s; + e = gf_isom_box_write_header(s, bs); + if (e) return e; + + gf_isom_audio_sample_entry_write((GF_AudioSampleEntryBox*)s, bs); + return gf_isom_box_write((GF_Box *)ptr->info, bs); +} + +GF_Err gppa_Size(GF_Box *s) +{ + GF_Err e; + GF_3GPPAudioSampleEntryBox *ptr = (GF_3GPPAudioSampleEntryBox *)s; + e = gf_isom_box_get_size(s); + if (e) return e; + gf_isom_audio_sample_entry_size((GF_AudioSampleEntryBox*)s); + e = gf_isom_box_size((GF_Box *)ptr->info); + if (e) return e; + ptr->size += ptr->info->size; + return GF_OK; +} + +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + + +GF_Box *gppv_New(u32 type) +{ + GF_3GPPVisualSampleEntryBox *tmp; + GF_SAFEALLOC(tmp, GF_3GPPVisualSampleEntryBox); + if (tmp == NULL) return NULL; + gf_isom_video_sample_entry_init((GF_VisualSampleEntryBox *)tmp); + tmp->type = type; + return (GF_Box *)tmp; +} +void gppv_del(GF_Box *s) +{ + GF_3GPPVisualSampleEntryBox *ptr = (GF_3GPPVisualSampleEntryBox *)s; + if (ptr == NULL) return; + if (ptr->info) gf_isom_box_del((GF_Box *)ptr->info); + if (ptr->protection_info) gf_isom_box_del((GF_Box *)ptr->protection_info); + gf_free(ptr); +} + +GF_Err gppv_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_3GPPVisualSampleEntryBox *ptr = (GF_3GPPVisualSampleEntryBox *)s; + e = gf_isom_video_sample_entry_read((GF_VisualSampleEntryBox *)ptr, bs); + if (e) return e; + /*FIXME - check for any other boxes...*/ + e = gf_isom_parse_box((GF_Box **)&ptr->info, bs); + return e; +} + +#ifndef GPAC_DISABLE_ISOM_WRITE + +GF_Err gppv_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_3GPPVisualSampleEntryBox *ptr = (GF_3GPPVisualSampleEntryBox*)s; + e = gf_isom_box_write_header(s, bs); + if (e) return e; + gf_isom_video_sample_entry_write((GF_VisualSampleEntryBox *)s, bs); + e = gf_isom_box_write((GF_Box *)ptr->info, bs); + if (e) return e; + return GF_OK; +} + +GF_Err gppv_Size(GF_Box *s) +{ + GF_Err e; + GF_3GPPVisualSampleEntryBox *ptr = (GF_3GPPVisualSampleEntryBox*)s; + e = gf_isom_box_get_size(s); + if (e) return e; + gf_isom_video_sample_entry_size((GF_VisualSampleEntryBox *)s); + e = gf_isom_box_size((GF_Box *)ptr->info); + if (e) return e; + ptr->size += ptr->info->size; + return GF_OK; +} + +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + + +GF_Box *gppc_New(u32 type) +{ + GF_3GPPConfigBox *tmp = (GF_3GPPConfigBox *) gf_malloc(sizeof(GF_3GPPConfigBox)); + if (tmp == NULL) return NULL; + memset(tmp, 0, sizeof(GF_3GPPConfigBox)); + tmp->type = type; + return (GF_Box *)tmp; +} + +void gppc_del(GF_Box *s) +{ + GF_3GPPConfigBox *ptr = (GF_3GPPConfigBox *)s; + if (ptr == NULL) return; + gf_free(ptr); +} + + +GF_Err gppc_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_3GPPConfigBox *ptr = (GF_3GPPConfigBox *)s; + if (ptr == NULL) return GF_BAD_PARAM; + memset(&ptr->cfg, 0, sizeof(GF_3GPConfig)); + + ptr->cfg.vendor = gf_bs_read_u32(bs); + ptr->cfg.decoder_version = gf_bs_read_u8(bs); + + switch (ptr->type) { + case GF_ISOM_BOX_TYPE_D263: + ptr->cfg.H263_level = gf_bs_read_u8(bs); + ptr->cfg.H263_profile = gf_bs_read_u8(bs); + break; + case GF_ISOM_BOX_TYPE_DAMR: + ptr->cfg.AMR_mode_set = gf_bs_read_u16(bs); + ptr->cfg.AMR_mode_change_period = gf_bs_read_u8(bs); + ptr->cfg.frames_per_sample = gf_bs_read_u8(bs); + break; + case GF_ISOM_BOX_TYPE_DEVC: + case GF_ISOM_BOX_TYPE_DQCP: + case GF_ISOM_BOX_TYPE_DSMV: + ptr->cfg.frames_per_sample = gf_bs_read_u8(bs); + break; + } + return GF_OK; +} + +#ifndef GPAC_DISABLE_ISOM_WRITE + +GF_Err gppc_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_3GPPConfigBox *ptr = (GF_3GPPConfigBox *)s; + e = gf_isom_box_write_header(s, bs); + if (e) return e; + + gf_bs_write_u32(bs, ptr->cfg.vendor); + gf_bs_write_u8(bs, ptr->cfg.decoder_version); + switch (ptr->cfg.type) { + case GF_ISOM_SUBTYPE_3GP_H263: + gf_bs_write_u8(bs, ptr->cfg.H263_level); + gf_bs_write_u8(bs, ptr->cfg.H263_profile); + break; + case GF_ISOM_SUBTYPE_3GP_AMR: + case GF_ISOM_SUBTYPE_3GP_AMR_WB: + gf_bs_write_u16(bs, ptr->cfg.AMR_mode_set); + gf_bs_write_u8(bs, ptr->cfg.AMR_mode_change_period); + gf_bs_write_u8(bs, ptr->cfg.frames_per_sample); + break; + case GF_ISOM_SUBTYPE_3GP_EVRC: + case GF_ISOM_SUBTYPE_3GP_QCELP: + case GF_ISOM_SUBTYPE_3GP_SMV: + gf_bs_write_u8(bs, ptr->cfg.frames_per_sample); + break; + } + return GF_OK; +} + +GF_Err gppc_Size(GF_Box *s) +{ + GF_Err e; + GF_3GPPConfigBox *ptr = (GF_3GPPConfigBox *)s; + + e = gf_isom_box_get_size(s); + if (e) return e; + s->size += 5; + switch (ptr->cfg.type) { + case GF_ISOM_SUBTYPE_3GP_H263: + s->size += 2; + break; + case GF_ISOM_SUBTYPE_3GP_AMR: + case GF_ISOM_SUBTYPE_3GP_AMR_WB: + s->size += 4; + break; + case GF_ISOM_SUBTYPE_3GP_EVRC: + case GF_ISOM_SUBTYPE_3GP_QCELP: + case GF_ISOM_SUBTYPE_3GP_SMV: + s->size += 1; + break; + } + return GF_OK; +} + +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + + +GF_Box *ftab_New() +{ + GF_FontTableBox *tmp; + GF_SAFEALLOC(tmp, GF_FontTableBox); + if (!tmp) return NULL; + tmp->type = GF_ISOM_BOX_TYPE_FTAB; + return (GF_Box *) tmp; +} +void ftab_del(GF_Box *s) +{ + GF_FontTableBox *ptr = (GF_FontTableBox *)s; + if (ptr->fonts) { + u32 i; + for (i=0; ientry_count; i++) + if (ptr->fonts[i].fontName) gf_free(ptr->fonts[i].fontName); + gf_free(ptr->fonts); + } + gf_free(ptr); +} +GF_Err ftab_Read(GF_Box *s, GF_BitStream *bs) +{ + u32 i; + GF_FontTableBox *ptr = (GF_FontTableBox *)s; + ptr->entry_count = gf_bs_read_u16(bs); + ptr->fonts = (GF_FontRecord *) gf_malloc(sizeof(GF_FontRecord)*ptr->entry_count); + for (i=0; ientry_count; i++) { + u32 len; + ptr->fonts[i].fontID = gf_bs_read_u16(bs); + len = gf_bs_read_u8(bs); + if (len) { + ptr->fonts[i].fontName = (char *)gf_malloc(sizeof(char)*(len+1)); + gf_bs_read_data(bs, ptr->fonts[i].fontName, len); + ptr->fonts[i].fontName[len] = 0; + } + } + return GF_OK; +} + +#ifndef GPAC_DISABLE_ISOM_WRITE +GF_Err ftab_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + u32 i; + GF_FontTableBox *ptr = (GF_FontTableBox *)s; + e = gf_isom_box_write_header(s, bs); + if (e) return e; + gf_bs_write_u16(bs, ptr->entry_count); + for (i=0; ientry_count; i++) { + gf_bs_write_u16(bs, ptr->fonts[i].fontID); + if (ptr->fonts[i].fontName) { + u32 len = strlen(ptr->fonts[i].fontName); + gf_bs_write_u8(bs, len); + gf_bs_write_data(bs, ptr->fonts[i].fontName, len); + } else { + gf_bs_write_u8(bs, 0); + } + } + return GF_OK; +} +GF_Err ftab_Size(GF_Box *s) +{ + u32 i; + GF_FontTableBox *ptr = (GF_FontTableBox *)s; + GF_Err e = gf_isom_box_get_size(s); + if (e) return e; + s->size += 2; + for (i=0; ientry_count; i++) { + s->size += 3; + if (ptr->fonts[i].fontName) s->size += strlen(ptr->fonts[i].fontName); + } + return GF_OK; +} + +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + + + +GF_Box *text_New() +{ + GF_TextSampleEntryBox *tmp; + GF_SAFEALLOC(tmp, GF_TextSampleEntryBox); + if (!tmp) + return NULL; + tmp->type = GF_ISOM_BOX_TYPE_TEXT; + return (GF_Box *) tmp; +} + +void text_del(GF_Box *s) +{ + GF_TextSampleEntryBox *ptr = (GF_TextSampleEntryBox*)s; + if (ptr->textName) + gf_free(ptr->textName); + gf_free(ptr); +} + +GF_Box *tx3g_New() +{ + GF_Tx3gSampleEntryBox *tmp; + GF_SAFEALLOC(tmp, GF_Tx3gSampleEntryBox); + if (!tmp) + return NULL; + tmp->type = GF_ISOM_BOX_TYPE_TX3G; + return (GF_Box *) tmp; +} + +void tx3g_del(GF_Box *s) +{ + GF_Tx3gSampleEntryBox *ptr = (GF_Tx3gSampleEntryBox*)s; + if (ptr->font_table) + gf_isom_box_del((GF_Box *)ptr->font_table); + gf_free(ptr); +} + +static u32 gpp_read_rgba(GF_BitStream *bs) +{ + u8 r, g, b, a; + u32 col; + r = gf_bs_read_u8(bs); + g = gf_bs_read_u8(bs); + b = gf_bs_read_u8(bs); + a = gf_bs_read_u8(bs); + col = a; col<<=8; + col |= r; col<<=8; + col |= g; col<<=8; + col |= b; + return col; +} + +#define GPP_BOX_SIZE 8 +static void gpp_read_box(GF_BitStream *bs, GF_BoxRecord *rec) +{ + rec->top = gf_bs_read_u16(bs); + rec->left = gf_bs_read_u16(bs); + rec->bottom = gf_bs_read_u16(bs); + rec->right = gf_bs_read_u16(bs); +} + +#define GPP_STYLE_SIZE 12 +static void gpp_read_style(GF_BitStream *bs, GF_StyleRecord *rec) +{ + rec->startCharOffset = gf_bs_read_u16(bs); + rec->endCharOffset = gf_bs_read_u16(bs); + rec->fontID = gf_bs_read_u16(bs); + rec->style_flags = gf_bs_read_u8(bs); + rec->font_size = gf_bs_read_u8(bs); + rec->text_color = gpp_read_rgba(bs); +} + +GF_Err tx3g_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_Box *a; + GF_Tx3gSampleEntryBox *ptr = (GF_Tx3gSampleEntryBox*)s; + + if (ptr->size < 18 + GPP_BOX_SIZE + GPP_STYLE_SIZE) return GF_ISOM_INVALID_FILE; + + gf_bs_read_data(bs, ptr->reserved, 6); + ptr->dataReferenceIndex = gf_bs_read_u16(bs); + ptr->displayFlags = gf_bs_read_u32(bs); + ptr->horizontal_justification = gf_bs_read_u8(bs); + ptr->vertical_justification = gf_bs_read_u8(bs); + ptr->back_color = gpp_read_rgba(bs); + gpp_read_box(bs, &ptr->default_box); + gpp_read_style(bs, &ptr->default_style); + ptr->size -= 18 + GPP_BOX_SIZE + GPP_STYLE_SIZE; + + while (ptr->size) { + e = gf_isom_parse_box(&a, bs); + if (e) return e; + if (ptr->sizesize) return GF_ISOM_INVALID_FILE; + ptr->size -= a->size; + if (a->type==GF_ISOM_BOX_TYPE_FTAB) { + if (ptr->font_table) gf_isom_box_del((GF_Box *) ptr->font_table); + ptr->font_table = (GF_FontTableBox *)a; + } else { + gf_isom_box_del(a); + } + } + return GF_OK; +} + +/*this is a quicktime specific box - see apple documentation*/ +GF_Err text_Read(GF_Box *s, GF_BitStream *bs) +{ + u16 pSize; + GF_TextSampleEntryBox *ptr = (GF_TextSampleEntryBox*)s; + gf_bs_read_data(bs, ptr->reserved, 6); + ptr->dataReferenceIndex = gf_bs_read_u16(bs); + + ptr->displayFlags = gf_bs_read_u32(bs); /*Display flags*/ + ptr->textJustification = gf_bs_read_u32(bs); /*Text justification*/ + gf_bs_read_data(bs, ptr->background_color, 6); /*Background color*/ + gpp_read_box(bs, &ptr->default_box); /*Default text box*/ + gf_bs_read_data(bs, ptr->reserved1, 8); /*Reserved*/ + ptr->fontNumber = gf_bs_read_u16(bs); /*Font number*/ + ptr->fontFace = gf_bs_read_u16(bs); /*Font face*/ + ptr->reserved2 = gf_bs_read_u8(bs); /*Reserved*/ + ptr->reserved3 = gf_bs_read_u16(bs); /*Reserved*/ + gf_bs_read_data(bs, ptr->foreground_color, 6); /*Foreground color*/ + if (ptr->size < 51) + return GF_ISOM_INVALID_FILE; + ptr->size -= 51; + if (!ptr->size) + return GF_OK; /*ffmpeg compatibility with iPod streams: no pascal string*/ + + pSize = gf_bs_read_u8(bs); /*a Pascal string begins with its size: get textName size*/ + ptr->size -= 1; + if (ptr->size < pSize) + return GF_ISOM_INVALID_FILE; + if (pSize) { + ptr->textName = (char*) gf_malloc(pSize+1 * sizeof(char)); + if (gf_bs_read_data(bs, ptr->textName, pSize) != pSize) { + gf_free(ptr->textName); + ptr->textName = NULL; + return GF_ISOM_INVALID_FILE; + } + ptr->textName[pSize] = '\0'; /*Font name*/ + } + ptr->size -= pSize; + + return GF_OK; +} + +void gpp_write_rgba(GF_BitStream *bs, u32 col) +{ + gf_bs_write_u8(bs, (col>>16) & 0xFF); + gf_bs_write_u8(bs, (col>>8) & 0xFF); + gf_bs_write_u8(bs, (col) & 0xFF); + gf_bs_write_u8(bs, (col>>24) & 0xFF); +} + +void gpp_write_box(GF_BitStream *bs, GF_BoxRecord *rec) +{ + gf_bs_write_u16(bs, rec->top); + gf_bs_write_u16(bs, rec->left); + gf_bs_write_u16(bs, rec->bottom); + gf_bs_write_u16(bs, rec->right); +} + +#define GPP_STYLE_SIZE 12 +void gpp_write_style(GF_BitStream *bs, GF_StyleRecord *rec) +{ + gf_bs_write_u16(bs, rec->startCharOffset); + gf_bs_write_u16(bs, rec->endCharOffset); + gf_bs_write_u16(bs, rec->fontID); + gf_bs_write_u8(bs, rec->style_flags); + gf_bs_write_u8(bs, rec->font_size); + gpp_write_rgba(bs, rec->text_color); +} + +#ifndef GPAC_DISABLE_ISOM_WRITE + +GF_Err tx3g_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_Tx3gSampleEntryBox *ptr = (GF_Tx3gSampleEntryBox*)s; + + e = gf_isom_box_write_header(s, bs); + if (e) return e; + gf_bs_write_data(bs, ptr->reserved, 6); + gf_bs_write_u16(bs, ptr->dataReferenceIndex); + gf_bs_write_u32(bs, ptr->displayFlags); + gf_bs_write_u8(bs, ptr->horizontal_justification); + gf_bs_write_u8(bs, ptr->vertical_justification); + gpp_write_rgba(bs, ptr->back_color); + gpp_write_box(bs, &ptr->default_box); + gpp_write_style(bs, &ptr->default_style); + return gf_isom_box_write((GF_Box *) ptr->font_table, bs); +} + +GF_Err text_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + u16 pSize; + GF_TextSampleEntryBox *ptr = (GF_TextSampleEntryBox*)s; + + e = gf_isom_box_write_header(s, bs); + if (e) return e; + gf_bs_write_data(bs, ptr->reserved, 6); + gf_bs_write_u16(bs, ptr->dataReferenceIndex); + gf_bs_write_u32(bs, ptr->displayFlags); /*Display flags*/ + gf_bs_write_u32(bs, ptr->textJustification); /*Text justification*/ + gf_bs_write_data(bs, ptr->background_color, 6); /*Background color*/ + gpp_write_box(bs, &ptr->default_box); /*Default text box*/ + gf_bs_write_data(bs, ptr->reserved1, 8); /*Reserved*/ + gf_bs_write_u16(bs, ptr->fontNumber); /*Font number*/ + gf_bs_write_u16(bs, ptr->fontFace); /*Font face*/ + gf_bs_write_u8(bs, ptr->reserved2); /*Reserved*/ + gf_bs_write_u16(bs, ptr->reserved3); /*Reserved*/ + gf_bs_write_data(bs, ptr->foreground_color, 6); /*Foreground color*/ + if (ptr->textName && (pSize=strlen(ptr->textName))) { + gf_bs_write_u8(bs, pSize); /*a Pascal string begins with its size*/ + gf_bs_write_data(bs, ptr->textName, pSize); /*Font name*/ + } else { + gf_bs_write_u8(bs, 0); + } + return GF_OK; +} + +GF_Err tx3g_Size(GF_Box *s) +{ + GF_Tx3gSampleEntryBox *ptr = (GF_Tx3gSampleEntryBox*)s; + GF_Err e = gf_isom_box_get_size(s); + if (e) return e; + /*base + this + box + style*/ + s->size += 18 + GPP_BOX_SIZE + GPP_STYLE_SIZE; + if (ptr->font_table) { + e = gf_isom_box_size((GF_Box *) ptr->font_table); + if (e) return e; + s->size += ptr->font_table->size; + } + return GF_OK; +} + +GF_Err text_Size(GF_Box *s) +{ + GF_TextSampleEntryBox *ptr = (GF_TextSampleEntryBox*)s; + GF_Err e = gf_isom_box_get_size(s); + if (e) return e; + /*base + this + string length*/ + s->size += 51 + 1; + if (ptr->textName) + s->size += strlen(ptr->textName); + return GF_OK; +} + +#endif + +GF_Box *styl_New() +{ + GF_TextStyleBox *tmp; + GF_SAFEALLOC(tmp, GF_TextStyleBox); + if (!tmp) return NULL; + tmp->type = GF_ISOM_BOX_TYPE_STYL; + return (GF_Box *) tmp; +} + +void styl_del(GF_Box *s) +{ + GF_TextStyleBox*ptr = (GF_TextStyleBox*)s; + if (ptr->styles) gf_free(ptr->styles); + gf_free(ptr); +} + +GF_Err styl_Read(GF_Box *s, GF_BitStream *bs) +{ + u32 i; + GF_TextStyleBox*ptr = (GF_TextStyleBox*)s; + ptr->entry_count = gf_bs_read_u16(bs); + if (ptr->entry_count) { + ptr->styles = (GF_StyleRecord*)gf_malloc(sizeof(GF_StyleRecord)*ptr->entry_count); + for (i=0; ientry_count; i++) { + gpp_read_style(bs, &ptr->styles[i]); + } + } + return GF_OK; +} + +#ifndef GPAC_DISABLE_ISOM_WRITE +GF_Err styl_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + u32 i; + GF_TextStyleBox*ptr = (GF_TextStyleBox*)s; + e = gf_isom_box_write_header(s, bs); + assert(e == GF_OK); + + gf_bs_write_u16(bs, ptr->entry_count); + for (i=0; ientry_count; i++) gpp_write_style(bs, &ptr->styles[i]); + return GF_OK; +} + +GF_Err styl_Size(GF_Box *s) +{ + GF_TextStyleBox*ptr = (GF_TextStyleBox*)s; + GF_Err e = gf_isom_box_get_size(s); + if (e) return e; + s->size += 2 + ptr->entry_count * GPP_STYLE_SIZE; + return GF_OK; +} + +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + +GF_Box *hlit_New() +{ + GF_TextHighlightBox *tmp; + GF_SAFEALLOC(tmp, GF_TextHighlightBox); + if (!tmp) return NULL; + tmp->type = GF_ISOM_BOX_TYPE_HLIT; + return (GF_Box *) tmp; +} + +void hlit_del(GF_Box *s) +{ + gf_free(s); +} + +GF_Err hlit_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_TextHighlightBox *ptr = (GF_TextHighlightBox *)s; + ptr->startcharoffset = gf_bs_read_u16(bs); + ptr->endcharoffset = gf_bs_read_u16(bs); + return GF_OK; +} + +#ifndef GPAC_DISABLE_ISOM_WRITE +GF_Err hlit_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_TextHighlightBox *ptr = (GF_TextHighlightBox *)s; + e = gf_isom_box_write_header(s, bs); + if (e) return e; + gf_bs_write_u16(bs, ptr->startcharoffset); + gf_bs_write_u16(bs, ptr->endcharoffset); + return GF_OK; +} + +GF_Err hlit_Size(GF_Box *s) +{ + GF_Err e = gf_isom_box_get_size(s); + if (e) return e; + s->size += 4; + return GF_OK; +} + +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + +GF_Box *hclr_New() +{ + GF_TextHighlightColorBox*tmp; + GF_SAFEALLOC(tmp, GF_TextHighlightColorBox); + if (!tmp) return NULL; + tmp->type = GF_ISOM_BOX_TYPE_HCLR; + return (GF_Box *) tmp; +} + +void hclr_del(GF_Box *s) +{ + gf_free(s); +} + +GF_Err hclr_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_TextHighlightColorBox*ptr = (GF_TextHighlightColorBox*)s; + ptr->hil_color = gpp_read_rgba(bs); + return GF_OK; +} + +#ifndef GPAC_DISABLE_ISOM_WRITE +GF_Err hclr_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_TextHighlightColorBox*ptr = (GF_TextHighlightColorBox*)s; + e = gf_isom_box_write_header(s, bs); + if (e) return e; + gpp_write_rgba(bs, ptr->hil_color); + return GF_OK; +} + +GF_Err hclr_Size(GF_Box *s) +{ + GF_Err e = gf_isom_box_get_size(s); + if (e) return e; + s->size += 4; + return GF_OK; +} + +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + +GF_Box *krok_New() +{ + GF_TextKaraokeBox*tmp; + GF_SAFEALLOC(tmp, GF_TextKaraokeBox); + if (!tmp) return NULL; + tmp->type = GF_ISOM_BOX_TYPE_KROK; + return (GF_Box *) tmp; +} + +void krok_del(GF_Box *s) +{ + GF_TextKaraokeBox*ptr = (GF_TextKaraokeBox*)s; + if (ptr->records) gf_free(ptr->records); + gf_free(ptr); +} + +GF_Err krok_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_TextKaraokeBox*ptr = (GF_TextKaraokeBox*)s; + + ptr->highlight_starttime = gf_bs_read_u32(bs); + ptr->nb_entries = gf_bs_read_u16(bs); + if (ptr->nb_entries) { + u32 i; + ptr->records = (KaraokeRecord*)gf_malloc(sizeof(KaraokeRecord)*ptr->nb_entries); + for (i=0; inb_entries; i++) { + ptr->records[i].highlight_endtime = gf_bs_read_u32(bs); + ptr->records[i].start_charoffset = gf_bs_read_u16(bs); + ptr->records[i].end_charoffset = gf_bs_read_u16(bs); + } + } + return GF_OK; +} + +#ifndef GPAC_DISABLE_ISOM_WRITE +GF_Err krok_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + u32 i; + GF_TextKaraokeBox*ptr = (GF_TextKaraokeBox*)s; + e = gf_isom_box_write_header(s, bs); + if (e) return e; + + gf_bs_write_u32(bs, ptr->highlight_starttime); + gf_bs_write_u16(bs, ptr->nb_entries); + for (i=0; inb_entries; i++) { + gf_bs_write_u32(bs, ptr->records[i].highlight_endtime); + gf_bs_write_u16(bs, ptr->records[i].start_charoffset); + gf_bs_write_u16(bs, ptr->records[i].end_charoffset); + } + return GF_OK; +} + +GF_Err krok_Size(GF_Box *s) +{ + GF_TextKaraokeBox*ptr = (GF_TextKaraokeBox*)s; + GF_Err e = gf_isom_box_get_size(s); + if (e) return e; + s->size += 6 * 8*ptr->nb_entries; + return GF_OK; +} + +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + +GF_Box *dlay_New() +{ + GF_TextScrollDelayBox*tmp; + GF_SAFEALLOC(tmp, GF_TextScrollDelayBox); + if (!tmp) return NULL; + tmp->type = GF_ISOM_BOX_TYPE_DLAY; + return (GF_Box *) tmp; +} + +void dlay_del(GF_Box *s) +{ + gf_free(s); +} + +GF_Err dlay_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_TextScrollDelayBox*ptr = (GF_TextScrollDelayBox*)s; + ptr->scroll_delay = gf_bs_read_u32(bs); + return GF_OK; +} + +#ifndef GPAC_DISABLE_ISOM_WRITE +GF_Err dlay_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_TextScrollDelayBox*ptr = (GF_TextScrollDelayBox*)s; + e = gf_isom_box_write_header(s, bs); + if (e) return e; + gf_bs_write_u32(bs, ptr->scroll_delay); + return GF_OK; +} + +GF_Err dlay_Size(GF_Box *s) +{ + GF_Err e = gf_isom_box_get_size(s); + if (e) return e; + s->size += 4; + return GF_OK; +} + +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + +GF_Box *href_New() +{ + GF_TextHyperTextBox*tmp; + GF_SAFEALLOC(tmp, GF_TextHyperTextBox); + if (!tmp) return NULL; + tmp->type = GF_ISOM_BOX_TYPE_HREF; + return (GF_Box *) tmp; +} + +void href_del(GF_Box *s) +{ + GF_TextHyperTextBox*ptr = (GF_TextHyperTextBox*)s; + if (ptr->URL) gf_free(ptr->URL); + if (ptr->URL_hint) gf_free(ptr->URL_hint); + gf_free(ptr); +} + +GF_Err href_Read(GF_Box *s, GF_BitStream *bs) +{ + u32 len; + GF_TextHyperTextBox*ptr = (GF_TextHyperTextBox*)s; + ptr->startcharoffset = gf_bs_read_u16(bs); + ptr->endcharoffset = gf_bs_read_u16(bs); + len = gf_bs_read_u8(bs); + if (len) { + ptr->URL = (char *) gf_malloc(sizeof(char) * (len+1)); + gf_bs_read_data(bs, ptr->URL, len); + ptr->URL[len] = 0; + } + len = gf_bs_read_u8(bs); + if (len) { + ptr->URL_hint = (char *) gf_malloc(sizeof(char) * (len+1)); + gf_bs_read_data(bs, ptr->URL_hint, len); + ptr->URL_hint[len]= 0; + } + return GF_OK; +} + +#ifndef GPAC_DISABLE_ISOM_WRITE +GF_Err href_Write(GF_Box *s, GF_BitStream *bs) +{ + u32 len; + GF_Err e; + GF_TextHyperTextBox*ptr = (GF_TextHyperTextBox*)s; + e = gf_isom_box_write_header(s, bs); + if (e) return e; + + gf_bs_write_u16(bs, ptr->startcharoffset); + gf_bs_write_u16(bs, ptr->endcharoffset); + if (ptr->URL) { + len = strlen(ptr->URL); + gf_bs_write_u8(bs, len); + gf_bs_write_data(bs, ptr->URL, len); + } else { + gf_bs_write_u8(bs, 0); + } + if (ptr->URL_hint) { + len = strlen(ptr->URL_hint); + gf_bs_write_u8(bs, len); + gf_bs_write_data(bs, ptr->URL_hint, len); + } else { + gf_bs_write_u8(bs, 0); + } + return GF_OK; +} + +GF_Err href_Size(GF_Box *s) +{ + GF_TextHyperTextBox*ptr = (GF_TextHyperTextBox*)s; + GF_Err e = gf_isom_box_get_size(s); + if (e) return e; + s->size += 6; + if (ptr->URL) s->size += strlen(ptr->URL); + if (ptr->URL_hint) s->size += strlen(ptr->URL_hint); + return GF_OK; +} + +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + + +GF_Box *tbox_New() +{ + GF_TextBoxBox*tmp; + GF_SAFEALLOC(tmp, GF_TextBoxBox); + if (!tmp) return NULL; + tmp->type = GF_ISOM_BOX_TYPE_TBOX; + return (GF_Box *) tmp; +} + +void tbox_del(GF_Box *s) +{ + gf_free(s); +} + +GF_Err tbox_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_TextBoxBox*ptr = (GF_TextBoxBox*)s; + gpp_read_box(bs, &ptr->box); + return GF_OK; +} + +#ifndef GPAC_DISABLE_ISOM_WRITE +GF_Err tbox_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_TextBoxBox*ptr = (GF_TextBoxBox*)s; + e = gf_isom_box_write_header(s, bs); + if (e) return e; + gpp_write_box(bs, &ptr->box); + return GF_OK; +} + +GF_Err tbox_Size(GF_Box *s) +{ + GF_Err e = gf_isom_box_get_size(s); + if (e) return e; + s->size += 8; + return GF_OK; +} + +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + + +GF_Box *blnk_New() +{ + GF_TextBlinkBox*tmp; + GF_SAFEALLOC(tmp, GF_TextBlinkBox); + if (!tmp) return NULL; + tmp->type = GF_ISOM_BOX_TYPE_BLNK; + return (GF_Box *) tmp; +} + +void blnk_del(GF_Box *s) +{ + gf_free(s); +} + +GF_Err blnk_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_TextBlinkBox*ptr = (GF_TextBlinkBox*)s; + ptr->startcharoffset = gf_bs_read_u16(bs); + ptr->endcharoffset = gf_bs_read_u16(bs); + return GF_OK; +} + +#ifndef GPAC_DISABLE_ISOM_WRITE +GF_Err blnk_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_TextBlinkBox*ptr = (GF_TextBlinkBox*)s; + e = gf_isom_box_write_header(s, bs); + if (e) return e; + gf_bs_write_u16(bs, ptr->startcharoffset); + gf_bs_write_u16(bs, ptr->endcharoffset); + return GF_OK; +} + +GF_Err blnk_Size(GF_Box *s) +{ + GF_Err e = gf_isom_box_get_size(s); + if (e) return e; + s->size += 4; + return GF_OK; +} + +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + +GF_Box *twrp_New() +{ + GF_TextWrapBox*tmp; + GF_SAFEALLOC(tmp, GF_TextWrapBox); + if (!tmp) return NULL; + tmp->type = GF_ISOM_BOX_TYPE_TWRP; + return (GF_Box *) tmp; +} + +void twrp_del(GF_Box *s) +{ + gf_free(s); +} + +GF_Err twrp_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_TextWrapBox*ptr = (GF_TextWrapBox*)s; + ptr->wrap_flag = gf_bs_read_u8(bs); + return GF_OK; +} + +#ifndef GPAC_DISABLE_ISOM_WRITE +GF_Err twrp_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_TextWrapBox*ptr = (GF_TextWrapBox*)s; + e = gf_isom_box_write_header(s, bs); + if (e) return e; + gf_bs_write_u8(bs, ptr->wrap_flag); + return GF_OK; +} +GF_Err twrp_Size(GF_Box *s) +{ + GF_Err e = gf_isom_box_get_size(s); + if (e) return e; + s->size += 1; + return GF_OK; +} + +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + +void tsel_del(GF_Box *s) +{ + GF_TrackSelectionBox *ptr; + ptr = (GF_TrackSelectionBox *) s; + if (ptr == NULL) return; + if (ptr->attributeList) gf_free(ptr->attributeList); + gf_free(ptr); +} + +GF_Err tsel_Read(GF_Box *s,GF_BitStream *bs) +{ + GF_Err e; + u32 i; + GF_TrackSelectionBox *ptr = (GF_TrackSelectionBox *) s; + e = gf_isom_full_box_read(s, bs); + if (e) return e; + ptr->switchGroup = gf_bs_read_u32(bs); + ptr->size -= 4; + if (ptr->size % 4) return GF_ISOM_INVALID_FILE; + ptr->attributeListCount = (u32)ptr->size/4; + ptr->attributeList = (u32 * ) gf_malloc(ptr->attributeListCount*sizeof(u32)); + if (ptr->attributeList == NULL) return GF_OUT_OF_MEM; + + for (i=0; i< ptr->attributeListCount; i++) { + ptr->attributeList[i] = gf_bs_read_u32(bs); + } + return GF_OK; +} + +GF_Box *tsel_New() +{ + GF_TrackSelectionBox *tmp; + + tmp = (GF_TrackSelectionBox *) gf_malloc(sizeof(GF_TrackSelectionBox)); + if (tmp == NULL) return NULL; + memset(tmp, 0, sizeof(GF_TrackSelectionBox)); + gf_isom_full_box_init((GF_Box *)tmp); + tmp->type = GF_ISOM_BOX_TYPE_TSEL; + return (GF_Box *)tmp; +} + + +#ifndef GPAC_DISABLE_ISOM_WRITE + +GF_Err tsel_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + u32 i; + GF_TrackSelectionBox *ptr = (GF_TrackSelectionBox *) s; + + e = gf_isom_full_box_write(s, bs); + if (e) return e; + gf_bs_write_u32(bs,ptr->switchGroup); + + for (i = 0; i < ptr->attributeListCount; i++ ) { + gf_bs_write_u32(bs, ptr->attributeList[i]); + } + + return GF_OK; +} + +GF_Err tsel_Size(GF_Box *s) +{ + GF_Err e; + GF_TrackSelectionBox *ptr = (GF_TrackSelectionBox *) s; + e = gf_isom_full_box_get_size(s); + if (e) return e; + ptr->size += 4 + (4*ptr->attributeListCount); + return GF_OK; +} + +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + + +GF_Box *dimC_New() +{ + GF_DIMSSceneConfigBox *tmp; + + GF_SAFEALLOC(tmp, GF_DIMSSceneConfigBox); + if (tmp == NULL) return NULL; + gf_isom_full_box_init((GF_Box *)tmp); + tmp->type = GF_ISOM_BOX_TYPE_DIMC; + return (GF_Box *)tmp; +} +void dimC_del(GF_Box *s) +{ + GF_DIMSSceneConfigBox *p = (GF_DIMSSceneConfigBox *)s; + if (p->contentEncoding) gf_free(p->contentEncoding); + if (p->textEncoding) gf_free(p->textEncoding); + gf_free(p); +} + +GF_Err dimC_Read(GF_Box *s, GF_BitStream *bs) +{ + char str[1024]; + u32 i; + GF_DIMSSceneConfigBox *p = (GF_DIMSSceneConfigBox *)s; + GF_Err e = gf_isom_full_box_read(s, bs); + if (e) return e; + + p->profile = gf_bs_read_u8(bs); + p->level = gf_bs_read_u8(bs); + p->pathComponents = gf_bs_read_int(bs, 4); + p->fullRequestHost = gf_bs_read_int(bs, 1); + p->streamType = gf_bs_read_int(bs, 1); + p->containsRedundant = gf_bs_read_int(bs, 2); + s->size -= 3; + + i=0; + str[0]=0; + while (1) { + str[i] = gf_bs_read_u8(bs); + if (!str[i]) break; + i++; + } + if (s->size < i) return GF_ISOM_INVALID_FILE; + s->size -= i; + p->textEncoding = gf_strdup(str); + + i=0; + str[0]=0; + while (1) { + str[i] = gf_bs_read_u8(bs); + if (!str[i]) break; + i++; + } + if (s->size < i) return GF_ISOM_INVALID_FILE; + s->size -= i; + p->contentEncoding = gf_strdup(str); + return GF_OK; +} + +#ifndef GPAC_DISABLE_ISOM_WRITE +GF_Err dimC_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_DIMSSceneConfigBox *p = (GF_DIMSSceneConfigBox *)s; + GF_Err e = gf_isom_full_box_write(s, bs); + if (e) return e; + gf_bs_write_u8(bs, p->profile); + gf_bs_write_u8(bs, p->level); + gf_bs_write_int(bs, p->pathComponents, 4); + gf_bs_write_int(bs, p->fullRequestHost, 1); + gf_bs_write_int(bs, p->streamType, 1); + gf_bs_write_int(bs, p->containsRedundant, 2); + gf_bs_write_data(bs, p->textEncoding, strlen(p->textEncoding)+1); + gf_bs_write_data(bs, p->contentEncoding, strlen(p->contentEncoding)+1); + return GF_OK; +} +GF_Err dimC_Size(GF_Box *s) +{ + GF_DIMSSceneConfigBox *p = (GF_DIMSSceneConfigBox *)s; + GF_Err e = gf_isom_full_box_get_size(s); + if (e) return e; + s->size += 3 + 1 + strlen(p->textEncoding) + 1 + strlen(p->contentEncoding); + return GF_OK; +} +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + + + +GF_Box *diST_New() +{ + GF_DIMSScriptTypesBox *tmp; + + GF_SAFEALLOC(tmp, GF_DIMSScriptTypesBox); + if (tmp == NULL) return NULL; + tmp->type = GF_ISOM_BOX_TYPE_DIST; + return (GF_Box *)tmp; +} +void diST_del(GF_Box *s) +{ + GF_DIMSScriptTypesBox *p = (GF_DIMSScriptTypesBox *)s; + if (p->content_script_types) gf_free(p->content_script_types); + gf_free(p); +} + +GF_Err diST_Read(GF_Box *s, GF_BitStream *bs) +{ + u32 i; + char str[1024]; + GF_DIMSScriptTypesBox *p = (GF_DIMSScriptTypesBox *)s; + + i=0; + str[0]=0; + while (1) { + str[i] = gf_bs_read_u8(bs); + if (!str[i]) break; + i++; + } + if (s->size < i) return GF_ISOM_INVALID_FILE; + s->size -= i; + p->content_script_types = gf_strdup(str); + return GF_OK; +} + +#ifndef GPAC_DISABLE_ISOM_WRITE +GF_Err diST_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_DIMSScriptTypesBox *p = (GF_DIMSScriptTypesBox *)s; + GF_Err e = gf_isom_box_write_header(s, bs); + if (e) return e; + if (p->content_script_types) + gf_bs_write_data(bs, p->content_script_types, strlen(p->content_script_types)+1); + else + gf_bs_write_u8(bs, 0); + return GF_OK; +} +GF_Err diST_Size(GF_Box *s) +{ + GF_Err e = gf_isom_box_get_size(s); + GF_DIMSScriptTypesBox *p = (GF_DIMSScriptTypesBox *)s; + if (e) return e; + s->size += p->content_script_types ? (strlen(p->content_script_types)+1) : 1; + return GF_OK; +} +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + + +GF_Box *dims_New() +{ + GF_DIMSSampleEntryBox *tmp; + GF_SAFEALLOC(tmp, GF_DIMSSampleEntryBox); + tmp->type = GF_ISOM_BOX_TYPE_DIMS; + return (GF_Box*)tmp; +} +void dims_del(GF_Box *s) +{ + GF_DIMSSampleEntryBox *p = (GF_DIMSSampleEntryBox *)s; + if (p->config) gf_isom_box_del((GF_Box *)p->config); + if (p->bitrate ) gf_isom_box_del((GF_Box *)p->bitrate); + if (p->protection_info) gf_isom_box_del((GF_Box *)p->protection_info); + if (p->scripts) gf_isom_box_del((GF_Box *)p->scripts); + gf_free(p); +} + +static GF_Err dims_AddBox(GF_Box *s, GF_Box *a) +{ + GF_DIMSSampleEntryBox *ptr = (GF_DIMSSampleEntryBox *)s; + switch (a->type) { + case GF_ISOM_BOX_TYPE_DIMC: + if (ptr->config) ERROR_ON_DUPLICATED_BOX(a, ptr) + ptr->config = (GF_DIMSSceneConfigBox*)a; + break; + case GF_ISOM_BOX_TYPE_DIST: + if (ptr->scripts) ERROR_ON_DUPLICATED_BOX(a, ptr) + ptr->scripts = (GF_DIMSScriptTypesBox*)a; + break; + case GF_ISOM_BOX_TYPE_BTRT: + if (ptr->bitrate) ERROR_ON_DUPLICATED_BOX(a, ptr) + ptr->bitrate = (GF_MPEG4BitRateBox*)a; + break; + case GF_ISOM_BOX_TYPE_SINF: + if (ptr->protection_info) ERROR_ON_DUPLICATED_BOX(a, ptr) + ptr->protection_info = (GF_ProtectionInfoBox*)a; + break; + default: + gf_isom_box_del(a); + break; + } + return GF_OK; +} +GF_Err dims_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_DIMSSampleEntryBox *p = (GF_DIMSSampleEntryBox *)s; + gf_bs_read_data(bs, p->reserved, 6); + p->dataReferenceIndex = gf_bs_read_u16(bs); + p->size -= 8; + return gf_isom_read_box_list(s, bs, dims_AddBox); +} + +#ifndef GPAC_DISABLE_ISOM_WRITE +GF_Err dims_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_DIMSSampleEntryBox *p = (GF_DIMSSampleEntryBox *)s; + GF_Err e = gf_isom_box_write_header(s, bs); + if (e) return e; + gf_bs_write_data(bs, p->reserved, 6); + gf_bs_write_u16(bs, p->dataReferenceIndex); + if (p->config) { + e = gf_isom_box_write((GF_Box *)p->config, bs); + if (e) return e; + } + if (p->scripts) { + e = gf_isom_box_write((GF_Box *)p->scripts, bs); + if (e) return e; + } + if (p->bitrate) { + e = gf_isom_box_write((GF_Box *)p->bitrate, bs); + if (e) return e; + } + if (p->protection_info) { + e = gf_isom_box_write((GF_Box *)p->protection_info, bs); + if (e) return e; + } + return GF_OK; +} + +GF_Err dims_Size(GF_Box *s) +{ + GF_Err e = gf_isom_box_get_size(s); + GF_DIMSSampleEntryBox *p = (GF_DIMSSampleEntryBox *)s; + if (e) return e; + s->size += 8; + + if (p->config) { + e = gf_isom_box_size((GF_Box *) p->config); + if (e) return e; + p->size += p->config->size; + } + if (p->protection_info) { + e = gf_isom_box_size((GF_Box *) p->protection_info); + if (e) return e; + p->size += p->protection_info->size; + } + if (p->bitrate) { + e = gf_isom_box_size((GF_Box *) p->bitrate); + if (e) return e; + p->size += p->bitrate->size; + } + if (p->scripts) { + e = gf_isom_box_size((GF_Box *) p->scripts); + if (e) return e; + p->size += p->scripts->size; + } + return GF_OK; +} +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + +#endif /*GPAC_DISABLE_ISOM*/ diff --git a/src/gpacmp4/box_code_apple.c b/src/gpacmp4/box_code_apple.c new file mode 100644 index 00000000..7a92d5ee --- /dev/null +++ b/src/gpacmp4/box_code_apple.c @@ -0,0 +1,325 @@ +/* +Author: Andrew Voznytsa + +Project: GPAC - Multimedia Framework C SDK +Module: ISO Media File Format sub-project + +Copyright: (c) 2006, Andrew Voznytsa +License: see License.txt in the top level directory. +*/ + +#include + +#ifndef GPAC_DISABLE_ISOM + +void ilst_del(GF_Box *s) +{ + GF_ItemListBox *ptr = (GF_ItemListBox *)s; + if (ptr == NULL) return; + gf_isom_box_array_del(ptr->tags); + gf_free(ptr); +} + +GF_Err ilst_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + u32 sub_type; + GF_Box *a; + GF_ItemListBox *ptr = (GF_ItemListBox *)s; + while (ptr->size) { + /*if no ilst type coded, break*/ + sub_type = gf_bs_peek_bits(bs, 32, 0); + if (sub_type) { + e = gf_isom_parse_box(&a, bs); + if (e) return e; + if (ptr->sizesize) return GF_ISOM_INVALID_FILE; + ptr->size -= a->size; + gf_list_add(ptr->tags, a); + } else { + gf_bs_read_u32(bs); + ptr->size -= 4; + } + } + return GF_OK; +} + +GF_Box *ilst_New() +{ + GF_ItemListBox *tmp = (GF_ItemListBox *) gf_malloc(sizeof(GF_ItemListBox)); + if (tmp == NULL) return NULL; + memset(tmp, 0, sizeof(GF_ItemListBox)); + tmp->type = GF_ISOM_BOX_TYPE_ILST; + tmp->tags = gf_list_new(); + return (GF_Box *)tmp; +} + +#ifndef GPAC_DISABLE_ISOM_WRITE + +GF_Err ilst_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_ItemListBox *ptr = (GF_ItemListBox *)s; + + e = gf_isom_box_write_header(s, bs); + if (e) return e; + + return gf_isom_box_array_write(s, ptr->tags, bs); +} + + +GF_Err ilst_Size(GF_Box *s) +{ + GF_Err e; + GF_ItemListBox *ptr = (GF_ItemListBox *)s; + + e = gf_isom_box_get_size(s); + if (e) return e; + + return gf_isom_box_array_size(s, ptr->tags); +} + +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + +void ListItem_del(GF_Box *s) +{ + GF_ListItemBox *ptr = (GF_ListItemBox *) s; + if (ptr == NULL) return; + if (ptr->data != NULL) { + if (ptr->data->data) gf_free(ptr->data->data); + gf_free(ptr->data); + } + gf_free(ptr); +} + +GF_Err ListItem_Read(GF_Box *s,GF_BitStream *bs) +{ + GF_Err e; + u32 sub_type; + GF_Box *a = NULL; + GF_ListItemBox *ptr = (GF_ListItemBox *)s; + + /*iTunes way: there's a data atom containing the data*/ + sub_type = gf_bs_peek_bits(bs, 32, 4); + if (sub_type == GF_ISOM_BOX_TYPE_DATA ) { + e = gf_isom_parse_box(&a, bs); + if (e) return e; + if (ptr->sizesize) return GF_ISOM_INVALID_FILE; + ptr->size -= a->size; + + if (a && ptr->data) gf_isom_box_del((GF_Box *) ptr->data); + ptr->data = (GF_DataBox *)a; + } + /*QT way*/ + else { + ptr->data->type = 0; + ptr->data->dataSize = gf_bs_read_u16(bs); + gf_bs_read_u16(bs); + ptr->data->data = (char *) gf_malloc(sizeof(char)*(ptr->data->dataSize + 1)); + gf_bs_read_data(bs, ptr->data->data, ptr->data->dataSize); + ptr->data->data[ptr->data->dataSize] = 0; + ptr->size -= ptr->data->dataSize; + } + return GF_OK; +} + +GF_Box *ListItem_New(u32 type) +{ + GF_ListItemBox *tmp; + + tmp = (GF_ListItemBox *) gf_malloc(sizeof(GF_ListItemBox)); + if (tmp == NULL) return NULL; + memset(tmp, 0, sizeof(GF_ListItemBox)); + + tmp->type = type; + + tmp->data = (GF_DataBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_DATA); + + if (tmp->data == NULL){ + gf_free(tmp); + return NULL; + } + + return (GF_Box *)tmp; +} + +#ifndef GPAC_DISABLE_ISOM_WRITE + +GF_Err ListItem_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_ListItemBox *ptr = (GF_ListItemBox *) s; + + e = gf_isom_box_write_header(s, bs); + if (e) return e; + + /*iTune way*/ + if (ptr->data->type) return gf_isom_box_write((GF_Box* )ptr->data, bs); + /*QT way*/ + gf_bs_write_u16(bs, ptr->data->dataSize); + gf_bs_write_u16(bs, 0); + gf_bs_write_data(bs, ptr->data->data, ptr->data->dataSize); + return GF_OK; +} + +GF_Err ListItem_Size(GF_Box *s) +{ + GF_Err e; + GF_ListItemBox *ptr = (GF_ListItemBox *)s; + + e = gf_isom_box_get_size(s); + if (e) return e; + + /*iTune way*/ + if (ptr->data && ptr->data->type) { + e = gf_isom_box_size((GF_Box *)ptr->data); + if (e) return e; + ptr->size += ptr->data->size; + } + /*QT way*/ + else { + ptr->size += ptr->data->dataSize + 4; + } + return GF_OK; +} + +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + +void data_del(GF_Box *s) +{ + GF_DataBox *ptr = (GF_DataBox *) s; + if (ptr == NULL) return; + if (ptr->data) + gf_free(ptr->data); + gf_free(ptr); + +} + +GF_Err data_Read(GF_Box *s,GF_BitStream *bs) +{ + GF_Err e; + GF_DataBox *ptr = (GF_DataBox *)s; + + e = gf_isom_full_box_read(s, bs); + if (e) return e; + ptr->reserved = gf_bs_read_int(bs, 32); + ptr->size -= 4; + if (ptr->size) { + ptr->dataSize = (u32) ptr->size; + ptr->data = (char*)gf_malloc(ptr->dataSize * sizeof(ptr->data[0]) + 1); + if (ptr->data == NULL) return GF_OUT_OF_MEM; + ptr->data[ptr->dataSize] = 0; + gf_bs_read_data(bs, ptr->data, ptr->dataSize); + } + + return GF_OK; +} + +GF_Box *data_New() +{ + GF_DataBox *tmp; + + tmp = (GF_DataBox *) gf_malloc(sizeof(GF_DataBox)); + if (tmp == NULL) return NULL; + memset(tmp, 0, sizeof(GF_DataBox)); + + gf_isom_full_box_init((GF_Box *)tmp); + tmp->type = GF_ISOM_BOX_TYPE_DATA; + + return (GF_Box *)tmp; +} + +#ifndef GPAC_DISABLE_ISOM_WRITE + +GF_Err data_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_DataBox *ptr = (GF_DataBox *) s; + + e = gf_isom_full_box_write(s, bs); + if (e) return e; + gf_bs_write_int(bs, ptr->reserved, 32); + if(ptr->data != NULL && ptr->dataSize > 0){ + gf_bs_write_data(bs, ptr->data, ptr->dataSize); + } + return GF_OK; +} + +GF_Err data_Size(GF_Box *s) +{ + GF_Err e; + GF_DataBox *ptr = (GF_DataBox *)s; + e = gf_isom_full_box_get_size(s); + if (e) return e; + ptr->size += 4; + if(ptr->data != NULL && ptr->dataSize > 0){ + ptr->size += ptr->dataSize; + } + return GF_OK; +} + +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + +GF_MetaBox *gf_isom_apple_get_meta_extensions(GF_ISOFile *mov) +{ + u32 i; + GF_MetaBox *meta; + GF_UserDataMap *map; + + if (!mov || !mov->moov) return NULL; + + if (!mov->moov->udta) return NULL; + map = udta_getEntry(mov->moov->udta, GF_ISOM_BOX_TYPE_META, NULL); + if (!map) return NULL; + + for(i = 0; i < gf_list_count(map->boxList); i++){ + meta = (GF_MetaBox*)gf_list_get(map->boxList, i); + + if(meta != NULL && meta->handler != NULL && meta->handler->handlerType == GF_ISOM_HANDLER_TYPE_MDIR) return meta; + } + + return NULL; +} + +#ifndef GPAC_DISABLE_ISOM_WRITE +GF_MetaBox *gf_isom_apple_create_meta_extensions(GF_ISOFile *mov) +{ + GF_Err e; + u32 i; + GF_MetaBox *meta; + GF_UserDataMap *map; + + if (!mov || !mov->moov) return NULL; + + if (!mov->moov->udta){ + e = moov_AddBox((GF_Box*)mov->moov, gf_isom_box_new(GF_ISOM_BOX_TYPE_UDTA)); + if (e) return NULL; + } + + map = udta_getEntry(mov->moov->udta, GF_ISOM_BOX_TYPE_META, NULL); + if (map){ + for(i = 0; i < gf_list_count(map->boxList); i++){ + meta = (GF_MetaBox*)gf_list_get(map->boxList, i); + + if(meta != NULL && meta->handler != NULL && meta->handler->handlerType == GF_ISOM_HANDLER_TYPE_MDIR) return meta; + } + } + + meta = (GF_MetaBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_META); + + if(meta != NULL){ + meta->handler = (GF_HandlerBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_HDLR); + if(meta->handler == NULL){ + gf_isom_box_del((GF_Box *)meta); + return NULL; + } + meta->handler->handlerType = GF_ISOM_HANDLER_TYPE_MDIR; + gf_list_add(meta->other_boxes, gf_isom_box_new(GF_ISOM_BOX_TYPE_ILST)); + udta_AddBox(mov->moov->udta, (GF_Box *)meta); + } + + return meta; +} +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + + +#endif /*GPAC_DISABLE_ISOM*/ diff --git a/src/gpacmp4/box_code_base.c b/src/gpacmp4/box_code_base.c new file mode 100644 index 00000000..5041c34e --- /dev/null +++ b/src/gpacmp4/box_code_base.c @@ -0,0 +1,8084 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / ISO Media File Format sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ +#include "../disable_warnings.h" +#include + +#ifndef GPAC_DISABLE_ISOM + +void co64_del(GF_Box *s) +{ + GF_ChunkLargeOffsetBox *ptr; + ptr = (GF_ChunkLargeOffsetBox *) s; + if (ptr == NULL) return; + if (ptr->offsets) gf_free(ptr->offsets); + gf_free(ptr); +} + +GF_Err co64_Read(GF_Box *s,GF_BitStream *bs) +{ + GF_Err e; + u32 entries; + GF_ChunkLargeOffsetBox *ptr = (GF_ChunkLargeOffsetBox *) s; + e = gf_isom_full_box_read(s, bs); + if (e) return e; + ptr->nb_entries = gf_bs_read_u32(bs); + ptr->offsets = (u64 *) gf_malloc(ptr->nb_entries * sizeof(u64) ); + if (ptr->offsets == NULL) return GF_OUT_OF_MEM; + ptr->alloc_size = ptr->nb_entries; + for (entries = 0; entries < ptr->nb_entries; entries++) { + ptr->offsets[entries] = gf_bs_read_u64(bs); + } + return GF_OK; +} + +GF_Box *co64_New() +{ + GF_ChunkLargeOffsetBox *tmp; + + tmp = (GF_ChunkLargeOffsetBox *) gf_malloc(sizeof(GF_ChunkLargeOffsetBox)); + if (tmp == NULL) return NULL; + memset(tmp, 0, sizeof(GF_ChunkLargeOffsetBox)); + gf_isom_full_box_init((GF_Box *)tmp); + tmp->type = GF_ISOM_BOX_TYPE_CO64; + return (GF_Box *)tmp; +} + + +#ifndef GPAC_DISABLE_ISOM_WRITE + +GF_Err co64_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + u32 i; + GF_ChunkLargeOffsetBox *ptr = (GF_ChunkLargeOffsetBox *) s; + + e = gf_isom_full_box_write(s, bs); + if (e) return e; + gf_bs_write_u32(bs, ptr->nb_entries); + for (i = 0; i < ptr->nb_entries; i++ ) { + gf_bs_write_u64(bs, ptr->offsets[i]); + } + return GF_OK; +} + +GF_Err co64_Size(GF_Box *s) +{ + GF_Err e; + GF_ChunkLargeOffsetBox *ptr = (GF_ChunkLargeOffsetBox *) s; + e = gf_isom_full_box_get_size(s); + if (e) return e; + ptr->size += 4 + (8 * ptr->nb_entries); + return GF_OK; +} + +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + +void cprt_del(GF_Box *s) +{ + GF_CopyrightBox *ptr = (GF_CopyrightBox *) s; + if (ptr == NULL) return; + if (ptr->notice) + gf_free(ptr->notice); + gf_free(ptr); +} + + +GF_Box *chpl_New() +{ + GF_ChapterListBox *tmp; + + tmp = (GF_ChapterListBox *) gf_malloc(sizeof(GF_ChapterListBox)); + if (tmp == NULL) return NULL; + memset(tmp, 0, sizeof(GF_ChapterListBox)); + tmp->list = gf_list_new(); + gf_isom_full_box_init((GF_Box *)tmp); + tmp->type = GF_ISOM_BOX_TYPE_CHPL; + tmp->version = 1; + return (GF_Box *)tmp; +} + +void chpl_del(GF_Box *s) +{ + GF_ChapterListBox *ptr = (GF_ChapterListBox *) s; + if (ptr == NULL) return; + while (gf_list_count(ptr->list)) { + GF_ChapterEntry *ce = (GF_ChapterEntry *)gf_list_get(ptr->list, 0); + if (ce->name) gf_free(ce->name); + gf_free(ce); + gf_list_rem(ptr->list, 0); + } + gf_list_del(ptr->list); + gf_free(ptr); +} + +/*this is using chpl format according to some NeroRecode samples*/ +GF_Err chpl_Read(GF_Box *s,GF_BitStream *bs) +{ + GF_Err e; + GF_ChapterEntry *ce; + u32 nb_chaps, len, i, count; + GF_ChapterListBox *ptr = (GF_ChapterListBox *)s; + + e = gf_isom_full_box_read(s, bs); + if (e) return e; + + /*reserved or ???*/ + gf_bs_read_u32(bs); + nb_chaps = gf_bs_read_u8(bs); + + count = 0; + while (nb_chaps) { + GF_SAFEALLOC(ce, GF_ChapterEntry); + ce->start_time = gf_bs_read_u64(bs); + len = gf_bs_read_u8(bs); + if (len) { + ce->name = (char *)gf_malloc(sizeof(char)*(len+1)); + gf_bs_read_data(bs, ce->name, len); + ce->name[len] = 0; + } else { + ce->name = gf_strdup(""); + } + + for (i=0;ilist, i); + if (ace->start_time >= ce->start_time) { + gf_list_insert(ptr->list, ce, i); + ce = NULL; + break; + } + } + if (ce) gf_list_add(ptr->list, ce); + count++; + nb_chaps--; + } + return GF_OK; +} + +#ifndef GPAC_DISABLE_ISOM_WRITE + +GF_Err chpl_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + u32 count, i; + GF_ChapterListBox *ptr = (GF_ChapterListBox *) s; + e = gf_isom_full_box_write(s, bs); + if (e) return e; + + count = gf_list_count(ptr->list); + gf_bs_write_u32(bs, 0); + gf_bs_write_u8(bs, count); + for (i=0; ilist, i); + gf_bs_write_u64(bs, ce->start_time); + if (ce->name) { + len = strlen(ce->name); if (len>255) len = 255; + gf_bs_write_u8(bs, len); + gf_bs_write_data(bs, ce->name, len); + } else { + gf_bs_write_u8(bs, 0); + } + } + return GF_OK; +} + +GF_Err chpl_Size(GF_Box *s) +{ + GF_Err e; + u32 count, i; + GF_ChapterListBox *ptr = (GF_ChapterListBox *)s; + e = gf_isom_full_box_get_size(s); + if (e) return e; + ptr->size += 5; + + count = gf_list_count(ptr->list); + for (i=0; ilist, i); + ptr->size += 9; /*64bit time stamp + 8bit str len*/ + if (ce->name) ptr->size += strlen(ce->name); + } + return GF_OK; +} + +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + + +GF_Err cprt_Read(GF_Box *s,GF_BitStream *bs) +{ + GF_Err e; + GF_CopyrightBox *ptr = (GF_CopyrightBox *)s; + + e = gf_isom_full_box_read(s, bs); + if (e) return e; + gf_bs_read_int(bs, 1); + //the spec is unclear here, just says "the value 0 is interpreted as undetermined" + ptr->packedLanguageCode[0] = gf_bs_read_int(bs, 5); + ptr->packedLanguageCode[1] = gf_bs_read_int(bs, 5); + ptr->packedLanguageCode[2] = gf_bs_read_int(bs, 5); + ptr->size -= 2; + //but before or after compaction ?? We assume before + if (ptr->packedLanguageCode[0] || ptr->packedLanguageCode[1] || ptr->packedLanguageCode[2]) { + ptr->packedLanguageCode[0] += 0x60; + ptr->packedLanguageCode[1] += 0x60; + ptr->packedLanguageCode[2] += 0x60; + } else { + ptr->packedLanguageCode[0] = 'u'; + ptr->packedLanguageCode[1] = 'n'; + ptr->packedLanguageCode[2] = 'd'; + } + if (ptr->size) { + u32 bytesToRead = (u32) ptr->size; + ptr->notice = (char*)gf_malloc(bytesToRead * sizeof(char)); + if (ptr->notice == NULL) return GF_OUT_OF_MEM; + gf_bs_read_data(bs, ptr->notice, bytesToRead); + } + return GF_OK; +} + +GF_Box *cprt_New() +{ + GF_CopyrightBox *tmp; + + tmp = (GF_CopyrightBox *) gf_malloc(sizeof(GF_CopyrightBox)); + if (tmp == NULL) return NULL; + memset(tmp, 0, sizeof(GF_CopyrightBox)); + + gf_isom_full_box_init((GF_Box *)tmp); + tmp->type = GF_ISOM_BOX_TYPE_CPRT; + tmp->packedLanguageCode[0] = 'u'; + tmp->packedLanguageCode[1] = 'n'; + tmp->packedLanguageCode[2] = 'd'; + + return (GF_Box *)tmp; +} + +#ifndef GPAC_DISABLE_ISOM_WRITE + +GF_Err cprt_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_CopyrightBox *ptr = (GF_CopyrightBox *) s; + + e = gf_isom_full_box_write(s, bs); + if (e) return e; + gf_bs_write_int(bs, 0, 1); + if (ptr->packedLanguageCode[0]) { + gf_bs_write_int(bs, ptr->packedLanguageCode[0] - 0x60, 5); + gf_bs_write_int(bs, ptr->packedLanguageCode[1] - 0x60, 5); + gf_bs_write_int(bs, ptr->packedLanguageCode[2] - 0x60, 5); + } else { + gf_bs_write_int(bs, 0, 15); + } + if (ptr->notice) { + gf_bs_write_data(bs, ptr->notice, (unsigned long)strlen(ptr->notice) + 1); + } + return GF_OK; +} + +GF_Err cprt_Size(GF_Box *s) +{ + GF_Err e; + GF_CopyrightBox *ptr = (GF_CopyrightBox *)s; + e = gf_isom_full_box_get_size(s); + if (e) return e; + ptr->size += 2; + if (ptr->notice) + ptr->size += strlen(ptr->notice) + 1; + return GF_OK; +} + +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + +void ctts_del(GF_Box *s) +{ + GF_CompositionOffsetBox *ptr = (GF_CompositionOffsetBox *)s; + if (ptr->entries) gf_free(ptr->entries); + gf_free(ptr); +} + + + +GF_Err ctts_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + u32 i; + u32 sampleCount; + GF_CompositionOffsetBox *ptr = (GF_CompositionOffsetBox *)s; + + e = gf_isom_full_box_read(s, bs); + if (e) return e; + ptr->nb_entries = gf_bs_read_u32(bs); + ptr->alloc_size = ptr->nb_entries; + ptr->entries = (GF_DttsEntry*) gf_malloc(sizeof(GF_DttsEntry)*ptr->alloc_size); + if (!ptr->entries) return GF_OUT_OF_MEM; + sampleCount = 0; + for (i=0; inb_entries; i++) { + ptr->entries[i].sampleCount = gf_bs_read_u32(bs); + ptr->entries[i].decodingOffset = gf_bs_read_u32(bs); + sampleCount += ptr->entries[i].sampleCount; + } +#ifndef GPAC_DISABLE_ISOM_WRITE + ptr->w_LastSampleNumber = sampleCount; +#endif + return GF_OK; +} + +GF_Box *ctts_New() +{ + GF_CompositionOffsetBox *tmp; + + tmp = (GF_CompositionOffsetBox *) gf_malloc(sizeof(GF_CompositionOffsetBox)); + if (tmp == NULL) return NULL; + memset(tmp, 0, sizeof(GF_CompositionOffsetBox)); + + gf_isom_full_box_init((GF_Box *) tmp); + tmp->type = GF_ISOM_BOX_TYPE_CTTS; + return (GF_Box *) tmp; +} + + + +#ifndef GPAC_DISABLE_ISOM_WRITE + +GF_Err ctts_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + u32 i; + GF_CompositionOffsetBox *ptr = (GF_CompositionOffsetBox *)s; + + e = gf_isom_full_box_write(s, bs); + if (e) return e; + gf_bs_write_u32(bs, ptr->nb_entries); + for (i=0; inb_entries; i++ ) { + gf_bs_write_u32(bs, ptr->entries[i].sampleCount); + gf_bs_write_u32(bs, ptr->entries[i].decodingOffset); + } + return GF_OK; +} + +GF_Err ctts_Size(GF_Box *s) +{ + GF_Err e; + GF_CompositionOffsetBox *ptr = (GF_CompositionOffsetBox *) s; + + e = gf_isom_full_box_get_size(s); + if (e) return e; + ptr->size += 4 + (8 * ptr->nb_entries); + return GF_OK; +} + +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + +void url_del(GF_Box *s) +{ + GF_DataEntryURLBox *ptr = (GF_DataEntryURLBox *)s; + if (ptr == NULL) return; + if (ptr->location) gf_free(ptr->location); + gf_free(ptr); + return; +} + + +GF_Err url_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_DataEntryURLBox *ptr = (GF_DataEntryURLBox *)s; + + e = gf_isom_full_box_read(s, bs); + if (e) return e; + if (ptr->size) { + ptr->location = (char*)gf_malloc((u32) ptr->size); + if (! ptr->location) return GF_OUT_OF_MEM; + gf_bs_read_data(bs, ptr->location, (u32)ptr->size); + } + return GF_OK; +} + +GF_Box *url_New() +{ + GF_DataEntryURLBox *tmp = (GF_DataEntryURLBox *) gf_malloc(sizeof(GF_DataEntryURLBox)); + if (tmp == NULL) return NULL; + memset(tmp, 0, sizeof(GF_DataEntryURLBox)); + + gf_isom_full_box_init((GF_Box *)tmp); + tmp->type = GF_ISOM_BOX_TYPE_URL; + return (GF_Box *)tmp; +} + +#ifndef GPAC_DISABLE_ISOM_WRITE + +GF_Err url_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_DataEntryURLBox *ptr = (GF_DataEntryURLBox *)s; + + e = gf_isom_full_box_write(s, bs); + if (e) return e; + //the flag set indicates we have a string (WE HAVE TO for URLs) + if ( !(ptr->flags & 1)) { + if (ptr->location) { + gf_bs_write_data(bs, ptr->location, (u32)strlen(ptr->location) + 1); + } + } + return GF_OK; +} + +GF_Err url_Size(GF_Box *s) +{ + GF_Err e; + GF_DataEntryURLBox *ptr = (GF_DataEntryURLBox *)s; + + e = gf_isom_full_box_get_size(s); + if (e) return e; + if ( !(ptr->flags & 1)) { + if (ptr->location) ptr->size += 1 + strlen(ptr->location); + } + return GF_OK; +} + +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + +void urn_del(GF_Box *s) +{ + GF_DataEntryURNBox *ptr = (GF_DataEntryURNBox *)s; + if (ptr == NULL) return; + if (ptr->location) gf_free(ptr->location); + if (ptr->nameURN) gf_free(ptr->nameURN); + gf_free(ptr); +} + + +GF_Err urn_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + u32 i, to_read; + char *tmpName; + GF_DataEntryURNBox *ptr = (GF_DataEntryURNBox *)s; + e = gf_isom_full_box_read(s, bs); + if (e) return e; + if (! ptr->size ) return GF_OK; + + //here we have to handle that in a clever way + to_read = (u32) ptr->size; + tmpName = (char*)gf_malloc(sizeof(char) * to_read); + if (!tmpName) return GF_OUT_OF_MEM; + //get the data + gf_bs_read_data(bs, tmpName, to_read); + + //then get the break + i = 0; + while ( (tmpName[i] != 0) && (i < to_read) ) { + i++; + } + //check the data is consistent + if (i == to_read) { + gf_free(tmpName); + return GF_ISOM_INVALID_FILE; + } + //no NULL char, URL is not specified + if (i == to_read - 1) { + ptr->nameURN = tmpName; + ptr->location = NULL; + return GF_OK; + } + //OK, this has both URN and URL + ptr->nameURN = (char*)gf_malloc(sizeof(char) * (i+1)); + if (!ptr->nameURN) { + gf_free(tmpName); + return GF_OUT_OF_MEM; + } + ptr->location = (char*)gf_malloc(sizeof(char) * (to_read - i - 1)); + if (!ptr->location) { + gf_free(tmpName); + gf_free(ptr->nameURN); + ptr->nameURN = NULL; + return GF_OUT_OF_MEM; + } + memcpy(ptr->nameURN, tmpName, i + 1); + memcpy(ptr->location, tmpName + i + 1, (to_read - i - 1)); + gf_free(tmpName); + return GF_OK; +} + +GF_Box *urn_New() +{ + GF_DataEntryURNBox *tmp = (GF_DataEntryURNBox *) gf_malloc(sizeof(GF_DataEntryURNBox)); + if (tmp == NULL) return NULL; + memset(tmp, 0, sizeof(GF_DataEntryURNBox)); + gf_isom_full_box_init((GF_Box *)tmp); + tmp->type = GF_ISOM_BOX_TYPE_URN; + return (GF_Box *)tmp; +} + +#ifndef GPAC_DISABLE_ISOM_WRITE + + +GF_Err urn_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_DataEntryURNBox *ptr = (GF_DataEntryURNBox *)s; + + e = gf_isom_full_box_write(s, bs); + if (e) return e; + //the flag set indicates we have a string (WE HAVE TO for URLs) + if ( !(ptr->flags & 1)) { + //to check, the spec says: First name, then location + if (ptr->nameURN) { + gf_bs_write_data(bs, ptr->nameURN, (u32)strlen(ptr->nameURN) + 1); + } + if (ptr->location) { + gf_bs_write_data(bs, ptr->location, (u32)strlen(ptr->location) + 1); + } + } + return GF_OK; +} + +GF_Err urn_Size(GF_Box *s) +{ + GF_Err e; + GF_DataEntryURNBox *ptr = (GF_DataEntryURNBox *)s; + + e = gf_isom_full_box_get_size(s); + if (e) return e; + if ( !(ptr->flags & 1)) { + if (ptr->nameURN) ptr->size += 1 + strlen(ptr->nameURN); + if (ptr->location) ptr->size += 1 + strlen(ptr->location); + } + return GF_OK; +} + +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + +void defa_del(GF_Box *s) +{ + GF_UnknownBox *ptr = (GF_UnknownBox *) s; + if (!s) return; + if (ptr->data) gf_free(ptr->data); + gf_free(ptr); +} + + +GF_Err defa_Read(GF_Box *s, GF_BitStream *bs) +{ + u32 bytesToRead; + GF_UnknownBox *ptr = (GF_UnknownBox *)s; + if (ptr->size > 0xFFFFFFFF) return GF_ISOM_INVALID_FILE; + bytesToRead = (u32) (ptr->size); + + if (bytesToRead) { + ptr->data = (char*)gf_malloc(bytesToRead); + if (ptr->data == NULL ) return GF_OUT_OF_MEM; + ptr->dataSize = bytesToRead; + gf_bs_read_data(bs, ptr->data, ptr->dataSize); + } + return GF_OK; +} + +//warning: we don't have any boxType, trick has to be done while creating.. +GF_Box *defa_New() +{ + GF_UnknownBox *tmp = (GF_UnknownBox *) gf_malloc(sizeof(GF_UnknownBox)); + memset(tmp, 0, sizeof(GF_UnknownBox)); + return (GF_Box *) tmp; +} + +#ifndef GPAC_DISABLE_ISOM_WRITE + +GF_Err defa_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_UnknownBox *ptr = (GF_UnknownBox *)s; + if (!s) return GF_BAD_PARAM; + + e = gf_isom_box_write_header(s, bs); + if (e) return e; + if (ptr->data) { + gf_bs_write_data(bs, ptr->data, ptr->dataSize); + } + return GF_OK; +} + +GF_Err defa_Size(GF_Box *s) +{ + GF_Err e; + GF_UnknownBox *ptr = (GF_UnknownBox *)s; + e = gf_isom_box_get_size(s); + if (e) return e; + ptr->size += ptr->dataSize; + return GF_OK; +} + +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + + +void uuid_del(GF_Box *s) +{ + GF_UnknownUUIDBox *ptr = (GF_UnknownUUIDBox *) s; + if (!s) return; + if (ptr->data) gf_free(ptr->data); + gf_free(ptr); +} + + +GF_Err uuid_Read(GF_Box *s, GF_BitStream *bs) +{ + u32 bytesToRead; + GF_UnknownUUIDBox *ptr = (GF_UnknownUUIDBox *)s; + if (ptr->size > 0xFFFFFFFF) return GF_ISOM_INVALID_FILE; + bytesToRead = (u32) (ptr->size); + + if (bytesToRead) { + ptr->data = (char*)gf_malloc(bytesToRead); + if (ptr->data == NULL ) return GF_OUT_OF_MEM; + ptr->dataSize = bytesToRead; + gf_bs_read_data(bs, ptr->data, ptr->dataSize); + } + return GF_OK; +} + +//warning: we don't have any boxType, trick has to be done while creating.. +GF_Box *uuid_New() +{ + GF_UnknownUUIDBox *tmp = (GF_UnknownUUIDBox *) gf_malloc(sizeof(GF_UnknownUUIDBox)); + memset(tmp, 0, sizeof(GF_UnknownUUIDBox)); + tmp->type = GF_ISOM_BOX_TYPE_UUID; + return (GF_Box *) tmp; +} + +#ifndef GPAC_DISABLE_ISOM_WRITE + +GF_Err uuid_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_UnknownUUIDBox *ptr = (GF_UnknownUUIDBox*)s; + if (!s) return GF_BAD_PARAM; + + e = gf_isom_box_write_header(s, bs); + if (e) return e; + if (ptr->data) { + gf_bs_write_data(bs, ptr->data, ptr->dataSize); + } + return GF_OK; +} + +GF_Err uuid_Size(GF_Box *s) +{ + GF_Err e; + GF_UnknownUUIDBox*ptr = (GF_UnknownUUIDBox*)s; + e = gf_isom_box_get_size(s); + if (e) return e; + ptr->size += ptr->dataSize; + return GF_OK; +} + +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + + +void dinf_del(GF_Box *s) +{ + GF_DataInformationBox *ptr = (GF_DataInformationBox *)s; + if (ptr == NULL) return; + gf_isom_box_del((GF_Box *)ptr->dref); + gf_free(ptr); +} + + +GF_Err dinf_AddBox(GF_Box *s, GF_Box *a) +{ + GF_DataInformationBox *ptr = (GF_DataInformationBox *)s; + switch(a->type) { + case GF_ISOM_BOX_TYPE_DREF: + if (ptr->dref) ERROR_ON_DUPLICATED_BOX(a, ptr) + ptr->dref = (GF_DataReferenceBox *)a; + return GF_OK; + } + GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[iso file] Warning box %s unknown type - discarding\n", gf_4cc_to_str(a->type))); + gf_isom_box_del(a); + return GF_OK; +} + +GF_Err dinf_Read(GF_Box *s, GF_BitStream *bs) +{ + return gf_isom_read_box_list(s, bs, dinf_AddBox); +} + +GF_Box *dinf_New() +{ + GF_DataInformationBox *tmp = (GF_DataInformationBox *) gf_malloc(sizeof(GF_DataInformationBox)); + if (tmp == NULL) return NULL; + memset(tmp, 0, sizeof(GF_DataInformationBox)); + tmp->type = GF_ISOM_BOX_TYPE_DINF; + return (GF_Box *)tmp; +} + +#ifndef GPAC_DISABLE_ISOM_WRITE + +GF_Err dinf_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_DataInformationBox *ptr = (GF_DataInformationBox *)s; + + e = gf_isom_box_write_header(s, bs); + if (e) return e; + if (ptr->dref) { + e = gf_isom_box_write((GF_Box *)ptr->dref, bs); + if (e) return e; + } + return GF_OK; +} + +GF_Err dinf_Size(GF_Box *s) +{ + GF_Err e; + GF_DataInformationBox *ptr = (GF_DataInformationBox *)s; + e = gf_isom_box_get_size(s); + if (ptr->dref) { + e = gf_isom_box_size((GF_Box *) ptr->dref); + if (e) return e; + ptr->size += ptr->dref->size; + } + return GF_OK; +} + +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + +void dref_del(GF_Box *s) +{ + GF_DataReferenceBox *ptr = (GF_DataReferenceBox *) s; + if (ptr == NULL) return; + gf_isom_box_array_del(ptr->boxList); + gf_free(ptr); +} + + +GF_Err dref_AddDataEntry(GF_DataReferenceBox *ptr, GF_Box *entry) +{ + return gf_list_add(ptr->boxList, entry); +} + +GF_Err dref_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + u32 count, i; + GF_Box *a; + GF_DataReferenceBox *ptr = (GF_DataReferenceBox *)s; + + if (ptr == NULL) return GF_BAD_PARAM; + e = gf_isom_full_box_read(s, bs); + if (e) return e; + count = gf_bs_read_u32(bs); + + for ( i = 0; i < count; i++ ) { + e = gf_isom_parse_box(&a, bs); + if (e) return e; + if (ptr->sizesize) return GF_ISOM_INVALID_FILE; + e = gf_list_add(ptr->boxList, a); + if (e) return e; + ptr->size -= a->size; + } + return GF_OK; +} + +GF_Box *dref_New() +{ + GF_DataReferenceBox *tmp = (GF_DataReferenceBox *) gf_malloc(sizeof(GF_DataReferenceBox)); + if (tmp == NULL) return NULL; + memset(tmp, 0, sizeof(GF_DataReferenceBox)); + + gf_isom_full_box_init((GF_Box *) tmp); + tmp->boxList = gf_list_new(); + if (!tmp->boxList) { + gf_free(tmp); + return NULL; + } + tmp->type = GF_ISOM_BOX_TYPE_DREF; + return (GF_Box *)tmp; +} + + +#ifndef GPAC_DISABLE_ISOM_WRITE + +GF_Err dref_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + u32 count; + GF_DataReferenceBox *ptr = (GF_DataReferenceBox *)s; + if (!s) return GF_BAD_PARAM; + + e = gf_isom_full_box_write(s, bs); + if (e) return e; + count = gf_list_count(ptr->boxList); + gf_bs_write_u32(bs, count); + return gf_isom_box_array_write(s, ptr->boxList, bs); +} + +GF_Err dref_Size(GF_Box *s) +{ + GF_Err e; + GF_DataReferenceBox *ptr = (GF_DataReferenceBox *)s; + if (!s) return GF_BAD_PARAM; + + e = gf_isom_full_box_get_size(s); + if (e) return e; + ptr->size += 4; + e = gf_isom_box_array_size(s, ptr->boxList); + if (e) return e; + return GF_OK; +} + +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + +void edts_del(GF_Box *s) +{ + GF_EditBox *ptr = (GF_EditBox *) s; + gf_isom_box_del((GF_Box *)ptr->editList); + gf_free(ptr); +} + + +GF_Err edts_AddBox(GF_Box *s, GF_Box *a) +{ + GF_EditBox *ptr = (GF_EditBox *)s; + if (a->type == GF_ISOM_BOX_TYPE_ELST) { + if (ptr->editList) return GF_BAD_PARAM; + ptr->editList = (GF_EditListBox *)a; + return GF_OK; + } + GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[iso file] Warning box %s unknown type - discarding\n", gf_4cc_to_str(a->type))); + gf_isom_box_del(a); + return GF_OK; +} + + +GF_Err edts_Read(GF_Box *s, GF_BitStream *bs) +{ + return gf_isom_read_box_list(s, bs, edts_AddBox); +} + +GF_Box *edts_New() +{ + GF_EditBox *tmp; + GF_SAFEALLOC(tmp, GF_EditBox); + if (tmp == NULL) return NULL; + tmp->type = GF_ISOM_BOX_TYPE_EDTS; + return (GF_Box *) tmp; +} + +#ifndef GPAC_DISABLE_ISOM_WRITE + +GF_Err edts_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_EditBox *ptr = (GF_EditBox *)s; + + //here we have a trick: if editList is empty, skip the box + if (gf_list_count(ptr->editList->entryList)) { + e = gf_isom_box_write_header(s, bs); + if (e) return e; + e = gf_isom_box_write((GF_Box *) ptr->editList, bs); + if (e) return e; + } + return GF_OK; +} + +GF_Err edts_Size(GF_Box *s) +{ + GF_Err e; + GF_EditBox *ptr = (GF_EditBox *)s; + + //here we have a trick: if editList is empty, skip the box + if (! gf_list_count(ptr->editList->entryList)) { + ptr->size = 0; + } else { + e = gf_isom_box_get_size(s); + if (e) return e; + e = gf_isom_box_size((GF_Box *)ptr->editList); + if (e) return e; + ptr->size += ptr->editList->size; + } + return GF_OK; +} + +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + +void elst_del(GF_Box *s) +{ + GF_EditListBox *ptr; + GF_EdtsEntry *p; + u32 nb_entries; + u32 i; + + ptr = (GF_EditListBox *)s; + if (ptr == NULL) return; + nb_entries = gf_list_count(ptr->entryList); + for (i = 0; i < nb_entries; i++) { + p = (GF_EdtsEntry*)gf_list_get(ptr->entryList, i); + if (p) gf_free(p); + } + gf_list_del(ptr->entryList); + gf_free(ptr); +} + + + + +GF_Err elst_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + u32 entries; + s32 tr; + u32 nb_entries; + GF_EdtsEntry *p; + GF_EditListBox *ptr = (GF_EditListBox *)s; + + e = gf_isom_full_box_read(s, bs); + if (e) return e; + nb_entries = gf_bs_read_u32(bs); + + for (entries = 0; entries < nb_entries; entries++ ) { + p = (GF_EdtsEntry *) gf_malloc(sizeof(GF_EdtsEntry)); + if (!p) return GF_OUT_OF_MEM; + if (ptr->version == 1) { + p->segmentDuration = gf_bs_read_u64(bs); + p->mediaTime = (s64) gf_bs_read_u64(bs); + } else { + p->segmentDuration = gf_bs_read_u32(bs); + tr = gf_bs_read_u32(bs); + p->mediaTime = (s64) tr; + } + p->mediaRate = gf_bs_read_u16(bs); + gf_bs_read_u16(bs); + gf_list_add(ptr->entryList, p); + } + return GF_OK; +} + +GF_Box *elst_New() +{ + GF_EditListBox *tmp; + + tmp = (GF_EditListBox *) gf_malloc(sizeof(GF_EditListBox)); + if (tmp == NULL) return NULL; + memset(tmp, 0, sizeof(GF_EditListBox)); + + gf_isom_full_box_init((GF_Box *)tmp); + tmp->entryList = gf_list_new(); + if (!tmp->entryList) { + gf_free(tmp); + return NULL; + } + tmp->type = GF_ISOM_BOX_TYPE_ELST; + return (GF_Box *)tmp; +} + +#ifndef GPAC_DISABLE_ISOM_WRITE + +GF_Err elst_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + u32 i; + u32 nb_entries; + GF_EdtsEntry *p; + GF_EditListBox *ptr = (GF_EditListBox *)s; + if (!ptr) return GF_BAD_PARAM; + + nb_entries = gf_list_count(ptr->entryList); + e = gf_isom_full_box_write(s, bs); + if (e) return e; + gf_bs_write_u32(bs, nb_entries); + for (i = 0; i < nb_entries; i++ ) { + p = (GF_EdtsEntry*)gf_list_get(ptr->entryList, i); + if (ptr->version == 1) { + gf_bs_write_u64(bs, p->segmentDuration); + gf_bs_write_u64(bs, p->mediaTime); + } else { + gf_bs_write_u32(bs, (u32) p->segmentDuration); + gf_bs_write_u32(bs, (s32) p->mediaTime); + } + gf_bs_write_u16(bs, p->mediaRate); + gf_bs_write_u16(bs, 0); + } + return GF_OK; +} + +GF_Err elst_Size(GF_Box *s) +{ + GF_Err e; + u32 durtimebytes; + u32 i, nb_entries; + GF_EditListBox *ptr = (GF_EditListBox *)s; + + e = gf_isom_full_box_get_size(s); + if (e) return e; + //entry count + ptr->size += 4; + nb_entries = gf_list_count(ptr->entryList); + ptr->version = 0; + for (i=0; ientryList, i); + if ((p->segmentDuration>0xFFFFFFFF) || (p->mediaTime>0xFFFFFFFF)) { + ptr->version = 1; + break; + } + } + durtimebytes = (ptr->version == 1 ? 16 : 8) + 4; + ptr->size += (nb_entries * durtimebytes); + return GF_OK; +} + + +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + +void esds_del(GF_Box *s) +{ + GF_ESDBox *ptr = (GF_ESDBox *)s; + if (ptr == NULL) return; + if (ptr->desc) gf_odf_desc_del((GF_Descriptor *)ptr->desc); + gf_free(ptr); +} + + +GF_Err esds_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + u32 descSize; + char *enc_desc; + u32 SLIsPredefined(GF_SLConfig *sl); + GF_ESDBox *ptr = (GF_ESDBox *)s; + + e = gf_isom_full_box_read(s, bs); + if (e) return e; + descSize = (u32) (ptr->size); + + if (descSize) { + enc_desc = (char*)gf_malloc(sizeof(char) * descSize); + if (!enc_desc) return GF_OUT_OF_MEM; + //get the payload + gf_bs_read_data(bs, enc_desc, descSize); + //send it to the OD Codec + e = gf_odf_desc_read(enc_desc, descSize, (GF_Descriptor **) &ptr->desc); + //OK, free our desc + gf_free(enc_desc); + //we do not abbort on error, but skip the descritpor + if (e) { + ptr->desc = NULL; + } else { + /*fix broken files*/ + if (!ptr->desc->URLString) { + if (!ptr->desc->slConfig) { + ptr->desc->slConfig = (GF_SLConfig *) gf_odf_desc_new(GF_ODF_SLC_TAG); + ptr->desc->slConfig->predefined = SLPredef_MP4; + } else if (ptr->desc->slConfig->predefined != SLPredef_MP4) { + ptr->desc->slConfig->predefined = SLPredef_MP4; + gf_odf_slc_set_pref(ptr->desc->slConfig); + } + } + } + } + return GF_OK; +} + +GF_Box *esds_New() +{ + GF_ESDBox *tmp = (GF_ESDBox *) gf_malloc(sizeof(GF_ESDBox)); + if (tmp == NULL) return NULL; + memset(tmp, 0, sizeof(GF_ESDBox)); + + gf_isom_full_box_init((GF_Box *) tmp); + tmp->type = GF_ISOM_BOX_TYPE_ESDS; + + return (GF_Box *)tmp; +} + + +#ifndef GPAC_DISABLE_ISOM_WRITE + +GF_Err esds_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + char *enc_desc; + u32 descSize = 0; + GF_ESDBox *ptr = (GF_ESDBox *)s; + + e = gf_isom_full_box_write(s, bs); + if (e) return e; + e = gf_odf_desc_write((GF_Descriptor *)ptr->desc, &enc_desc, &descSize); + if (e) return e; + gf_bs_write_data(bs, enc_desc, descSize); + //free our buffer + gf_free(enc_desc); + return GF_OK; +} + +GF_Err esds_Size(GF_Box *s) +{ + GF_Err e; + u32 descSize = 0; + GF_ESDBox *ptr = (GF_ESDBox *)s; + e = gf_isom_full_box_get_size(s); + if (e) return e; + descSize = gf_odf_desc_size((GF_Descriptor *)ptr->desc); + ptr->size += descSize; + return GF_OK; +} + +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + +void free_del(GF_Box *s) +{ + GF_FreeSpaceBox *ptr = (GF_FreeSpaceBox *)s; + if (ptr->data) gf_free(ptr->data); + gf_free(ptr); +} + + +GF_Err free_Read(GF_Box *s, GF_BitStream *bs) +{ + u32 bytesToRead; + GF_FreeSpaceBox *ptr = (GF_FreeSpaceBox *)s; + + if (ptr->size > 0xFFFFFFFF) return GF_IO_ERR; + + bytesToRead = (u32) (ptr->size); + + if (bytesToRead) { + ptr->data = (char*)gf_malloc(bytesToRead * sizeof(char)); + gf_bs_read_data(bs, ptr->data, bytesToRead); + ptr->dataSize = bytesToRead; + } + return GF_OK; +} + +GF_Box *free_New() +{ + GF_FreeSpaceBox *tmp = (GF_FreeSpaceBox *) gf_malloc(sizeof(GF_FreeSpaceBox)); + if (tmp == NULL) return NULL; + memset(tmp, 0, sizeof(GF_FreeSpaceBox)); + tmp->type = GF_ISOM_BOX_TYPE_FREE; + return (GF_Box *)tmp; +} + +#ifndef GPAC_DISABLE_ISOM_WRITE + +GF_Err free_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_FreeSpaceBox *ptr = (GF_FreeSpaceBox *)s; + e = gf_isom_box_write_header(s, bs); + if (e) return e; + if (ptr->dataSize) { + if (ptr->data) { + gf_bs_write_data(bs, ptr->data, ptr->dataSize); + } else { + u32 i = 0; + while (idataSize) { + gf_bs_write_u8(bs, 0); + i++; + } + } + } + return GF_OK; +} + +GF_Err free_Size(GF_Box *s) +{ + GF_Err e; + GF_FreeSpaceBox *ptr = (GF_FreeSpaceBox *)s; + e = gf_isom_box_get_size(s); + if (e) return e; + ptr->size += ptr->dataSize; + return GF_OK; +} + +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + +void ftyp_del(GF_Box *s) +{ + GF_FileTypeBox *ptr = (GF_FileTypeBox *) s; + if (ptr->altBrand) gf_free(ptr->altBrand); + gf_free(ptr); +} + +GF_Box *ftyp_New() +{ + GF_FileTypeBox *tmp; + + tmp = (GF_FileTypeBox *) gf_malloc(sizeof(GF_FileTypeBox)); + if (tmp == NULL) return NULL; + memset(tmp, 0, sizeof(GF_FileTypeBox)); + + tmp->type = GF_ISOM_BOX_TYPE_FTYP; + return (GF_Box *)tmp; +} + +GF_Err ftyp_Read(GF_Box *s,GF_BitStream *bs) +{ + u32 i; + GF_FileTypeBox *ptr = (GF_FileTypeBox *)s; + + ptr->majorBrand = gf_bs_read_u32(bs); + ptr->minorVersion = gf_bs_read_u32(bs); + ptr->size -= 8; + + ptr->altCount = ( (u32) (ptr->size)) / 4; + if (!ptr->altCount) return GF_OK; + if (ptr->altCount * 4 != (u32) (ptr->size)) return GF_ISOM_INVALID_FILE; + + ptr->altBrand = (u32*)gf_malloc(sizeof(u32)*ptr->altCount); + for (i = 0; ialtCount; i++) { + ptr->altBrand[i] = gf_bs_read_u32(bs); + } + return GF_OK; +} + + + +#ifndef GPAC_DISABLE_ISOM_WRITE + +GF_Err ftyp_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + u32 i; + GF_FileTypeBox *ptr = (GF_FileTypeBox *) s; + + e = gf_isom_box_write_header(s, bs); + if (e) return e; + gf_bs_write_u32(bs, ptr->majorBrand); + gf_bs_write_u32(bs, ptr->minorVersion); + for (i=0; ialtCount; i++) { + gf_bs_write_u32(bs, ptr->altBrand[i]); + } + return GF_OK; +} + +GF_Err ftyp_Size(GF_Box *s) +{ + GF_Err e; + GF_FileTypeBox *ptr = (GF_FileTypeBox *)s; + + e = gf_isom_box_get_size(s); + if (e) return e; + ptr->size += 8 + ptr->altCount * 4; + return GF_OK; +} + +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + + + +void gnrm_del(GF_Box *s) +{ + GF_GenericSampleEntryBox *ptr = (GF_GenericSampleEntryBox *)s; + if (ptr->data) gf_free(ptr->data); + gf_free(ptr); +} + +GF_Box *gnrm_New() +{ + GF_GenericSampleEntryBox *tmp = (GF_GenericSampleEntryBox *) gf_malloc(sizeof(GF_GenericSampleEntryBox)); + if (tmp == NULL) return NULL; + memset(tmp, 0, sizeof(GF_GenericSampleEntryBox)); + tmp->type = GF_ISOM_BOX_TYPE_GNRM; + return (GF_Box *)tmp; +} + + +#ifndef GPAC_DISABLE_ISOM_WRITE + +GF_Err gnrm_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_GenericSampleEntryBox *ptr = (GF_GenericSampleEntryBox *)s; + + //carefull we are not writing the box type but the entry type so switch for write + ptr->type = ptr->EntryType; + e = gf_isom_box_write_header(s, bs); + if (e) return e; + ptr->type = GF_ISOM_BOX_TYPE_GNRM; + gf_bs_write_data(bs, ptr->reserved, 6); + gf_bs_write_u16(bs, ptr->dataReferenceIndex); + gf_bs_write_data(bs, ptr->data, ptr->data_size); + return GF_OK; +} + +GF_Err gnrm_Size(GF_Box *s) +{ + GF_Err e; + GF_GenericSampleEntryBox *ptr = (GF_GenericSampleEntryBox *)s; + s->type = ptr->EntryType; + e = gf_isom_box_get_size(s); + s->type = GF_ISOM_BOX_TYPE_GNRM; + if (e) return e; + ptr->size += 8+ptr->data_size; + return GF_OK; +} + +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + + +void gnrv_del(GF_Box *s) +{ + GF_GenericVisualSampleEntryBox *ptr = (GF_GenericVisualSampleEntryBox *)s; + if (ptr->data) gf_free(ptr->data); + gf_free(ptr); +} + +GF_Box *gnrv_New() +{ + GF_GenericVisualSampleEntryBox *tmp = (GF_GenericVisualSampleEntryBox *) gf_malloc(sizeof(GF_GenericVisualSampleEntryBox)); + if (tmp == NULL) return NULL; + memset(tmp, 0, sizeof(GF_GenericVisualSampleEntryBox)); + tmp->type = GF_ISOM_BOX_TYPE_GNRV; + gf_isom_video_sample_entry_init((GF_VisualSampleEntryBox*) tmp); + return (GF_Box *)tmp; +} + + +#ifndef GPAC_DISABLE_ISOM_WRITE + +GF_Err gnrv_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_GenericVisualSampleEntryBox *ptr = (GF_GenericVisualSampleEntryBox *)s; + + //carefull we are not writing the box type but the entry type so switch for write + ptr->type = ptr->EntryType; + e = gf_isom_box_write_header(s, bs); + if (e) return e; + ptr->type = GF_ISOM_BOX_TYPE_GNRV; + + gf_isom_video_sample_entry_write((GF_VisualSampleEntryBox *)ptr, bs); + gf_bs_write_data(bs, ptr->data, ptr->data_size); + return GF_OK; +} + +GF_Err gnrv_Size(GF_Box *s) +{ + GF_Err e; + GF_GenericVisualSampleEntryBox *ptr = (GF_GenericVisualSampleEntryBox *)s; + s->type = ptr->EntryType; + e = gf_isom_box_get_size(s); + s->type = GF_ISOM_BOX_TYPE_GNRV; + if (e) return e; + gf_isom_video_sample_entry_size((GF_VisualSampleEntryBox *)s); + ptr->size += ptr->data_size; + return GF_OK; +} + +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + + + +void gnra_del(GF_Box *s) +{ + GF_GenericAudioSampleEntryBox *ptr = (GF_GenericAudioSampleEntryBox *)s; + if (ptr->data) gf_free(ptr->data); + gf_free(ptr); +} + +GF_Box *gnra_New() +{ + GF_GenericAudioSampleEntryBox *tmp = (GF_GenericAudioSampleEntryBox *) gf_malloc(sizeof(GF_GenericAudioSampleEntryBox)); + if (tmp == NULL) return NULL; + memset(tmp, 0, sizeof(GF_GenericAudioSampleEntryBox)); + tmp->type = GF_ISOM_BOX_TYPE_GNRA; + gf_isom_audio_sample_entry_init((GF_AudioSampleEntryBox*) tmp); + return (GF_Box *)tmp; +} + + +#ifndef GPAC_DISABLE_ISOM_WRITE + +GF_Err gnra_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_GenericAudioSampleEntryBox *ptr = (GF_GenericAudioSampleEntryBox *)s; + + //carefull we are not writing the box type but the entry type so switch for write + ptr->type = ptr->EntryType; + e = gf_isom_box_write_header(s, bs); + if (e) return e; + ptr->type = GF_ISOM_BOX_TYPE_GNRA; + + gf_isom_audio_sample_entry_write((GF_AudioSampleEntryBox *)ptr, bs); + gf_bs_write_data(bs, ptr->data, ptr->data_size); + return GF_OK; +} + +GF_Err gnra_Size(GF_Box *s) +{ + GF_Err e; + GF_GenericAudioSampleEntryBox *ptr = (GF_GenericAudioSampleEntryBox *)s; + s->type = ptr->EntryType; + e = gf_isom_box_get_size(s); + s->type = GF_ISOM_BOX_TYPE_GNRA; + if (e) return e; + gf_isom_audio_sample_entry_size((GF_AudioSampleEntryBox *)s); + ptr->size += ptr->data_size; + return GF_OK; +} + +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + +void hdlr_del(GF_Box *s) +{ + GF_HandlerBox *ptr = (GF_HandlerBox *)s; + if (ptr == NULL) return; + if (ptr->nameUTF8) gf_free(ptr->nameUTF8); + gf_free(ptr); +} + + +GF_Err hdlr_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_HandlerBox *ptr = (GF_HandlerBox *)s; + + e = gf_isom_full_box_read(s, bs); + if (e) return e; + ptr->reserved1 = gf_bs_read_u32(bs); + ptr->handlerType = gf_bs_read_u32(bs); + gf_bs_read_data(bs, (char*)ptr->reserved2, 12); + ptr->size -= 20; + if (ptr->size) { + ptr->nameUTF8 = (char*)gf_malloc((u32) ptr->size); + if (ptr->nameUTF8 == NULL) return GF_OUT_OF_MEM; + gf_bs_read_data(bs, ptr->nameUTF8, (u32) ptr->size); + /*safety check in case the string is not null-terminated*/ + if (ptr->nameUTF8[ptr->size-1]) { + char *str = (char*)gf_malloc((u32) ptr->size + 1); + memcpy(str, ptr->nameUTF8, (u32) ptr->size); + str[ptr->size] = 0; + gf_free(ptr->nameUTF8); + ptr->nameUTF8 = str; + } + } + return GF_OK; +} + +GF_Box *hdlr_New() +{ + GF_HandlerBox *tmp = (GF_HandlerBox *) gf_malloc(sizeof(GF_HandlerBox)); + if (tmp == NULL) return NULL; + memset(tmp, 0, sizeof(GF_HandlerBox)); + gf_isom_full_box_init((GF_Box *)tmp); + tmp->type = GF_ISOM_BOX_TYPE_HDLR; + return (GF_Box *)tmp; +} + + +#ifndef GPAC_DISABLE_ISOM_WRITE + +GF_Err hdlr_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_HandlerBox *ptr = (GF_HandlerBox *)s; + e = gf_isom_full_box_write(s, bs); + if (e) return e; + gf_bs_write_u32(bs, ptr->reserved1); + gf_bs_write_u32(bs, ptr->handlerType); + gf_bs_write_data(bs, (char*)ptr->reserved2, 12); + if (ptr->nameUTF8) gf_bs_write_data(bs, ptr->nameUTF8, strlen(ptr->nameUTF8)); + /*NULL-terminated string is written*/ + gf_bs_write_u8(bs, 0); + return GF_OK; +} + +GF_Err hdlr_Size(GF_Box *s) +{ + GF_Err e; + GF_HandlerBox *ptr = (GF_HandlerBox *)s; + e = gf_isom_full_box_get_size(s); + if (e) return e; + ptr->size += 20 + 1; + if (ptr->nameUTF8) ptr->size += strlen(ptr->nameUTF8); + return GF_OK; +} + +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + + +void hinf_del(GF_Box *s) +{ + GF_HintInfoBox *hinf = (GF_HintInfoBox *)s; + gf_isom_box_array_del(hinf->boxList); + gf_list_del(hinf->dataRates); + gf_free(hinf); +} + +GF_Box *hinf_New() +{ + GF_HintInfoBox *tmp = (GF_HintInfoBox *)gf_malloc(sizeof(GF_HintInfoBox)); + if (!tmp) return NULL; + memset(tmp, 0, sizeof(GF_HintInfoBox)); + + tmp->boxList = gf_list_new(); + if (!tmp->boxList) { + gf_free(tmp); + return NULL; + } + tmp->dataRates = gf_list_new(); + if (!tmp->dataRates) { + gf_list_del(tmp->boxList); + gf_free(tmp); + return NULL; + } + tmp->type = GF_ISOM_BOX_TYPE_HINF; + return (GF_Box *)tmp; +} + +GF_Err hinf_AddBox(GF_Box *s, GF_Box *a) +{ + GF_MAXRBox *maxR; + GF_HintInfoBox *hinf = (GF_HintInfoBox *)s; + u32 i; + switch (a->type) { + case GF_ISOM_BOX_TYPE_MAXR: + i=0; + while ((maxR = (GF_MAXRBox *)gf_list_enum(hinf->dataRates, &i))) { + if (maxR->granularity == ((GF_MAXRBox *)a)->granularity) return GF_ISOM_INVALID_FILE; + } + gf_list_add(hinf->dataRates, a); + break; + default: + break; + } + return gf_list_add(hinf->boxList, a); +} + + +GF_Err hinf_Read(GF_Box *s, GF_BitStream *bs) +{ + return gf_isom_read_box_list(s, bs, hinf_AddBox); +} + +#ifndef GPAC_DISABLE_ISOM_WRITE + +GF_Err hinf_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_HintInfoBox *ptr = (GF_HintInfoBox *)s; + if (!s) return GF_BAD_PARAM; + e = gf_isom_box_write_header(s, bs); + if (e) return e; + return gf_isom_box_array_write(s, ptr->boxList, bs); +} + +GF_Err hinf_Size(GF_Box *s) +{ + GF_Err e; + GF_HintInfoBox *ptr = (GF_HintInfoBox *)s; + e = gf_isom_box_get_size(s); + if (e) return e; + return gf_isom_box_array_size(s, ptr->boxList); +} +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + +void hmhd_del(GF_Box *s) +{ + GF_HintMediaHeaderBox *ptr = (GF_HintMediaHeaderBox *)s; + if (ptr == NULL) return; + gf_free(ptr); +} + + +GF_Err hmhd_Read(GF_Box *s,GF_BitStream *bs) +{ + GF_Err e; + GF_HintMediaHeaderBox *ptr = (GF_HintMediaHeaderBox *)s; + + e = gf_isom_full_box_read(s, bs); + if (e) return e; + ptr->maxPDUSize = gf_bs_read_u16(bs); + ptr->avgPDUSize = gf_bs_read_u16(bs); + ptr->maxBitrate = gf_bs_read_u32(bs); + ptr->avgBitrate = gf_bs_read_u32(bs); + ptr->slidingAverageBitrate = gf_bs_read_u32(bs); + return GF_OK; +} + +GF_Box *hmhd_New() +{ + GF_HintMediaHeaderBox *tmp = (GF_HintMediaHeaderBox *) gf_malloc(sizeof(GF_HintMediaHeaderBox)); + if (tmp == NULL) return NULL; + memset(tmp, 0, sizeof(GF_HintMediaHeaderBox)); + + gf_isom_full_box_init((GF_Box *)tmp); + tmp->type = GF_ISOM_BOX_TYPE_HMHD; + + return (GF_Box *)tmp; +} + + +#ifndef GPAC_DISABLE_ISOM_WRITE + +GF_Err hmhd_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_HintMediaHeaderBox *ptr = (GF_HintMediaHeaderBox *)s; + + e = gf_isom_full_box_write(s, bs); + if (e) return e; + gf_bs_write_u16(bs, ptr->maxPDUSize); + gf_bs_write_u16(bs, ptr->avgPDUSize); + gf_bs_write_u32(bs, ptr->maxBitrate); + gf_bs_write_u32(bs, ptr->avgBitrate); + gf_bs_write_u32(bs, ptr->slidingAverageBitrate); + return GF_OK; +} + +GF_Err hmhd_Size(GF_Box *s) +{ + GF_Err e; + GF_HintMediaHeaderBox *ptr = (GF_HintMediaHeaderBox *)s; + e = gf_isom_full_box_get_size(s); + if (e) return e; + ptr->size += 16; + return GF_OK; +} + +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + +GF_Box *hnti_New() +{ + GF_HintTrackInfoBox *tmp = (GF_HintTrackInfoBox *)gf_malloc(sizeof(GF_HintTrackInfoBox)); + if (!tmp) return NULL; + memset(tmp, 0, sizeof(GF_HintTrackInfoBox)); + tmp->boxList = gf_list_new(); + if (!tmp->boxList) { + gf_free(tmp); + return NULL; + } + tmp->type = GF_ISOM_BOX_TYPE_HNTI; + return (GF_Box *)tmp; +} + +void hnti_del(GF_Box *a) +{ + GF_Box *t; + GF_RTPBox *rtp; + GF_HintTrackInfoBox *ptr = (GF_HintTrackInfoBox *)a; + while (gf_list_count(ptr->boxList)) { + t = (GF_Box*)gf_list_get(ptr->boxList, 0); + if (t->type != GF_ISOM_BOX_TYPE_RTP) { + gf_isom_box_del(t); + } else { + rtp = (GF_RTPBox *)t; + if (rtp->sdpText) gf_free(rtp->sdpText); + gf_free(rtp); + } + gf_list_rem(ptr->boxList, 0); + } + gf_list_del(ptr->boxList); + gf_free(ptr); +} + +GF_Err hnti_AddBox(GF_HintTrackInfoBox *hnti, GF_Box *a) +{ + if (!hnti || !a) return GF_BAD_PARAM; + + switch (a->type) { + //this is the value for GF_RTPBox - same as HintSampleEntry for RTP !!! + case GF_ISOM_BOX_TYPE_RTP: + case GF_ISOM_BOX_TYPE_SDP: + if (hnti->SDP) return GF_BAD_PARAM; + hnti->SDP = a; + break; + default: + break; + } + return gf_list_add(hnti->boxList, a); +} + +GF_Err hnti_Read(GF_Box *s, GF_BitStream *bs) +{ + u32 type; + u32 length; + GF_Err e; + GF_Box *a; + GF_RTPBox *rtp; + + GF_HintTrackInfoBox *ptr = (GF_HintTrackInfoBox *)s; + if (ptr == NULL) return GF_BAD_PARAM; + + //WARNING: because of the HNTI at movie level, we cannot use the generic parsing scheme! + //this because the child SDP box at the movie level has a type of RTP, used for + //the HintSampleEntry ! + while (ptr->size) { + //get the type of the box (4 bytes after our current position in the bitstream) + //before parsing... + type = gf_bs_peek_bits(bs, 32, 4); + if (type != GF_ISOM_BOX_TYPE_RTP) { + e = gf_isom_parse_box(&a, bs); + if (e) return e; + e = hnti_AddBox(ptr, a); + if (e) return e; + if (ptr->sizesize) return GF_ISOM_INVALID_FILE; + ptr->size-=a->size; + } else { + u32 sr; + rtp = (GF_RTPBox*)gf_malloc(sizeof(GF_RTPBox)); + if (!rtp) return GF_OUT_OF_MEM; + rtp->size = gf_bs_read_u32(bs); + rtp->type = gf_bs_read_u32(bs); + sr = 8; + //"ITS LENGTH IS CALCULATED BY SUBSTRACTING 8 (or 12) from the box size" - QT specs + //this means that we don't have any NULL char as a delimiter in QT ... + if (rtp->size == 1) return GF_BAD_PARAM; + rtp->subType = gf_bs_read_u32(bs); + sr += 4; + if (rtp->subType != GF_ISOM_BOX_TYPE_SDP) return GF_NOT_SUPPORTED; + if (rtp->size < sr) return GF_ISOM_INVALID_FILE; + length = (u32) (rtp->size - sr); + rtp->sdpText = (char*)gf_malloc(sizeof(char) * (length + 1)); + if (!rtp->sdpText) { + gf_free(rtp); + return GF_OUT_OF_MEM; + } + gf_bs_read_data(bs, rtp->sdpText, length); + rtp->sdpText[length] = 0; + sr += length; + e = hnti_AddBox(ptr, (GF_Box *)rtp); + if (e) return e; + if (ptr->sizesize) return GF_ISOM_INVALID_FILE; + ptr->size -= rtp->size; + } + } + return GF_OK; +} + +#ifndef GPAC_DISABLE_ISOM_WRITE +GF_Err hnti_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + u32 i, count; + GF_Box *a; + GF_RTPBox *rtp; + + GF_HintTrackInfoBox *ptr = (GF_HintTrackInfoBox *)s; + if (ptr == NULL) return GF_BAD_PARAM; + + e = gf_isom_box_write_header(s, bs); + if (e) return e; + + count = gf_list_count(ptr->boxList); + for (i = 0; i < count; i ++) { + a = (GF_Box*)gf_list_get(ptr->boxList, i); + if (a->type != GF_ISOM_BOX_TYPE_RTP) { + e = gf_isom_box_write(a, bs); + if (e) return e; + } else { + //write the GF_RTPBox by hand + rtp = (GF_RTPBox *)a; + e = gf_isom_box_write_header(a, bs); + if (e) return e; + gf_bs_write_u32(bs, rtp->subType); + //don't write the NULL char + gf_bs_write_data(bs, rtp->sdpText, strlen(rtp->sdpText)); + } + } + return GF_OK; +} + + +GF_Err hnti_Size(GF_Box *s) +{ + GF_Err e; + u32 i, count; + GF_Box *a; + GF_RTPBox *rtp; + + GF_HintTrackInfoBox *ptr = (GF_HintTrackInfoBox *)s; + if (ptr == NULL) return GF_BAD_PARAM; + + e = gf_isom_box_get_size(s); + if (e) return e; + + count = gf_list_count(ptr->boxList); + for (i = 0; i < count; i ++) { + a = (GF_Box*)gf_list_get(ptr->boxList, i); + if (a->type != GF_ISOM_BOX_TYPE_RTP) { + e = gf_isom_box_size(a); + if (e) return e; + } else { + //get the GF_RTPBox size by hand + rtp = (GF_RTPBox *)a; + e = gf_isom_box_get_size(a); + if (e) return e; + //don't count the NULL char... + rtp->size += 4 + strlen(rtp->sdpText); + } + ptr->size += a->size; + } + return GF_OK; +} +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + +/********************************************************** + GF_SDPBox +**********************************************************/ + +void sdp_del(GF_Box *s) +{ + GF_SDPBox *ptr = (GF_SDPBox *)s; + if (ptr->sdpText) gf_free(ptr->sdpText); + gf_free(ptr); + +} +GF_Err sdp_Read(GF_Box *s, GF_BitStream *bs) +{ + u32 length; + GF_SDPBox *ptr = (GF_SDPBox *)s; + if (ptr == NULL) return GF_BAD_PARAM; + + length = (u32) (ptr->size); + //sdp text has no delimiter !!! + ptr->sdpText = (char*)gf_malloc(sizeof(char) * (length+1)); + if (!ptr->sdpText) return GF_OUT_OF_MEM; + + gf_bs_read_data(bs, ptr->sdpText, length); + ptr->sdpText[length] = 0; + return GF_OK; +} +GF_Box *sdp_New() +{ + GF_SDPBox *tmp = (GF_SDPBox *) gf_malloc(sizeof(GF_SDPBox)); + if (!tmp) return NULL; + memset(tmp, 0, sizeof(GF_SDPBox)); + tmp->type = GF_ISOM_BOX_TYPE_SDP; + return (GF_Box *)tmp; +} +#ifndef GPAC_DISABLE_ISOM_WRITE +GF_Err sdp_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_SDPBox *ptr = (GF_SDPBox *)s; + if (ptr == NULL) return GF_BAD_PARAM; + e = gf_isom_box_write_header(s, bs); + if (e) return e; + //don't write the NULL char!!! + gf_bs_write_data(bs, ptr->sdpText, strlen(ptr->sdpText)); + return GF_OK; +} +GF_Err sdp_Size(GF_Box *s) +{ + GF_Err e; + GF_SDPBox *ptr = (GF_SDPBox *)s; + e = gf_isom_box_get_size(s); + if (e) return e; + //don't count the NULL char!!! + ptr->size += strlen(ptr->sdpText); + return GF_OK; +} + +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + + +/********************************************************** + TRPY GF_Box +**********************************************************/ + +void trpy_del(GF_Box *s) +{ + gf_free((GF_TRPYBox *)s); +} +GF_Err trpy_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_TRPYBox *ptr = (GF_TRPYBox *)s; + ptr->nbBytes = gf_bs_read_u64(bs); + return GF_OK; +} +GF_Box *trpy_New() +{ + GF_TRPYBox *tmp = (GF_TRPYBox *) gf_malloc(sizeof(GF_TRPYBox)); + if (!tmp) return NULL; + tmp->type = GF_ISOM_BOX_TYPE_TRPY; + tmp->nbBytes = 0; + return (GF_Box *)tmp; +} +#ifndef GPAC_DISABLE_ISOM_WRITE + +GF_Err trpy_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_TRPYBox *ptr = (GF_TRPYBox *)s; + if (ptr == NULL) return GF_BAD_PARAM; + + e = gf_isom_box_write_header(s, bs); + if (e) return e; + gf_bs_write_u64(bs, ptr->nbBytes); + return GF_OK; +} +GF_Err trpy_Size(GF_Box *s) +{ + GF_Err e; + e = gf_isom_box_get_size(s); + if (e) return e; + s->size += 8; + return GF_OK; +} +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + +/********************************************************** + TOTL GF_Box +**********************************************************/ + +void totl_del(GF_Box *s) +{ + gf_free((GF_TRPYBox *)s); +} +GF_Err totl_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_TOTLBox *ptr = (GF_TOTLBox *)s; + ptr->nbBytes = gf_bs_read_u32(bs); + return GF_OK; +} +GF_Box *totl_New() +{ + GF_TOTLBox *tmp = (GF_TOTLBox *) gf_malloc(sizeof(GF_TOTLBox)); + if (!tmp) return NULL; + tmp->type = GF_ISOM_BOX_TYPE_TOTL; + tmp->nbBytes = 0; + return (GF_Box *)tmp; +} + +#ifndef GPAC_DISABLE_ISOM_WRITE + +GF_Err totl_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_TOTLBox *ptr = (GF_TOTLBox *)s; + if (ptr == NULL) return GF_BAD_PARAM; + e = gf_isom_box_write_header(s, bs); + if (e) return e; + gf_bs_write_u32(bs, ptr->nbBytes); + return GF_OK; +} +GF_Err totl_Size(GF_Box *s) +{ + GF_Err e; + e = gf_isom_box_get_size(s); + if (e) return e; + s->size += 4; + return GF_OK; +} +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + + +/********************************************************** + NUMP GF_Box +**********************************************************/ + +void nump_del(GF_Box *s) +{ + gf_free((GF_NUMPBox *)s); +} +GF_Err nump_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_NUMPBox *ptr = (GF_NUMPBox *)s; + ptr->nbPackets = gf_bs_read_u64(bs); + return GF_OK; +} +GF_Box *nump_New() +{ + GF_NUMPBox *tmp = (GF_NUMPBox *) gf_malloc(sizeof(GF_NUMPBox)); + if (!tmp) return NULL; + tmp->type = GF_ISOM_BOX_TYPE_NUMP; + tmp->nbPackets = 0; + return (GF_Box *)tmp; +} + +#ifndef GPAC_DISABLE_ISOM_WRITE +GF_Err nump_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_NUMPBox *ptr = (GF_NUMPBox *)s; + if (ptr == NULL) return GF_BAD_PARAM; + e = gf_isom_box_write_header(s, bs); + if (e) return e; + gf_bs_write_u64(bs, ptr->nbPackets); + return GF_OK; +} +GF_Err nump_Size(GF_Box *s) +{ + GF_Err e; + e = gf_isom_box_get_size(s); + if (e) return e; + s->size += 8; + return GF_OK; +} +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + + +/********************************************************** + NPCK GF_Box +**********************************************************/ + +void npck_del(GF_Box *s) +{ + gf_free((GF_NPCKBox *)s); +} +GF_Err npck_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_NPCKBox *ptr = (GF_NPCKBox *)s; + ptr->nbPackets = gf_bs_read_u32(bs); + return GF_OK; +} +GF_Box *npck_New() +{ + GF_NPCKBox *tmp = (GF_NPCKBox *) gf_malloc(sizeof(GF_NPCKBox)); + if (!tmp) return NULL; + tmp->type = GF_ISOM_BOX_TYPE_NPCK; + tmp->nbPackets = 0; + return (GF_Box *)tmp; +} +#ifndef GPAC_DISABLE_ISOM_WRITE +GF_Err npck_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_NPCKBox *ptr = (GF_NPCKBox *)s; + if (ptr == NULL) return GF_BAD_PARAM; + e = gf_isom_box_write_header(s, bs); + if (e) return e; + gf_bs_write_u32(bs, ptr->nbPackets); + return GF_OK; +} +GF_Err npck_Size(GF_Box *s) +{ + GF_Err e; + e = gf_isom_box_get_size(s); + if (e) return e; + s->size += 4; + return GF_OK; +} +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + + +/********************************************************** + TPYL GF_Box +**********************************************************/ + +void tpyl_del(GF_Box *s) +{ + gf_free((GF_NTYLBox *)s); +} +GF_Err tpyl_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_NTYLBox *ptr = (GF_NTYLBox *)s; + if (ptr == NULL) return GF_BAD_PARAM; + ptr->nbBytes = gf_bs_read_u64(bs); + return GF_OK; +} +GF_Box *tpyl_New() +{ + GF_NTYLBox *tmp = (GF_NTYLBox *) gf_malloc(sizeof(GF_NTYLBox)); + if (!tmp) return NULL; + tmp->type = GF_ISOM_BOX_TYPE_TPYL; + tmp->nbBytes = 0; + return (GF_Box *)tmp; +} +#ifndef GPAC_DISABLE_ISOM_WRITE +GF_Err tpyl_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_NTYLBox *ptr = (GF_NTYLBox *)s; + if (ptr == NULL) return GF_BAD_PARAM; + e = gf_isom_box_write_header(s, bs); + if (e) return e; + gf_bs_write_u64(bs, ptr->nbBytes); + return GF_OK; +} +GF_Err tpyl_Size(GF_Box *s) +{ + GF_Err e; + e = gf_isom_box_get_size(s); + if (e) return e; + s->size += 8; + return GF_OK; +} +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + +/********************************************************** + TPAY GF_Box +**********************************************************/ + +void tpay_del(GF_Box *s) +{ + gf_free((GF_TPAYBox *)s); +} +GF_Err tpay_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_TPAYBox *ptr = (GF_TPAYBox *)s; + ptr->nbBytes = gf_bs_read_u32(bs); + return GF_OK; +} +GF_Box *tpay_New() +{ + GF_TPAYBox *tmp = (GF_TPAYBox *) gf_malloc(sizeof(GF_TPAYBox)); + if (!tmp) return NULL; + tmp->type = GF_ISOM_BOX_TYPE_TPAY; + tmp->nbBytes = 0; + return (GF_Box *)tmp; +} +#ifndef GPAC_DISABLE_ISOM_WRITE +GF_Err tpay_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_TPAYBox *ptr = (GF_TPAYBox *)s; + if (ptr == NULL) return GF_BAD_PARAM; + e = gf_isom_box_write_header(s, bs); + if (e) return e; + gf_bs_write_u32(bs, ptr->nbBytes); + return GF_OK; +} +GF_Err tpay_Size(GF_Box *s) +{ + GF_Err e; + e = gf_isom_box_get_size(s); + if (e) return e; + s->size += 4; + return GF_OK; +} +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + + +/********************************************************** + MAXR GF_Box +**********************************************************/ + +void maxr_del(GF_Box *s) +{ + gf_free((GF_MAXRBox *)s); +} +GF_Err maxr_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_MAXRBox *ptr = (GF_MAXRBox *)s; + if (ptr == NULL) return GF_BAD_PARAM; + ptr->granularity = gf_bs_read_u32(bs); + ptr->maxDataRate = gf_bs_read_u32(bs); + return GF_OK; +} +GF_Box *maxr_New() +{ + GF_MAXRBox *tmp = (GF_MAXRBox *) gf_malloc(sizeof(GF_MAXRBox)); + if (!tmp) return NULL; + tmp->type = GF_ISOM_BOX_TYPE_MAXR; + tmp->granularity = tmp->maxDataRate = 0; + return (GF_Box *)tmp; +} +#ifndef GPAC_DISABLE_ISOM_WRITE +GF_Err maxr_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_MAXRBox *ptr = (GF_MAXRBox *)s; + if (ptr == NULL) return GF_BAD_PARAM; + e = gf_isom_box_write_header(s, bs); + if (e) return e; + gf_bs_write_u32(bs, ptr->granularity); + gf_bs_write_u32(bs, ptr->maxDataRate); + return GF_OK; +} +GF_Err maxr_Size(GF_Box *s) +{ + GF_Err e; + e = gf_isom_box_get_size(s); + if (e) return e; + s->size += 8; + return GF_OK; +} +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + + +/********************************************************** + DMED GF_Box +**********************************************************/ + +void dmed_del(GF_Box *s) +{ + gf_free((GF_DMEDBox *)s); +} +GF_Err dmed_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_DMEDBox *ptr = (GF_DMEDBox *)s; + ptr->nbBytes = gf_bs_read_u64(bs); + return GF_OK; +} +GF_Box *dmed_New() +{ + GF_DMEDBox *tmp = (GF_DMEDBox *) gf_malloc(sizeof(GF_DMEDBox)); + if (!tmp) return NULL; + tmp->type = GF_ISOM_BOX_TYPE_DMED; + tmp->nbBytes = 0; + return (GF_Box *)tmp; +} +#ifndef GPAC_DISABLE_ISOM_WRITE +GF_Err dmed_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_DMEDBox *ptr = (GF_DMEDBox *)s; + if (ptr == NULL) return GF_BAD_PARAM; + e = gf_isom_box_write_header(s, bs); + if (e) return e; + gf_bs_write_u64(bs, ptr->nbBytes); + return GF_OK; +} +GF_Err dmed_Size(GF_Box *s) +{ + GF_Err e; + e = gf_isom_box_get_size(s); + if (e) return e; + s->size += 8; + return GF_OK; +} +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + +/********************************************************** + DIMM GF_Box +**********************************************************/ + +void dimm_del(GF_Box *s) +{ + gf_free((GF_DIMMBox *)s); +} +GF_Err dimm_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_DIMMBox *ptr = (GF_DIMMBox *)s; + ptr->nbBytes = gf_bs_read_u64(bs); + return GF_OK; +} +GF_Box *dimm_New() +{ + GF_DIMMBox *tmp = (GF_DIMMBox *) gf_malloc(sizeof(GF_DIMMBox)); + if (!tmp) return NULL; + tmp->type = GF_ISOM_BOX_TYPE_DIMM; + tmp->nbBytes = 0; + return (GF_Box *)tmp; +} +#ifndef GPAC_DISABLE_ISOM_WRITE +GF_Err dimm_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_DIMMBox *ptr = (GF_DIMMBox *)s; + if (ptr == NULL) return GF_BAD_PARAM; + e = gf_isom_box_write_header(s, bs); + if (e) return e; + gf_bs_write_u64(bs, ptr->nbBytes); + return GF_OK; +} +GF_Err dimm_Size(GF_Box *s) +{ + GF_Err e; + e = gf_isom_box_get_size(s); + if (e) return e; + s->size += 8; + return GF_OK; +} +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + +/********************************************************** + DREP GF_Box +**********************************************************/ + +void drep_del(GF_Box *s) +{ + gf_free((GF_DREPBox *)s); +} +GF_Err drep_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_DREPBox *ptr = (GF_DREPBox *)s; + ptr->nbBytes = gf_bs_read_u64(bs); + return GF_OK; +} +GF_Box *drep_New() +{ + GF_DREPBox *tmp = (GF_DREPBox *) gf_malloc(sizeof(GF_DREPBox)); + if (!tmp) return NULL; + tmp->type = GF_ISOM_BOX_TYPE_DREP; + tmp->nbBytes = 0; + return (GF_Box *)tmp; +} +#ifndef GPAC_DISABLE_ISOM_WRITE +GF_Err drep_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_DREPBox *ptr = (GF_DREPBox *)s; + if (ptr == NULL) return GF_BAD_PARAM; + e = gf_isom_box_write_header(s, bs); + if (e) return e; + gf_bs_write_u64(bs, ptr->nbBytes); + return GF_OK; +} +GF_Err drep_Size(GF_Box *s) +{ + GF_Err e; + e = gf_isom_box_get_size(s); + if (e) return e; + s->size += 8; + return GF_OK; +} +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + + + +/********************************************************** + TMIN GF_Box +**********************************************************/ + +void tmin_del(GF_Box *s) +{ + gf_free((GF_TMINBox *)s); +} +GF_Err tmin_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_TMINBox *ptr = (GF_TMINBox *)s; + ptr->minTime = gf_bs_read_u32(bs); + return GF_OK; +} +GF_Box *tmin_New() +{ + GF_TMINBox *tmp = (GF_TMINBox *) gf_malloc(sizeof(GF_TMINBox)); + if (!tmp) return NULL; + tmp->type = GF_ISOM_BOX_TYPE_TMIN; + tmp->minTime = 0; + return (GF_Box *)tmp; +} +#ifndef GPAC_DISABLE_ISOM_WRITE +GF_Err tmin_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_TMINBox *ptr = (GF_TMINBox *)s; + if (ptr == NULL) return GF_BAD_PARAM; + e = gf_isom_box_write_header(s, bs); + if (e) return e; + gf_bs_write_u32(bs, ptr->minTime); + return GF_OK; +} +GF_Err tmin_Size(GF_Box *s) +{ + GF_Err e; + e = gf_isom_box_get_size(s); + if (e) return e; + s->size += 4; + return GF_OK; +} +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + + +/********************************************************** + TMAX GF_Box +**********************************************************/ + +void tmax_del(GF_Box *s) +{ + gf_free((GF_TMAXBox *)s); +} +GF_Err tmax_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_TMAXBox *ptr = (GF_TMAXBox *)s; + ptr->maxTime = gf_bs_read_u32(bs); + return GF_OK; +} +GF_Box *tmax_New() +{ + GF_TMAXBox *tmp = (GF_TMAXBox *) gf_malloc(sizeof(GF_TMAXBox)); + if (!tmp) return NULL; + tmp->type = GF_ISOM_BOX_TYPE_TMAX; + tmp->maxTime = 0; + return (GF_Box *)tmp; +} +#ifndef GPAC_DISABLE_ISOM_WRITE +GF_Err tmax_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_TMAXBox *ptr = (GF_TMAXBox *)s; + if (ptr == NULL) return GF_BAD_PARAM; + e = gf_isom_box_write_header(s, bs); + if (e) return e; + gf_bs_write_u32(bs, ptr->maxTime); + return GF_OK; +} +GF_Err tmax_Size(GF_Box *s) +{ + GF_Err e; + e = gf_isom_box_get_size(s); + if (e) return e; + s->size += 4; + return GF_OK; +} +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + + +/********************************************************** + PMAX GF_Box +**********************************************************/ + +void pmax_del(GF_Box *s) +{ + gf_free((GF_PMAXBox *)s); +} +GF_Err pmax_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_PMAXBox *ptr = (GF_PMAXBox *)s; + ptr->maxSize = gf_bs_read_u32(bs); + return GF_OK; +} +GF_Box *pmax_New() +{ + GF_PMAXBox *tmp = (GF_PMAXBox *) gf_malloc(sizeof(GF_PMAXBox)); + if (!tmp) return NULL; + tmp->type = GF_ISOM_BOX_TYPE_PMAX; + tmp->maxSize = 0; + return (GF_Box *)tmp; +} +#ifndef GPAC_DISABLE_ISOM_WRITE +GF_Err pmax_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_PMAXBox *ptr = (GF_PMAXBox *)s; + if (ptr == NULL) return GF_BAD_PARAM; + e = gf_isom_box_write_header(s, bs); + if (e) return e; + gf_bs_write_u32(bs, ptr->maxSize); + return GF_OK; +} +GF_Err pmax_Size(GF_Box *s) +{ + GF_Err e; + e = gf_isom_box_get_size(s); + if (e) return e; + s->size += 4; + return GF_OK; +} +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + + +/********************************************************** + DMAX GF_Box +**********************************************************/ + +void dmax_del(GF_Box *s) +{ + gf_free((GF_DMAXBox *)s); +} +GF_Err dmax_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_DMAXBox *ptr = (GF_DMAXBox *)s; + ptr->maxDur = gf_bs_read_u32(bs); + return GF_OK; +} +GF_Box *dmax_New() +{ + GF_DMAXBox *tmp = (GF_DMAXBox *) gf_malloc(sizeof(GF_DMAXBox)); + if (!tmp) return NULL; + tmp->type = GF_ISOM_BOX_TYPE_DMAX; + tmp->maxDur = 0; + return (GF_Box *)tmp; +} +#ifndef GPAC_DISABLE_ISOM_WRITE +GF_Err dmax_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_DMAXBox *ptr = (GF_DMAXBox *)s; + if (ptr == NULL) return GF_BAD_PARAM; + e = gf_isom_box_write_header(s, bs); + if (e) return e; + gf_bs_write_u32(bs, ptr->maxDur); + return GF_OK; +} +GF_Err dmax_Size(GF_Box *s) +{ + GF_Err e; + e = gf_isom_box_get_size(s); + if (e) return e; + s->size += 4; + return GF_OK; +} +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + + +/********************************************************** + PAYT GF_Box +**********************************************************/ + +void payt_del(GF_Box *s) +{ + GF_PAYTBox *payt = (GF_PAYTBox *)s; + if (payt->payloadString) gf_free(payt->payloadString); + gf_free(payt); +} +GF_Err payt_Read(GF_Box *s, GF_BitStream *bs) +{ + u32 length; + GF_PAYTBox *ptr = (GF_PAYTBox *)s; + + ptr->payloadCode = gf_bs_read_u32(bs); + length = gf_bs_read_u8(bs); + ptr->payloadString = (char*)gf_malloc(sizeof(char) * (length+1) ); + if (! ptr->payloadString) return GF_OUT_OF_MEM; + gf_bs_read_data(bs, ptr->payloadString, length); + ptr->payloadString[length] = 0; + ptr->size -= 4+length+1; + return GF_OK; +} +GF_Box *payt_New() +{ + GF_PAYTBox *tmp = (GF_PAYTBox *) gf_malloc(sizeof(GF_PAYTBox)); + if (!tmp) return NULL; + tmp->type = GF_ISOM_BOX_TYPE_PAYT; + tmp->payloadCode = 0; + tmp->payloadString = NULL; + return (GF_Box *)tmp; +} +#ifndef GPAC_DISABLE_ISOM_WRITE +GF_Err payt_Write(GF_Box *s, GF_BitStream *bs) +{ + u32 len; + GF_Err e; + GF_PAYTBox *ptr = (GF_PAYTBox *)s; + if (ptr == NULL) return GF_BAD_PARAM; + e = gf_isom_box_write_header(s, bs); + if (e) return e; + gf_bs_write_u32(bs, ptr->payloadCode); + len = strlen(ptr->payloadString); + gf_bs_write_u8(bs, len); + if (len) gf_bs_write_data(bs, ptr->payloadString, len); + return GF_OK; +} +GF_Err payt_Size(GF_Box *s) +{ + GF_Err e; + GF_PAYTBox *ptr = (GF_PAYTBox *)s; + e = gf_isom_box_get_size(s); + if (e) return e; + s->size += 4; + if (ptr->payloadString) ptr->size += strlen(ptr->payloadString) + 1; + return GF_OK; +} +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + + +/********************************************************** + PAYT GF_Box +**********************************************************/ + +void name_del(GF_Box *s) +{ + GF_NameBox *name = (GF_NameBox *)s; + if (name->string) gf_free(name->string); + gf_free(name); +} +GF_Err name_Read(GF_Box *s, GF_BitStream *bs) +{ + u32 length; + GF_NameBox *ptr = (GF_NameBox *)s; + + length = (u32) (ptr->size); + ptr->string = (char*)gf_malloc(sizeof(char) * length); + if (! ptr->string) return GF_OUT_OF_MEM; + + gf_bs_read_data(bs, ptr->string, length); + return GF_OK; +} +GF_Box *name_New() +{ + GF_NameBox *tmp = (GF_NameBox *) gf_malloc(sizeof(GF_NameBox)); + if (!tmp) return NULL; + tmp->type = GF_ISOM_BOX_TYPE_NAME; + tmp->string = NULL; + return (GF_Box *)tmp; +} +#ifndef GPAC_DISABLE_ISOM_WRITE +GF_Err name_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_NameBox *ptr = (GF_NameBox *)s; + if (ptr == NULL) return GF_BAD_PARAM; + e = gf_isom_box_write_header(s, bs); + if (e) return e; + if (ptr->string) { + gf_bs_write_data(bs, ptr->string, strlen(ptr->string) + 1); + } + return GF_OK; +} +GF_Err name_Size(GF_Box *s) +{ + GF_Err e; + GF_NameBox *ptr = (GF_NameBox *)s; + e = gf_isom_box_get_size(s); + if (e) return e; + if (ptr->string) ptr->size += strlen(ptr->string) + 1; + return GF_OK; +} +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + +void iods_del(GF_Box *s) +{ + GF_ObjectDescriptorBox *ptr = (GF_ObjectDescriptorBox *)s; + if (ptr == NULL) return; + if (ptr->descriptor) gf_odf_desc_del(ptr->descriptor); + gf_free(ptr); +} + + +GF_Err iods_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + u32 descSize; + char *desc; + GF_ObjectDescriptorBox *ptr = (GF_ObjectDescriptorBox *)s; + + e = gf_isom_full_box_read(s, bs); + if (e) return e; + //use the OD codec... + descSize = (u32) (ptr->size); + desc = (char*)gf_malloc(sizeof(char) * descSize); + gf_bs_read_data(bs, desc, descSize); + e = gf_odf_desc_read(desc, descSize, &ptr->descriptor); + //OK, free our desc + gf_free(desc); + return GF_OK; +} + +GF_Box *iods_New() +{ + GF_ObjectDescriptorBox *tmp = (GF_ObjectDescriptorBox *) gf_malloc(sizeof(GF_ObjectDescriptorBox)); + if (tmp == NULL) return NULL; + memset(tmp, 0, sizeof(GF_ObjectDescriptorBox)); + gf_isom_full_box_init((GF_Box *)tmp); + tmp->type = GF_ISOM_BOX_TYPE_IODS; + return (GF_Box *)tmp; +} + + + +#ifndef GPAC_DISABLE_ISOM_WRITE + +GF_Err iods_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + u32 descSize; + char *desc; + GF_ObjectDescriptorBox *ptr = (GF_ObjectDescriptorBox *)s; + e = gf_isom_full_box_write(s, bs); + if (e) return e; + //call our OD codec + e = gf_odf_desc_write(ptr->descriptor, &desc, &descSize); + if (e) return e; + gf_bs_write_data(bs, desc, descSize); + //and free our stuff maybe!! + gf_free(desc); + return GF_OK; +} + +GF_Err iods_Size(GF_Box *s) +{ + GF_Err e; + GF_ObjectDescriptorBox *ptr = (GF_ObjectDescriptorBox *)s; + e = gf_isom_full_box_get_size(s); + if (e) return e; + ptr->size += gf_odf_desc_size(ptr->descriptor); + return GF_OK; +} + +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + +void mdat_del(GF_Box *s) +{ + GF_MediaDataBox *ptr = (GF_MediaDataBox *)s; + if (!s) return; + + if (ptr->data) gf_free(ptr->data); + gf_free(ptr); +} + + +GF_Err mdat_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_MediaDataBox *ptr = (GF_MediaDataBox *)s; + if (ptr == NULL) return GF_BAD_PARAM; + + ptr->dataSize = s->size; + //then skip these bytes + gf_bs_skip_bytes(bs, ptr->dataSize); + return GF_OK; +} + +GF_Box *mdat_New() +{ + GF_MediaDataBox *tmp = (GF_MediaDataBox *) gf_malloc(sizeof(GF_MediaDataBox)); + if (tmp == NULL) return NULL; + memset(tmp, 0, sizeof(GF_MediaDataBox)); + tmp->type = GF_ISOM_BOX_TYPE_MDAT; + return (GF_Box *)tmp; +} + +#ifndef GPAC_DISABLE_ISOM_WRITE + +GF_Err mdat_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_MediaDataBox *ptr = (GF_MediaDataBox *)s; + e = gf_isom_box_write_header(s, bs); + if (e) return e; + + //make sure we have some data ... + //if not, we handle that independantly (edit files) + if (ptr->data) { + gf_bs_write_data(bs, ptr->data, (u32) ptr->dataSize); + } + return GF_OK; +} + +GF_Err mdat_Size(GF_Box *s) +{ + GF_Err e; + GF_MediaDataBox *ptr = (GF_MediaDataBox *)s; + e = gf_isom_box_get_size(s); + if (e) return e; + + ptr->size += ptr->dataSize; + return GF_OK; +} + +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + +void mdhd_del(GF_Box *s) +{ + GF_MediaHeaderBox *ptr = (GF_MediaHeaderBox *)s; + if (ptr == NULL) return; + gf_free(ptr); +} + +GF_Err mdhd_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_MediaHeaderBox *ptr = (GF_MediaHeaderBox *)s; + + e = gf_isom_full_box_read(s, bs); + if (e) return e; + if (ptr->version == 1) { + ptr->creationTime = gf_bs_read_u64(bs); + ptr->modificationTime = gf_bs_read_u64(bs); + ptr->timeScale = gf_bs_read_u32(bs); + ptr->duration = gf_bs_read_u64(bs); + } else { + ptr->creationTime = gf_bs_read_u32(bs); + ptr->modificationTime = gf_bs_read_u32(bs); + ptr->timeScale = gf_bs_read_u32(bs); + ptr->duration = gf_bs_read_u32(bs); + } + //our padding bit + gf_bs_read_int(bs, 1); + //the spec is unclear here, just says "the value 0 is interpreted as undetermined" + ptr->packedLanguage[0] = gf_bs_read_int(bs, 5); + ptr->packedLanguage[1] = gf_bs_read_int(bs, 5); + ptr->packedLanguage[2] = gf_bs_read_int(bs, 5); + //but before or after compaction ?? We assume before + if (ptr->packedLanguage[0] || ptr->packedLanguage[1] || ptr->packedLanguage[2]) { + ptr->packedLanguage[0] += 0x60; + ptr->packedLanguage[1] += 0x60; + ptr->packedLanguage[2] += 0x60; + } else { + ptr->packedLanguage[0] = 'u'; + ptr->packedLanguage[1] = 'n'; + ptr->packedLanguage[2] = 'd'; + } + ptr->reserved = gf_bs_read_u16(bs); + return GF_OK; +} + +GF_Box *mdhd_New() +{ + GF_MediaHeaderBox *tmp = (GF_MediaHeaderBox *) gf_malloc(sizeof(GF_MediaHeaderBox)); + if (tmp == NULL) return NULL; + memset(tmp, 0, sizeof(GF_MediaHeaderBox)); + + gf_isom_full_box_init((GF_Box *) tmp); + tmp->type = GF_ISOM_BOX_TYPE_MDHD; + + tmp->packedLanguage[0] = 'u'; + tmp->packedLanguage[1] = 'n'; + tmp->packedLanguage[2] = 'd'; + return (GF_Box *)tmp; +} + + +#ifndef GPAC_DISABLE_ISOM_WRITE + +GF_Err mdhd_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_MediaHeaderBox *ptr = (GF_MediaHeaderBox *)s; + e = gf_isom_full_box_write(s, bs); + if (e) return e; + if (ptr->version == 1) { + gf_bs_write_u64(bs, ptr->creationTime); + gf_bs_write_u64(bs, ptr->modificationTime); + gf_bs_write_u32(bs, ptr->timeScale); + gf_bs_write_u64(bs, ptr->duration); + } else { + gf_bs_write_u32(bs, (u32) ptr->creationTime); + gf_bs_write_u32(bs, (u32) ptr->modificationTime); + gf_bs_write_u32(bs, ptr->timeScale); + gf_bs_write_u32(bs, (u32) ptr->duration); + } + //SPECS: BIT(1) of padding + gf_bs_write_int(bs, 0, 1); + gf_bs_write_int(bs, ptr->packedLanguage[0] - 0x60, 5); + gf_bs_write_int(bs, ptr->packedLanguage[1] - 0x60, 5); + gf_bs_write_int(bs, ptr->packedLanguage[2] - 0x60, 5); + gf_bs_write_u16(bs, ptr->reserved); + return GF_OK; +} + +GF_Err mdhd_Size(GF_Box *s) +{ + GF_Err e; + GF_MediaHeaderBox *ptr = (GF_MediaHeaderBox *)s; + ptr->version = (ptr->duration>0xFFFFFFFF) ? 1 : 0; + e = gf_isom_full_box_get_size(s); + if (e) return e; + ptr->size += 4; + ptr->size += (ptr->version == 1) ? 28 : 16; + return GF_OK; +} + +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + + +void mdia_del(GF_Box *s) +{ + GF_MediaBox *ptr = (GF_MediaBox *)s; + if (ptr == NULL) return; + if (ptr->mediaHeader) gf_isom_box_del((GF_Box *)ptr->mediaHeader); + if (ptr->information) gf_isom_box_del((GF_Box *)ptr->information); + if (ptr->handler) gf_isom_box_del((GF_Box *)ptr->handler); + gf_free(ptr); +} + + +GF_Err mdia_AddBox(GF_Box *s, GF_Box *a) +{ + GF_MediaBox *ptr = (GF_MediaBox *)s; + switch(a->type) { + case GF_ISOM_BOX_TYPE_MDHD: + if (ptr->mediaHeader) ERROR_ON_DUPLICATED_BOX(a, ptr) + ptr->mediaHeader = (GF_MediaHeaderBox *)a; + return GF_OK; + + case GF_ISOM_BOX_TYPE_HDLR: + if (ptr->handler) ERROR_ON_DUPLICATED_BOX(a, ptr) + ptr->handler = (GF_HandlerBox *)a; + return GF_OK; + + case GF_ISOM_BOX_TYPE_MINF: + if (ptr->information) ERROR_ON_DUPLICATED_BOX(a, ptr) + ptr->information = (GF_MediaInformationBox *)a; + return GF_OK; + } + GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[iso file] Warning box %s unknown type - discarding\n", gf_4cc_to_str(a->type))); + gf_isom_box_del(a); + return GF_OK; +} + + +GF_Err mdia_Read(GF_Box *s, GF_BitStream *bs) +{ + return gf_isom_read_box_list(s, bs, mdia_AddBox); +} + +GF_Box *mdia_New() +{ + GF_MediaBox *tmp = (GF_MediaBox *) gf_malloc(sizeof(GF_MediaBox)); + if (tmp == NULL) return NULL; + memset(tmp, 0, sizeof(GF_MediaBox)); + tmp->type = GF_ISOM_BOX_TYPE_MDIA; + return (GF_Box *)tmp; +} + +#ifndef GPAC_DISABLE_ISOM_WRITE + +GF_Err mdia_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_MediaBox *ptr = (GF_MediaBox *)s; + e = gf_isom_box_write_header(s, bs); + if (e) return e; + //Header first + if (ptr->mediaHeader) { + e = gf_isom_box_write((GF_Box *) ptr->mediaHeader, bs); + if (e) return e; + } + //then handler + if (ptr->handler) { + e = gf_isom_box_write((GF_Box *) ptr->handler, bs); + if (e) return e; + } + if (ptr->information) { + e = gf_isom_box_write((GF_Box *) ptr->information, bs); + if (e) return e; + } + return GF_OK; +} + +GF_Err mdia_Size(GF_Box *s) +{ + GF_Err e; + GF_MediaBox *ptr = (GF_MediaBox *)s; + e = gf_isom_box_get_size(s); + if (e) return e; + + if (ptr->mediaHeader) { + e = gf_isom_box_size((GF_Box *) ptr->mediaHeader); + if (e) return e; + ptr->size += ptr->mediaHeader->size; + } + if (ptr->handler) { + e = gf_isom_box_size((GF_Box *) ptr->handler); + if (e) return e; + ptr->size += ptr->handler->size; + } + if (ptr->information) { + e = gf_isom_box_size((GF_Box *) ptr->information); + if (e) return e; + ptr->size += ptr->information->size; + } + return GF_OK; +} + +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + + +#ifndef GPAC_DISABLE_ISOM_FRAGMENTS + +void mfhd_del(GF_Box *s) +{ + GF_MovieFragmentHeaderBox *ptr = (GF_MovieFragmentHeaderBox *)s; + if (ptr == NULL) return; + gf_free(ptr); +} + +GF_Err mfhd_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_MovieFragmentHeaderBox *ptr = (GF_MovieFragmentHeaderBox *)s; + e = gf_isom_full_box_read(s, bs); + if (e) return e; + ptr->sequence_number = gf_bs_read_u32(bs); + return GF_OK; +} + +GF_Box *mfhd_New() +{ + GF_MovieFragmentHeaderBox *tmp = (GF_MovieFragmentHeaderBox *) gf_malloc(sizeof(GF_MovieFragmentHeaderBox)); + if (tmp == NULL) return NULL; + memset(tmp, 0, sizeof(GF_MovieFragmentHeaderBox)); + tmp->type = GF_ISOM_BOX_TYPE_MFHD; + return (GF_Box *)tmp; +} + + +#ifndef GPAC_DISABLE_ISOM_WRITE + + +GF_Err mfhd_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_MovieFragmentHeaderBox *ptr = (GF_MovieFragmentHeaderBox *) s; + if (!s) return GF_BAD_PARAM; + + e = gf_isom_full_box_write(s, bs); + if (e) return e; + gf_bs_write_u32(bs, ptr->sequence_number); + return GF_OK; +} + +GF_Err mfhd_Size(GF_Box *s) +{ + GF_Err e; + GF_MovieFragmentHeaderBox *ptr = (GF_MovieFragmentHeaderBox *)s; + e = gf_isom_full_box_get_size(s); + if (e) return e; + ptr->size += 4; + return GF_OK; +} + + + +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + +#endif /*GPAC_DISABLE_ISOM_FRAGMENTS*/ + + +void minf_del(GF_Box *s) +{ + GF_MediaInformationBox *ptr = (GF_MediaInformationBox *)s; + if (ptr == NULL) return; + + //if we have a Handler not self-contained, delete it (the self-contained belongs to the movie) + if (ptr->dataHandler) { + gf_isom_datamap_close(ptr); + } + if (ptr->InfoHeader) gf_isom_box_del((GF_Box *)ptr->InfoHeader); + if (ptr->dataInformation) gf_isom_box_del((GF_Box *)ptr->dataInformation); + if (ptr->sampleTable) gf_isom_box_del((GF_Box *)ptr->sampleTable); + gf_isom_box_array_del(ptr->boxes); + gf_free(ptr); +} + +GF_Err minf_AddBox(GF_Box *s, GF_Box *a) +{ + GF_MediaInformationBox *ptr = (GF_MediaInformationBox *)s; + switch (a->type) { + case GF_ISOM_BOX_TYPE_NMHD: + case GF_ISOM_BOX_TYPE_VMHD: + case GF_ISOM_BOX_TYPE_SMHD: + case GF_ISOM_BOX_TYPE_HMHD: + case GF_ISOM_BOX_TYPE_GMHD: + if (ptr->InfoHeader) ERROR_ON_DUPLICATED_BOX(a, ptr) + ptr->InfoHeader = a; + return GF_OK; + + case GF_ISOM_BOX_TYPE_DINF: + if (ptr->dataInformation) ERROR_ON_DUPLICATED_BOX(a, ptr) + ptr->dataInformation = (GF_DataInformationBox *)a; + return GF_OK; + + case GF_ISOM_BOX_TYPE_STBL: + if (ptr->sampleTable ) ERROR_ON_DUPLICATED_BOX(a, ptr) + ptr->sampleTable = (GF_SampleTableBox *)a; + return GF_OK; + default: + return gf_list_add(ptr->boxes, a); + } + return GF_OK; +} + + +GF_Err minf_Read(GF_Box *s, GF_BitStream *bs) +{ + return gf_isom_read_box_list(s, bs, minf_AddBox); +} + +GF_Box *minf_New() +{ + GF_MediaInformationBox *tmp = (GF_MediaInformationBox *) gf_malloc(sizeof(GF_MediaInformationBox)); + if (tmp == NULL) return NULL; + memset(tmp, 0, sizeof(GF_MediaInformationBox)); + tmp->type = GF_ISOM_BOX_TYPE_MINF; + tmp->boxes = gf_list_new(); + return (GF_Box *)tmp; +} + + +#ifndef GPAC_DISABLE_ISOM_WRITE + +GF_Err minf_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_MediaInformationBox *ptr = (GF_MediaInformationBox *)s; + if (!s) return GF_BAD_PARAM; + + e = gf_isom_box_write_header(s, bs); + if (e) return e; + + //Header first + if (ptr->InfoHeader) { + e = gf_isom_box_write((GF_Box *) ptr->InfoHeader, bs); + if (e) return e; + } + //then dataInfo + if (ptr->dataInformation) { + e = gf_isom_box_write((GF_Box *) ptr->dataInformation, bs); + if (e) return e; + } + //then sampleTable + if (ptr->sampleTable) { + e = gf_isom_box_write((GF_Box *) ptr->sampleTable, bs); + if (e) return e; + } + return gf_isom_box_array_write(s, ptr->boxes, bs); +} + +GF_Err minf_Size(GF_Box *s) +{ + GF_Err e; + GF_MediaInformationBox *ptr = (GF_MediaInformationBox *)s; + + e = gf_isom_box_get_size(s); + if (e) return e; + if (ptr->InfoHeader) { + e = gf_isom_box_size((GF_Box *) ptr->InfoHeader); + if (e) return e; + ptr->size += ptr->InfoHeader->size; + } + if (ptr->dataInformation) { + e = gf_isom_box_size((GF_Box *) ptr->dataInformation); + if (e) return e; + ptr->size += ptr->dataInformation->size; + } + if (ptr->sampleTable) { + e = gf_isom_box_size((GF_Box *) ptr->sampleTable); + if (e) return e; + ptr->size += ptr->sampleTable->size; + } + return gf_isom_box_array_size(s, ptr->boxes); +} + +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + +#ifndef GPAC_DISABLE_ISOM_FRAGMENTS + +void moof_del(GF_Box *s) +{ + GF_MovieFragmentBox *ptr = (GF_MovieFragmentBox *)s; + if (ptr == NULL) return; + + if (ptr->mfhd) gf_isom_box_del((GF_Box *) ptr->mfhd); + gf_isom_box_array_del(ptr->TrackList); + if (ptr->mdat) gf_free(ptr->mdat); + gf_free(ptr); +} + +GF_Err moof_AddBox(GF_Box *s, GF_Box *a) +{ + GF_MovieFragmentBox *ptr = (GF_MovieFragmentBox *)s; + switch (a->type) { + case GF_ISOM_BOX_TYPE_MFHD: + if (ptr->mfhd) ERROR_ON_DUPLICATED_BOX(a, ptr) + ptr->mfhd = (GF_MovieFragmentHeaderBox *) a; + return GF_OK; + case GF_ISOM_BOX_TYPE_TRAF: + return gf_list_add(ptr->TrackList, a); + default: + GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[iso file] Warning box %s unknown type - discarding\n", gf_4cc_to_str(a->type))); + gf_isom_box_del(a); + return GF_OK; + } +} + +GF_Err moof_Read(GF_Box *s, GF_BitStream *bs) +{ + return gf_isom_read_box_list(s, bs, moof_AddBox); +} + +GF_Box *moof_New() +{ + GF_MovieFragmentBox *tmp = (GF_MovieFragmentBox *) gf_malloc(sizeof(GF_MovieFragmentBox)); + if (tmp == NULL) return NULL; + memset(tmp, 0, sizeof(GF_MovieFragmentBox)); + tmp->type = GF_ISOM_BOX_TYPE_MOOF; + tmp->TrackList = gf_list_new(); + return (GF_Box *)tmp; +} + + +#ifndef GPAC_DISABLE_ISOM_WRITE + +GF_Err moof_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_MovieFragmentBox *ptr = (GF_MovieFragmentBox *) s; + if (!s) return GF_BAD_PARAM; + + e = gf_isom_box_write_header(s, bs); + if (e) return e; + //Header First + if (ptr->mfhd) { + e = gf_isom_box_write((GF_Box *) ptr->mfhd, bs); + if (e) return e; + } + //then the track list + return gf_isom_box_array_write(s, ptr->TrackList, bs); +} + +GF_Err moof_Size(GF_Box *s) +{ + GF_Err e; + GF_MovieFragmentBox *ptr = (GF_MovieFragmentBox *)s; + e = gf_isom_box_get_size(s); + if (e) return e; + + + if (ptr->mfhd) { + e = gf_isom_box_size((GF_Box *) ptr->mfhd); + if (e) return e; + ptr->size += ptr->mfhd->size; + } + return gf_isom_box_array_size(s, ptr->TrackList); +} + + + +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + +#endif /*GPAC_DISABLE_ISOM_FRAGMENTS*/ + +void moov_del(GF_Box *s) +{ + GF_MovieBox *ptr = (GF_MovieBox *)s; + if (ptr == NULL) return; + + if (ptr->mvhd) gf_isom_box_del((GF_Box *)ptr->mvhd); + if (ptr->meta) gf_isom_box_del((GF_Box *)ptr->meta); + if (ptr->iods) gf_isom_box_del((GF_Box *)ptr->iods); + if (ptr->udta) gf_isom_box_del((GF_Box *)ptr->udta); +#ifndef GPAC_DISABLE_ISOM_FRAGMENTS + if (ptr->mvex) gf_isom_box_del((GF_Box *)ptr->mvex); +#endif + + gf_isom_box_array_del(ptr->trackList); + gf_isom_box_array_del(ptr->boxes); + gf_free(ptr); +} + + +GF_Err moov_AddBox(GF_Box *s, GF_Box *a) +{ + GF_MovieBox *ptr = (GF_MovieBox *)s; + switch (a->type ) { + case GF_ISOM_BOX_TYPE_IODS: + if (ptr->iods) ERROR_ON_DUPLICATED_BOX(a, ptr) + ptr->iods = (GF_ObjectDescriptorBox *)a; + //if no IOD, delete the box + if (!ptr->iods->descriptor) { + ptr->iods = NULL; + gf_isom_box_del(a); + } + return GF_OK; + + case GF_ISOM_BOX_TYPE_MVHD: + if (ptr->mvhd) ERROR_ON_DUPLICATED_BOX(a, ptr) + ptr->mvhd = (GF_MovieHeaderBox *)a; + return GF_OK; + + case GF_ISOM_BOX_TYPE_UDTA: + if (ptr->udta) ERROR_ON_DUPLICATED_BOX(a, ptr) + ptr->udta = (GF_UserDataBox *)a; + return GF_OK; + +#ifndef GPAC_DISABLE_ISOM_FRAGMENTS + case GF_ISOM_BOX_TYPE_MVEX: + if (ptr->mvex) ERROR_ON_DUPLICATED_BOX(a, ptr) + ptr->mvex = (GF_MovieExtendsBox *)a; + ptr->mvex->mov = ptr->mov; + return GF_OK; +#endif + + case GF_ISOM_BOX_TYPE_META: + if (ptr->meta) ERROR_ON_DUPLICATED_BOX(a, ptr) + ptr->meta = (GF_MetaBox *)a; + return GF_OK; + + case GF_ISOM_BOX_TYPE_TRAK: + //set our pointer to this obj + ((GF_TrackBox *)a)->moov = ptr; + return gf_list_add(ptr->trackList, a); + default: + return gf_list_add(ptr->boxes, a); + } +} + + + +GF_Err moov_Read(GF_Box *s, GF_BitStream *bs) +{ + return gf_isom_read_box_list(s, bs, moov_AddBox); +} + +GF_Box *moov_New() +{ + GF_MovieBox *tmp = (GF_MovieBox *) gf_malloc(sizeof(GF_MovieBox)); + if (tmp == NULL) return NULL; + memset(tmp, 0, sizeof(GF_MovieBox)); + tmp->trackList = gf_list_new(); + if (!tmp->trackList) { + gf_free(tmp); + return NULL; + } + tmp->boxes = gf_list_new(); + if (!tmp->boxes) { + gf_list_del(tmp->trackList); + gf_free(tmp); + return NULL; + } + tmp->type = GF_ISOM_BOX_TYPE_MOOV; + return (GF_Box *)tmp; +} + + + +#ifndef GPAC_DISABLE_ISOM_WRITE + + +GF_Err moov_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_MovieBox *ptr = (GF_MovieBox *)s; + if (!s) return GF_BAD_PARAM; + e = gf_isom_box_write_header(s, bs); + if (e) return e; + + if (ptr->mvhd) { + e = gf_isom_box_write((GF_Box *) ptr->mvhd, bs); + if (e) return e; + } + if (ptr->iods) { + e = gf_isom_box_write((GF_Box *) ptr->iods, bs); + if (e) return e; + } + if (ptr->meta) { + e = gf_isom_box_write((GF_Box *) ptr->meta, bs); + if (e) return e; + } +#ifndef GPAC_DISABLE_ISOM_FRAGMENTS + if (ptr->mvex) { + e = gf_isom_box_write((GF_Box *) ptr->mvex, bs); + if (e) return e; + } +#endif + + e = gf_isom_box_array_write(s, ptr->trackList, bs); + if (e) return e; + + if (ptr->udta) { + e = gf_isom_box_write((GF_Box *) ptr->udta, bs); + if (e) return e; + } + return gf_isom_box_array_write(s, ptr->boxes, bs); +} + +GF_Err moov_Size(GF_Box *s) +{ + GF_Err e; + GF_MovieBox *ptr = (GF_MovieBox *)s; + e = gf_isom_box_get_size(s); + if (e) return e; + + if (ptr->mvhd) { + e = gf_isom_box_size((GF_Box *) ptr->mvhd); + if (e) return e; + ptr->size += ptr->mvhd->size; + } + if (ptr->iods) { + e = gf_isom_box_size((GF_Box *) ptr->iods); + if (e) return e; + ptr->size += ptr->iods->size; + } + if (ptr->udta) { + e = gf_isom_box_size((GF_Box *) ptr->udta); + if (e) return e; + ptr->size += ptr->udta->size; + } + if (ptr->meta) { + e = gf_isom_box_size((GF_Box *) ptr->meta); + if (e) return e; + ptr->size += ptr->meta->size; + } +#ifndef GPAC_DISABLE_ISOM_FRAGMENTS + if (ptr->mvex) { + e = gf_isom_box_size((GF_Box *) ptr->mvex); + if (e) return e; + ptr->size += ptr->mvex->size; + } +#endif + + e = gf_isom_box_array_size(s, ptr->trackList); + if (e) return e; + return gf_isom_box_array_size(s, ptr->boxes); +} + +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + +void mp4a_del(GF_Box *s) +{ + GF_MPEGAudioSampleEntryBox *ptr = (GF_MPEGAudioSampleEntryBox *)s; + if (ptr == NULL) return; + if (ptr->esd) gf_isom_box_del((GF_Box *)ptr->esd); + if (ptr->slc) gf_odf_desc_del((GF_Descriptor *)ptr->slc); + if (ptr->protection_info) gf_isom_box_del((GF_Box *)ptr->protection_info); + gf_free(ptr); +} + +GF_Err mp4a_AddBox(GF_Box *s, GF_Box *a) +{ + GF_MPEGAudioSampleEntryBox *ptr = (GF_MPEGAudioSampleEntryBox *)s; + switch (a->type) { + case GF_ISOM_BOX_TYPE_ESDS: + if (ptr->esd) ERROR_ON_DUPLICATED_BOX(a, ptr) + ptr->esd = (GF_ESDBox *)a; + break; + case GF_ISOM_BOX_TYPE_SINF: + if (ptr->protection_info) return GF_ISOM_INVALID_FILE; + ptr->protection_info = (GF_ProtectionInfoBox*)a; + break; + case GF_4CC('w','a','v','e'): + if (ptr->esd) ERROR_ON_DUPLICATED_BOX(a, ptr) + /*HACK for QT files: get the esds box from the track*/ + { + GF_UnknownBox *wave = (GF_UnknownBox *)a; + u32 offset = 0; + while ((wave->data[offset+4]!='e') && (wave->data[offset+5]!='s')) { + offset++; + if (offset == wave->dataSize) break; + } + if (offset < wave->dataSize) { + GF_Box *a; + GF_Err e; + GF_BitStream *bs = gf_bs_new(wave->data+offset, wave->dataSize-offset, GF_BITSTREAM_READ); + e = gf_isom_parse_box(&a, bs); + assert(e == GF_OK); + gf_bs_del(bs); + ptr->esd = (GF_ESDBox *)a; + } + gf_isom_box_del(a); + } + break; + default: + GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[iso file] Warning box %s unknown type - discarding\n", gf_4cc_to_str(a->type))); + gf_isom_box_del(a); + break; + } + return GF_OK; +} +GF_Err mp4a_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_MPEGAudioSampleEntryBox *ptr; + char *data; + u32 i, size; + GF_Err e; + u64 pos; + + e = gf_isom_audio_sample_entry_read((GF_AudioSampleEntryBox*)s, bs); + if (e) return e; + pos = gf_bs_get_position(bs); + size = (u32) s->size; + + e = gf_isom_read_box_list(s, bs, mp4a_AddBox); + if (!e) return GF_OK; + + /*hack for some weird files (possibly recorded with live.com tools, needs further investigations)*/ + ptr = (GF_MPEGAudioSampleEntryBox *)s; + gf_bs_seek(bs, pos); + data = (char*)gf_malloc(sizeof(char) * size); + gf_bs_read_data(bs, data, size); + for (i=0; iesd, mybs); + gf_bs_del(mybs); + break; + } + } + gf_free(data); + return e; +} + +GF_Box *mp4a_New() +{ + GF_MPEGAudioSampleEntryBox *tmp = (GF_MPEGAudioSampleEntryBox *)gf_malloc(sizeof(GF_MPEGAudioSampleEntryBox)); + if (tmp == NULL) return NULL; + memset(tmp, 0, sizeof(GF_MPEGAudioSampleEntryBox)); + tmp->type = GF_ISOM_BOX_TYPE_MP4A; + gf_isom_audio_sample_entry_init((GF_AudioSampleEntryBox*)tmp); + return (GF_Box *)tmp; +} + +GF_Box *enca_New() +{ + GF_Box *tmp = mp4a_New(); + tmp->type = GF_ISOM_BOX_TYPE_ENCA; + return tmp; +} + + +#ifndef GPAC_DISABLE_ISOM_WRITE + +GF_Err mp4a_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_MPEGAudioSampleEntryBox *ptr = (GF_MPEGAudioSampleEntryBox *)s; + e = gf_isom_box_write_header(s, bs); + if (e) return e; + gf_isom_audio_sample_entry_write((GF_AudioSampleEntryBox*)s, bs); + e = gf_isom_box_write((GF_Box *)ptr->esd, bs); + if (e) return e; + if (ptr->protection_info && (ptr->type == GF_ISOM_BOX_TYPE_ENCA)) { + e = gf_isom_box_write((GF_Box *)ptr->protection_info, bs); + if (e) return e; + } + return GF_OK; +} + +GF_Err mp4a_Size(GF_Box *s) +{ + GF_Err e; + GF_MPEGAudioSampleEntryBox *ptr = (GF_MPEGAudioSampleEntryBox *)s; + e = gf_isom_box_get_size(s); + if (e) return e; + + gf_isom_audio_sample_entry_size((GF_AudioSampleEntryBox*)s); + + e = gf_isom_box_size((GF_Box *)ptr->esd); + if (e) return e; + ptr->size += ptr->esd->size; + if (ptr->protection_info && (ptr->type == GF_ISOM_BOX_TYPE_ENCA)) { + e = gf_isom_box_size((GF_Box *)ptr->protection_info); + if (e) return e; + ptr->size += ptr->protection_info->size; + } + return GF_OK; +} + +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + + +void mp4s_del(GF_Box *s) +{ + GF_MPEGSampleEntryBox *ptr = (GF_MPEGSampleEntryBox *)s; + if (ptr == NULL) return; + if (ptr->esd) gf_isom_box_del((GF_Box *)ptr->esd); + if (ptr->slc) gf_odf_desc_del((GF_Descriptor *)ptr->slc); + if (ptr->protection_info) gf_isom_box_del((GF_Box *)ptr->protection_info); + gf_free(ptr); +} + +GF_Err mp4s_AddBox(GF_Box *s, GF_Box *a) +{ + GF_MPEGSampleEntryBox *ptr = (GF_MPEGSampleEntryBox *)s; + switch (a->type) { + case GF_ISOM_BOX_TYPE_ESDS: + if (ptr->esd) ERROR_ON_DUPLICATED_BOX(a, ptr) + ptr->esd = (GF_ESDBox *)a; + break; + case GF_ISOM_BOX_TYPE_SINF: + if (ptr->protection_info) return GF_ISOM_INVALID_FILE; + ptr->protection_info = (GF_ProtectionInfoBox*)a; + break; + default: + GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[iso file] Warning box %s unknown type - discarding\n", gf_4cc_to_str(a->type))); + gf_isom_box_del(a); + break; + } + return GF_OK; +} + +GF_Err mp4s_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_MPEGSampleEntryBox *ptr = (GF_MPEGSampleEntryBox *)s; + gf_bs_read_data(bs, ptr->reserved, 6); + ptr->dataReferenceIndex = gf_bs_read_u16(bs); + ptr->size -= 8; + return gf_isom_read_box_list(s, bs, mp4s_AddBox); +} + +GF_Box *mp4s_New() +{ + GF_MPEGSampleEntryBox *tmp = (GF_MPEGSampleEntryBox *) gf_malloc(sizeof(GF_MPEGSampleEntryBox)); + if (tmp == NULL) return NULL; + memset(tmp, 0, sizeof(GF_MPEGSampleEntryBox)); + + tmp->type = GF_ISOM_BOX_TYPE_MP4S; + return (GF_Box *)tmp; +} + +GF_Box *encs_New() +{ + GF_Box *tmp = mp4s_New(); + tmp->type = GF_ISOM_BOX_TYPE_ENCS; + return tmp; +} + + +#ifndef GPAC_DISABLE_ISOM_WRITE + +GF_Err mp4s_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_MPEGSampleEntryBox *ptr = (GF_MPEGSampleEntryBox *)s; + + e = gf_isom_box_write_header(s, bs); + if (e) return e; + gf_bs_write_data(bs, ptr->reserved, 6); + gf_bs_write_u16(bs, ptr->dataReferenceIndex); + e = gf_isom_box_write((GF_Box *)ptr->esd, bs); + if (e) return e; + if (ptr->protection_info && (ptr->type == GF_ISOM_BOX_TYPE_ENCS)) { + e = gf_isom_box_write((GF_Box *)ptr->protection_info, bs); + } + return e; +} + +GF_Err mp4s_Size(GF_Box *s) +{ + GF_Err e; + GF_MPEGSampleEntryBox *ptr = (GF_MPEGSampleEntryBox *)s; + + e = gf_isom_box_get_size(s); + if (e) return e; + ptr->size += 8; + e = gf_isom_box_size((GF_Box *)ptr->esd); + if (e) return e; + ptr->size += ptr->esd->size; + if (ptr->protection_info && (ptr->type == GF_ISOM_BOX_TYPE_ENCS)) { + e = gf_isom_box_size((GF_Box *)ptr->protection_info); + if (e) return e; + ptr->size += ptr->protection_info->size; + } + return GF_OK; +} + +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + +void mp4v_del(GF_Box *s) +{ + GF_MPEGVisualSampleEntryBox *ptr = (GF_MPEGVisualSampleEntryBox *)s; + if (ptr == NULL) return; + if (ptr->esd) gf_isom_box_del((GF_Box *)ptr->esd); + if (ptr->slc) gf_odf_desc_del((GF_Descriptor *)ptr->slc); + if (ptr->avc_config) gf_isom_box_del((GF_Box *) ptr->avc_config); + if (ptr->svc_config) gf_isom_box_del((GF_Box *) ptr->svc_config); + if (ptr->bitrate) gf_isom_box_del((GF_Box *) ptr->bitrate); + if (ptr->descr) gf_isom_box_del((GF_Box *) ptr->descr); + if (ptr->ipod_ext) gf_isom_box_del((GF_Box *)ptr->ipod_ext); + /*for publishing*/ + if (ptr->emul_esd) gf_odf_desc_del((GF_Descriptor *)ptr->emul_esd); + + if (ptr->pasp) gf_isom_box_del((GF_Box *)ptr->pasp); + if (ptr->rvcc) gf_isom_box_del((GF_Box *)ptr->rvcc); + + if (ptr->protection_info) gf_isom_box_del((GF_Box *)ptr->protection_info); + gf_free(ptr); +} + +GF_Err mp4v_AddBox(GF_Box *s, GF_Box *a) +{ + GF_MPEGVisualSampleEntryBox *ptr = (GF_MPEGVisualSampleEntryBox *)s; + switch (a->type) { + case GF_ISOM_BOX_TYPE_ESDS: + if (ptr->esd) ERROR_ON_DUPLICATED_BOX(a, ptr) + ptr->esd = (GF_ESDBox *)a; + break; + case GF_ISOM_BOX_TYPE_SINF: + if (ptr->protection_info) return GF_ISOM_INVALID_FILE; + ptr->protection_info = (GF_ProtectionInfoBox*)a; + break; + case GF_ISOM_BOX_TYPE_AVCC: + if (ptr->avc_config) ERROR_ON_DUPLICATED_BOX(a, ptr) + ptr->avc_config = (GF_AVCConfigurationBox *)a; + break; + case GF_ISOM_BOX_TYPE_SVCC: + if (ptr->svc_config) ERROR_ON_DUPLICATED_BOX(a, ptr) + ptr->svc_config = (GF_AVCConfigurationBox *)a; + break; + case GF_ISOM_BOX_TYPE_BTRT: + if (ptr->bitrate) ERROR_ON_DUPLICATED_BOX(a, ptr) + ptr->bitrate = (GF_MPEG4BitRateBox *)a; + break; + case GF_ISOM_BOX_TYPE_M4DS: + if (ptr->descr) ERROR_ON_DUPLICATED_BOX(a, ptr) + ptr->descr = (GF_MPEG4ExtensionDescriptorsBox *)a; + break; + case GF_ISOM_BOX_TYPE_UUID: + if (ptr->ipod_ext) ERROR_ON_DUPLICATED_BOX(a, ptr) + ptr->ipod_ext = (GF_UnknownUUIDBox *)a; + break; + case GF_ISOM_BOX_TYPE_PASP: + if (ptr->pasp) ERROR_ON_DUPLICATED_BOX(a, ptr) + ptr->pasp = (GF_PixelAspectRatioBox *)a; + break; + case GF_ISOM_BOX_TYPE_RVCC: + if (ptr->rvcc) ERROR_ON_DUPLICATED_BOX(a, ptr) + ptr->rvcc = (GF_RVCConfigurationBox *)a; + break; + default: + GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[iso file] Warning box %s unknown type - discarding\n", gf_4cc_to_str(a->type))); + gf_isom_box_del(a); + break; + } + return GF_OK; +} + +GF_Err mp4v_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_MPEGVisualSampleEntryBox *mp4v = (GF_MPEGVisualSampleEntryBox*)s; + GF_Err e; + e = gf_isom_video_sample_entry_read((GF_VisualSampleEntryBox *)s, bs); + if (e) return e; + e = gf_isom_read_box_list(s, bs, mp4v_AddBox); + if (e) return e; + /*this is an AVC sample desc*/ + if (mp4v->avc_config || mp4v->svc_config) AVC_RewriteESDescriptor(mp4v); + return GF_OK; +} + +static GF_Box *mp4v_encv_avc1_new(u32 type) +{ + GF_MPEGVisualSampleEntryBox *tmp; + GF_SAFEALLOC(tmp, GF_MPEGVisualSampleEntryBox); + if (tmp == NULL) return NULL; + + gf_isom_video_sample_entry_init((GF_VisualSampleEntryBox *)tmp); + tmp->type = type; + return (GF_Box *)tmp; +} +GF_Box *mp4v_New() +{ + return mp4v_encv_avc1_new(GF_ISOM_BOX_TYPE_MP4V); +} + +GF_Box *avc1_New() +{ + return mp4v_encv_avc1_new(GF_ISOM_BOX_TYPE_AVC1); +} + +GF_Box *avc2_New() +{ + return mp4v_encv_avc1_new(GF_ISOM_BOX_TYPE_AVC2); +} + +GF_Box *svc1_New() +{ + return mp4v_encv_avc1_new(GF_ISOM_BOX_TYPE_SVC1); +} + +GF_Box *encv_New() +{ + return mp4v_encv_avc1_new(GF_ISOM_BOX_TYPE_ENCV); +} + + +#ifndef GPAC_DISABLE_ISOM_WRITE + +GF_Err mp4v_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_MPEGVisualSampleEntryBox *ptr = (GF_MPEGVisualSampleEntryBox *)s; + e = gf_isom_box_write_header(s, bs); + if (e) return e; + gf_isom_video_sample_entry_write((GF_VisualSampleEntryBox *)s, bs); + + if (ptr->pasp) { + e = gf_isom_box_write((GF_Box *)ptr->pasp, bs); + if (e) return e; + } + + /*mp4v*/ + if (ptr->esd) { + e = gf_isom_box_write((GF_Box *)ptr->esd, bs); + if (e) return e; + } + /*avc1*/ + else { + if (ptr->avc_config && ptr->avc_config->config) { + e = gf_isom_box_write((GF_Box *) ptr->avc_config, bs); + if (e) return e; + } + if (ptr->ipod_ext) { + e = gf_isom_box_write((GF_Box *) ptr->ipod_ext, bs); + if (e) return e; + } + if (ptr->bitrate) { + e = gf_isom_box_write((GF_Box *) ptr->bitrate, bs); + if (e) return e; + } + if (ptr->descr) { + e = gf_isom_box_write((GF_Box *) ptr->descr, bs); + if (e) return e; + } + if (ptr->svc_config && ptr->svc_config->config) { + e = gf_isom_box_write((GF_Box *) ptr->svc_config, bs); + if (e) return e; + } + } + if (ptr->protection_info && (ptr->type == GF_ISOM_BOX_TYPE_ENCV)) { + e = gf_isom_box_write((GF_Box *)ptr->protection_info, bs); + if (e) return e; + } + if (ptr->rvcc) { + e = gf_isom_box_write((GF_Box *)ptr->rvcc, bs); + if (e) return e; + } + return e; +} + +GF_Err mp4v_Size(GF_Box *s) +{ + GF_Err e; + GF_MPEGVisualSampleEntryBox *ptr = (GF_MPEGVisualSampleEntryBox *)s; + e = gf_isom_box_get_size(s); + if (e) return e; + + gf_isom_video_sample_entry_size((GF_VisualSampleEntryBox *)s); + + if (ptr->esd) { + e = gf_isom_box_size((GF_Box *)ptr->esd); + if (e) return e; + ptr->size += ptr->esd->size; + } else { + if (!ptr->avc_config && !ptr->svc_config) + return GF_ISOM_INVALID_FILE; + + if (ptr->avc_config && ptr->avc_config->config) { + e = gf_isom_box_size((GF_Box *) ptr->avc_config); + if (e) return e; + ptr->size += ptr->avc_config->size; + } + if (ptr->svc_config && ptr->svc_config->config) { + e = gf_isom_box_size((GF_Box *) ptr->svc_config); + if (e) return e; + ptr->size += ptr->svc_config->size; + } + if (ptr->ipod_ext) { + e = gf_isom_box_size((GF_Box *) ptr->ipod_ext); + if (e) return e; + ptr->size += ptr->ipod_ext->size; + } + if (ptr->bitrate) { + e = gf_isom_box_size((GF_Box *) ptr->bitrate); + if (e) return e; + ptr->size += ptr->bitrate->size; + } + if (ptr->descr) { + e = gf_isom_box_size((GF_Box *) ptr->descr); + if (e) return e; + ptr->size += ptr->descr->size; + } + } + if (ptr->pasp) { + e = gf_isom_box_size((GF_Box *)ptr->pasp); + if (e) return e; + ptr->size += ptr->pasp->size; + } + if (ptr->rvcc) { + e = gf_isom_box_size((GF_Box *)ptr->rvcc); + if (e) return e; + ptr->size += ptr->rvcc->size; + } + if (ptr->protection_info && (ptr->type == GF_ISOM_BOX_TYPE_ENCV)) { + e = gf_isom_box_size((GF_Box *)ptr->protection_info); + if (e) return e; + ptr->size += ptr->protection_info->size; + } + return GF_OK; +} + +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + + + +#ifndef GPAC_DISABLE_ISOM_FRAGMENTS + +void mvex_del(GF_Box *s) +{ + GF_MovieExtendsBox *ptr = (GF_MovieExtendsBox *)s; + if (ptr == NULL) return; + if (ptr->mehd) gf_isom_box_del((GF_Box*)ptr->mehd); + gf_isom_box_array_del(ptr->TrackExList); + gf_free(ptr); +} + + +GF_Err mvex_AddBox(GF_Box *s, GF_Box *a) +{ + GF_MovieExtendsBox *ptr = (GF_MovieExtendsBox *)s; + + switch (a->type) { + case GF_ISOM_BOX_TYPE_TREX: + return gf_list_add(ptr->TrackExList, a); + case GF_ISOM_BOX_TYPE_MEHD: + if (ptr->mehd) break; + ptr->mehd = (GF_MovieExtendsHeaderBox*)a; + return GF_OK; + } + GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[iso file] Warning box %s unknown type - discarding\n", gf_4cc_to_str(a->type))); + gf_isom_box_del(a); + return GF_OK; +} + + + +GF_Err mvex_Read(GF_Box *s, GF_BitStream *bs) +{ + return gf_isom_read_box_list(s, bs, mvex_AddBox); +} + +GF_Box *mvex_New() +{ + GF_MovieExtendsBox *tmp = (GF_MovieExtendsBox *) gf_malloc(sizeof(GF_MovieExtendsBox)); + if (tmp == NULL) return NULL; + memset(tmp, 0, sizeof(GF_MovieExtendsBox)); + tmp->TrackExList = gf_list_new(); + if (!tmp->TrackExList) { + gf_free(tmp); + return NULL; + } + tmp->type = GF_ISOM_BOX_TYPE_MVEX; + return (GF_Box *)tmp; +} + + + +#ifndef GPAC_DISABLE_ISOM_WRITE + + +GF_Err mvex_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_MovieExtendsBox *ptr = (GF_MovieExtendsBox *) s; + if (!s) return GF_BAD_PARAM; + e = gf_isom_box_write_header(s, bs); + if (e) return e; + return gf_isom_box_array_write(s, ptr->TrackExList, bs); +} + +GF_Err mvex_Size(GF_Box *s) +{ + GF_Err e; + GF_MovieExtendsBox *ptr = (GF_MovieExtendsBox *)s; + e = gf_isom_box_get_size(s); + if (e) return e; + return gf_isom_box_array_size(s, ptr->TrackExList); +} + + + +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + +GF_Box *mehd_New() +{ + GF_MovieExtendsHeaderBox *tmp; + GF_SAFEALLOC(tmp, GF_MovieExtendsHeaderBox); + if (tmp == NULL) return NULL; + tmp->type = GF_ISOM_BOX_TYPE_MEHD; + return (GF_Box *)tmp; +} +void mehd_del(GF_Box *s) +{ + gf_free(s); +} +GF_Err mehd_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_MovieExtendsHeaderBox *ptr = (GF_MovieExtendsHeaderBox *)s; + + e = gf_isom_full_box_read(s, bs); + if (e) return e; + if (ptr->version==1) { + ptr->fragment_duration = gf_bs_read_u64(bs); + } else { + ptr->fragment_duration = (u64) gf_bs_read_u32(bs); + } + return GF_OK; +} +#ifndef GPAC_DISABLE_ISOM_WRITE +GF_Err mehd_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_MovieExtendsHeaderBox *ptr = (GF_MovieExtendsHeaderBox *)s; + GF_Err e = gf_isom_full_box_write(s, bs); + if (e) return e; + if (ptr->version == 1) { + gf_bs_write_u64(bs, ptr->fragment_duration); + } else { + gf_bs_write_u32(bs, (u32) ptr->fragment_duration); + } + return GF_OK; +} +GF_Err mehd_Size(GF_Box *s) +{ + GF_Err e = gf_isom_full_box_get_size(s); + GF_MovieExtendsHeaderBox *ptr = (GF_MovieExtendsHeaderBox *)s; + if (e) return e; + ptr->version = (ptr->fragment_duration>0xFFFFFFFF) ? 1 : 0; + s->size += (ptr->version == 1) ? 8 : 4; + return GF_OK; +} +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + +#endif /*GPAC_DISABLE_ISOM_FRAGMENTS*/ + + +void mvhd_del(GF_Box *s) +{ + GF_MovieHeaderBox *ptr = (GF_MovieHeaderBox *)s; + if (ptr == NULL) return; + gf_free(ptr); +} + + +GF_Err mvhd_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_MovieHeaderBox *ptr = (GF_MovieHeaderBox *)s; + if (ptr == NULL) return GF_BAD_PARAM; + e = gf_isom_full_box_read(s, bs); + if (e) return e; + if (ptr->version == 1) { + ptr->creationTime = gf_bs_read_u64(bs); + ptr->modificationTime = gf_bs_read_u64(bs); + ptr->timeScale = gf_bs_read_u32(bs); + ptr->duration = gf_bs_read_u64(bs); + } else { + ptr->creationTime = gf_bs_read_u32(bs); + ptr->modificationTime = gf_bs_read_u32(bs); + ptr->timeScale = gf_bs_read_u32(bs); + ptr->duration = gf_bs_read_u32(bs); + } + ptr->preferredRate = gf_bs_read_u32(bs); + ptr->preferredVolume = gf_bs_read_u16(bs); + gf_bs_read_data(bs, ptr->reserved, 10); + ptr->matrixA = gf_bs_read_u32(bs); + ptr->matrixB = gf_bs_read_u32(bs); + ptr->matrixU = gf_bs_read_u32(bs); + ptr->matrixC = gf_bs_read_u32(bs); + ptr->matrixD = gf_bs_read_u32(bs); + ptr->matrixV = gf_bs_read_u32(bs); + ptr->matrixX = gf_bs_read_u32(bs); + ptr->matrixY = gf_bs_read_u32(bs); + ptr->matrixW = gf_bs_read_u32(bs); + ptr->previewTime = gf_bs_read_u32(bs); + ptr->previewDuration = gf_bs_read_u32(bs); + ptr->posterTime = gf_bs_read_u32(bs); + ptr->selectionTime = gf_bs_read_u32(bs); + ptr->selectionDuration = gf_bs_read_u32(bs); + ptr->currentTime = gf_bs_read_u32(bs); + ptr->nextTrackID = gf_bs_read_u32(bs); + return GF_OK; +} + +GF_Box *mvhd_New() +{ + GF_MovieHeaderBox *tmp = (GF_MovieHeaderBox *) gf_malloc(sizeof(GF_MovieHeaderBox)); + if (tmp == NULL) return NULL; + memset(tmp, 0, sizeof(GF_MovieHeaderBox)); + + gf_isom_full_box_init((GF_Box *)tmp); + tmp->type = GF_ISOM_BOX_TYPE_MVHD; + tmp->preferredRate = (1<<16); + tmp->preferredVolume = (1<<8); + + tmp->matrixA = (1<<16); + tmp->matrixD = (1<<16); + tmp->matrixW = (1<<30); + + tmp->nextTrackID = 1; + + return (GF_Box *)tmp; +} + + +#ifndef GPAC_DISABLE_ISOM_WRITE + +GF_Err mvhd_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_MovieHeaderBox *ptr = (GF_MovieHeaderBox *)s; + e = gf_isom_full_box_write(s, bs); + if (e) return e; + if (ptr->version == 1) { + gf_bs_write_u64(bs, ptr->creationTime); + gf_bs_write_u64(bs, ptr->modificationTime); + gf_bs_write_u32(bs, ptr->timeScale); + gf_bs_write_u64(bs, ptr->duration); + } else { + gf_bs_write_u32(bs, (u32) ptr->creationTime); + gf_bs_write_u32(bs, (u32) ptr->modificationTime); + gf_bs_write_u32(bs, ptr->timeScale); + gf_bs_write_u32(bs, (u32) ptr->duration); + } + gf_bs_write_u32(bs, ptr->preferredRate); + gf_bs_write_u16(bs, ptr->preferredVolume); + gf_bs_write_data(bs, ptr->reserved, 10); + gf_bs_write_u32(bs, ptr->matrixA); + gf_bs_write_u32(bs, ptr->matrixB); + gf_bs_write_u32(bs, ptr->matrixU); + gf_bs_write_u32(bs, ptr->matrixC); + gf_bs_write_u32(bs, ptr->matrixD); + gf_bs_write_u32(bs, ptr->matrixV); + gf_bs_write_u32(bs, ptr->matrixX); + gf_bs_write_u32(bs, ptr->matrixY); + gf_bs_write_u32(bs, ptr->matrixW); + gf_bs_write_u32(bs, ptr->previewTime); + gf_bs_write_u32(bs, ptr->previewDuration); + gf_bs_write_u32(bs, ptr->posterTime); + gf_bs_write_u32(bs, ptr->selectionTime); + gf_bs_write_u32(bs, ptr->selectionDuration); + gf_bs_write_u32(bs, ptr->currentTime); + gf_bs_write_u32(bs, ptr->nextTrackID); + return GF_OK; +} + +GF_Err mvhd_Size(GF_Box *s) +{ + GF_Err e; + GF_MovieHeaderBox *ptr = (GF_MovieHeaderBox *)s; + ptr->version = (ptr->duration>0xFFFFFFFF) ? 1 : 0; + e = gf_isom_full_box_get_size(s); + if (e) return e; + ptr->size += (ptr->version == 1) ? 28 : 16; + ptr->size += 80; + return GF_OK; +} + +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + + +void nmhd_del(GF_Box *s) +{ + GF_MPEGMediaHeaderBox *ptr = (GF_MPEGMediaHeaderBox *)s; + if (ptr == NULL) return; + gf_free(ptr); +} + + + +GF_Err nmhd_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e = gf_isom_full_box_read(s, bs); + if (e) return e; + return GF_OK; +} + +GF_Box *nmhd_New() +{ + GF_MPEGMediaHeaderBox *tmp = (GF_MPEGMediaHeaderBox *) gf_malloc(sizeof(GF_MPEGMediaHeaderBox)); + if (tmp == NULL) return NULL; + gf_isom_full_box_init((GF_Box *) tmp); + tmp->type = GF_ISOM_BOX_TYPE_NMHD; + return (GF_Box *)tmp; +} + + +#ifndef GPAC_DISABLE_ISOM_WRITE + +GF_Err nmhd_Write(GF_Box *s, GF_BitStream *bs) +{ + return gf_isom_full_box_write(s, bs); +} + +GF_Err nmhd_Size(GF_Box *s) +{ + return gf_isom_full_box_get_size(s); +} + +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + + + +void padb_del(GF_Box *s) +{ + GF_PaddingBitsBox *ptr = (GF_PaddingBitsBox *) s; + if (ptr == NULL) return; + if (ptr->padbits) gf_free(ptr->padbits); + gf_free(ptr); +} + + +GF_Err padb_Read(GF_Box *s,GF_BitStream *bs) +{ + GF_Err e; + u32 i; + GF_PaddingBitsBox *ptr = (GF_PaddingBitsBox *)s; + + e = gf_isom_full_box_read( s, bs); + if (e) return e; + + ptr->SampleCount = gf_bs_read_u32(bs); + + ptr->padbits = (u8 *)gf_malloc(sizeof(u8)*ptr->SampleCount); + for (i=0; iSampleCount; i += 2) { + gf_bs_read_int(bs, 1); + if (i+1 < ptr->SampleCount) { + ptr->padbits[i+1] = gf_bs_read_int(bs, 3); + } else { + gf_bs_read_int(bs, 3); + } + gf_bs_read_int(bs, 1); + ptr->padbits[i] = gf_bs_read_int(bs, 3); + } + return GF_OK; +} + +GF_Box *padb_New() +{ + GF_PaddingBitsBox *tmp; + + tmp = (GF_PaddingBitsBox *) gf_malloc(sizeof(GF_PaddingBitsBox)); + if (tmp == NULL) return NULL; + memset(tmp, 0, sizeof(GF_PaddingBitsBox)); + + gf_isom_full_box_init((GF_Box *)tmp); + tmp->type = GF_ISOM_BOX_TYPE_FADB; + return (GF_Box *)tmp; +} + + + +#ifndef GPAC_DISABLE_ISOM_WRITE + +GF_Err padb_Write(GF_Box *s, GF_BitStream *bs) +{ + u32 i; + GF_Err e; + GF_PaddingBitsBox *ptr = (GF_PaddingBitsBox *) s; + + e = gf_isom_full_box_write(s, bs); + if (e) return e; + gf_bs_write_int(bs, ptr->SampleCount, 32); + + for (i=0 ; iSampleCount; i += 2) { + gf_bs_write_int(bs, 0, 1); + if (i+1 < ptr->SampleCount) { + gf_bs_write_int(bs, ptr->padbits[i+1], 3); + } else { + gf_bs_write_int(bs, 0, 3); + } + gf_bs_write_int(bs, 0, 1); + gf_bs_write_int(bs, ptr->padbits[i], 3); + } + return GF_OK; +} + +GF_Err padb_Size(GF_Box *s) +{ + GF_Err e; + GF_PaddingBitsBox *ptr = (GF_PaddingBitsBox *)s; + e = gf_isom_full_box_get_size(s); + if (e) return e; + ptr->size += 4; + if (ptr->SampleCount) ptr->size += (ptr->SampleCount + 1) / 2; + + + return GF_OK; +} + +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + + +void rely_del(GF_Box *s) +{ + GF_RelyHintBox *rely = (GF_RelyHintBox *)s; + gf_free(rely); +} + +GF_Err rely_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_RelyHintBox *ptr = (GF_RelyHintBox *)s; + ptr->reserved = gf_bs_read_int(bs, 6); + ptr->prefered = gf_bs_read_int(bs, 1); + ptr->required = gf_bs_read_int(bs, 1); + return GF_OK; +} + +GF_Box *rely_New() +{ + GF_RelyHintBox *tmp = (GF_RelyHintBox *)gf_malloc(sizeof(GF_RelyHintBox)); + if (!tmp) return NULL; + memset(tmp, 0, sizeof(GF_RelyHintBox)); + tmp->type = GF_ISOM_BOX_TYPE_RELY; + + return (GF_Box *)tmp; +} + + +#ifndef GPAC_DISABLE_ISOM_WRITE +GF_Err rely_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_RelyHintBox *ptr = (GF_RelyHintBox *)s; + if (ptr == NULL) return GF_BAD_PARAM; + e = gf_isom_box_write_header(s, bs); + if (e) return e; + gf_bs_write_int(bs, ptr->reserved, 6); + gf_bs_write_int(bs, ptr->prefered, 1); + gf_bs_write_int(bs, ptr->required, 1); + return GF_OK; +} + +GF_Err rely_Size(GF_Box *s) +{ + GF_Err e; + e = gf_isom_box_get_size(s); + if (e) return e; + s->size += 1; + return GF_OK; +} +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + + +void rtpo_del(GF_Box *s) +{ + GF_RTPOBox *rtpo = (GF_RTPOBox *)s; + gf_free(rtpo); +} + +GF_Err rtpo_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_RTPOBox *ptr = (GF_RTPOBox *)s; + ptr->timeOffset = gf_bs_read_u32(bs); + return GF_OK; +} + +GF_Box *rtpo_New() +{ + GF_RTPOBox *tmp = (GF_RTPOBox *) gf_malloc(sizeof(GF_RTPOBox)); + if (!tmp) return NULL; + memset(tmp, 0, sizeof(GF_RTPOBox)); + tmp->type = GF_ISOM_BOX_TYPE_RTPO; + return (GF_Box *)tmp; +} +#ifndef GPAC_DISABLE_ISOM_WRITE +GF_Err rtpo_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_RTPOBox *ptr = (GF_RTPOBox *)s; + if (ptr == NULL) return GF_BAD_PARAM; + + //here we have no pb, just remembed that some entries will have to + //be 4-bytes aligned ... + e = gf_isom_box_write_header(s, bs); + if (e) return e; + gf_bs_write_u32(bs, ptr->timeOffset); + return GF_OK; +} + +GF_Err rtpo_Size(GF_Box *s) +{ + GF_Err e; + e = gf_isom_box_get_size(s); + if (e) return e; + s->size += 4; + return GF_OK; +} +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + +void smhd_del(GF_Box *s) +{ + GF_SoundMediaHeaderBox *ptr = (GF_SoundMediaHeaderBox *)s; + if (ptr == NULL ) return; + gf_free(ptr); +} + + +GF_Err smhd_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_SoundMediaHeaderBox *ptr = (GF_SoundMediaHeaderBox *)s; + e = gf_isom_full_box_read(s, bs); + if (e) return e; + ptr->reserved = gf_bs_read_u32(bs); + return GF_OK; +} + +GF_Box *smhd_New() +{ + GF_SoundMediaHeaderBox *tmp = (GF_SoundMediaHeaderBox *) gf_malloc(sizeof(GF_SoundMediaHeaderBox)); + if (tmp == NULL) return NULL; + memset(tmp, 0, sizeof(GF_SoundMediaHeaderBox)); + gf_isom_full_box_init((GF_Box *) tmp); + tmp->type = GF_ISOM_BOX_TYPE_SMHD; + return (GF_Box *)tmp; +} + + +#ifndef GPAC_DISABLE_ISOM_WRITE + +GF_Err smhd_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_SoundMediaHeaderBox *ptr = (GF_SoundMediaHeaderBox *)s; + e = gf_isom_full_box_write(s, bs); + if (e) return e; + gf_bs_write_u32(bs, ptr->reserved); + return GF_OK; +} + +GF_Err smhd_Size(GF_Box *s) +{ + GF_Err e; + GF_SoundMediaHeaderBox *ptr = (GF_SoundMediaHeaderBox *)s; + e = gf_isom_full_box_get_size(s); + if (e) return e; + ptr->reserved = 0; + ptr->size += 4; + return GF_OK; +} + +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + + + +void snro_del(GF_Box *s) +{ + GF_SeqOffHintEntryBox *snro = (GF_SeqOffHintEntryBox *)s; + gf_free(snro); +} + +GF_Err snro_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_SeqOffHintEntryBox *ptr = (GF_SeqOffHintEntryBox *)s; + ptr->SeqOffset = gf_bs_read_u32(bs); + return GF_OK; +} + +GF_Box *snro_New() +{ + GF_SeqOffHintEntryBox *tmp = (GF_SeqOffHintEntryBox *) gf_malloc(sizeof(GF_SeqOffHintEntryBox)); + if (!tmp) return NULL; + memset(tmp, 0, sizeof(GF_SeqOffHintEntryBox)); + tmp->type = GF_ISOM_BOX_TYPE_SNRO; + return (GF_Box *)tmp; +} + + +#ifndef GPAC_DISABLE_ISOM_WRITE +GF_Err snro_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_SeqOffHintEntryBox *ptr = (GF_SeqOffHintEntryBox *)s; + if (ptr == NULL) return GF_BAD_PARAM; + + e = gf_isom_box_write_header(s, bs); + if (e) return e; + gf_bs_write_u32(bs, ptr->SeqOffset); + return GF_OK; +} + +GF_Err snro_Size(GF_Box *s) +{ + GF_Err e; + e = gf_isom_box_get_size(s); + if (e) return e; + s->size += 4; + return GF_OK; +} +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + + +#define WRITE_SAMPLE_FRAGMENTS 1 + +void stbl_del(GF_Box *s) +{ + GF_SampleTableBox *ptr = (GF_SampleTableBox *)s; + if (ptr == NULL) return; + + if (ptr->ChunkOffset) gf_isom_box_del(ptr->ChunkOffset); + if (ptr->CompositionOffset) gf_isom_box_del((GF_Box *) ptr->CompositionOffset); + if (ptr->DegradationPriority) gf_isom_box_del((GF_Box *) ptr->DegradationPriority); + if (ptr->SampleDescription) gf_isom_box_del((GF_Box *) ptr->SampleDescription); + if (ptr->SampleSize) gf_isom_box_del((GF_Box *) ptr->SampleSize); + if (ptr->SampleToChunk) gf_isom_box_del((GF_Box *) ptr->SampleToChunk); + if (ptr->ShadowSync) gf_isom_box_del((GF_Box *) ptr->ShadowSync); + if (ptr->SyncSample) gf_isom_box_del((GF_Box *) ptr->SyncSample); + if (ptr->TimeToSample) gf_isom_box_del((GF_Box *) ptr->TimeToSample); + if (ptr->SampleDep) gf_isom_box_del((GF_Box *) ptr->SampleDep); + if (ptr->PaddingBits) gf_isom_box_del((GF_Box *) ptr->PaddingBits); + if (ptr->Fragments) gf_isom_box_del((GF_Box *) ptr->Fragments); + if (ptr->SubSamples) gf_isom_box_del((GF_Box *) ptr->SubSamples); + + gf_free(ptr); +} + +GF_Err stbl_AddBox(GF_SampleTableBox *ptr, GF_Box *a) +{ + if (!a) return GF_OK; + switch (a->type) { + case GF_ISOM_BOX_TYPE_STTS: + if (ptr->TimeToSample) ERROR_ON_DUPLICATED_BOX(a, ptr) + ptr->TimeToSample = (GF_TimeToSampleBox *)a; + break; + case GF_ISOM_BOX_TYPE_CTTS: + if (ptr->CompositionOffset) ERROR_ON_DUPLICATED_BOX(a, ptr) + ptr->CompositionOffset = (GF_CompositionOffsetBox *)a; + break; + case GF_ISOM_BOX_TYPE_STSS: + if (ptr->SyncSample) ERROR_ON_DUPLICATED_BOX(a, ptr) + ptr->SyncSample = (GF_SyncSampleBox *)a; + break; + case GF_ISOM_BOX_TYPE_STSD: + if (ptr->SampleDescription) ERROR_ON_DUPLICATED_BOX(a, ptr) + ptr->SampleDescription =(GF_SampleDescriptionBox *)a; + break; + case GF_ISOM_BOX_TYPE_STZ2: + case GF_ISOM_BOX_TYPE_STSZ: + if (ptr->SampleSize) ERROR_ON_DUPLICATED_BOX(a, ptr) + ptr->SampleSize = (GF_SampleSizeBox *)a; + break; + case GF_ISOM_BOX_TYPE_STSC: + if (ptr->SampleToChunk) ERROR_ON_DUPLICATED_BOX(a, ptr) + ptr->SampleToChunk = (GF_SampleToChunkBox *)a; + break; + case GF_ISOM_BOX_TYPE_FADB: + if (ptr->PaddingBits) ERROR_ON_DUPLICATED_BOX(a, ptr) + ptr->PaddingBits = (GF_PaddingBitsBox *) a; + break; + + //WARNING: AS THIS MAY CHANGE DYNAMICALLY DURING EDIT, + case GF_ISOM_BOX_TYPE_CO64: + case GF_ISOM_BOX_TYPE_STCO: + if (ptr->ChunkOffset) { + gf_isom_box_del(ptr->ChunkOffset); + } + ptr->ChunkOffset = a; + return GF_OK; + case GF_ISOM_BOX_TYPE_STSH: + if (ptr->ShadowSync) ERROR_ON_DUPLICATED_BOX(a, ptr) + ptr->ShadowSync = (GF_ShadowSyncBox *)a; + break; + case GF_ISOM_BOX_TYPE_STDP: + if (ptr->DegradationPriority) ERROR_ON_DUPLICATED_BOX(a, ptr) + ptr->DegradationPriority = (GF_DegradationPriorityBox *)a; + break; + case GF_ISOM_BOX_TYPE_SDTP: + if (ptr->SampleDep) ERROR_ON_DUPLICATED_BOX(a, ptr) + ptr->SampleDep= (GF_SampleDependencyTypeBox *)a; + break; + + case GF_ISOM_BOX_TYPE_STSF: + if (ptr->Fragments) ERROR_ON_DUPLICATED_BOX(a, ptr) + ptr->Fragments = (GF_SampleFragmentBox *)a; + break; + + case GF_ISOM_BOX_TYPE_SUBS: + if (ptr->SubSamples) ERROR_ON_DUPLICATED_BOX(a, ptr) + ptr->SubSamples = (GF_SubSampleInformationBox *)a; + break; + + default: + GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[iso file] Warning box %s unknown type - discarding\n", gf_4cc_to_str(a->type))); + gf_isom_box_del(a); + break; + } + return GF_OK; +} + + + + +GF_Err stbl_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_Box *a; + //we need to parse DegPrior in a special way + GF_SampleTableBox *ptr = (GF_SampleTableBox *)s; + + while (ptr->size) { + e = gf_isom_parse_box(&a, bs); + if (e) return e; + //we need to read the DegPriority in a different way... + if ((a->type == GF_ISOM_BOX_TYPE_STDP) || (a->type == GF_ISOM_BOX_TYPE_SDTP)) { + u64 s = a->size; +/* + if (!ptr->SampleSize) { + gf_isom_box_del(a); + return GF_ISOM_INVALID_FILE; + } +*/ + if (a->type == GF_ISOM_BOX_TYPE_STDP) { + if (ptr->SampleSize) ((GF_DegradationPriorityBox *)a)->nb_entries = ptr->SampleSize->sampleCount; + e = stdp_Read(a, bs); + } else { + if (ptr->SampleSize) ((GF_SampleDependencyTypeBox *)a)->sampleCount = ptr->SampleSize->sampleCount; + e = sdtp_Read(a, bs); + } + if (e) { + gf_isom_box_del(a); + return e; + } + + if (a->size>8) { + GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[iso file] Box \"%s\" has %d extra bytes\n", gf_4cc_to_str(a->type), a->size)); + gf_bs_skip_bytes(bs, a->size-8); + } + a->size = s; + } + if (ptr->sizesize) { + gf_isom_box_del(a); + return GF_ISOM_INVALID_FILE; + } + ptr->size -= a->size; + e = stbl_AddBox(ptr, a); + if (e) return e; + } + return GF_OK; +} + +GF_Box *stbl_New() +{ + GF_SampleTableBox *tmp = (GF_SampleTableBox *) gf_malloc(sizeof(GF_SampleTableBox)); + if (tmp == NULL) return NULL; + memset(tmp, 0, sizeof(GF_SampleTableBox)); + + tmp->type = GF_ISOM_BOX_TYPE_STBL; + //maxSamplePer chunk is 10 by default + tmp->MaxSamplePerChunk = 10; + tmp->groupID = 1; + return (GF_Box *)tmp; +} + + + +#ifndef GPAC_DISABLE_ISOM_WRITE + +GF_Err stbl_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_SampleTableBox *ptr = (GF_SampleTableBox *)s; + if (!s) return GF_BAD_PARAM; + e = gf_isom_box_write_header(s, bs); + if (e) return e; + + if (ptr->SampleDescription) { + e = gf_isom_box_write((GF_Box *) ptr->SampleDescription, bs); + if (e) return e; + } + if (ptr->TimeToSample) { + e = gf_isom_box_write((GF_Box *) ptr->TimeToSample, bs); + if (e) return e; + } + if (ptr->CompositionOffset) { + e = gf_isom_box_write((GF_Box *) ptr->CompositionOffset, bs); + if (e) return e; + } + if (ptr->SyncSample) { + e = gf_isom_box_write((GF_Box *) ptr->SyncSample, bs); + if (e) return e; + } + if (ptr->ShadowSync) { + e = gf_isom_box_write((GF_Box *) ptr->ShadowSync, bs); + if (e) return e; + } + if (ptr->SampleToChunk) { + e = gf_isom_box_write((GF_Box *) ptr->SampleToChunk, bs); + if (e) return e; + } + if (ptr->SampleSize) { + e = gf_isom_box_write((GF_Box *) ptr->SampleSize, bs); + if (e) return e; + } + if (ptr->ChunkOffset) { + e = gf_isom_box_write(ptr->ChunkOffset, bs); + if (e) return e; + } + if (ptr->DegradationPriority) { + e = gf_isom_box_write((GF_Box *) ptr->DegradationPriority, bs); + if (e) return e; + } + if (ptr->SampleDep && ptr->SampleDep->sampleCount) { + e = gf_isom_box_write((GF_Box *) ptr->SampleDep, bs); + if (e) return e; + } + if (ptr->PaddingBits) { + e = gf_isom_box_write((GF_Box *) ptr->PaddingBits, bs); + if (e) return e; + } + if (ptr->SubSamples) { + e = gf_isom_box_write((GF_Box *) ptr->SubSamples, bs); + if (e) return e; + } + +#if WRITE_SAMPLE_FRAGMENTS + //sampleFragments + if (ptr->Fragments) { + e = gf_isom_box_write((GF_Box *) ptr->Fragments, bs); + if (e) return e; + } +#endif + return GF_OK; +} + +GF_Err stbl_Size(GF_Box *s) +{ + GF_Err e; + GF_SampleTableBox *ptr = (GF_SampleTableBox *)s; + e = gf_isom_box_get_size(s); + if (e) return e; + + //Mandatory boxs (but not internally :) + if (ptr->SampleDescription) { + e = gf_isom_box_size((GF_Box *) ptr->SampleDescription); + if (e) return e; + ptr->size += ptr->SampleDescription->size; + } + if (ptr->SampleSize) { + e = gf_isom_box_size((GF_Box *) ptr->SampleSize); + if (e) return e; + ptr->size += ptr->SampleSize->size; + } + if (ptr->SampleToChunk) { + e = gf_isom_box_size((GF_Box *) ptr->SampleToChunk); + if (e) return e; + ptr->size += ptr->SampleToChunk->size; + } + if (ptr->TimeToSample) { + e = gf_isom_box_size((GF_Box *) ptr->TimeToSample); + if (e) return e; + ptr->size += ptr->TimeToSample->size; + } + if (ptr->ChunkOffset) { + e = gf_isom_box_size(ptr->ChunkOffset); + if (e) return e; + ptr->size += ptr->ChunkOffset->size; + } + + //optional boxs + if (ptr->CompositionOffset) { + e = gf_isom_box_size((GF_Box *) ptr->CompositionOffset); + if (e) return e; + ptr->size += ptr->CompositionOffset->size; + } + if (ptr->DegradationPriority) { + e = gf_isom_box_size((GF_Box *) ptr->DegradationPriority); + if (e) return e; + ptr->size += ptr->DegradationPriority->size; + } + if (ptr->ShadowSync) { + e = gf_isom_box_size((GF_Box *) ptr->ShadowSync); + if (e) return e; + ptr->size += ptr->ShadowSync->size; + } + if (ptr->SyncSample) { + e = gf_isom_box_size((GF_Box *) ptr->SyncSample); + if (e) return e; + ptr->size += ptr->SyncSample->size; + } + if (ptr->SampleDep && ptr->SampleDep->sampleCount) { + e = gf_isom_box_size((GF_Box *) ptr->SampleDep); + if (e) return e; + ptr->size += ptr->SampleDep->size; + } + //padb + if (ptr->PaddingBits) { + e = gf_isom_box_size((GF_Box *) ptr->PaddingBits); + if (e) return e; + ptr->size += ptr->PaddingBits->size; + } +#if WRITE_SAMPLE_FRAGMENTS + //sample fragments + if (ptr->Fragments) { + e = gf_isom_box_size((GF_Box *) ptr->Fragments); + if (e) return e; + ptr->size += ptr->Fragments->size; + } +#endif + + if (ptr->SubSamples) { + e = gf_isom_box_size((GF_Box *) ptr->SubSamples); + if (e) return e; + ptr->size += ptr->SubSamples->size; + } + + return GF_OK; +} + +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + + +void stco_del(GF_Box *s) +{ + GF_ChunkOffsetBox *ptr = (GF_ChunkOffsetBox *)s; + if (ptr == NULL) return; + if (ptr->offsets) gf_free(ptr->offsets); + gf_free(ptr); +} + + +GF_Err stco_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + u32 entries; + GF_ChunkOffsetBox *ptr = (GF_ChunkOffsetBox *)s; + + e = gf_isom_full_box_read(s, bs); + if (e) return e; + ptr->nb_entries = gf_bs_read_u32(bs); + + if (ptr->nb_entries) { + ptr->offsets = (u32 *) gf_malloc(ptr->nb_entries * sizeof(u32) ); + if (ptr->offsets == NULL) return GF_OUT_OF_MEM; + ptr->alloc_size = ptr->nb_entries; + + for (entries = 0; entries < ptr->nb_entries; entries++) { + ptr->offsets[entries] = gf_bs_read_u32(bs); + } + } + return GF_OK; +} + +GF_Box *stco_New() +{ + GF_ChunkOffsetBox *tmp = (GF_ChunkOffsetBox *) gf_malloc(sizeof(GF_ChunkOffsetBox)); + if (tmp == NULL) return NULL; + memset(tmp, 0, sizeof(GF_ChunkOffsetBox)); + gf_isom_full_box_init((GF_Box *)tmp); + tmp->type = GF_ISOM_BOX_TYPE_STCO; + return (GF_Box *)tmp; +} + + + +#ifndef GPAC_DISABLE_ISOM_WRITE + +GF_Err stco_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + u32 i; + GF_ChunkOffsetBox *ptr = (GF_ChunkOffsetBox *)s; + e = gf_isom_full_box_write(s, bs); + if (e) return e; + gf_bs_write_u32(bs, ptr->nb_entries); + for (i = 0; i < ptr->nb_entries; i++) { + gf_bs_write_u32(bs, ptr->offsets[i]); + } + return GF_OK; +} + + +GF_Err stco_Size(GF_Box *s) +{ + GF_Err e; + GF_ChunkOffsetBox *ptr = (GF_ChunkOffsetBox *)s; + e = gf_isom_full_box_get_size(s); + if (e) return e; + ptr->size += 4 + (4 * ptr->nb_entries); + return GF_OK; +} + +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + + + +void stdp_del(GF_Box *s) +{ + GF_DegradationPriorityBox *ptr = (GF_DegradationPriorityBox *)s; + if (ptr == NULL ) return; + if (ptr->priorities) gf_free(ptr->priorities); + gf_free(ptr); +} + +//this is called through stbl_read... +GF_Err stdp_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + u32 entry; + GF_DegradationPriorityBox *ptr = (GF_DegradationPriorityBox *)s; + + e = gf_isom_full_box_read(s, bs); + if (e) return e; + /*out-of-order stdp, assume no padding at the end*/ + if (!ptr->nb_entries) ptr->nb_entries = (u32) (ptr->size-8) / 2; + ptr->priorities = (u16 *) gf_malloc(ptr->nb_entries * sizeof(u16)); + if (ptr->priorities == NULL) return GF_OUT_OF_MEM; + for (entry = 0; entry < ptr->nb_entries; entry++) { + //we have a bit for padding + gf_bs_read_int(bs, 1); + ptr->priorities[entry] = gf_bs_read_int(bs, 15); + } + ptr->size -= 2*ptr->nb_entries; + return GF_OK; +} + +GF_Box *stdp_New() +{ + GF_DegradationPriorityBox *tmp = (GF_DegradationPriorityBox *) gf_malloc(sizeof(GF_DegradationPriorityBox)); + if (tmp == NULL) return NULL; + memset(tmp, 0, sizeof(GF_DegradationPriorityBox)); + gf_isom_full_box_init((GF_Box *)tmp); + tmp->type = GF_ISOM_BOX_TYPE_STDP; + return (GF_Box *)tmp; +} + + +#ifndef GPAC_DISABLE_ISOM_WRITE + +GF_Err stdp_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + u32 i; + GF_DegradationPriorityBox *ptr = (GF_DegradationPriorityBox *)s; + e = gf_isom_full_box_write(s, bs); + if (e) return e; + + for (i = 0; i < ptr->nb_entries; i++) { + gf_bs_write_int(bs, 0, 1); + gf_bs_write_int(bs, ptr->priorities[i], 15); + } + return GF_OK; +} + +GF_Err stdp_Size(GF_Box *s) +{ + GF_Err e; + GF_DegradationPriorityBox *ptr = (GF_DegradationPriorityBox *)s; + e = gf_isom_full_box_get_size(s); + if (e) return e; + ptr->size += (2 * ptr->nb_entries); + return GF_OK; +} + +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + + +void stsc_del(GF_Box *s) +{ + GF_SampleToChunkBox *ptr = (GF_SampleToChunkBox *)s; + if (ptr == NULL) return; + if (ptr->entries) gf_free(ptr->entries); + gf_free(ptr); +} + + +GF_Err stsc_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + u32 i; + GF_SampleToChunkBox *ptr = (GF_SampleToChunkBox *)s; + + e = gf_isom_full_box_read(s, bs); + if (e) return e; + ptr->nb_entries = gf_bs_read_u32(bs); + ptr->alloc_size = ptr->nb_entries; + ptr->entries = (GF_StscEntry *) gf_malloc(sizeof(GF_StscEntry)*ptr->alloc_size); + if (!ptr->entries) return GF_OUT_OF_MEM; + + for (i = 0; i < ptr->nb_entries; i++) { + ptr->entries[i].firstChunk = gf_bs_read_u32(bs); + ptr->entries[i].samplesPerChunk = gf_bs_read_u32(bs); + ptr->entries[i].sampleDescriptionIndex = gf_bs_read_u32(bs); + ptr->entries[i].isEdited = 0; + ptr->entries[i].nextChunk = 0; + + //update the next chunk in the previous entry + if (i) ptr->entries[i-1].nextChunk = ptr->entries[i].firstChunk; + } + ptr->currentIndex = 0; + ptr->firstSampleInCurrentChunk = 0; + ptr->currentChunk = 0; + ptr->ghostNumber = 0; + return GF_OK; +} + +GF_Box *stsc_New() +{ + GF_SampleToChunkBox *tmp = (GF_SampleToChunkBox *) gf_malloc(sizeof(GF_SampleToChunkBox)); + if (tmp == NULL) return NULL; + memset(tmp, 0, sizeof(GF_SampleToChunkBox)); + gf_isom_full_box_init((GF_Box *)tmp); + tmp->type = GF_ISOM_BOX_TYPE_STSC; + return (GF_Box *)tmp; +} + + +#ifndef GPAC_DISABLE_ISOM_WRITE + +GF_Err stsc_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + u32 i; + GF_SampleToChunkBox *ptr = (GF_SampleToChunkBox *)s; + + e = gf_isom_full_box_write(s, bs); + if (e) return e; + gf_bs_write_u32(bs, ptr->nb_entries); + for (i=0; inb_entries; i++) { + gf_bs_write_u32(bs, ptr->entries[i].firstChunk); + gf_bs_write_u32(bs, ptr->entries[i].samplesPerChunk); + gf_bs_write_u32(bs, ptr->entries[i].sampleDescriptionIndex); + } + return GF_OK; +} + +GF_Err stsc_Size(GF_Box *s) +{ + GF_Err e; + GF_SampleToChunkBox *ptr = (GF_SampleToChunkBox *)s; + e = gf_isom_full_box_get_size(s); + if (e) return e; + ptr->size += 4 + (12 * ptr->nb_entries); + return GF_OK; +} + +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + +void stsd_del(GF_Box *s) +{ + GF_SampleDescriptionBox *ptr = (GF_SampleDescriptionBox *)s; + if (ptr == NULL) return; + gf_isom_box_array_del(ptr->boxList); + gf_free(ptr); +} + +GF_Err stsd_AddBox(GF_SampleDescriptionBox *ptr, GF_Box *a) +{ + GF_UnknownBox *def; + if (!a) return GF_OK; + + switch (a->type) { + case GF_ISOM_BOX_TYPE_MP4S: + case GF_ISOM_BOX_TYPE_ENCS: + case GF_ISOM_BOX_TYPE_MP4A: + case GF_ISOM_BOX_TYPE_ENCA: + case GF_ISOM_BOX_TYPE_MP4V: + case GF_ISOM_BOX_TYPE_ENCV: + case GF_ISOM_BOX_TYPE_GHNT: + case GF_ISOM_BOX_TYPE_RTP_STSD: + case GF_ISOM_BOX_TYPE_AVC1: + case GF_ISOM_BOX_TYPE_AVC2: + case GF_ISOM_BOX_TYPE_SVC1: + case GF_ISOM_BOX_TYPE_TX3G: + case GF_ISOM_BOX_TYPE_TEXT: + case GF_ISOM_BOX_TYPE_ENCT: + case GF_ISOM_BOX_TYPE_METX: + case GF_ISOM_BOX_TYPE_METT: + case GF_ISOM_BOX_TYPE_DIMS: + case GF_ISOM_BOX_TYPE_AC3: + case GF_ISOM_BOX_TYPE_LSR1: + return gf_list_add(ptr->boxList, a); + /*for 3GP config, we must set the type*/ + case GF_ISOM_SUBTYPE_3GP_AMR: + case GF_ISOM_SUBTYPE_3GP_AMR_WB: + case GF_ISOM_SUBTYPE_3GP_EVRC: + case GF_ISOM_SUBTYPE_3GP_QCELP: + case GF_ISOM_SUBTYPE_3GP_SMV: + { + ((GF_3GPPAudioSampleEntryBox *)a)->info->cfg.type = a->type; + return gf_list_add(ptr->boxList, a); + } + case GF_ISOM_SUBTYPE_3GP_H263: + { + ((GF_3GPPVisualSampleEntryBox *)a)->info->cfg.type = a->type; + return gf_list_add(ptr->boxList, a); + } + + //unknown sample description: we need a specific box to handle the data ref index + //rather than a default box ... + default: + def = (GF_UnknownBox *)a; + /*we need at least 8 bytes for unknown sample entries*/ + if (def->dataSize < 8) { + gf_isom_box_del(a); + return GF_OK; + } + return gf_list_add(ptr->boxList, a); + } +} + + +GF_Err stsd_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + u32 nb_entries; + u32 i; + GF_Box *a; + GF_SampleDescriptionBox *ptr = (GF_SampleDescriptionBox *)s; + + e = gf_isom_full_box_read(s, bs); + if (e) return e; + nb_entries = gf_bs_read_u32(bs); + for (i = 0; i < nb_entries; i++) { + e = gf_isom_parse_box(&a, bs); + if (e) return e; + e = stsd_AddBox(ptr, a); + if (e) return e; + } + return GF_OK; +} + +GF_Box *stsd_New() +{ + GF_SampleDescriptionBox *tmp = (GF_SampleDescriptionBox *) gf_malloc(sizeof(GF_SampleDescriptionBox)); + if (tmp == NULL) return NULL; + memset(tmp, 0, sizeof(GF_SampleDescriptionBox)); + gf_isom_full_box_init((GF_Box *)tmp); + tmp->boxList = gf_list_new(); + if (! tmp->boxList) { + gf_free(tmp); + return NULL; + } + tmp->type = GF_ISOM_BOX_TYPE_STSD; + return (GF_Box *)tmp; +} + + +#ifndef GPAC_DISABLE_ISOM_WRITE + +GF_Err stsd_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + u32 nb_entries; + GF_SampleDescriptionBox *ptr = (GF_SampleDescriptionBox *)s; + + e = gf_isom_full_box_write(s, bs); + if (e) return e; + nb_entries = gf_list_count(ptr->boxList); + gf_bs_write_u32(bs, nb_entries); + return gf_isom_box_array_write(s, ptr->boxList, bs); +} + +GF_Err stsd_Size(GF_Box *s) +{ + GF_Err e; + GF_SampleDescriptionBox *ptr = (GF_SampleDescriptionBox *)s; + e = gf_isom_full_box_get_size(s); + if (e) return e; + ptr->size += 4; + return gf_isom_box_array_size(s, ptr->boxList); +} + +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + +void stsf_del(GF_Box *s) +{ + u32 nb_entries; + u32 i; + GF_StsfEntry *pe; + GF_SampleFragmentBox *ptr = (GF_SampleFragmentBox *)s; + if (ptr == NULL) return; + + if (ptr->entryList) { + nb_entries = gf_list_count(ptr->entryList); + for ( i = 0; i < nb_entries; i++ ) { + pe = (GF_StsfEntry*)gf_list_get(ptr->entryList, i); + if (pe->fragmentSizes) gf_free(pe->fragmentSizes); + gf_free(pe); + } + gf_list_del(ptr->entryList); + } + gf_free(ptr); +} + + + +GF_Err stsf_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + u32 entries, i; + u32 nb_entries; + GF_StsfEntry *p; + GF_SampleFragmentBox *ptr = (GF_SampleFragmentBox *)s; + + p = NULL; + if (!ptr) return GF_BAD_PARAM; + e = gf_isom_full_box_read(s, bs); + if (e) return e; + nb_entries = gf_bs_read_u32(bs); + + p = NULL; + for ( entries = 0; entries < nb_entries; entries++ ) { + p = (GF_StsfEntry *) gf_malloc(sizeof(GF_StsfEntry)); + if (!p) return GF_OUT_OF_MEM; + p->SampleNumber = gf_bs_read_u32(bs); + p->fragmentCount = gf_bs_read_u32(bs); + p->fragmentSizes = (u16*)gf_malloc(sizeof(GF_StsfEntry) * p->fragmentCount); + for (i=0; ifragmentCount; i++) { + p->fragmentSizes[i] = gf_bs_read_u16(bs); + } + gf_list_add(ptr->entryList, p); + } +#ifndef GPAC_DISABLE_ISOM_WRITE + ptr->w_currentEntry = p; + ptr->w_currentEntryIndex = nb_entries-1; +#endif + return GF_OK; +} + +GF_Box *stsf_New() +{ + GF_SampleFragmentBox *tmp; + + tmp = (GF_SampleFragmentBox *) gf_malloc(sizeof(GF_SampleFragmentBox)); + if (tmp == NULL) return NULL; + memset(tmp, 0, sizeof(GF_SampleFragmentBox)); + + gf_isom_full_box_init((GF_Box *) tmp); + tmp->entryList = gf_list_new(); + if (! tmp->entryList) { + gf_free(tmp); + return NULL; + } + tmp->type = GF_ISOM_BOX_TYPE_STSF; + return (GF_Box *) tmp; +} + + + + +#ifndef GPAC_DISABLE_ISOM_WRITE + +GF_Err stsf_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + u32 i, j; + u32 nb_entries; + GF_StsfEntry *p; + GF_SampleFragmentBox *ptr = (GF_SampleFragmentBox *)s; + + e = gf_isom_full_box_write(s, bs); + if (e) return e; + nb_entries = gf_list_count(ptr->entryList); + gf_bs_write_u32(bs, nb_entries); + for ( i = 0; i < nb_entries; i++ ) { + p = (GF_StsfEntry*)gf_list_get(ptr->entryList, i); + gf_bs_write_u32(bs, p->SampleNumber); + gf_bs_write_u32(bs, p->fragmentCount); + for (j=0;jfragmentCount;j++) { + gf_bs_write_u16(bs, p->fragmentSizes[j]); + } + } + return GF_OK; +} + +GF_Err stsf_Size(GF_Box *s) +{ + GF_Err e; + GF_StsfEntry *p; + u32 nb_entries, i; + GF_SampleFragmentBox *ptr = (GF_SampleFragmentBox *) s; + + e = gf_isom_full_box_get_size(s); + if (e) return e; + nb_entries = gf_list_count(ptr->entryList); + ptr->size += 4; + for (i=0;ientryList, i); + ptr->size += 8 + 2*p->fragmentCount; + } + return GF_OK; +} + +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + +void stsh_del(GF_Box *s) +{ + u32 i = 0; + GF_StshEntry *ent; + GF_ShadowSyncBox *ptr = (GF_ShadowSyncBox *)s; + if (ptr == NULL) return; + while ( (ent = (GF_StshEntry *)gf_list_enum(ptr->entries, &i)) ) { + gf_free(ent); + } + gf_list_del(ptr->entries); + gf_free(ptr); +} + + + +GF_Err stsh_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + u32 count, i; + GF_StshEntry *ent; + GF_ShadowSyncBox *ptr = (GF_ShadowSyncBox *)s; + + e = gf_isom_full_box_read(s, bs); + if (e) return e; + count = gf_bs_read_u32(bs); + + for (i = 0; i < count; i++) { + ent = (GF_StshEntry *) gf_malloc(sizeof(GF_StshEntry)); + if (!ent) return GF_OUT_OF_MEM; + ent->shadowedSampleNumber = gf_bs_read_u32(bs); + ent->syncSampleNumber = gf_bs_read_u32(bs); + e = gf_list_add(ptr->entries, ent); + if (e) return e; + } + return GF_OK; +} + +GF_Box *stsh_New() +{ + GF_ShadowSyncBox *tmp = (GF_ShadowSyncBox *) gf_malloc(sizeof(GF_ShadowSyncBox)); + if (tmp == NULL) return NULL; + memset(tmp, 0, sizeof(GF_ShadowSyncBox)); + gf_isom_full_box_init((GF_Box *)tmp); + tmp->entries = gf_list_new(); + if (!tmp->entries) { + gf_free(tmp); + return NULL; + } + tmp->type = GF_ISOM_BOX_TYPE_STSH; + return (GF_Box *)tmp; +} + + +#ifndef GPAC_DISABLE_ISOM_WRITE + +GF_Err stsh_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + u32 i; + GF_StshEntry *ent; + GF_ShadowSyncBox *ptr = (GF_ShadowSyncBox *)s; + + e = gf_isom_full_box_write(s, bs); + if (e) return e; + gf_bs_write_u32(bs, gf_list_count(ptr->entries)); + i=0; + while ((ent = (GF_StshEntry *)gf_list_enum(ptr->entries, &i))) { + gf_bs_write_u32(bs, ent->shadowedSampleNumber); + gf_bs_write_u32(bs, ent->syncSampleNumber); + } + return GF_OK; +} + +GF_Err stsh_Size(GF_Box *s) +{ + GF_Err e; + GF_ShadowSyncBox *ptr = (GF_ShadowSyncBox *)s; + e = gf_isom_full_box_get_size(s); + if (e) return e; + ptr->size += 4 + (8 * gf_list_count(ptr->entries)); + return GF_OK; +} + +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + + + +void stss_del(GF_Box *s) +{ + GF_SyncSampleBox *ptr = (GF_SyncSampleBox *)s; + if (ptr == NULL) return; + if (ptr->sampleNumbers) gf_free(ptr->sampleNumbers); + gf_free(ptr); +} + +GF_Err stss_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + u32 i; + GF_SyncSampleBox *ptr = (GF_SyncSampleBox *)s; + + e = gf_isom_full_box_read(s, bs); + if (e) return e; + ptr->nb_entries = gf_bs_read_u32(bs); + ptr->alloc_size = ptr->nb_entries; + ptr->sampleNumbers = (u32 *) gf_malloc( ptr->alloc_size * sizeof(u32)); + if (ptr->sampleNumbers == NULL) return GF_OUT_OF_MEM; + + for (i = 0; i < ptr->nb_entries; i++) { + ptr->sampleNumbers[i] = gf_bs_read_u32(bs); + } + return GF_OK; +} + +GF_Box *stss_New() +{ + GF_SyncSampleBox *tmp = (GF_SyncSampleBox *) gf_malloc(sizeof(GF_SyncSampleBox)); + if (tmp == NULL) return NULL; + memset(tmp, 0, sizeof(GF_SyncSampleBox)); + gf_isom_full_box_init((GF_Box *)tmp); + tmp->type = GF_ISOM_BOX_TYPE_STSS; + return (GF_Box*)tmp; +} + + +#ifndef GPAC_DISABLE_ISOM_WRITE + +GF_Err stss_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + u32 i; + GF_SyncSampleBox *ptr = (GF_SyncSampleBox *)s; + + e = gf_isom_full_box_write(s, bs); + if (e) return e; + gf_bs_write_u32(bs, ptr->nb_entries); + for (i = 0; i < ptr->nb_entries; i++) { + gf_bs_write_u32(bs, ptr->sampleNumbers[i]); + } + return GF_OK; +} + +GF_Err stss_Size(GF_Box *s) +{ + GF_Err e; + GF_SyncSampleBox *ptr = (GF_SyncSampleBox *)s; + e = gf_isom_full_box_get_size(s); + if (e) return e; + ptr->size += 4 + (4 * ptr->nb_entries); + return GF_OK; +} + +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + + +void stsz_del(GF_Box *s) +{ + GF_SampleSizeBox *ptr = (GF_SampleSizeBox *)s; + if (ptr == NULL) return; + if (ptr->sizes) gf_free(ptr->sizes); + gf_free(ptr); +} + + +GF_Err stsz_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + u32 i, estSize; + GF_SampleSizeBox *ptr = (GF_SampleSizeBox *)s; + if (ptr == NULL) return GF_BAD_PARAM; + + e = gf_isom_full_box_read(s, bs); + if (e) return e; + //support for CompactSizes + if (s->type == GF_ISOM_BOX_TYPE_STSZ) { + ptr->sampleSize = gf_bs_read_u32(bs); + ptr->sampleCount = gf_bs_read_u32(bs); + ptr->size -= 8; + } else { + //24-reserved + gf_bs_read_int(bs, 24); + i = gf_bs_read_u8(bs); + ptr->sampleCount = gf_bs_read_u32(bs); + ptr->size -= 8; + switch (i) { + case 4: + case 8: + case 16: + ptr->sampleSize = i; + break; + default: + //try to fix the file + //no samples, no parsing pb + if (!ptr->sampleCount) { + ptr->sampleSize = 16; + return GF_OK; + } + estSize = (u32) (ptr->size) / ptr->sampleCount; + if (!estSize && ((ptr->sampleCount+1)/2 == (ptr->size)) ) { + ptr->sampleSize = 4; + break; + } else if (estSize == 1 || estSize == 2) { + ptr->sampleSize = 8 * estSize; + } else { + return GF_ISOM_INVALID_FILE; + } + } + } + if (s->type == GF_ISOM_BOX_TYPE_STSZ) { + if (! ptr->sampleSize && ptr->sampleCount) { + ptr->sizes = (u32 *) gf_malloc(ptr->sampleCount * sizeof(u32)); + ptr->alloc_size = ptr->sampleCount; + if (! ptr->sizes) return GF_OUT_OF_MEM; + for (i = 0; i < ptr->sampleCount; i++) { + ptr->sizes[i] = gf_bs_read_u32(bs); + } + } + } else { + //note we could optimize the mem usage by keeping the table compact + //in memory. But that would complicate both caching and editing + //we therefore keep all sizes as u32 and uncompress the table + ptr->sizes = (u32 *) gf_malloc(ptr->sampleCount * sizeof(u32)); + if (! ptr->sizes) return GF_OUT_OF_MEM; + ptr->alloc_size = ptr->sampleCount; + + for (i = 0; i < ptr->sampleCount; ) { + switch (ptr->sampleSize) { + case 4: + ptr->sizes[i] = gf_bs_read_int(bs, 4); + if (i+1 < ptr->sampleCount) { + ptr->sizes[i+1] = gf_bs_read_int(bs, 4); + } else { + //0 padding in odd sample count + gf_bs_read_int(bs, 4); + } + i += 2; + break; + default: + ptr->sizes[i] = gf_bs_read_int(bs, ptr->sampleSize); + i += 1; + break; + } + } + } + return GF_OK; +} + +GF_Box *stsz_New() +{ + GF_SampleSizeBox *tmp = (GF_SampleSizeBox *) gf_malloc(sizeof(GF_SampleSizeBox)); + if (tmp == NULL) return NULL; + memset(tmp, 0, sizeof(GF_SampleSizeBox)); + + gf_isom_full_box_init((GF_Box *)tmp); + //type is unknown here, can be regular or compact table + return (GF_Box *)tmp; +} + + +#ifndef GPAC_DISABLE_ISOM_WRITE + +GF_Err stsz_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + u32 i; + GF_SampleSizeBox *ptr = (GF_SampleSizeBox *)s; + + e = gf_isom_full_box_write(s, bs); + if (e) return e; + //in both versions this is still valid + if (ptr->type == GF_ISOM_BOX_TYPE_STSZ) { + gf_bs_write_u32(bs, ptr->sampleSize); + } else { + gf_bs_write_u24(bs, 0); + gf_bs_write_u8(bs, ptr->sampleSize); + } + gf_bs_write_u32(bs, ptr->sampleCount); + + if (ptr->type == GF_ISOM_BOX_TYPE_STSZ) { + if (! ptr->sampleSize) { + for (i = 0; i < ptr->sampleCount; i++) { + gf_bs_write_u32(bs, ptr->sizes[i]); + } + } + } else { + for (i = 0; i < ptr->sampleCount; ) { + switch (ptr->sampleSize) { + case 4: + gf_bs_write_int(bs, ptr->sizes[i], 4); + if (i+1 < ptr->sampleCount) { + gf_bs_write_int(bs, ptr->sizes[i+1], 4); + } else { + //0 padding in odd sample count + gf_bs_write_int(bs, 0, 4); + } + i += 2; + break; + default: + gf_bs_write_int(bs, ptr->sizes[i], ptr->sampleSize); + i += 1; + break; + } + } + } + return GF_OK; +} + +GF_Err stsz_Size(GF_Box *s) +{ + GF_Err e; + u32 i, fieldSize, size; + GF_SampleSizeBox *ptr = (GF_SampleSizeBox *)s; + e = gf_isom_full_box_get_size(s); + if (e) return e; + + ptr->size += 8; + if (!ptr->sampleCount) return GF_OK; + + //regular table + if (ptr->type == GF_ISOM_BOX_TYPE_STSZ) { + if (ptr->sampleSize) return GF_OK; + ptr->size += (4 * ptr->sampleCount); + return GF_OK; + } + + fieldSize = 4; + size = ptr->sizes[0]; + + for (i=0; i < ptr->sampleCount; i++) { + if (ptr->sizes[i] <= 0xF) continue; + //switch to 8-bit table + else if (ptr->sizes[i] <= 0xFF) { + fieldSize = 8; + } + //switch to 16-bit table + else if (ptr->sizes[i] <= 0xFFFF) { + fieldSize = 16; + } + //switch to 32-bit table + else { + fieldSize = 32; + } + + //check the size + if (size != ptr->sizes[i]) size = 0; + } + //if all samples are of the same size, switch to regular (more compact) + if (size) { + ptr->type = GF_ISOM_BOX_TYPE_STSZ; + ptr->sampleSize = size; + gf_free(ptr->sizes); + ptr->sizes = NULL; + } + + if (fieldSize == 32) { + //oops, doesn't fit in a compact table + ptr->type = GF_ISOM_BOX_TYPE_STSZ; + ptr->size += (4 * ptr->sampleCount); + return GF_OK; + } + + //make sure we are a compact table (no need to change the mem representation) + ptr->type = GF_ISOM_BOX_TYPE_STZ2; + ptr->sampleSize = fieldSize; + if (fieldSize == 4) { + //do not forget the 0 padding field for odd count + ptr->size += (ptr->sampleCount + 1) / 2; + } else { + ptr->size += (ptr->sampleCount) * (fieldSize/8); + } + return GF_OK; +} + +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + + +void stts_del(GF_Box *s) +{ + GF_TimeToSampleBox *ptr = (GF_TimeToSampleBox *)s; + if (ptr->entries) gf_free(ptr->entries); + gf_free(ptr); +} + + +GF_Err stts_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + u32 i; + GF_TimeToSampleBox *ptr = (GF_TimeToSampleBox *)s; + + e = gf_isom_full_box_read(s, bs); + if (e) return e; + +#ifndef GPAC_DISABLE_ISOM_WRITE + ptr->w_LastDTS = 0; +#endif + ptr->nb_entries = gf_bs_read_u32(bs); + ptr->alloc_size = ptr->nb_entries; + ptr->entries = (GF_SttsEntry *) gf_malloc(sizeof(GF_SttsEntry)*ptr->alloc_size); + if (!ptr->entries) return GF_OUT_OF_MEM; + for (i=0; inb_entries; i++) { + ptr->entries[i].sampleCount = gf_bs_read_u32(bs); + ptr->entries[i].sampleDelta = gf_bs_read_u32(bs); +#ifndef GPAC_DISABLE_ISOM_WRITE + ptr->w_currentSampleNum += ptr->entries[i].sampleCount; + ptr->w_LastDTS += ptr->entries[i].sampleCount * ptr->entries[i].sampleDelta; +#endif + } + //remove the last sample delta. +#ifndef GPAC_DISABLE_ISOM_WRITE + if (ptr->nb_entries) ptr->w_LastDTS -= ptr->entries[ptr->nb_entries-1].sampleDelta; +#endif + return GF_OK; +} + +GF_Box *stts_New() +{ + GF_TimeToSampleBox *tmp = (GF_TimeToSampleBox *) gf_malloc(sizeof(GF_TimeToSampleBox)); + if (tmp == NULL) return NULL; + memset(tmp, 0, sizeof(GF_TimeToSampleBox)); + gf_isom_full_box_init((GF_Box *)tmp); + tmp->type = GF_ISOM_BOX_TYPE_STTS; + return (GF_Box *)tmp; +} + + +#ifndef GPAC_DISABLE_ISOM_WRITE + +GF_Err stts_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + u32 i; + GF_TimeToSampleBox *ptr = (GF_TimeToSampleBox *)s; + + e = gf_isom_full_box_write(s, bs); + if (e) return e; + gf_bs_write_u32(bs, ptr->nb_entries); + for (i=0; inb_entries;i++) { + gf_bs_write_u32(bs, ptr->entries[i].sampleCount); + gf_bs_write_u32(bs, ptr->entries[i].sampleDelta); + } + return GF_OK; +} + +GF_Err stts_Size(GF_Box *s) +{ + GF_Err e; + GF_TimeToSampleBox *ptr = (GF_TimeToSampleBox *)s; + e = gf_isom_full_box_get_size(s); + if (e) return e; + ptr->size += 4 + (8 * ptr->nb_entries); + return GF_OK; +} + + +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + + +#ifndef GPAC_DISABLE_ISOM_FRAGMENTS + +void tfhd_del(GF_Box *s) +{ + GF_TrackFragmentHeaderBox *ptr = (GF_TrackFragmentHeaderBox *)s; + if (ptr == NULL) return; + gf_free(ptr); +} + +GF_Err tfhd_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_TrackFragmentHeaderBox *ptr = (GF_TrackFragmentHeaderBox *)s; + + e = gf_isom_full_box_read(s, bs); + if (e) return e; + + ptr->trackID = gf_bs_read_u32(bs); + + //The rest depends on the flags + if (ptr->flags & GF_ISOM_TRAF_BASE_OFFSET) { + ptr->base_data_offset = gf_bs_read_u64(bs); + } + if (ptr->flags & GF_ISOM_TRAF_SAMPLE_DESC) { + ptr->sample_desc_index = gf_bs_read_u32(bs); + } + if (ptr->flags & GF_ISOM_TRAF_SAMPLE_DUR) { + ptr->def_sample_duration = gf_bs_read_u32(bs); + } + if (ptr->flags & GF_ISOM_TRAF_SAMPLE_SIZE) { + ptr->def_sample_size = gf_bs_read_u32(bs); + } + if (ptr->flags & GF_ISOM_TRAF_SAMPLE_FLAGS) { + ptr->def_sample_flags = gf_bs_read_u32(bs); + } + return GF_OK; +} + +GF_Box *tfhd_New() +{ + GF_TrackFragmentHeaderBox *tmp = (GF_TrackFragmentHeaderBox *) gf_malloc(sizeof(GF_TrackFragmentHeaderBox)); + if (tmp == NULL) return NULL; + memset(tmp, 0, sizeof(GF_TrackFragmentHeaderBox)); + tmp->type = GF_ISOM_BOX_TYPE_TFHD; + //NO FLAGS SET BY DEFAULT + return (GF_Box *)tmp; +} + + + +#ifndef GPAC_DISABLE_ISOM_WRITE + + +GF_Err tfhd_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_TrackFragmentHeaderBox *ptr = (GF_TrackFragmentHeaderBox *) s; + if (!s) return GF_BAD_PARAM; + + e = gf_isom_full_box_write(s, bs); + if (e) return e; + gf_bs_write_u32(bs, ptr->trackID); + + //The rest depends on the flags + if (ptr->flags & GF_ISOM_TRAF_BASE_OFFSET) { + gf_bs_write_u64(bs, ptr->base_data_offset); + } + if (ptr->flags & GF_ISOM_TRAF_SAMPLE_DESC) { + gf_bs_write_u32(bs, ptr->sample_desc_index); + } + if (ptr->flags & GF_ISOM_TRAF_SAMPLE_DUR) { + gf_bs_write_u32(bs, ptr->def_sample_duration); + } + if (ptr->flags & GF_ISOM_TRAF_SAMPLE_SIZE) { + gf_bs_write_u32(bs, ptr->def_sample_size); + } + if (ptr->flags & GF_ISOM_TRAF_SAMPLE_FLAGS) { + gf_bs_write_u32(bs, ptr->def_sample_flags); + } + return GF_OK; +} + +GF_Err tfhd_Size(GF_Box *s) +{ + GF_Err e; + GF_TrackFragmentHeaderBox *ptr = (GF_TrackFragmentHeaderBox *)s; + e = gf_isom_full_box_get_size(s); + if (e) return e; + ptr->size += 4; + + //The rest depends on the flags + if (ptr->flags & GF_ISOM_TRAF_BASE_OFFSET) ptr->size += 8; + if (ptr->flags & GF_ISOM_TRAF_SAMPLE_DESC) ptr->size += 4; + if (ptr->flags & GF_ISOM_TRAF_SAMPLE_DUR) ptr->size += 4; + if (ptr->flags & GF_ISOM_TRAF_SAMPLE_SIZE) ptr->size += 4; + if (ptr->flags & GF_ISOM_TRAF_SAMPLE_FLAGS) ptr->size += 4; + return GF_OK; +} + + + +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + +#endif /*GPAC_DISABLE_ISOM_FRAGMENTS*/ + +void tims_del(GF_Box *s) +{ + GF_TSHintEntryBox *tims = (GF_TSHintEntryBox *)s; + gf_free(tims); +} + +GF_Err tims_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_TSHintEntryBox *ptr = (GF_TSHintEntryBox *)s; + ptr->timeScale = gf_bs_read_u32(bs); + return GF_OK; +} + +GF_Box *tims_New() +{ + GF_TSHintEntryBox *tmp = (GF_TSHintEntryBox *) gf_malloc(sizeof(GF_TSHintEntryBox)); + if (!tmp) return NULL; + memset(tmp, 0, sizeof(GF_TSHintEntryBox)); + tmp->type = GF_ISOM_BOX_TYPE_TIMS; + return (GF_Box *)tmp; +} + +#ifndef GPAC_DISABLE_ISOM_WRITE + +GF_Err tims_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_TSHintEntryBox *ptr = (GF_TSHintEntryBox *)s; + if (ptr == NULL) return GF_BAD_PARAM; + e = gf_isom_box_write_header(s, bs); + if (e) return e; + gf_bs_write_u32(bs, ptr->timeScale); + return GF_OK; +} + +GF_Err tims_Size(GF_Box *s) +{ + GF_Err e; + e = gf_isom_box_get_size(s); + if (e) return e; + s->size += 4; + return GF_OK; +} + +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + + +void tkhd_del(GF_Box *s) +{ + GF_TrackHeaderBox *ptr = (GF_TrackHeaderBox *)s; + if (ptr == NULL) return; + gf_free(ptr); + return; +} + + +GF_Err tkhd_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_TrackHeaderBox *ptr = (GF_TrackHeaderBox *)s; + e = gf_isom_full_box_read(s, bs); + if (e) return e; + if (ptr->version == 1) { + ptr->creationTime = gf_bs_read_u64(bs); + ptr->modificationTime = gf_bs_read_u64(bs); + ptr->trackID = gf_bs_read_u32(bs); + ptr->reserved1 = gf_bs_read_u32(bs); + ptr->duration = gf_bs_read_u64(bs); + } else { + ptr->creationTime = gf_bs_read_u32(bs); + ptr->modificationTime = gf_bs_read_u32(bs); + ptr->trackID = gf_bs_read_u32(bs); + ptr->reserved1 = gf_bs_read_u32(bs); + ptr->duration = gf_bs_read_u32(bs); + } + ptr->reserved2[0] = gf_bs_read_u32(bs); + ptr->reserved2[1] = gf_bs_read_u32(bs); + ptr->layer = gf_bs_read_u16(bs); + ptr->alternate_group = gf_bs_read_u16(bs); + ptr->volume = gf_bs_read_u16(bs); + ptr->reserved3 = gf_bs_read_u16(bs); + ptr->matrix[0] = gf_bs_read_u32(bs); + ptr->matrix[1] = gf_bs_read_u32(bs); + ptr->matrix[2] = gf_bs_read_u32(bs); + ptr->matrix[3] = gf_bs_read_u32(bs); + ptr->matrix[4] = gf_bs_read_u32(bs); + ptr->matrix[5] = gf_bs_read_u32(bs); + ptr->matrix[6] = gf_bs_read_u32(bs); + ptr->matrix[7] = gf_bs_read_u32(bs); + ptr->matrix[8] = gf_bs_read_u32(bs); + ptr->width = gf_bs_read_u32(bs); + ptr->height = gf_bs_read_u32(bs); + return GF_OK; +} + +GF_Box *tkhd_New() +{ + GF_TrackHeaderBox *tmp = (GF_TrackHeaderBox *) gf_malloc(sizeof(GF_TrackHeaderBox)); + if (tmp == NULL) return NULL; + memset(tmp, 0, sizeof(GF_TrackHeaderBox)); + gf_isom_full_box_init((GF_Box *)tmp); + tmp->type = GF_ISOM_BOX_TYPE_TKHD; + tmp->matrix[0] = 0x00010000; + tmp->matrix[4] = 0x00010000; + tmp->matrix[8] = 0x40000000; + return (GF_Box *)tmp; +} + + + +#ifndef GPAC_DISABLE_ISOM_WRITE + +GF_Err tkhd_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_TrackHeaderBox *ptr = (GF_TrackHeaderBox *)s; + + e = gf_isom_full_box_write(s, bs); + if (e) return e; + if (ptr->version == 1) { + gf_bs_write_u64(bs, ptr->creationTime); + gf_bs_write_u64(bs, ptr->modificationTime); + gf_bs_write_u32(bs, ptr->trackID); + gf_bs_write_u32(bs, ptr->reserved1); + gf_bs_write_u64(bs, ptr->duration); + } else { + gf_bs_write_u32(bs, (u32) ptr->creationTime); + gf_bs_write_u32(bs, (u32) ptr->modificationTime); + gf_bs_write_u32(bs, ptr->trackID); + gf_bs_write_u32(bs, ptr->reserved1); + gf_bs_write_u32(bs, (u32) ptr->duration); + } + gf_bs_write_u32(bs, ptr->reserved2[0]); + gf_bs_write_u32(bs, ptr->reserved2[1]); + gf_bs_write_u16(bs, ptr->layer); + gf_bs_write_u16(bs, ptr->alternate_group); + gf_bs_write_u16(bs, ptr->volume); + gf_bs_write_u16(bs, ptr->reserved3); + gf_bs_write_u32(bs, ptr->matrix[0]); + gf_bs_write_u32(bs, ptr->matrix[1]); + gf_bs_write_u32(bs, ptr->matrix[2]); + gf_bs_write_u32(bs, ptr->matrix[3]); + gf_bs_write_u32(bs, ptr->matrix[4]); + gf_bs_write_u32(bs, ptr->matrix[5]); + gf_bs_write_u32(bs, ptr->matrix[6]); + gf_bs_write_u32(bs, ptr->matrix[7]); + gf_bs_write_u32(bs, ptr->matrix[8]); + gf_bs_write_u32(bs, ptr->width); + gf_bs_write_u32(bs, ptr->height); + return GF_OK; +} + +GF_Err tkhd_Size(GF_Box *s) +{ + GF_Err e; + GF_TrackHeaderBox *ptr = (GF_TrackHeaderBox *)s; + e = gf_isom_full_box_get_size(s); + if (e) return e; + ptr->version = (ptr->duration>0xFFFFFFFF) ? 1 : 0; + ptr->size += (ptr->version == 1) ? 32 : 20; + ptr->size += 60; + return GF_OK; +} + +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + + + +#ifndef GPAC_DISABLE_ISOM_FRAGMENTS + +void traf_del(GF_Box *s) +{ + GF_TrackFragmentBox *ptr = (GF_TrackFragmentBox *)s; + if (ptr == NULL) return; + if (ptr->tfhd) gf_isom_box_del((GF_Box *) ptr->tfhd); + if (ptr->sdtp) gf_isom_box_del((GF_Box *) ptr->sdtp); + if (ptr->subs) gf_isom_box_del((GF_Box *) ptr->subs); + if (ptr->tfdt) gf_isom_box_del((GF_Box *) ptr->tfdt); + gf_isom_box_array_del(ptr->TrackRuns); + gf_free(ptr); +} + +GF_Err traf_AddBox(GF_Box *s, GF_Box *a) +{ + GF_TrackFragmentBox *ptr = (GF_TrackFragmentBox *)s; + + switch (a->type) { + case GF_ISOM_BOX_TYPE_TFHD: + if (ptr->tfhd) ERROR_ON_DUPLICATED_BOX(a, ptr) + ptr->tfhd = (GF_TrackFragmentHeaderBox *) a; + return GF_OK; + case GF_ISOM_BOX_TYPE_TRUN: + return gf_list_add(ptr->TrackRuns, a); + case GF_ISOM_BOX_TYPE_SDTP: + if (ptr->sdtp) ERROR_ON_DUPLICATED_BOX(a, ptr) + ptr->sdtp = (GF_SampleDependencyTypeBox *)a; + return GF_OK; + case GF_ISOM_BOX_TYPE_TFDT: + if (ptr->tfdt) ERROR_ON_DUPLICATED_BOX(a, ptr) + ptr->tfdt = (GF_TFBaseMediaDecodeTimeBox*) a; + return GF_OK; + case GF_ISOM_BOX_TYPE_SUBS: + if (ptr->subs) ERROR_ON_DUPLICATED_BOX(a, ptr) + ptr->subs = (GF_SubSampleInformationBox *)a; + return GF_OK; + default: + GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[iso file] Warning box %s unknown type - discarding\n", gf_4cc_to_str(a->type))); + gf_isom_box_del(a); + return GF_OK; + } +} + + +GF_Err traf_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e = gf_isom_read_box_list(s, bs, traf_AddBox); + return e; +} + +GF_Box *traf_New() +{ + GF_TrackFragmentBox *tmp; + GF_SAFEALLOC(tmp, GF_TrackFragmentBox); + if (tmp == NULL) return NULL; + tmp->type = GF_ISOM_BOX_TYPE_TRAF; + tmp->TrackRuns = gf_list_new(); + return (GF_Box *)tmp; +} + + + +#ifndef GPAC_DISABLE_ISOM_WRITE + + +GF_Err traf_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_TrackFragmentBox *ptr = (GF_TrackFragmentBox *) s; + if (!s) return GF_BAD_PARAM; + + e = gf_isom_box_write_header(s, bs); + if (e) return e; + + //Header first + if (ptr->tfhd) { + e = gf_isom_box_write((GF_Box *) ptr->tfhd, bs); + if (e) return e; + } + if (ptr->subs) { + e = gf_isom_box_write((GF_Box *) ptr->subs, bs); + if (e) return e; + } + if (ptr->tfdt) { + e = gf_isom_box_write((GF_Box *) ptr->tfdt, bs); + if (e) return e; + } + e = gf_isom_box_array_write(s, ptr->TrackRuns, bs); + if (e) return e; + if (ptr->sdtp) { + e = gf_isom_box_write((GF_Box *) ptr->sdtp, bs); + } + return e; +} + +GF_Err traf_Size(GF_Box *s) +{ + GF_Err e; + GF_TrackFragmentBox *ptr = (GF_TrackFragmentBox *)s; + + e = gf_isom_box_get_size(s); + if (e) return e; + if (ptr->tfhd) { + e = gf_isom_box_size((GF_Box *) ptr->tfhd); + if (e) return e; + ptr->size += ptr->tfhd->size; + } + if (ptr->subs) { + e = gf_isom_box_size((GF_Box *) ptr->subs); + if (e) return e; + ptr->size += ptr->subs->size; + } + if (ptr->sdtp) { + e = gf_isom_box_size((GF_Box *) ptr->sdtp); + if (e) return e; + ptr->size += ptr->sdtp->size; + } + if (ptr->tfdt) { + e = gf_isom_box_size((GF_Box *) ptr->tfdt); + if (e) return e; + ptr->size += ptr->tfdt->size; + } + return gf_isom_box_array_size(s, ptr->TrackRuns); +} + + + +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + +#endif /*GPAC_DISABLE_ISOM_FRAGMENTS*/ + + +void trak_del(GF_Box *s) +{ + GF_TrackBox *ptr = (GF_TrackBox *) s; + if (ptr == NULL) return; + + if (ptr->Header) gf_isom_box_del((GF_Box *)ptr->Header); + if (ptr->udta) gf_isom_box_del((GF_Box *)ptr->udta); + if (ptr->Media) gf_isom_box_del((GF_Box *)ptr->Media); + if (ptr->References) gf_isom_box_del((GF_Box *)ptr->References); + if (ptr->editBox) gf_isom_box_del((GF_Box *)ptr->editBox); + if (ptr->meta) gf_isom_box_del((GF_Box *)ptr->meta); + gf_isom_box_array_del(ptr->boxes); + if (ptr->name) gf_free(ptr->name); + gf_free(ptr); +} + +static void gf_isom_check_sample_desc(GF_TrackBox *trak) +{ + GF_BitStream *bs; + GF_GenericVisualSampleEntryBox *genv; + GF_GenericAudioSampleEntryBox *gena; + GF_GenericSampleEntryBox *genm; + GF_UnknownBox *a; + u32 i; + + i=0; + while ((a = (GF_UnknownBox*)gf_list_enum(trak->Media->information->sampleTable->SampleDescription->boxList, &i))) { + switch (a->type) { + case GF_ISOM_BOX_TYPE_MP4S: + case GF_ISOM_BOX_TYPE_ENCS: + case GF_ISOM_BOX_TYPE_MP4A: + case GF_ISOM_BOX_TYPE_ENCA: + case GF_ISOM_BOX_TYPE_MP4V: + case GF_ISOM_BOX_TYPE_ENCV: + case GF_ISOM_SUBTYPE_3GP_AMR: + case GF_ISOM_SUBTYPE_3GP_AMR_WB: + case GF_ISOM_SUBTYPE_3GP_EVRC: + case GF_ISOM_SUBTYPE_3GP_QCELP: + case GF_ISOM_SUBTYPE_3GP_SMV: + case GF_ISOM_SUBTYPE_3GP_H263: + case GF_ISOM_BOX_TYPE_GHNT: + case GF_ISOM_BOX_TYPE_RTP_STSD: + case GF_ISOM_BOX_TYPE_AVC1: + case GF_ISOM_BOX_TYPE_METX: + case GF_ISOM_BOX_TYPE_METT: + case GF_ISOM_BOX_TYPE_AVC2: + case GF_ISOM_BOX_TYPE_SVC1: + case GF_ISOM_BOX_TYPE_TX3G: + case GF_ISOM_BOX_TYPE_TEXT: + case GF_ISOM_BOX_TYPE_ENCT: + case GF_ISOM_BOX_TYPE_DIMS: + case GF_ISOM_BOX_TYPE_AC3: + case GF_ISOM_BOX_TYPE_LSR1: + continue; + default: + break; + } + /*only process visual or audio*/ + switch (trak->Media->handler->handlerType) { + case GF_ISOM_MEDIA_VISUAL: + /*remove entry*/ + gf_list_rem(trak->Media->information->sampleTable->SampleDescription->boxList, i-1); + genv = (GF_GenericVisualSampleEntryBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_GNRV); + bs = gf_bs_new(a->data, a->dataSize, GF_BITSTREAM_READ); + genv->size = a->size; + gf_isom_video_sample_entry_read((GF_VisualSampleEntryBox *) genv, bs); + genv->data_size = (u32) gf_bs_available(bs); + if (genv->data_size) { + genv->data = (char*)gf_malloc(sizeof(char) * genv->data_size); + gf_bs_read_data(bs, genv->data, genv->data_size); + } + gf_bs_del(bs); + genv->size = a->size; + genv->EntryType = a->type; + gf_isom_box_del((GF_Box *)a); + gf_list_insert(trak->Media->information->sampleTable->SampleDescription->boxList, genv, i-1); + break; + case GF_ISOM_MEDIA_AUDIO: + /*remove entry*/ + gf_list_rem(trak->Media->information->sampleTable->SampleDescription->boxList, i-1); + gena = (GF_GenericAudioSampleEntryBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_GNRA); + gena->size = a->size; + bs = gf_bs_new(a->data, a->dataSize, GF_BITSTREAM_READ); + gf_isom_audio_sample_entry_read((GF_AudioSampleEntryBox *) gena, bs); + gena->data_size = (u32) gf_bs_available(bs); + if (gena->data_size) { + gena->data = (char*)gf_malloc(sizeof(char) * gena->data_size); + gf_bs_read_data(bs, gena->data, gena->data_size); + } + gf_bs_del(bs); + gena->size = a->size; + gena->EntryType = a->type; + gf_isom_box_del((GF_Box *)a); + gf_list_insert(trak->Media->information->sampleTable->SampleDescription->boxList, gena, i-1); + break; + + default: + /*remove entry*/ + gf_list_rem(trak->Media->information->sampleTable->SampleDescription->boxList, i-1); + genm = (GF_GenericSampleEntryBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_GNRM); + genm->size = a->size; + bs = gf_bs_new(a->data, a->dataSize, GF_BITSTREAM_READ); + gf_bs_read_data(bs, genm->reserved, 6); + genm->dataReferenceIndex = gf_bs_read_u16(bs); + genm->data_size = (u32) gf_bs_available(bs); + if (genm->data_size) { + genm->data = (char*)gf_malloc(sizeof(char) * genm->data_size); + gf_bs_read_data(bs, genm->data, genm->data_size); + } + gf_bs_del(bs); + genm->size = a->size; + genm->EntryType = a->type; + gf_isom_box_del((GF_Box *)a); + gf_list_insert(trak->Media->information->sampleTable->SampleDescription->boxList, genm, i-1); + break; + } + + } +} + + +GF_Err trak_AddBox(GF_Box *s, GF_Box *a) +{ + GF_TrackBox *ptr = (GF_TrackBox *)s; + if (!a) return GF_OK; + switch(a->type) { + case GF_ISOM_BOX_TYPE_TKHD: + if (ptr->Header) ERROR_ON_DUPLICATED_BOX(a, ptr) + ptr->Header = (GF_TrackHeaderBox *)a; + return GF_OK; + case GF_ISOM_BOX_TYPE_EDTS: + if (ptr->editBox) ERROR_ON_DUPLICATED_BOX(a, ptr) + ptr->editBox = (GF_EditBox *)a; + return GF_OK; + case GF_ISOM_BOX_TYPE_UDTA: + if (ptr->udta) ERROR_ON_DUPLICATED_BOX(a, ptr) + ptr->udta = (GF_UserDataBox *)a; + return GF_OK; + case GF_ISOM_BOX_TYPE_META: + if (ptr->meta) ERROR_ON_DUPLICATED_BOX(a, ptr) + ptr->meta = (GF_MetaBox *)a; + return GF_OK; + case GF_ISOM_BOX_TYPE_TREF: + if (ptr->References) ERROR_ON_DUPLICATED_BOX(a, ptr) + ptr->References = (GF_TrackReferenceBox *)a; + return GF_OK; + case GF_ISOM_BOX_TYPE_MDIA: + if (ptr->Media) ERROR_ON_DUPLICATED_BOX(a, ptr) + ptr->Media = (GF_MediaBox *)a; + ((GF_MediaBox *)a)->mediaTrack = ptr; + return GF_OK; + default: + GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[iso file] Warning box %s unknown type - discarding\n", gf_4cc_to_str(a->type))); + gf_isom_box_del(a); + return GF_OK; + } + return GF_OK; +} + + +GF_Err trak_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_TrackBox *ptr = (GF_TrackBox *)s; + e = gf_isom_read_box_list(s, bs, trak_AddBox); + if (e) return e; + gf_isom_check_sample_desc(ptr); + return GF_OK; +} + +GF_Box *trak_New() +{ + GF_TrackBox *tmp = (GF_TrackBox *) gf_malloc(sizeof(GF_TrackBox)); + if (tmp == NULL) return NULL; + memset(tmp, 0, sizeof(GF_TrackBox)); + tmp->type = GF_ISOM_BOX_TYPE_TRAK; + tmp->boxes = gf_list_new(); + return (GF_Box *)tmp; +} + + +#ifndef GPAC_DISABLE_ISOM_WRITE + +GF_Err trak_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_TrackBox *ptr = (GF_TrackBox *)s; + e = gf_isom_box_write_header(s, bs); + if (e) return e; + + if (ptr->Header) { + e = gf_isom_box_write((GF_Box *) ptr->Header, bs); + if (e) return e; + } + if (ptr->References) { + e = gf_isom_box_write((GF_Box *) ptr->References, bs); + if (e) return e; + } + if (ptr->editBox) { + e = gf_isom_box_write((GF_Box *) ptr->editBox, bs); + if (e) return e; + } + if (ptr->Media) { + e = gf_isom_box_write((GF_Box *) ptr->Media, bs); + if (e) return e; + } + if (ptr->meta) { + e = gf_isom_box_write((GF_Box *) ptr->meta, bs); + if (e) return e; + } + if (ptr->udta) { + e = gf_isom_box_write((GF_Box *) ptr->udta, bs); + if (e) return e; + } + return gf_isom_box_array_write(s, ptr->boxes, bs); +} + +GF_Err trak_Size(GF_Box *s) +{ + GF_Err e; + GF_TrackBox *ptr = (GF_TrackBox *)s; + + e = gf_isom_box_get_size(s); + if (e) return e; + + if (ptr->Header) { + e = gf_isom_box_size((GF_Box *) ptr->Header); + if (e) return e; + ptr->size += ptr->Header->size; + } + if (ptr->udta) { + e = gf_isom_box_size((GF_Box *) ptr->udta); + if (e) return e; + ptr->size += ptr->udta->size; + } + if (ptr->References) { + e = gf_isom_box_size((GF_Box *) ptr->References); + if (e) return e; + ptr->size += ptr->References->size; + } + if (ptr->editBox) { + e = gf_isom_box_size((GF_Box *) ptr->editBox); + if (e) return e; + ptr->size += ptr->editBox->size; + } + if (ptr->Media) { + e = gf_isom_box_size((GF_Box *) ptr->Media); + if (e) return e; + ptr->size += ptr->Media->size; + } + if (ptr->meta) { + e = gf_isom_box_size((GF_Box *) ptr->meta); + if (e) return e; + ptr->size += ptr->meta->size; + } + return gf_isom_box_array_size(s, ptr->boxes); +} + +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + + +void tref_del(GF_Box *s) +{ + GF_TrackReferenceBox *ptr = (GF_TrackReferenceBox *)s; + if (ptr == NULL) return; + gf_isom_box_array_del(ptr->boxList); + gf_free(ptr); +} + + +GF_Err tref_AddBox(GF_Box *s, GF_Box *a) +{ + GF_TrackReferenceBox *ptr = (GF_TrackReferenceBox *)s; + return gf_list_add(ptr->boxList, a); +} + +GF_Err tref_Read(GF_Box *s, GF_BitStream *bs) +{ + return gf_isom_read_box_list_ex(s, bs, tref_AddBox, s->type); +} + +GF_Box *tref_New() +{ + GF_TrackReferenceBox *tmp = (GF_TrackReferenceBox *) gf_malloc(sizeof(GF_TrackReferenceBox)); + if (tmp == NULL) return NULL; + memset(tmp, 0, sizeof(GF_TrackReferenceBox)); + tmp->boxList = gf_list_new(); + if (!tmp->boxList) { + gf_free(tmp); + return NULL; + } + tmp->type = GF_ISOM_BOX_TYPE_TREF; + return (GF_Box *)tmp; +} + + + +#ifndef GPAC_DISABLE_ISOM_WRITE + +GF_Err tref_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_TrackReferenceBox *ptr = (GF_TrackReferenceBox *)s; + e = gf_isom_box_write_header(s, bs); + if (e) return e; + return gf_isom_box_array_write(s, ptr->boxList, bs); +} + +GF_Err tref_Size(GF_Box *s) +{ + GF_Err e; + GF_TrackReferenceBox *ptr = (GF_TrackReferenceBox *)s; + e = gf_isom_box_get_size(s); + if (e) return e; + return gf_isom_box_array_size(s, ptr->boxList); +} + +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + +void reftype_del(GF_Box *s) +{ + GF_TrackReferenceTypeBox *ptr = (GF_TrackReferenceTypeBox *)s; + if (!ptr) return; + if (ptr->trackIDs) gf_free(ptr->trackIDs); + gf_free(ptr); +} + + +GF_Err reftype_Read(GF_Box *s, GF_BitStream *bs) +{ + u32 bytesToRead; + u32 i; + GF_TrackReferenceTypeBox *ptr = (GF_TrackReferenceTypeBox *)s; + + bytesToRead = (u32) (ptr->size); + if (!bytesToRead) return GF_OK; + + ptr->trackIDCount = (u32) (bytesToRead) / sizeof(u32); + ptr->trackIDs = (u32 *) gf_malloc(ptr->trackIDCount * sizeof(u32)); + if (!ptr->trackIDs) return GF_OUT_OF_MEM; + + for (i = 0; i < ptr->trackIDCount; i++) { + ptr->trackIDs[i] = gf_bs_read_u32(bs); + } + return GF_OK; +} + +GF_Box *reftype_New() +{ + GF_TrackReferenceTypeBox *tmp = (GF_TrackReferenceTypeBox *) gf_malloc(sizeof(GF_TrackReferenceTypeBox)); + if (tmp == NULL) return NULL; + memset(tmp, 0, sizeof(GF_TrackReferenceTypeBox)); + tmp->type = GF_ISOM_BOX_TYPE_REFT; + return (GF_Box *)tmp; +} + + +GF_Err reftype_AddRefTrack(GF_TrackReferenceTypeBox *ref, u32 trackID, u16 *outRefIndex) +{ + u32 i; + if (!ref || !trackID) return GF_BAD_PARAM; + + if (outRefIndex) *outRefIndex = 0; + //don't add a dep if already here !! + for (i = 0; i < ref->trackIDCount; i++) { + if (ref->trackIDs[i] == trackID) { + if (outRefIndex) *outRefIndex = i+1; + return GF_OK; + } + } + + ref->trackIDs = (u32 *) gf_realloc(ref->trackIDs, (ref->trackIDCount + 1) * sizeof(u32) ); + if (!ref->trackIDs) return GF_OUT_OF_MEM; + ref->trackIDs[ref->trackIDCount] = trackID; + ref->trackIDCount++; + if (outRefIndex) *outRefIndex = ref->trackIDCount; + return GF_OK; +} + + + +#ifndef GPAC_DISABLE_ISOM_WRITE + +GF_Err reftype_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + u32 i; + GF_TrackReferenceTypeBox *ptr = (GF_TrackReferenceTypeBox *)s; + ptr->type = ptr->reference_type; + e = gf_isom_box_write_header(s, bs); + ptr->type = GF_ISOM_BOX_TYPE_REFT; + if (e) return e; + for (i = 0; i < ptr->trackIDCount; i++) { + gf_bs_write_u32(bs, ptr->trackIDs[i]); + } + return GF_OK; +} + + +GF_Err reftype_Size(GF_Box *s) +{ + GF_Err e; + GF_TrackReferenceTypeBox *ptr = (GF_TrackReferenceTypeBox *)s; + e = gf_isom_box_get_size(s); + if (e) return e; + ptr->size += (ptr->trackIDCount * sizeof(u32)); + return GF_OK; +} + +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + + + +#ifndef GPAC_DISABLE_ISOM_FRAGMENTS + +void trex_del(GF_Box *s) +{ + GF_TrackExtendsBox *ptr = (GF_TrackExtendsBox *)s; + if (ptr == NULL) return; + gf_free(ptr); +} + + +GF_Err trex_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_TrackExtendsBox *ptr = (GF_TrackExtendsBox *)s; + + e = gf_isom_full_box_read(s, bs); + if (e) return e; + + ptr->trackID = gf_bs_read_u32(bs); + ptr->def_sample_desc_index = gf_bs_read_u32(bs); + ptr->def_sample_duration = gf_bs_read_u32(bs); + ptr->def_sample_size = gf_bs_read_u32(bs); + ptr->def_sample_flags = gf_bs_read_u32(bs); + return GF_OK; +} + +GF_Box *trex_New() +{ + GF_TrackExtendsBox *tmp = (GF_TrackExtendsBox *) gf_malloc(sizeof(GF_TrackExtendsBox)); + if (tmp == NULL) return NULL; + memset(tmp, 0, sizeof(GF_TrackExtendsBox)); + tmp->type = GF_ISOM_BOX_TYPE_TREX; + return (GF_Box *)tmp; +} + + + +#ifndef GPAC_DISABLE_ISOM_WRITE + + +GF_Err trex_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_TrackExtendsBox *ptr = (GF_TrackExtendsBox *) s; + if (!s) return GF_BAD_PARAM; + e = gf_isom_full_box_write(s, bs); + if (e) return e; + + gf_bs_write_u32(bs, ptr->trackID); + gf_bs_write_u32(bs, ptr->def_sample_desc_index); + gf_bs_write_u32(bs, ptr->def_sample_duration); + gf_bs_write_u32(bs, ptr->def_sample_size); + gf_bs_write_u32(bs, ptr->def_sample_flags); + return GF_OK; +} + +GF_Err trex_Size(GF_Box *s) +{ + GF_Err e; + GF_TrackExtendsBox *ptr = (GF_TrackExtendsBox *)s; + e = gf_isom_full_box_get_size(s); + if (e) return e; + ptr->size += 20; + return GF_OK; +} + + + +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + +#endif /*GPAC_DISABLE_ISOM_FRAGMENTS*/ + + + +#ifndef GPAC_DISABLE_ISOM_FRAGMENTS + +void trun_del(GF_Box *s) +{ + GF_TrunEntry *p; + GF_TrackFragmentRunBox *ptr = (GF_TrackFragmentRunBox *)s; + if (ptr == NULL) return; + + while (gf_list_count(ptr->entries)) { + p = (GF_TrunEntry*)gf_list_get(ptr->entries, 0); + gf_list_rem(ptr->entries, 0); + gf_free(p); + } + gf_list_del(ptr->entries); + if (ptr->cache) gf_bs_del(ptr->cache); + gf_free(ptr); +} + +GF_Err trun_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + u32 i; + GF_TrunEntry *p; + GF_TrackFragmentRunBox *ptr = (GF_TrackFragmentRunBox *)s; + + e = gf_isom_full_box_read(s, bs); + if (e) return e; + + //check this is a good file + if ((ptr->flags & GF_ISOM_TRUN_FIRST_FLAG) && (ptr->flags & GF_ISOM_TRUN_FLAGS)) + return GF_ISOM_INVALID_FILE; + + ptr->sample_count = gf_bs_read_u32(bs); + + //The rest depends on the flags + if (ptr->flags & GF_ISOM_TRUN_DATA_OFFSET) { + ptr->data_offset = gf_bs_read_u32(bs); + ptr->size -= 4; + } + if (ptr->flags & GF_ISOM_TRUN_FIRST_FLAG) { + ptr->first_sample_flags = gf_bs_read_u32(bs); + ptr->size -= 4; + } + + //read each entry (even though nothing may be written) + for (i=0; isample_count; i++) { + u32 trun_size = 0; + p = (GF_TrunEntry *) gf_malloc(sizeof(GF_TrunEntry)); + if (!p) return GF_OUT_OF_MEM; + memset(p, 0, sizeof(GF_TrunEntry)); + + if (ptr->flags & GF_ISOM_TRUN_DURATION) { + p->Duration = gf_bs_read_u32(bs); + trun_size += 4; + } + if (ptr->flags & GF_ISOM_TRUN_SIZE) { + p->size = gf_bs_read_u32(bs); + trun_size += 4; + } + //SHOULDN'T BE USED IF GF_ISOM_TRUN_FIRST_FLAG IS DEFINED + if (ptr->flags & GF_ISOM_TRUN_FLAGS) { + p->flags = gf_bs_read_u32(bs); + trun_size += 4; + } + if (ptr->flags & GF_ISOM_TRUN_CTS_OFFSET) { + p->CTS_Offset = gf_bs_read_u32(bs); + } + gf_list_add(ptr->entries, p); + if (ptr->sizesize-=trun_size; + } + return GF_OK; +} + +GF_Box *trun_New() +{ + GF_TrackFragmentRunBox *tmp = (GF_TrackFragmentRunBox *) gf_malloc(sizeof(GF_TrackFragmentRunBox)); + if (tmp == NULL) return NULL; + memset(tmp, 0, sizeof(GF_TrackFragmentRunBox)); + tmp->type = GF_ISOM_BOX_TYPE_TRUN; + tmp->entries = gf_list_new(); + //NO FLAGS SET BY DEFAULT + return (GF_Box *)tmp; +} + + + +#ifndef GPAC_DISABLE_ISOM_WRITE + + +GF_Err trun_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_TrunEntry *p; + GF_Err e; + u32 i, count; + GF_TrackFragmentRunBox *ptr = (GF_TrackFragmentRunBox *) s; + if (!s) return GF_BAD_PARAM; + + e = gf_isom_full_box_write(s, bs); + if (e) return e; + + gf_bs_write_u32(bs, ptr->sample_count); + + //The rest depends on the flags + if (ptr->flags & GF_ISOM_TRUN_DATA_OFFSET) { + gf_bs_write_u32(bs, ptr->data_offset); + } + if (ptr->flags & GF_ISOM_TRUN_FIRST_FLAG) { + gf_bs_write_u32(bs, ptr->first_sample_flags); + } + + //if nothing to do, this will be skipped automatically + count = gf_list_count(ptr->entries); + for (i=0; ientries, i); + + if (ptr->flags & GF_ISOM_TRUN_DURATION) { + gf_bs_write_u32(bs, p->Duration); + } + if (ptr->flags & GF_ISOM_TRUN_SIZE) { + gf_bs_write_u32(bs, p->size); + } + //SHOULDN'T BE USED IF GF_ISOM_TRUN_FIRST_FLAG IS DEFINED + if (ptr->flags & GF_ISOM_TRUN_FLAGS) { + gf_bs_write_u32(bs, p->flags); + } + if (ptr->flags & GF_ISOM_TRUN_CTS_OFFSET) { + gf_bs_write_u32(bs, p->CTS_Offset); + } + } + return GF_OK; +} + +GF_Err trun_Size(GF_Box *s) +{ + GF_Err e; + u32 i, count; + GF_TrackFragmentRunBox *ptr = (GF_TrackFragmentRunBox *)s; + + e = gf_isom_full_box_get_size(s); + if (e) return e; + + ptr->size += 4; + //The rest depends on the flags + if (ptr->flags & GF_ISOM_TRUN_DATA_OFFSET) ptr->size += 4; + if (ptr->flags & GF_ISOM_TRUN_FIRST_FLAG) ptr->size += 4; + + //if nothing to do, this will be skipped automatically + count = gf_list_count(ptr->entries); + for (i=0; iflags & GF_ISOM_TRUN_DURATION) ptr->size += 4; + if (ptr->flags & GF_ISOM_TRUN_SIZE) ptr->size += 4; + //SHOULDN'T BE USED IF GF_ISOM_TRUN_FIRST_FLAG IS DEFINED + if (ptr->flags & GF_ISOM_TRUN_FLAGS) ptr->size += 4; + if (ptr->flags & GF_ISOM_TRUN_CTS_OFFSET) ptr->size += 4; + } + + return GF_OK; +} + + + +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + +#endif /*GPAC_DISABLE_ISOM_FRAGMENTS*/ + + +void tsro_del(GF_Box *s) +{ + GF_TimeOffHintEntryBox *tsro = (GF_TimeOffHintEntryBox *)s; + gf_free(tsro); +} + +GF_Err tsro_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_TimeOffHintEntryBox *ptr = (GF_TimeOffHintEntryBox *)s; + ptr->TimeOffset = gf_bs_read_u32(bs); + return GF_OK; +} + +GF_Box *tsro_New() +{ + GF_TimeOffHintEntryBox *tmp = (GF_TimeOffHintEntryBox *) gf_malloc(sizeof(GF_TimeOffHintEntryBox)); + if (!tmp) return NULL; + memset(tmp, 0, sizeof(GF_TimeOffHintEntryBox)); + tmp->type = GF_ISOM_BOX_TYPE_TSRO; + return (GF_Box *)tmp; +} + + +#ifndef GPAC_DISABLE_ISOM_WRITE +GF_Err tsro_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_TimeOffHintEntryBox *ptr = (GF_TimeOffHintEntryBox *)s; + if (ptr == NULL) return GF_BAD_PARAM; + + e = gf_isom_box_write_header(s, bs); + if (e) return e; + gf_bs_write_u32(bs, ptr->TimeOffset); + return GF_OK; +} + +GF_Err tsro_Size(GF_Box *s) +{ + GF_Err e; + e = gf_isom_box_get_size(s); + if (e) return e; + s->size += 4; + return GF_OK; +} +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + + +void udta_del(GF_Box *s) +{ + u32 i; + GF_UserDataMap *map; + GF_UserDataBox *ptr = (GF_UserDataBox *)s; + if (ptr == NULL) return; + i=0; + while ((map = (GF_UserDataMap *)gf_list_enum(ptr->recordList, &i))) { + gf_isom_box_array_del(map->boxList); + gf_free(map); + } + gf_list_del(ptr->recordList); + gf_free(ptr); +} + +GF_UserDataMap *udta_getEntry(GF_UserDataBox *ptr, u32 box_type, bin128 *uuid) +{ + u32 i; + GF_UserDataMap *map; + if (ptr == NULL) return NULL; + i=0; + while ((map = (GF_UserDataMap *)gf_list_enum(ptr->recordList, &i))) { + if (map->boxType == box_type) { + if ((box_type != GF_ISOM_BOX_TYPE_UUID) || !uuid) return map; + if (!memcmp(map->uuid, *uuid, 16)) return map; + } + } + return NULL; +} + +GF_Err udta_AddBox(GF_UserDataBox *ptr, GF_Box *a) +{ + GF_Err e; + GF_UserDataMap *map; + if (!ptr) return GF_BAD_PARAM; + if (!a) return GF_OK; + + map = udta_getEntry(ptr, a->type, (a->type==GF_ISOM_BOX_TYPE_UUID) ? & ((GF_UUIDBox *)a)->uuid : NULL); + if (map == NULL) { + map = (GF_UserDataMap *) gf_malloc(sizeof(GF_UserDataMap)); + if (map == NULL) return GF_OUT_OF_MEM; + memset(map, 0, sizeof(GF_UserDataMap)); + + map->boxType = a->type; + if (a->type == GF_ISOM_BOX_TYPE_UUID) + memcpy(map->uuid, ((GF_UUIDBox *)a)->uuid, 16); + map->boxList = gf_list_new(); + if (!map->boxList) { + gf_free(map); + return GF_OUT_OF_MEM; + } + e = gf_list_add(ptr->recordList, map); + if (e) return e; + } + return gf_list_add(map->boxList, a); +} + + +GF_Err udta_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + u32 sub_type; + GF_Box *a; + GF_UserDataBox *ptr = (GF_UserDataBox *)s; + while (ptr->size) { + /*if no udta type coded, break*/ + sub_type = gf_bs_peek_bits(bs, 32, 0); + if (sub_type) { + e = gf_isom_parse_box(&a, bs); + if (e) return e; + e = udta_AddBox(ptr, a); + if (e) return e; + if (ptr->sizesize) return GF_ISOM_INVALID_FILE; + ptr->size -= a->size; + } else { + gf_bs_read_u32(bs); + ptr->size -= 4; + } + } + return GF_OK; +} + +GF_Box *udta_New() +{ + GF_UserDataBox *tmp = (GF_UserDataBox *) gf_malloc(sizeof(GF_UserDataBox)); + if (tmp == NULL) return NULL; + memset(tmp, 0, sizeof(GF_UserDataBox)); + tmp->recordList = gf_list_new(); + if (!tmp->recordList) { + gf_free(tmp); + return NULL; + } + tmp->type = GF_ISOM_BOX_TYPE_UDTA; + return (GF_Box *)tmp; +} + + +#ifndef GPAC_DISABLE_ISOM_WRITE + +GF_Err udta_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + u32 i; + GF_UserDataMap *map; + GF_UserDataBox *ptr = (GF_UserDataBox *)s; + + e = gf_isom_box_write_header(s, bs); + if (e) return e; + i=0; + while ((map = (GF_UserDataMap *)gf_list_enum(ptr->recordList, &i))) { + //warning: here we are not passing the actual "parent" of the list + //but the UDTA box. The parent itself is not an box, we don't care about it + e = gf_isom_box_array_write(s, map->boxList, bs); + if (e) return e; + } + return GF_OK; +} + +GF_Err udta_Size(GF_Box *s) +{ + GF_Err e; + u32 i; + GF_UserDataMap *map; + GF_UserDataBox *ptr = (GF_UserDataBox *)s; + + e = gf_isom_box_get_size(s); + if (e) return e; + i=0; + while ((map = (GF_UserDataMap *)gf_list_enum(ptr->recordList, &i))) { + //warning: here we are not passing the actual "parent" of the list + //but the UDTA box. The parent itself is not an box, we don't care about it + e = gf_isom_box_array_size(s, map->boxList); + if (e) return e; + } + return GF_OK; +} + +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + + +void vmhd_del(GF_Box *s) +{ + GF_VideoMediaHeaderBox *ptr = (GF_VideoMediaHeaderBox *)s; + if (ptr == NULL) return; + gf_free(ptr); +} + + +GF_Err vmhd_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_VideoMediaHeaderBox *ptr = (GF_VideoMediaHeaderBox *)s; + e = gf_isom_full_box_read(s, bs); + if (e) return e; + ptr->reserved = gf_bs_read_u64(bs); + return GF_OK; +} + +GF_Box *vmhd_New() +{ + GF_VideoMediaHeaderBox *tmp = (GF_VideoMediaHeaderBox *) gf_malloc(sizeof(GF_VideoMediaHeaderBox)); + if (tmp == NULL) return NULL; + memset(tmp, 0, sizeof(GF_VideoMediaHeaderBox)); + gf_isom_full_box_init((GF_Box *)tmp); + tmp->flags = 1; + tmp->type = GF_ISOM_BOX_TYPE_VMHD; + return (GF_Box *)tmp; +} + + + +#ifndef GPAC_DISABLE_ISOM_WRITE + +GF_Err vmhd_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_VideoMediaHeaderBox *ptr = (GF_VideoMediaHeaderBox *)s; + + e = gf_isom_full_box_write(s, bs); + if (e) return e; + gf_bs_write_u64(bs, ptr->reserved); + return GF_OK; +} + +GF_Err vmhd_Size(GF_Box *s) +{ + GF_Err e; + GF_VideoMediaHeaderBox *ptr = (GF_VideoMediaHeaderBox *)s; + e = gf_isom_full_box_get_size(s); + if (e) return e; + ptr->size += 8; + return GF_OK; +} + +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + + +void void_del(GF_Box *s) +{ + gf_free(s); +} + + +GF_Err void_Read(GF_Box *s, GF_BitStream *bs) +{ + if (s->size) return GF_ISOM_INVALID_FILE; + return GF_OK; +} + +GF_Box *void_New() +{ + GF_Box *tmp = (GF_Box *) gf_malloc(sizeof(GF_Box)); + if (!tmp) return NULL; + tmp->type = GF_ISOM_BOX_TYPE_VOID; + return tmp; +} + + +#ifndef GPAC_DISABLE_ISOM_WRITE + +GF_Err void_Write(GF_Box *s, GF_BitStream *bs) +{ + gf_bs_write_u32(bs, 0); + return GF_OK; +} + +GF_Err void_Size(GF_Box *s) +{ + s->size = 4; + return GF_OK; +} + +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + + + +GF_Box *pdin_New() +{ + GF_ProgressiveDownloadBox *tmp = (GF_ProgressiveDownloadBox*) gf_malloc(sizeof(GF_ProgressiveDownloadBox)); + if (tmp == NULL) return NULL; + memset(tmp, 0, sizeof(GF_ProgressiveDownloadBox)); + gf_isom_full_box_init((GF_Box *)tmp); + tmp->flags = 1; + tmp->type = GF_ISOM_BOX_TYPE_PDIN; + return (GF_Box *)tmp; +} + + +void pdin_del(GF_Box *s) +{ + GF_ProgressiveDownloadBox *ptr = (GF_ProgressiveDownloadBox*)s; + if (ptr == NULL) return; + if (ptr->rates) gf_free(ptr->rates); + if (ptr->times) gf_free(ptr->times); + gf_free(ptr); +} + + +GF_Err pdin_Read(GF_Box *s, GF_BitStream *bs) +{ + u32 i; + GF_Err e; + GF_ProgressiveDownloadBox *ptr = (GF_ProgressiveDownloadBox*)s; + + e = gf_isom_full_box_read(s, bs); + if (e) return e; + + ptr->count = (u32) (ptr->size) / 8; + ptr->rates = (u32*)gf_malloc(sizeof(u32)*ptr->count); + ptr->times = (u32*)gf_malloc(sizeof(u32)*ptr->count); + for (i=0; icount; i++) { + ptr->rates[i] = gf_bs_read_u32(bs); + ptr->times[i] = gf_bs_read_u32(bs); + } + return GF_OK; +} + + +#ifndef GPAC_DISABLE_ISOM_WRITE + +GF_Err pdin_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + u32 i; + GF_ProgressiveDownloadBox *ptr = (GF_ProgressiveDownloadBox *)s; + e = gf_isom_full_box_write(s, bs); + if (e) return e; + for (i=0; icount; i++) { + gf_bs_write_u32(bs, ptr->rates[i]); + gf_bs_write_u32(bs, ptr->times[i]); + } + return GF_OK; +} + +GF_Err pdin_Size(GF_Box *s) +{ + GF_Err e; + GF_ProgressiveDownloadBox *ptr = (GF_ProgressiveDownloadBox *)s; + e = gf_isom_full_box_get_size(s); + if (e) return e; + ptr->size += 8*ptr->count; + return GF_OK; +} + +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + + + + +GF_Box *sdtp_New() +{ + GF_SampleDependencyTypeBox *tmp = (GF_SampleDependencyTypeBox*) gf_malloc(sizeof(GF_SampleDependencyTypeBox)); + if (tmp == NULL) return NULL; + memset(tmp, 0, sizeof(GF_SampleDependencyTypeBox)); + gf_isom_full_box_init((GF_Box *)tmp); + tmp->flags = 1; + tmp->type = GF_ISOM_BOX_TYPE_SDTP; + return (GF_Box *)tmp; +} + + +void sdtp_del(GF_Box *s) +{ + GF_SampleDependencyTypeBox *ptr = (GF_SampleDependencyTypeBox*)s; + if (ptr == NULL) return; + if (ptr->sample_info) gf_free(ptr->sample_info); + gf_free(ptr); +} + + +GF_Err sdtp_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_SampleDependencyTypeBox *ptr = (GF_SampleDependencyTypeBox*)s; + + e = gf_isom_full_box_read(s, bs); + if (e) return e; + /*out-of-order sdtp, assume no padding at the end*/ + if (!ptr->sampleCount) ptr->sampleCount = (u32) (ptr->size - 8); + ptr->sample_info = (u8 *) gf_malloc(sizeof(u8)*ptr->sampleCount); + gf_bs_read_data(bs, (char*)ptr->sample_info, ptr->sampleCount); + ptr->size -= ptr->sampleCount; + return GF_OK; +} + + +#ifndef GPAC_DISABLE_ISOM_WRITE + +GF_Err sdtp_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_SampleDependencyTypeBox *ptr = (GF_SampleDependencyTypeBox *)s; + e = gf_isom_full_box_write(s, bs); + if (e) return e; + gf_bs_write_data(bs, (char*)ptr->sample_info, ptr->sampleCount); + return GF_OK; +} + +GF_Err sdtp_Size(GF_Box *s) +{ + GF_Err e; + GF_SampleDependencyTypeBox *ptr = (GF_SampleDependencyTypeBox *)s; + e = gf_isom_full_box_get_size(s); + if (e) return e; + ptr->size += ptr->sampleCount; + return GF_OK; +} + +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + + +GF_Box *pasp_New() +{ + GF_PixelAspectRatioBox *tmp; + GF_SAFEALLOC(tmp, GF_PixelAspectRatioBox); + if (tmp == NULL) return NULL; + tmp->type = GF_ISOM_BOX_TYPE_PASP; + return (GF_Box *)tmp; +} + + +void pasp_del(GF_Box *s) +{ + GF_PixelAspectRatioBox *ptr = (GF_PixelAspectRatioBox*)s; + if (ptr == NULL) return; + gf_free(ptr); +} + + +GF_Err pasp_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_PixelAspectRatioBox *ptr = (GF_PixelAspectRatioBox*)s; + ptr->hSpacing = gf_bs_read_u32(bs); + ptr->vSpacing = gf_bs_read_u32(bs); + ptr->size -= 8; + return GF_OK; +} + + +#ifndef GPAC_DISABLE_ISOM_WRITE + +GF_Err pasp_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_PixelAspectRatioBox *ptr = (GF_PixelAspectRatioBox *)s; + GF_Err e = gf_isom_box_write_header(s, bs); + if (e) return e; + gf_bs_write_u32(bs, ptr->hSpacing); + gf_bs_write_u32(bs, ptr->vSpacing); + return GF_OK; +} + +GF_Err pasp_Size(GF_Box *s) +{ + GF_Err e = gf_isom_box_get_size(s); + if (e) return e; + s->size += 8; + return GF_OK; +} + +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + + + + +GF_Box *metx_New(u32 type) +{ + GF_MetaDataSampleEntryBox *tmp; + GF_SAFEALLOC(tmp, GF_MetaDataSampleEntryBox); + if (tmp == NULL) return NULL; + tmp->type = type; + return (GF_Box *)tmp; +} + + +void metx_del(GF_Box *s) +{ + GF_MetaDataSampleEntryBox *ptr = (GF_MetaDataSampleEntryBox*)s; + if (ptr == NULL) return; + if (ptr->content_encoding) gf_free(ptr->content_encoding); + if (ptr->mime_type_or_namespace) gf_free(ptr->mime_type_or_namespace); + if (ptr->xml_schema_loc) gf_free(ptr->xml_schema_loc); + if (ptr->bitrate) gf_isom_box_del((GF_Box *) ptr->bitrate); + gf_free(ptr); +} + + +GF_Err metx_AddBox(GF_Box *s, GF_Box *a) +{ + GF_MPEGVisualSampleEntryBox *ptr = (GF_MPEGVisualSampleEntryBox *)s; + switch (a->type) { + case GF_ISOM_BOX_TYPE_SINF: + if (ptr->protection_info) return GF_ISOM_INVALID_FILE; + ptr->protection_info = (GF_ProtectionInfoBox*)a; + break; + case GF_ISOM_BOX_TYPE_BTRT: + if (ptr->bitrate) ERROR_ON_DUPLICATED_BOX(a, ptr) + ptr->bitrate = (GF_MPEG4BitRateBox *)a; + break; + default: + GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[iso file] Warning box %s unknown type - discarding\n", gf_4cc_to_str(a->type))); + gf_isom_box_del(a); + break; + } + return GF_OK; +} + +GF_Err metx_Read(GF_Box *s, GF_BitStream *bs) +{ + u32 size, i; + char *str; + GF_MetaDataSampleEntryBox *ptr = (GF_MetaDataSampleEntryBox*)s; + + gf_bs_read_data(bs, ptr->reserved, 6); + ptr->dataReferenceIndex = gf_bs_read_u16(bs); + + size = (u32) ptr->size - 8; + str = (char *) gf_malloc(sizeof(char)*size); + + i=0; + + while (size) { + str[i] = gf_bs_read_u8(bs); + size--; + if (!str[i]) + break; + i++; + } + if (i) ptr->content_encoding = gf_strdup(str); + + i=0; + while (size) { + str[i] = gf_bs_read_u8(bs); + size--; + if (!str[i]) + break; + i++; + } + if (i) ptr->mime_type_or_namespace = gf_strdup(str); + + if (ptr->type==GF_ISOM_BOX_TYPE_METX) { + i=0; + while (size) { + str[i] = gf_bs_read_u8(bs); + size--; + if (!str[i]) + break; + i++; + } + if (i) ptr->xml_schema_loc = gf_strdup(str); + } + ptr->size = size; + gf_free(str); + return gf_isom_read_box_list(s, bs, metx_AddBox); +} + + +#ifndef GPAC_DISABLE_ISOM_WRITE + +GF_Err metx_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_MetaDataSampleEntryBox *ptr = (GF_MetaDataSampleEntryBox *)s; + GF_Err e = gf_isom_box_write_header(s, bs); + if (e) return e; + + gf_bs_write_data(bs, ptr->reserved, 6); + gf_bs_write_u16(bs, ptr->dataReferenceIndex); + + if (ptr->content_encoding) + gf_bs_write_data(bs, ptr->content_encoding, strlen(ptr->content_encoding)); + gf_bs_write_u8(bs, 0); + + if (ptr->mime_type_or_namespace) + gf_bs_write_data(bs, ptr->mime_type_or_namespace, strlen(ptr->mime_type_or_namespace)); + gf_bs_write_u8(bs, 0); + + if (ptr->xml_schema_loc) + gf_bs_write_data(bs, ptr->xml_schema_loc, strlen(ptr->xml_schema_loc)); + gf_bs_write_u8(bs, 0); + + if (ptr->bitrate) { + e = gf_isom_box_write((GF_Box *)ptr->bitrate, bs); + if (e) return e; + } + if (ptr->protection_info) { + e = gf_isom_box_write((GF_Box *)ptr->protection_info, bs); + if (e) return e; + } + return GF_OK; +} + +GF_Err metx_Size(GF_Box *s) +{ + GF_MetaDataSampleEntryBox *ptr = (GF_MetaDataSampleEntryBox *)s; + GF_Err e = gf_isom_box_get_size(s); + if (e) return e; + ptr->size += 8; + + if (ptr->content_encoding) + ptr->size += strlen(ptr->content_encoding); + ptr->size++; + if (ptr->mime_type_or_namespace) + ptr->size += strlen(ptr->mime_type_or_namespace); + ptr->size++; + if (ptr->xml_schema_loc) + ptr->size += strlen(ptr->xml_schema_loc); + ptr->size++; + + if (ptr->bitrate) { + e = gf_isom_box_size((GF_Box *)ptr->bitrate); + if (e) return e; + ptr->size += ptr->bitrate->size; + } + if (ptr->protection_info) { + e = gf_isom_box_size((GF_Box *)ptr->protection_info); + if (e) return e; + ptr->size += ptr->protection_info->size; + } + return GF_OK; +} + +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + + + +GF_Box *dac3_New() +{ + GF_AC3ConfigBox *tmp = (GF_AC3ConfigBox *) gf_malloc(sizeof(GF_AC3ConfigBox)); + if (tmp == NULL) return NULL; + memset(tmp, 0, sizeof(GF_AC3ConfigBox)); + tmp->type = GF_ISOM_BOX_TYPE_DAC3; + return (GF_Box *)tmp; +} + +void dac3_del(GF_Box *s) +{ + GF_AC3ConfigBox *ptr = (GF_AC3ConfigBox *)s; + gf_free(ptr); +} + + +GF_Err dac3_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_AC3ConfigBox *ptr = (GF_AC3ConfigBox *)s; + if (ptr == NULL) return GF_BAD_PARAM; + + ptr->cfg.fscod = gf_bs_read_int(bs, 2); + ptr->cfg.bsid = gf_bs_read_int(bs, 5); + ptr->cfg.bsmod = gf_bs_read_int(bs, 3); + ptr->cfg.acmod = gf_bs_read_int(bs, 3); + ptr->cfg.lfon = gf_bs_read_int(bs, 1); + ptr->cfg.brcode = gf_bs_read_int(bs, 5); + gf_bs_read_int(bs, 5); + return GF_OK; +} + + +#ifndef GPAC_DISABLE_ISOM_WRITE + +GF_Err dac3_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_AC3ConfigBox *ptr = (GF_AC3ConfigBox *)s; + e = gf_isom_box_write_header(s, bs); + if (e) return e; + + gf_bs_write_int(bs, ptr->cfg.fscod, 2); + gf_bs_write_int(bs, ptr->cfg.bsid, 5); + gf_bs_write_int(bs, ptr->cfg.bsmod, 3); + gf_bs_write_int(bs, ptr->cfg.acmod, 3); + gf_bs_write_int(bs, ptr->cfg.lfon, 1); + gf_bs_write_int(bs, ptr->cfg.brcode, 5); + gf_bs_write_int(bs, 0, 5); + return GF_OK; +} + +GF_Err dac3_Size(GF_Box *s) +{ + GF_Err e; + e = gf_isom_box_get_size(s); + if (e) return e; + s->size += 3; + return GF_OK; +} + +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + + + +void ac3_del(GF_Box *s) +{ + GF_AC3SampleEntryBox *ptr = (GF_AC3SampleEntryBox *)s; + if (ptr == NULL) return; + if (ptr->info) gf_isom_box_del((GF_Box *)ptr->info); + if (ptr->protection_info) gf_isom_box_del((GF_Box *)ptr->protection_info); + gf_free(ptr); +} + + +GF_Err ac3_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_AC3SampleEntryBox *ptr = (GF_AC3SampleEntryBox *)s; + e = gf_isom_audio_sample_entry_read((GF_AudioSampleEntryBox*)s, bs); + if (e) return e; + e = gf_isom_parse_box((GF_Box **)&ptr->info, bs); + if (e) return e; + return GF_OK; +} + +GF_Box *ac3_New() +{ + GF_AC3SampleEntryBox *tmp; + GF_SAFEALLOC(tmp, GF_AC3SampleEntryBox); + if (tmp == NULL) return NULL; + gf_isom_audio_sample_entry_init((GF_AudioSampleEntryBox*)tmp); + tmp->type = GF_ISOM_BOX_TYPE_AC3; + return (GF_Box *)tmp; +} + + +#ifndef GPAC_DISABLE_ISOM_WRITE + +GF_Err ac3_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_AC3SampleEntryBox *ptr = (GF_AC3SampleEntryBox *)s; + e = gf_isom_box_write_header(s, bs); + if (e) return e; + gf_isom_audio_sample_entry_write((GF_AudioSampleEntryBox*)s, bs); + return gf_isom_box_write((GF_Box *)ptr->info, bs); +} + +GF_Err ac3_Size(GF_Box *s) +{ + GF_Err e; + GF_AC3SampleEntryBox *ptr = (GF_AC3SampleEntryBox *)s; + e = gf_isom_box_get_size(s); + if (e) return e; + gf_isom_audio_sample_entry_size((GF_AudioSampleEntryBox*)s); + e = gf_isom_box_size((GF_Box *)ptr->info); + if (e) return e; + ptr->size += ptr->info->size; + return GF_OK; +} + +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + + +void lsrc_del(GF_Box *s) +{ + GF_LASERConfigurationBox *ptr = (GF_LASERConfigurationBox *)s; + if (ptr == NULL) return; + if (ptr->hdr) gf_free(ptr->hdr); + gf_free(ptr); +} + + +GF_Err lsrc_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_LASERConfigurationBox *ptr = (GF_LASERConfigurationBox *)s; + ptr->hdr_size = (u32) ptr->size; + ptr->hdr = (char *) gf_malloc(sizeof(char)*ptr->hdr_size); + gf_bs_read_data(bs, ptr->hdr, ptr->hdr_size); + return GF_OK; +} + +GF_Box *lsrc_New() +{ + GF_LASERConfigurationBox *tmp; + GF_SAFEALLOC(tmp, GF_LASERConfigurationBox); + if (tmp == NULL) return NULL; + tmp->type = GF_ISOM_BOX_TYPE_LSRC; + return (GF_Box *)tmp; +} + + +#ifndef GPAC_DISABLE_ISOM_WRITE + +GF_Err lsrc_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_LASERConfigurationBox *ptr = (GF_LASERConfigurationBox *)s; + e = gf_isom_box_write_header(s, bs); + if (e) return e; + gf_bs_write_data(bs, ptr->hdr, ptr->hdr_size); + return GF_OK; +} + +GF_Err lsrc_Size(GF_Box *s) +{ + GF_Err e; + GF_LASERConfigurationBox *ptr = (GF_LASERConfigurationBox *)s; + e = gf_isom_box_get_size(s); + if (e) return e; + ptr->size += ptr->hdr_size; + return GF_OK; +} + +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + + +void lsr1_del(GF_Box *s) +{ + GF_LASeRSampleEntryBox *ptr = (GF_LASeRSampleEntryBox *)s; + if (ptr == NULL) return; + if (ptr->slc) gf_odf_desc_del((GF_Descriptor *)ptr->slc); + if (ptr->lsr_config) gf_isom_box_del((GF_Box *) ptr->lsr_config); + if (ptr->bitrate) gf_isom_box_del((GF_Box *) ptr->bitrate); + if (ptr->descr) gf_isom_box_del((GF_Box *) ptr->descr); + gf_free(ptr); +} + +GF_Err lsr1_AddBox(GF_Box *s, GF_Box *a) +{ + GF_LASeRSampleEntryBox *ptr = (GF_LASeRSampleEntryBox *)s; + switch (a->type) { + case GF_ISOM_BOX_TYPE_LSRC: + if (ptr->lsr_config) ERROR_ON_DUPLICATED_BOX(a, ptr) + ptr->lsr_config = (GF_LASERConfigurationBox *)a; + break; + case GF_ISOM_BOX_TYPE_BTRT: + if (ptr->bitrate) ERROR_ON_DUPLICATED_BOX(a, ptr) + ptr->bitrate = (GF_MPEG4BitRateBox *)a; + break; + case GF_ISOM_BOX_TYPE_M4DS: + if (ptr->descr) ERROR_ON_DUPLICATED_BOX(a, ptr) + ptr->descr = (GF_MPEG4ExtensionDescriptorsBox *)a; + break; + default: + GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[iso file] Warning box %s unknown type - discarding\n", gf_4cc_to_str(a->type))); + gf_isom_box_del(a); + break; + } + return GF_OK; +} + +GF_Err lsr1_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_LASeRSampleEntryBox *ptr = (GF_LASeRSampleEntryBox*)s; + gf_bs_read_data(bs, ptr->reserved, 6); + ptr->dataReferenceIndex = gf_bs_read_u16(bs); + ptr->size -= 8; + return gf_isom_read_box_list(s, bs, lsr1_AddBox); +} + +GF_Box *lsr1_New() +{ + GF_LASeRSampleEntryBox *tmp; + GF_SAFEALLOC(tmp, GF_LASeRSampleEntryBox); + if (tmp == NULL) return NULL; + tmp->type = GF_ISOM_BOX_TYPE_LSR1; + return (GF_Box *)tmp; +} + + +#ifndef GPAC_DISABLE_ISOM_WRITE + + +GF_Err lsr1_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_LASeRSampleEntryBox *ptr = (GF_LASeRSampleEntryBox *)s; + e = gf_isom_box_write_header(s, bs); + if (e) return e; + + gf_bs_write_data(bs, ptr->reserved, 6); + gf_bs_write_u16(bs, ptr->dataReferenceIndex); + if (ptr->lsr_config) { + e = gf_isom_box_write((GF_Box *)ptr->lsr_config, bs); + if (e) return e; + } + if (ptr->descr) { + e = gf_isom_box_write((GF_Box *)ptr->descr, bs); + if (e) return e; + } + if (ptr->bitrate) { + e = gf_isom_box_write((GF_Box *)ptr->bitrate, bs); + if (e) return e; + } + return e; +} + +GF_Err lsr1_Size(GF_Box *s) +{ + GF_Err e; + GF_LASeRSampleEntryBox *ptr = (GF_LASeRSampleEntryBox *)s; + e = gf_isom_box_get_size(s); + if (e) return e; + s->size += 8; + if (ptr->lsr_config) { + e = gf_isom_box_size((GF_Box *)ptr->lsr_config); + if (e) return e; + ptr->size += ptr->lsr_config->size; + } + if (ptr->bitrate) { + e = gf_isom_box_size((GF_Box *)ptr->bitrate); + if (e) return e; + ptr->size += ptr->bitrate->size; + } + if (ptr->descr) { + e = gf_isom_box_size((GF_Box *)ptr->bitrate); + if (e) return e; + ptr->size += ptr->bitrate->size; + } + return GF_OK; +} + +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + +#ifndef GPAC_DISABLE_ISOM_FRAGMENTS + +void sidx_del(GF_Box *s) +{ + GF_SegmentIndexBox *ptr = (GF_SegmentIndexBox *) s; + if (ptr == NULL) return; + if (ptr->refs) gf_free(ptr->refs); + gf_free(ptr); +} + +GF_Err sidx_Read(GF_Box *s,GF_BitStream *bs) +{ + GF_Err e; + u32 i; + GF_SegmentIndexBox *ptr = (GF_SegmentIndexBox*) s; + e = gf_isom_full_box_read(s, bs); + if (e) return e; + ptr->reference_ID = gf_bs_read_u32(bs); + ptr->timescale = gf_bs_read_u32(bs); + ptr->size -= 8; + if (ptr->version==0) { + ptr->earliest_presentation_time = gf_bs_read_u32(bs); + ptr->first_offset = gf_bs_read_u32(bs); + ptr->size -= 8; + } else { + ptr->earliest_presentation_time = gf_bs_read_u64(bs); + ptr->first_offset = gf_bs_read_u64(bs); + ptr->size -= 16; + } + gf_bs_read_u16(bs); /* reserved */ + ptr->nb_refs = gf_bs_read_u16(bs); + ptr->size -= 4; + ptr->refs = (GF_SIDXReference *) gf_malloc(sizeof(GF_SIDXReference)*ptr->nb_refs); + for (i=0; inb_refs; i++) { + ptr->refs[i].reference_type = gf_bs_read_int(bs, 1); + ptr->refs[i].reference_size = gf_bs_read_int(bs, 31); + ptr->refs[i].subsegment_duration = gf_bs_read_u32(bs); + ptr->refs[i].contains_RAP = gf_bs_read_int(bs, 1); + ptr->refs[i].RAP_delta_time = gf_bs_read_int(bs, 31); + ptr->size -= 12; + } + return GF_OK; +} + +GF_Box *sidx_New() +{ + GF_SegmentIndexBox *tmp; + + GF_SAFEALLOC(tmp, GF_SegmentIndexBox); + if (tmp == NULL) return NULL; + gf_isom_full_box_init((GF_Box *)tmp); + tmp->type = GF_ISOM_BOX_TYPE_SIDX; + return (GF_Box *)tmp; +} + + +#ifndef GPAC_DISABLE_ISOM_WRITE + +GF_Err sidx_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + u32 i; + GF_SegmentIndexBox *ptr = (GF_SegmentIndexBox*) s; + + e = gf_isom_full_box_write(s, bs); + if (e) return e; + + gf_bs_write_u32(bs, ptr->reference_ID); + gf_bs_write_u32(bs, ptr->timescale); + if (ptr->version==0) { + gf_bs_write_u32(bs, (u32) ptr->earliest_presentation_time); + gf_bs_write_u32(bs, (u32) ptr->first_offset); + } else { + gf_bs_write_u64(bs, ptr->earliest_presentation_time); + gf_bs_write_u64(bs, ptr->first_offset); + } + gf_bs_write_u16(bs, 0); + gf_bs_write_u16(bs, ptr->nb_refs); + for (i=0; inb_refs; i++ ) { + gf_bs_write_int(bs, ptr->refs[i].reference_type, 1); + gf_bs_write_int(bs, ptr->refs[i].reference_size, 31); + gf_bs_write_u32(bs, ptr->refs[i].subsegment_duration); + gf_bs_write_int(bs, ptr->refs[i].contains_RAP, 1); + gf_bs_write_int(bs, ptr->refs[i].RAP_delta_time, 31); + } + return GF_OK; +} + +GF_Err sidx_Size(GF_Box *s) +{ + GF_Err e; + GF_SegmentIndexBox *ptr = (GF_SegmentIndexBox*) s; + e = gf_isom_full_box_get_size(s); + if (e) return e; + + ptr->size += 12; + if (ptr->version==0) { + ptr->size += 8; + } else { + ptr->size += 16; + } + ptr->size += ptr->nb_refs * 12; + return GF_OK; +} + +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + +#endif /*GPAC_DISABLE_ISOM_FRAGMENTS*/ + + +GF_Box *subs_New() +{ + GF_SubSampleInformationBox *tmp = (GF_SubSampleInformationBox *) gf_malloc(sizeof(GF_SubSampleInformationBox)); + if (tmp == NULL) return NULL; + memset(tmp, 0, sizeof(GF_SubSampleInformationBox)); + tmp->type = GF_ISOM_BOX_TYPE_SUBS; + tmp->Samples = gf_list_new(); + return (GF_Box *)tmp; +} + +void subs_del(GF_Box *s) +{ + GF_SubSampleInformationBox *ptr = (GF_SubSampleInformationBox *)s; + if (ptr == NULL) return; + + while (gf_list_count(ptr->Samples)) { + GF_SampleEntry *pSamp; + pSamp = (GF_SampleEntry*)gf_list_get(ptr->Samples, 0); + while (gf_list_count(pSamp->SubSamples)) { + GF_SubSampleEntry *pSubSamp; + pSubSamp = (GF_SubSampleEntry*) gf_list_get(pSamp->SubSamples, 0); + gf_free(pSubSamp); + gf_list_rem(pSamp->SubSamples, 0); + } + gf_list_del(pSamp->SubSamples); + gf_free(pSamp); + gf_list_rem(ptr->Samples, 0); + } + gf_list_del(ptr->Samples); + gf_free(ptr); +} + +GF_Err subs_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + u32 i, j, entry_count; + u16 subsample_count; + GF_SampleEntry *pSamp; + GF_SubSampleEntry *pSubSamp; + GF_SubSampleInformationBox *ptr = (GF_SubSampleInformationBox *) s; + + if (!s) return GF_BAD_PARAM; + e = gf_isom_full_box_write(s, bs); + if (e) return e; + entry_count = gf_list_count(ptr->Samples); + gf_bs_write_u32(bs, entry_count); + + for (i=0; iSamples, i); + subsample_count = gf_list_count(pSamp->SubSamples); + gf_bs_write_u32(bs, pSamp->sample_delta); + gf_bs_write_u16(bs, subsample_count); + + for (j=0; jSubSamples, j); + if (ptr->version == 1) { + gf_bs_write_u32(bs, pSubSamp->subsample_size); + } else { + gf_bs_write_u16(bs, pSubSamp->subsample_size); + } + gf_bs_write_u8(bs, pSubSamp->subsample_priority); + gf_bs_write_u8(bs, pSubSamp->discardable); + gf_bs_write_u32(bs, pSubSamp->reserved); + } + } + return e; +} + +GF_Err subs_Size(GF_Box *s) +{ + GF_Err e; + GF_SubSampleInformationBox *ptr = (GF_SubSampleInformationBox *) s; + GF_SampleEntry *pSamp; + u32 entry_count, i; + u16 subsample_count; + + // determine the size of the full box + e = gf_isom_full_box_get_size(s); + if (e) return e; + + // add 4 byte for entry_count + ptr->size += 4; + entry_count = gf_list_count(ptr->Samples); + for (i=0; iSamples, i); + subsample_count = gf_list_count(pSamp->SubSamples); + // 4 byte for sample_delta, 2 byte for subsample_count + // and 6 + (4 or 2) bytes for each subsample + ptr->size += 4 + 2 + subsample_count * (6 + (ptr->version==1 ? 4 : 2)); + } + return GF_OK; +} + + +GF_Err subs_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_SubSampleInformationBox *ptr = (GF_SubSampleInformationBox *)s; + u32 entry_count, i, j; + u16 subsample_count; + + e = gf_isom_full_box_read(s, bs); + if (e) return e; + + entry_count = gf_bs_read_u32(bs); + ptr->size -= 4; + + for (i=0; iSubSamples = gf_list_new(); + pSamp->sample_delta = gf_bs_read_u32(bs); + subsample_count = gf_bs_read_u16(bs); + + for (j=0; jversion==1) { + pSubSamp->subsample_size = gf_bs_read_u32(bs); + } else { + pSubSamp->subsample_size = gf_bs_read_u16(bs); + } + pSubSamp->subsample_priority = gf_bs_read_u8(bs); + pSubSamp->discardable = gf_bs_read_u8(bs); + pSubSamp->reserved = gf_bs_read_u32(bs); + + gf_list_add(pSamp->SubSamples, pSubSamp); + } + gf_list_add(ptr->Samples, pSamp); + } + return GF_OK; +} + + +GF_Box *tfdt_New() +{ + GF_TFBaseMediaDecodeTimeBox *tmp; + GF_SAFEALLOC(tmp, GF_TFBaseMediaDecodeTimeBox); + tmp->type = GF_ISOM_BOX_TYPE_TFDT; + tmp->version = 0; + return (GF_Box *)tmp; +} + +void tfdt_del(GF_Box *s) +{ + gf_free(s); +} + +/*this is using chpl format according to some NeroRecode samples*/ +GF_Err tfdt_Read(GF_Box *s,GF_BitStream *bs) +{ + GF_Err e; + GF_TFBaseMediaDecodeTimeBox *ptr = (GF_TFBaseMediaDecodeTimeBox *)s; + e = gf_isom_full_box_read(s, bs); + if (e) return e; + + if (ptr->version==1) { + ptr->baseMediaDecodeTime = gf_bs_read_u64(bs); + ptr->size -= 8; + } else { + ptr->baseMediaDecodeTime = (u32) gf_bs_read_u32(bs); + ptr->size -= 4; + } + return GF_OK; +} + +#ifndef GPAC_DISABLE_ISOM_WRITE + +GF_Err tfdt_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_TFBaseMediaDecodeTimeBox *ptr = (GF_TFBaseMediaDecodeTimeBox *) s; + e = gf_isom_full_box_write(s, bs); + if (e) return e; + + if (ptr->version==1) { + gf_bs_write_u64(bs, ptr->baseMediaDecodeTime); + } else { + gf_bs_write_u32(bs, (u32) ptr->baseMediaDecodeTime); + } + return GF_OK; +} + +GF_Err tfdt_Size(GF_Box *s) +{ + GF_Err e; + GF_TFBaseMediaDecodeTimeBox *ptr = (GF_TFBaseMediaDecodeTimeBox *)s; + e = gf_isom_full_box_get_size(s); + if (e) return e; + if (ptr->baseMediaDecodeTime<=0xFFFFFFFF) { + ptr->version = 0; + ptr->size += 4; + } else { + ptr->version = 1; + ptr->size += 8; + } + return GF_OK; +} + +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + + + + +GF_Box *rvcc_New() +{ + GF_RVCConfigurationBox *tmp; + GF_SAFEALLOC(tmp, GF_RVCConfigurationBox); + tmp->type = GF_ISOM_BOX_TYPE_RVCC; + return (GF_Box *)tmp; +} + +void rvcc_del(GF_Box *s) +{ + gf_free(s); +} + +/*this is using chpl format according to some NeroRecode samples*/ +GF_Err rvcc_Read(GF_Box *s,GF_BitStream *bs) +{ + GF_RVCConfigurationBox *ptr = (GF_RVCConfigurationBox*)s; + ptr->predefined_rvc_config = gf_bs_read_u16(bs); + ptr->size -= 2; + if (!ptr->predefined_rvc_config) { + ptr->rvc_meta_idx = gf_bs_read_u16(bs); + ptr->size -= 2; + } + return GF_OK; +} + +#ifndef GPAC_DISABLE_ISOM_WRITE + +GF_Err rvcc_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_RVCConfigurationBox *ptr = (GF_RVCConfigurationBox*) s; + + e = gf_isom_box_write_header(s, bs); + if (e) return e; + + gf_bs_write_u16(bs, ptr->predefined_rvc_config); + if (!ptr->predefined_rvc_config) { + gf_bs_write_u16(bs, ptr->rvc_meta_idx); + } + return GF_OK; +} + +GF_Err rvcc_Size(GF_Box *s) +{ + GF_Err e; + GF_RVCConfigurationBox *ptr = (GF_RVCConfigurationBox *)s; + e = gf_isom_box_get_size(s); + if (e) return e; + ptr->size += 2; + if (! ptr->predefined_rvc_config) ptr->size += 2; + return GF_OK; +} + +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + + + + + +#endif /*GPAC_DISABLE_ISOM*/ diff --git a/src/gpacmp4/box_code_isma.c b/src/gpacmp4/box_code_isma.c new file mode 100644 index 00000000..aab77ae9 --- /dev/null +++ b/src/gpacmp4/box_code_isma.c @@ -0,0 +1,889 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Cyril Concolato 2005 + * All rights reserved + * + * This file is part of GPAC / ISO Media File Format sub-project + * + * GPAC is free software you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation either version 2, or (at your option) + * any later version. + * + * GPAC 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include + +#ifndef GPAC_DISABLE_ISOM + +/* ProtectionInfo Box */ +GF_Box *sinf_New() +{ + GF_ProtectionInfoBox *tmp = (GF_ProtectionInfoBox *) gf_malloc(sizeof(GF_ProtectionInfoBox)); + if (tmp == NULL) return NULL; + memset(tmp, 0, sizeof(GF_ProtectionInfoBox)); + tmp->type = GF_ISOM_BOX_TYPE_SINF; + return (GF_Box *)tmp; +} + +void sinf_del(GF_Box *s) +{ + GF_ProtectionInfoBox *ptr = (GF_ProtectionInfoBox *)s; + if (ptr == NULL) return; + if (ptr->original_format) gf_isom_box_del((GF_Box *)ptr->original_format); + if (ptr->info) gf_isom_box_del((GF_Box *)ptr->info); + if (ptr->scheme_type) gf_isom_box_del((GF_Box *)ptr->scheme_type); + gf_free(ptr); +} + +GF_Err sinf_AddBox(GF_Box *s, GF_Box *a) +{ + GF_ProtectionInfoBox *ptr = (GF_ProtectionInfoBox *)s; + switch (a->type) { + case GF_ISOM_BOX_TYPE_FRMA: + if (ptr->original_format) return GF_ISOM_INVALID_FILE; + ptr->original_format = (GF_OriginalFormatBox*)a; + break; + case GF_ISOM_BOX_TYPE_SCHM: + if (ptr->scheme_type) return GF_ISOM_INVALID_FILE; + ptr->scheme_type = (GF_SchemeTypeBox*)a; + break; + case GF_ISOM_BOX_TYPE_SCHI: + if (ptr->info) return GF_ISOM_INVALID_FILE; + ptr->info = (GF_SchemeInformationBox*)a; + break; + default: + gf_isom_box_del(a); + break; + } + return GF_OK; +} + +GF_Err sinf_Read(GF_Box *s, GF_BitStream *bs) +{ + return gf_isom_read_box_list(s, bs, sinf_AddBox); +} + +#ifndef GPAC_DISABLE_ISOM_WRITE +GF_Err sinf_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_ProtectionInfoBox *ptr = (GF_ProtectionInfoBox *)s; + if (!s) return GF_BAD_PARAM; + e = gf_isom_box_write_header(s, bs); + if (e) return e; + //frma + e = gf_isom_box_write((GF_Box *) ptr->original_format, bs); + if (e) return e; + // schm + e = gf_isom_box_write((GF_Box *) ptr->scheme_type, bs); + if (e) return e; + // schi + e = gf_isom_box_write((GF_Box *) ptr->info, bs); + if (e) return e; + return GF_OK; +} + +GF_Err sinf_Size(GF_Box *s) +{ + GF_Err e; + GF_ProtectionInfoBox *ptr = (GF_ProtectionInfoBox *)s; + if (!s) return GF_BAD_PARAM; + e = gf_isom_box_get_size(s); + if (e) return e; + e = gf_isom_box_size((GF_Box *) ptr->original_format); + if (e) return e; + ptr->size += ptr->original_format->size; + e = gf_isom_box_size((GF_Box *) ptr->scheme_type); + if (e) return e; + ptr->size += ptr->scheme_type->size; + e = gf_isom_box_size((GF_Box *) ptr->info); + if (e) return e; + ptr->size += ptr->info->size; + return GF_OK; +} +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + +/* OriginalFormat Box */ +GF_Box *frma_New() +{ + GF_OriginalFormatBox *tmp = (GF_OriginalFormatBox *) gf_malloc(sizeof(GF_OriginalFormatBox)); + if (tmp == NULL) return NULL; + memset(tmp, 0, sizeof(GF_OriginalFormatBox)); + tmp->type = GF_ISOM_BOX_TYPE_FRMA; + return (GF_Box *)tmp; +} + +void frma_del(GF_Box *s) +{ + GF_OriginalFormatBox *ptr = (GF_OriginalFormatBox *)s; + if (ptr == NULL) return; + gf_free(ptr); +} + +GF_Err frma_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_OriginalFormatBox *ptr = (GF_OriginalFormatBox *)s; + ptr->data_format = gf_bs_read_u32(bs); + return GF_OK; +} + +#ifndef GPAC_DISABLE_ISOM_WRITE +GF_Err frma_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_OriginalFormatBox *ptr = (GF_OriginalFormatBox *)s; + if (!s) return GF_BAD_PARAM; + e = gf_isom_box_write_header(s, bs); + if (e) return e; + gf_bs_write_u32(bs, ptr->data_format); + return GF_OK; +} + +GF_Err frma_Size(GF_Box *s) +{ + GF_Err e; + GF_OriginalFormatBox *ptr = (GF_OriginalFormatBox *)s; + if (!s) return GF_BAD_PARAM; + e = gf_isom_box_get_size(s); + if (e) return e; + ptr->size += 4; + return GF_OK; +} +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + +/* SchemeType Box */ +GF_Box *schm_New() +{ + GF_SchemeTypeBox *tmp = (GF_SchemeTypeBox *) gf_malloc(sizeof(GF_SchemeTypeBox)); + if (tmp == NULL) return NULL; + memset(tmp, 0, sizeof(GF_SchemeTypeBox)); + gf_isom_full_box_init((GF_Box *)tmp); + tmp->type = GF_ISOM_BOX_TYPE_SCHM; + return (GF_Box *)tmp; +} + +void schm_del(GF_Box *s) +{ + GF_SchemeTypeBox *ptr = (GF_SchemeTypeBox *)s; + if (ptr == NULL) return; + if (ptr->URI) gf_free(ptr->URI); + gf_free(ptr); +} + +GF_Err schm_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_SchemeTypeBox *ptr = (GF_SchemeTypeBox *)s; + e = gf_isom_full_box_read(s, bs); + if (e) return e; + ptr->scheme_type = gf_bs_read_u32(bs); + ptr->scheme_version = gf_bs_read_u32(bs); + ptr->size -= 8; + if (ptr->size && (ptr->flags & 0x000001)) { + u32 len = (u32) (ptr->size); + ptr->URI = (char*)gf_malloc(sizeof(char)*len); + if (!ptr->URI) return GF_OUT_OF_MEM; + gf_bs_read_data(bs, ptr->URI, len); + } + return GF_OK; +} + +#ifndef GPAC_DISABLE_ISOM_WRITE +GF_Err schm_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_SchemeTypeBox *ptr = (GF_SchemeTypeBox *) s; + if (!s) return GF_BAD_PARAM; + e = gf_isom_full_box_write(s, bs); + assert(e == GF_OK); + gf_bs_write_u32(bs, ptr->scheme_type); + gf_bs_write_u32(bs, ptr->scheme_version); + if (ptr->flags & 0x000001) gf_bs_write_data(bs, ptr->URI, strlen(ptr->URI)+1); + return GF_OK; +} + +GF_Err schm_Size(GF_Box *s) +{ + GF_Err e; + GF_SchemeTypeBox *ptr = (GF_SchemeTypeBox *) s; + if (!s) return GF_BAD_PARAM; + e = gf_isom_full_box_get_size(s); + if (e) return e; + ptr->size += 8; + if (ptr->flags & 0x000001) ptr->size += strlen(ptr->URI)+1; + return GF_OK; +} +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + +/* SchemeInformation Box */ +GF_Box *schi_New() +{ + GF_SchemeInformationBox *tmp = (GF_SchemeInformationBox *) gf_malloc(sizeof(GF_SchemeInformationBox)); + if (tmp == NULL) return NULL; + memset(tmp, 0, sizeof(GF_SchemeInformationBox)); + tmp->type = GF_ISOM_BOX_TYPE_SCHI; + return (GF_Box *)tmp; +} + +void schi_del(GF_Box *s) +{ + GF_SchemeInformationBox *ptr = (GF_SchemeInformationBox *)s; + if (ptr == NULL) return; + if (ptr->ikms) gf_isom_box_del((GF_Box *)ptr->ikms); + if (ptr->isfm) gf_isom_box_del((GF_Box *)ptr->isfm); + gf_free(ptr); +} + +GF_Err schi_AddBox(GF_Box *s, GF_Box *a) +{ + GF_SchemeInformationBox *ptr = (GF_SchemeInformationBox *)s; + switch (a->type) { + case GF_ISOM_BOX_TYPE_IKMS: + if (ptr->ikms) return GF_ISOM_INVALID_FILE; + ptr->ikms = (GF_ISMAKMSBox*)a; + return GF_OK; + case GF_ISOM_BOX_TYPE_ISFM: + if (ptr->isfm) return GF_ISOM_INVALID_FILE; + ptr->isfm = (GF_ISMASampleFormatBox*)a; + return GF_OK; + case GF_ISOM_BOX_TYPE_ODKM: + if (ptr->okms) return GF_ISOM_INVALID_FILE; + ptr->okms = (GF_OMADRMKMSBox*)a; + return GF_OK; + default: + gf_isom_box_del(a); + return GF_OK; + } +} + +GF_Err schi_Read(GF_Box *s, GF_BitStream *bs) +{ + return gf_isom_read_box_list(s, bs, schi_AddBox); +} + +#ifndef GPAC_DISABLE_ISOM_WRITE +GF_Err schi_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_SchemeInformationBox *ptr = (GF_SchemeInformationBox *)s; + if (!s) return GF_BAD_PARAM; + e = gf_isom_box_write_header(s, bs); + if (e) return e; + + if (ptr->ikms) { + e = gf_isom_box_write((GF_Box *) ptr->ikms, bs); + if (e) return e; + } + if (ptr->isfm) { + e = gf_isom_box_write((GF_Box *) ptr->isfm, bs); + if (e) return e; + } + if (ptr->okms) { + e = gf_isom_box_write((GF_Box *) ptr->okms, bs); + if (e) return e; + } + return GF_OK; +} + +GF_Err schi_Size(GF_Box *s) +{ + GF_Err e; + GF_SchemeInformationBox *ptr = (GF_SchemeInformationBox *)s; + if (!s) return GF_BAD_PARAM; + e = gf_isom_box_get_size(s); + if (e) return e; + + if (ptr->ikms) { + e = gf_isom_box_size((GF_Box *) ptr->ikms); + if (e) return e; + ptr->size += ptr->ikms->size; + } + if (ptr->isfm) { + e = gf_isom_box_size((GF_Box *) ptr->isfm); + if (e) return e; + ptr->size += ptr->isfm->size; + } + if (ptr->okms) { + e = gf_isom_box_size((GF_Box *) ptr->okms); + if (e) return e; + ptr->size += ptr->okms->size; + } + return GF_OK; +} + +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + +/* ISMAKMS Box */ +GF_Box *iKMS_New() +{ + GF_ISMAKMSBox *tmp = (GF_ISMAKMSBox *) gf_malloc(sizeof(GF_ISMAKMSBox)); + if (tmp == NULL) return NULL; + memset(tmp, 0, sizeof(GF_ISMAKMSBox)); + gf_isom_full_box_init((GF_Box *)tmp); + tmp->type = GF_ISOM_BOX_TYPE_IKMS; + return (GF_Box *)tmp; +} + +void iKMS_del(GF_Box *s) +{ + GF_ISMAKMSBox *ptr = (GF_ISMAKMSBox *)s; + if (ptr == NULL) return; + if (ptr->URI) gf_free(ptr->URI); + gf_free(ptr); +} + +GF_Err iKMS_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + u32 len; + GF_ISMAKMSBox *ptr = (GF_ISMAKMSBox *)s; + + e = gf_isom_full_box_read(s, bs); + if (e) return e; + len = (u32) (ptr->size); + ptr->URI = (char*) gf_malloc(sizeof(char)*len); + if (!ptr->URI) return GF_OUT_OF_MEM; + gf_bs_read_data(bs, ptr->URI, len); + return GF_OK; +} + +#ifndef GPAC_DISABLE_ISOM_WRITE +GF_Err iKMS_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_ISMAKMSBox *ptr = (GF_ISMAKMSBox *)s; + if (!s) return GF_BAD_PARAM; + e = gf_isom_full_box_write(s, bs); + if (e) return e; + gf_bs_write_data(bs, ptr->URI, strlen(ptr->URI)+1); + return GF_OK; +} + +GF_Err iKMS_Size(GF_Box *s) +{ + GF_Err e; + GF_ISMAKMSBox *ptr = (GF_ISMAKMSBox *)s; + if (!s) return GF_BAD_PARAM; + e = gf_isom_full_box_get_size(s); + if (e) return e; + ptr->size += strlen(ptr->URI)+1; + return GF_OK; +} +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + +/* ISMASampleFormat Box */ +GF_Box *iSFM_New() +{ + GF_ISMASampleFormatBox *tmp = (GF_ISMASampleFormatBox *) gf_malloc(sizeof(GF_ISMASampleFormatBox)); + if (tmp == NULL) return NULL; + memset(tmp, 0, sizeof(GF_ISMASampleFormatBox)); + gf_isom_full_box_init((GF_Box *)tmp); + tmp->type = GF_ISOM_BOX_TYPE_ISFM; + return (GF_Box *)tmp; +} + +void iSFM_del(GF_Box *s) +{ + GF_ISMASampleFormatBox *ptr = (GF_ISMASampleFormatBox *)s; + if (ptr == NULL) return; + gf_free(ptr); +} + + +GF_Err iSFM_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_ISMASampleFormatBox *ptr = (GF_ISMASampleFormatBox *)s; + if (ptr == NULL) return GF_BAD_PARAM; + e = gf_isom_full_box_read(s, bs); + if (e) return e; + ptr->selective_encryption = gf_bs_read_int(bs, 1); + gf_bs_read_int(bs, 7); + ptr->key_indicator_length = gf_bs_read_u8(bs); + ptr->IV_length = gf_bs_read_u8(bs); + return GF_OK; +} + +#ifndef GPAC_DISABLE_ISOM_WRITE +GF_Err iSFM_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_ISMASampleFormatBox *ptr = (GF_ISMASampleFormatBox *)s; + if (!s) return GF_BAD_PARAM; + e = gf_isom_full_box_write(s, bs); + if (e) return e; + gf_bs_write_int(bs, ptr->selective_encryption, 1); + gf_bs_write_int(bs, 0, 7); + gf_bs_write_u8(bs, ptr->key_indicator_length); + gf_bs_write_u8(bs, ptr->IV_length); + return GF_OK; +} + +GF_Err iSFM_Size(GF_Box *s) +{ + GF_Err e; + GF_ISMASampleFormatBox *ptr = (GF_ISMASampleFormatBox *)s; + if (!s) return GF_BAD_PARAM; + e = gf_isom_full_box_get_size(s); + if (e) return e; + ptr->size += 3; + return GF_OK; +} +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + + + +/* OMADRMCommonHeader Box */ +GF_Box *ohdr_New() +{ + GF_OMADRMCommonHeaderBox *tmp; + GF_SAFEALLOC(tmp, GF_OMADRMCommonHeaderBox); + if (tmp == NULL) return NULL; + gf_isom_full_box_init((GF_Box *)tmp); + tmp->type = GF_ISOM_BOX_TYPE_OHDR; + tmp->ExtendedHeaders = gf_list_new(); + return (GF_Box *)tmp; +} + +void ohdr_del(GF_Box *s) +{ + GF_OMADRMCommonHeaderBox *ptr = (GF_OMADRMCommonHeaderBox*)s; + if (ptr == NULL) return; + gf_isom_box_array_del(ptr->ExtendedHeaders); + if (ptr->ContentID) gf_free(ptr->ContentID); + if (ptr->RightsIssuerURL) gf_free(ptr->RightsIssuerURL); + if (ptr->TextualHeaders) gf_free(ptr->TextualHeaders); + gf_free(ptr); +} + +GF_Err ohdr_AddBox(GF_Box *s, GF_Box *a) +{ + GF_OMADRMCommonHeaderBox *ptr = (GF_OMADRMCommonHeaderBox*)s; + return gf_list_add(ptr->ExtendedHeaders, a); +} + +GF_Err ohdr_Read(GF_Box *s, GF_BitStream *bs) +{ + u16 cid_len, ri_len; + GF_Err e; + GF_OMADRMCommonHeaderBox *ptr = (GF_OMADRMCommonHeaderBox*)s; + if (ptr == NULL) return GF_BAD_PARAM; + e = gf_isom_full_box_read(s, bs); + if (e) return e; + ptr->EncryptionMethod = gf_bs_read_u8(bs); + ptr->PaddingScheme = gf_bs_read_u8(bs); + ptr->PlaintextLength = gf_bs_read_u64(bs); + cid_len = gf_bs_read_u16(bs); + ri_len = gf_bs_read_u16(bs); + ptr->TextualHeadersLen = gf_bs_read_u16(bs); + ptr->size -= 1+1+8+2+2+2; + if (ptr->sizeTextualHeadersLen) return GF_ISOM_INVALID_FILE; + + if (cid_len) { + ptr->ContentID = (char *)gf_malloc(sizeof(char)*(cid_len+1)); + gf_bs_read_data(bs, ptr->ContentID, cid_len); + ptr->ContentID[cid_len]=0; + } + + if (ri_len) { + ptr->RightsIssuerURL = (char *)gf_malloc(sizeof(char)*(ri_len+1)); + gf_bs_read_data(bs, ptr->RightsIssuerURL, ri_len); + ptr->RightsIssuerURL[ri_len]=0; + } + + if (ptr->TextualHeadersLen) { + ptr->TextualHeaders = (char *)gf_malloc(sizeof(char)*(ptr->TextualHeadersLen+1)); + gf_bs_read_data(bs, ptr->TextualHeaders, ptr->TextualHeadersLen); + ptr->TextualHeaders[ptr->TextualHeadersLen] = 0; + } + + ptr->size -= cid_len+ri_len+ptr->TextualHeadersLen; + + return gf_isom_read_box_list(s, bs, ohdr_AddBox); +} + +#ifndef GPAC_DISABLE_ISOM_WRITE +GF_Err ohdr_Write(GF_Box *s, GF_BitStream *bs) +{ + u16 cid_len, ri_len; + GF_Err e; + GF_OMADRMCommonHeaderBox *ptr = (GF_OMADRMCommonHeaderBox *)s; + if (!s) return GF_BAD_PARAM; + e = gf_isom_full_box_write(s, bs); + if (e) return e; + gf_bs_write_u8(bs, ptr->EncryptionMethod); + gf_bs_write_u8(bs, ptr->PaddingScheme); + gf_bs_write_u64(bs, ptr->PlaintextLength); + + cid_len = ptr->ContentID ? strlen(ptr->ContentID) : 0; + gf_bs_write_u16(bs, cid_len); + ri_len = ptr->RightsIssuerURL ? strlen(ptr->RightsIssuerURL) : 0; + gf_bs_write_u16(bs, ri_len); + gf_bs_write_u16(bs, ptr->TextualHeadersLen); + + if (cid_len) gf_bs_write_data(bs, ptr->ContentID, strlen(ptr->ContentID)); + if (ri_len) gf_bs_write_data(bs, ptr->RightsIssuerURL, strlen(ptr->RightsIssuerURL)); + if (ptr->TextualHeadersLen) gf_bs_write_data(bs, ptr->TextualHeaders, ptr->TextualHeadersLen); + ptr->size -= cid_len+ri_len+ptr->TextualHeadersLen; + return gf_isom_box_array_write(s, ptr->ExtendedHeaders, bs); +} + +GF_Err ohdr_Size(GF_Box *s) +{ + GF_Err e; + GF_OMADRMCommonHeaderBox *ptr = (GF_OMADRMCommonHeaderBox *)s; + if (!s) return GF_BAD_PARAM; + e = gf_isom_full_box_get_size(s); + if (e) return e; + ptr->size += 1+1+8+2+2+2; + if (ptr->ContentID) ptr->size += strlen(ptr->ContentID); + if (ptr->RightsIssuerURL) ptr->size += strlen(ptr->RightsIssuerURL); + if (ptr->TextualHeadersLen) ptr->size += ptr->TextualHeadersLen; + return gf_isom_box_array_size(s, ptr->ExtendedHeaders); +} +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + + +/* OMADRMGroupID Box */ +GF_Box *grpi_New() +{ + GF_OMADRMGroupIDBox *tmp; + GF_SAFEALLOC(tmp, GF_OMADRMGroupIDBox); + if (tmp == NULL) return NULL; + gf_isom_full_box_init((GF_Box *)tmp); + tmp->type = GF_ISOM_BOX_TYPE_GRPI; + return (GF_Box *)tmp; +} + +void grpi_del(GF_Box *s) +{ + GF_OMADRMGroupIDBox *ptr = (GF_OMADRMGroupIDBox *)s; + if (ptr == NULL) return; + if (ptr->GroupID) gf_free(ptr->GroupID); + if (ptr->GroupKey) gf_free(ptr->GroupKey); + gf_free(ptr); +} + +GF_Err grpi_Read(GF_Box *s, GF_BitStream *bs) +{ + u16 gid_len; + GF_Err e; + GF_OMADRMGroupIDBox *ptr = (GF_OMADRMGroupIDBox*)s; + if (ptr == NULL) return GF_BAD_PARAM; + e = gf_isom_full_box_read(s, bs); + if (e) return e; + gid_len = gf_bs_read_u16(bs); + ptr->GKEncryptionMethod = gf_bs_read_u8(bs); + ptr->GKLength = gf_bs_read_u16(bs); + + ptr->size -= 1+2+2; + if (ptr->sizeGKLength) return GF_ISOM_INVALID_FILE; + + ptr->GroupID = (char *) gf_malloc(sizeof(char)*(gid_len+1)); + gf_bs_read_data(bs, ptr->GroupID, gid_len); + ptr->GroupID[gid_len]=0; + + ptr->GroupKey = (char *)gf_malloc(sizeof(char)*ptr->GKLength); + gf_bs_read_data(bs, ptr->GroupKey, ptr->GKLength); + ptr->size -= gid_len+ptr->GKLength; + return GF_OK; +} + +#ifndef GPAC_DISABLE_ISOM_WRITE +GF_Err grpi_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + u16 gid_len; + GF_OMADRMGroupIDBox *ptr = (GF_OMADRMGroupIDBox *)s; + if (!s) return GF_BAD_PARAM; + e = gf_isom_full_box_write(s, bs); + if (e) return e; + gid_len = ptr->GroupID ? strlen(ptr->GroupID) : 0; + gf_bs_write_u16(bs, gid_len); + gf_bs_write_u8(bs, ptr->GKEncryptionMethod); + gf_bs_write_u16(bs, ptr->GKLength); + gf_bs_write_data(bs, ptr->GroupID, gid_len); + gf_bs_write_data(bs, ptr->GroupKey, ptr->GKLength); + return GF_OK; +} + +GF_Err grpi_Size(GF_Box *s) +{ + GF_Err e; + GF_OMADRMGroupIDBox *ptr = (GF_OMADRMGroupIDBox *)s; + if (!s) return GF_BAD_PARAM; + e = gf_isom_full_box_get_size(s); + if (e) return e; + ptr->size += 2+2+1 + ptr->GKLength; + if (ptr->GroupID) ptr->size += strlen(ptr->GroupID); + return GF_OK; +} +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + + + + +/* OMADRMMutableInformation Box */ +GF_Box *mdri_New() +{ + GF_OMADRMMutableInformationBox *tmp; + GF_SAFEALLOC(tmp, GF_OMADRMMutableInformationBox); + if (tmp == NULL) return NULL; + tmp->type = GF_ISOM_BOX_TYPE_MDRI; + tmp->boxes = gf_list_new(); + return (GF_Box *)tmp; +} + +void mdri_del(GF_Box *s) +{ + GF_OMADRMMutableInformationBox*ptr = (GF_OMADRMMutableInformationBox*)s; + if (ptr == NULL) return; + gf_isom_box_array_del(ptr->boxes); + gf_free(ptr); +} + +GF_Err mdri_AddBox(GF_Box *s, GF_Box *a) +{ + GF_OMADRMMutableInformationBox *ptr = (GF_OMADRMMutableInformationBox *)s; + return gf_list_add(ptr->boxes, a); +} + +GF_Err mdri_Read(GF_Box *s, GF_BitStream *bs) +{ + return gf_isom_read_box_list(s, bs, mdri_AddBox); +} + +#ifndef GPAC_DISABLE_ISOM_WRITE +GF_Err mdri_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_OMADRMMutableInformationBox*ptr = (GF_OMADRMMutableInformationBox*)s; + GF_Err e = gf_isom_box_write_header(s, bs); + if (e) return e; + return gf_isom_box_array_write(s, ptr->boxes, bs); +} + +GF_Err mdri_Size(GF_Box *s) +{ + GF_Err e; + GF_OMADRMMutableInformationBox *ptr = (GF_OMADRMMutableInformationBox *)s; + if (!s) return GF_BAD_PARAM; + e = gf_isom_box_get_size(s); + if (e) return e; + + return gf_isom_box_array_size(s, ptr->boxes); +} +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + + +/* OMADRMTransactionTracking Box */ +GF_Box *odtt_New() +{ + GF_OMADRMTransactionTrackingBox *tmp; + GF_SAFEALLOC(tmp, GF_OMADRMTransactionTrackingBox); + if (tmp == NULL) return NULL; + gf_isom_full_box_init((GF_Box *)tmp); + tmp->type = GF_ISOM_BOX_TYPE_ODTT; + return (GF_Box *)tmp; +} + +void odtt_del(GF_Box *s) +{ + GF_OMADRMTransactionTrackingBox *ptr = (GF_OMADRMTransactionTrackingBox*)s; + gf_free(ptr); +} + +GF_Err odtt_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_OMADRMTransactionTrackingBox *ptr = (GF_OMADRMTransactionTrackingBox *)s; + if (ptr == NULL) return GF_BAD_PARAM; + e = gf_isom_full_box_read(s, bs); + if (e) return e; + gf_bs_read_data(bs, ptr->TransactionID, 16); + ptr->size -= 16; + return GF_OK; +} + +#ifndef GPAC_DISABLE_ISOM_WRITE +GF_Err odtt_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_OMADRMTransactionTrackingBox *ptr = (GF_OMADRMTransactionTrackingBox*)s; + if (!s) return GF_BAD_PARAM; + e = gf_isom_full_box_write(s, bs); + if (e) return e; + gf_bs_write_data(bs, ptr->TransactionID, 16); + return GF_OK; +} + +GF_Err odtt_Size(GF_Box *s) +{ + GF_Err e; + if (!s) return GF_BAD_PARAM; + e = gf_isom_full_box_get_size(s); + if (e) return e; + s->size += 16; + return GF_OK; +} +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + + + +/* OMADRMRightsObject Box */ +GF_Box *odrb_New() +{ + GF_OMADRMRightsObjectBox *tmp; + GF_SAFEALLOC(tmp, GF_OMADRMRightsObjectBox); + if (tmp == NULL) return NULL; + gf_isom_full_box_init((GF_Box *)tmp); + tmp->type = GF_ISOM_BOX_TYPE_ODRB; + return (GF_Box *)tmp; +} + +void odrb_del(GF_Box *s) +{ + GF_OMADRMRightsObjectBox *ptr = (GF_OMADRMRightsObjectBox*)s; + if (ptr->oma_ro) gf_free(ptr->oma_ro); + gf_free(ptr); +} + +GF_Err odrb_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_OMADRMRightsObjectBox *ptr = (GF_OMADRMRightsObjectBox *)s; + if (ptr == NULL) return GF_BAD_PARAM; + e = gf_isom_full_box_read(s, bs); + if (e) return e; + ptr->oma_ro_size = (u32) ptr->size; + ptr->oma_ro = (char*) gf_malloc(sizeof(char)*ptr->oma_ro_size); + gf_bs_read_data(bs, ptr->oma_ro, ptr->oma_ro_size); + ptr->size = 0; + return GF_OK; +} + +#ifndef GPAC_DISABLE_ISOM_WRITE +GF_Err odrb_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_OMADRMRightsObjectBox *ptr = (GF_OMADRMRightsObjectBox *)s; + if (!s) return GF_BAD_PARAM; + e = gf_isom_full_box_write(s, bs); + if (e) return e; + gf_bs_write_data(bs, ptr->oma_ro, ptr->oma_ro_size); + return GF_OK; +} + +GF_Err odrb_Size(GF_Box *s) +{ + GF_Err e; + GF_OMADRMRightsObjectBox *ptr = (GF_OMADRMRightsObjectBox *)s; + if (!s) return GF_BAD_PARAM; + e = gf_isom_full_box_get_size(s); + if (e) return e; + s->size += ptr->oma_ro_size; + return GF_OK; +} +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + + + + +/* OMADRMKMS Box */ +GF_Box *odkm_New() +{ + GF_OMADRMKMSBox *tmp; + GF_SAFEALLOC(tmp, GF_OMADRMKMSBox); + if (tmp == NULL) return NULL; + gf_isom_full_box_init((GF_Box *)tmp); + tmp->type = GF_ISOM_BOX_TYPE_ODKM; + return (GF_Box *)tmp; +} + +void odkm_del(GF_Box *s) +{ + GF_OMADRMKMSBox *ptr = (GF_OMADRMKMSBox *)s; + if (ptr->hdr) gf_isom_box_del((GF_Box*)ptr->hdr); + if (ptr->fmt) gf_isom_box_del((GF_Box*)ptr->fmt); + gf_free(ptr); +} + +GF_Err odkm_Add(GF_Box *s, GF_Box *a) +{ + GF_OMADRMKMSBox *ptr = (GF_OMADRMKMSBox *)s; + switch (a->type) { + case GF_ISOM_BOX_TYPE_OHDR: + if (ptr->hdr) gf_isom_box_del((GF_Box*)ptr->hdr); + ptr->hdr = (GF_OMADRMCommonHeaderBox *)a; + return GF_OK; + case GF_ISOM_BOX_TYPE_ODAF: + if (ptr->fmt) gf_isom_box_del((GF_Box*)ptr->fmt); + ptr->fmt = (GF_OMADRMAUFormatBox*)a; + return GF_OK; + default: + gf_isom_box_del(a); + return GF_OK; + } +} + +GF_Err odkm_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + if (s == NULL) return GF_BAD_PARAM; + e = gf_isom_full_box_read(s, bs); + if (e) return e; + return gf_isom_read_box_list(s, bs, odkm_Add); +} + +#ifndef GPAC_DISABLE_ISOM_WRITE +GF_Err odkm_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_OMADRMKMSBox *ptr = (GF_OMADRMKMSBox *)s; + if (!s) return GF_BAD_PARAM; + e = gf_isom_full_box_write(s, bs); + if (e) return e; + if (ptr->hdr) { + e = gf_isom_box_write((GF_Box*)ptr->hdr, bs); + if (e) return e; + } + if (ptr->fmt) { + e = gf_isom_box_write((GF_Box*)ptr->fmt, bs); + if (e) return e; + } + return GF_OK; +} + +GF_Err odkm_Size(GF_Box *s) +{ + GF_Err e; + GF_OMADRMKMSBox *ptr = (GF_OMADRMKMSBox *)s; + if (!s) return GF_BAD_PARAM; + e = gf_isom_full_box_get_size(s); + if (e) return e; + if (ptr->hdr) { + e = gf_isom_box_size((GF_Box*)ptr->hdr); + if (e) return e; + ptr->size += ptr->hdr->size; + } + if (ptr->fmt) { + e = gf_isom_box_size((GF_Box*)ptr->fmt); + if (e) return e; + ptr->size += ptr->fmt->size; + } + return GF_OK; +} +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + + +#endif /*GPAC_DISABLE_ISOM*/ diff --git a/src/gpacmp4/box_code_meta.c b/src/gpacmp4/box_code_meta.c new file mode 100644 index 00000000..824e9dcc --- /dev/null +++ b/src/gpacmp4/box_code_meta.c @@ -0,0 +1,776 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Cyril Concolato / Jean Le Feuvre 2005 + * All rights reserved + * + * This file is part of GPAC / ISO Media File Format sub-project + * + * GPAC is free software you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation either version 2, or (at your option) + * any later version. + * + * GPAC 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include + +#ifndef GPAC_DISABLE_ISOM + +GF_Box *meta_New() +{ + GF_MetaBox *tmp = (GF_MetaBox *) gf_malloc(sizeof(GF_MetaBox)); + if (tmp == NULL) return NULL; + memset(tmp, 0, sizeof(GF_MetaBox)); + gf_isom_full_box_init((GF_Box *)tmp); + tmp->type = GF_ISOM_BOX_TYPE_META; + tmp->other_boxes = gf_list_new(); + return (GF_Box *)tmp; +} + +void meta_del(GF_Box *s) +{ + u32 count, i; + GF_MetaBox *ptr = (GF_MetaBox *)s; + if (ptr == NULL) return; + gf_isom_box_del((GF_Box *)ptr->handler); + if (ptr->primary_resource) gf_isom_box_del((GF_Box *)ptr->primary_resource); + if (ptr->file_locations) gf_isom_box_del((GF_Box *)ptr->file_locations); + if (ptr->item_locations) gf_isom_box_del((GF_Box *)ptr->item_locations); + if (ptr->protections) gf_isom_box_del((GF_Box *)ptr->protections); + if (ptr->item_infos) gf_isom_box_del((GF_Box *)ptr->item_infos); + if (ptr->IPMP_control) gf_isom_box_del((GF_Box *)ptr->IPMP_control); + count = gf_list_count(ptr->other_boxes); + for (i = 0; i < count; i++) { + GF_Box *a = (GF_Box *)gf_list_get(ptr->other_boxes, i); + gf_isom_box_del(a); + } + gf_list_del(ptr->other_boxes); + gf_free(ptr); +} + +GF_Err meta_AddBox(GF_Box *s, GF_Box *a) +{ + GF_MetaBox *ptr = (GF_MetaBox *)s; + switch (a->type) { + case GF_ISOM_BOX_TYPE_HDLR: + if (ptr->handler) return GF_ISOM_INVALID_FILE; + ptr->handler = (GF_HandlerBox*)a; + break; + case GF_ISOM_BOX_TYPE_PITM: + if (ptr->primary_resource) return GF_ISOM_INVALID_FILE; + ptr->primary_resource = (GF_PrimaryItemBox*)a; + break; + case GF_ISOM_BOX_TYPE_DINF: + if (ptr->file_locations) return GF_ISOM_INVALID_FILE; + ptr->file_locations = (GF_DataInformationBox*)a; + break; + case GF_ISOM_BOX_TYPE_ILOC: + if (ptr->item_locations) return GF_ISOM_INVALID_FILE; + ptr->item_locations = (GF_ItemLocationBox*)a; + break; + case GF_ISOM_BOX_TYPE_IPRO: + if (ptr->protections) return GF_ISOM_INVALID_FILE; + ptr->protections = (GF_ItemProtectionBox*)a; + break; + case GF_ISOM_BOX_TYPE_IINF: + if (ptr->item_infos) return GF_ISOM_INVALID_FILE; + ptr->item_infos = (GF_ItemInfoBox*)a; + break; + //case ???: ptr->IPMP_control = (???*)a; break; + case GF_ISOM_BOX_TYPE_XML: + case GF_ISOM_BOX_TYPE_BXML: + case GF_ISOM_BOX_TYPE_ILST: + gf_list_add(ptr->other_boxes, a); break; + default: + gf_isom_box_del(a); + break; + } + return GF_OK; +} + +GF_Err meta_Read(GF_Box *s, GF_BitStream *bs) +{ + u32 next_size = gf_bs_peek_bits(bs, 32, 4); + GF_Err e; + /*try to hack around QT files which don't use a full box for meta*/ + if (next_sizesize) { + e = gf_isom_full_box_read(s, bs); + if (e) return e; + } + return gf_isom_read_box_list(s, bs, meta_AddBox); +} + +#ifndef GPAC_DISABLE_ISOM_WRITE +GF_Err meta_Write(GF_Box *s, GF_BitStream *bs) +{ + u32 count, i; + GF_Err e; + GF_MetaBox *ptr = (GF_MetaBox *)s; + if (!s) return GF_BAD_PARAM; + e = gf_isom_full_box_write(s, bs); + if (e) return e; + e = gf_isom_box_write((GF_Box *) ptr->handler, bs); + if (e) return e; + if (ptr->primary_resource) { + e = gf_isom_box_write((GF_Box *) ptr->primary_resource, bs); + if (e) return e; + } + if (ptr->file_locations) { + e = gf_isom_box_write((GF_Box *) ptr->file_locations, bs); + if (e) return e; + } + if (ptr->item_locations) { + e = gf_isom_box_write((GF_Box *) ptr->item_locations, bs); + if (e) return e; + } + if (ptr->protections) { + e = gf_isom_box_write((GF_Box *) ptr->protections, bs); + if (e) return e; + } + if (ptr->item_infos) { + e = gf_isom_box_write((GF_Box *) ptr->item_infos, bs); + if (e) return e; + } + if (ptr->IPMP_control) { + e = gf_isom_box_write((GF_Box *) ptr->IPMP_control, bs); + if (e) return e; + } + if ((count = gf_list_count(ptr->other_boxes))) { + for (i = 0; i < count; i++) { + GF_Box *a = (GF_Box *)gf_list_get(ptr->other_boxes, i); + e = gf_isom_box_write(a, bs); + if (e) return e; + } + } + return GF_OK; +} + +GF_Err meta_Size(GF_Box *s) +{ + u32 i, count; + GF_Err e; + GF_MetaBox *ptr = (GF_MetaBox *)s; + if (!s) return GF_BAD_PARAM; + e = gf_isom_full_box_get_size(s); + if (e) return e; + e = gf_isom_box_size((GF_Box *) ptr->handler); + if (e) return e; + ptr->size += ptr->handler->size; + if (ptr->primary_resource) { + e = gf_isom_box_size((GF_Box *) ptr->primary_resource); + if (e) return e; + ptr->size += ptr->primary_resource->size; + } + if (ptr->file_locations) { + e = gf_isom_box_size((GF_Box *) ptr->file_locations); + if (e) return e; + ptr->size += ptr->file_locations->size; + } + if (ptr->item_locations) { + e = gf_isom_box_size((GF_Box *) ptr->item_locations); + if (e) return e; + ptr->size += ptr->item_locations->size; + } + if (ptr->protections) { + e = gf_isom_box_size((GF_Box *) ptr->protections); + if (e) return e; + ptr->size += ptr->protections->size; + } + if (ptr->item_infos) { + e = gf_isom_box_size((GF_Box *) ptr->item_infos); + if (e) return e; + ptr->size += ptr->item_infos->size; + } + if (ptr->IPMP_control) { + e = gf_isom_box_size((GF_Box *) ptr->IPMP_control); + if (e) return e; + ptr->size += ptr->IPMP_control->size; + } + if ((count = gf_list_count(ptr->other_boxes))) { + for (i = 0; i < count; i++) { + GF_Box *a = (GF_Box *)gf_list_get(ptr->other_boxes, i); + e = gf_isom_box_size(a); + if (e) return e; + ptr->size += a->size; + } + } + return GF_OK; +} +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + +GF_Box *xml_New() +{ + GF_XMLBox *tmp = (GF_XMLBox *) gf_malloc(sizeof(GF_XMLBox)); + if (tmp == NULL) return NULL; + memset(tmp, 0, sizeof(GF_XMLBox)); + gf_isom_full_box_init((GF_Box *)tmp); + tmp->type = GF_ISOM_BOX_TYPE_XML; + return (GF_Box *)tmp; +} + +void xml_del(GF_Box *s) +{ + GF_XMLBox *ptr = (GF_XMLBox *)s; + if (ptr == NULL) return; + if (ptr->xml_length && ptr->xml) gf_free(ptr->xml); + gf_free(ptr); +} + +GF_Err xml_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_XMLBox *ptr = (GF_XMLBox *)s; + if (ptr == NULL) return GF_BAD_PARAM; + e = gf_isom_full_box_read(s, bs); + if (e) return e; + ptr->xml_length = (u32)(ptr->size); + ptr->xml = (char *)gf_malloc(sizeof(char)*ptr->xml_length); + if (!ptr->xml) return GF_OUT_OF_MEM; + gf_bs_read_data(bs, ptr->xml, ptr->xml_length); + return GF_OK; +} + +#ifndef GPAC_DISABLE_ISOM_WRITE +GF_Err xml_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_XMLBox *ptr = (GF_XMLBox *)s; + if (!s) return GF_BAD_PARAM; + e = gf_isom_full_box_write(s, bs); + if (e) return e; + if (ptr->xml_length) gf_bs_write_data(bs, ptr->xml, ptr->xml_length); + return GF_OK; +} + +GF_Err xml_Size(GF_Box *s) +{ + GF_Err e; + GF_XMLBox *ptr = (GF_XMLBox *)s; + if (!s) return GF_BAD_PARAM; + e = gf_isom_full_box_get_size(s); + if (e) return e; + ptr->size += ptr->xml_length; + return GF_OK; +} +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + +GF_Box *bxml_New() +{ + GF_BinaryXMLBox *tmp = (GF_BinaryXMLBox *) gf_malloc(sizeof(GF_BinaryXMLBox)); + if (tmp == NULL) return NULL; + memset(tmp, 0, sizeof(GF_BinaryXMLBox)); + gf_isom_full_box_init((GF_Box *)tmp); + tmp->type = GF_ISOM_BOX_TYPE_BXML; + return (GF_Box *)tmp; +} + +void bxml_del(GF_Box *s) +{ + GF_BinaryXMLBox *ptr = (GF_BinaryXMLBox *)s; + if (ptr == NULL) return; + if (ptr->data_length && ptr->data) gf_free(ptr->data); + gf_free(ptr); +} + +GF_Err bxml_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_BinaryXMLBox *ptr = (GF_BinaryXMLBox *)s; + e = gf_isom_full_box_read(s, bs); + if (e) return e; + ptr->data_length = (u32)(ptr->size); + ptr->data = (char*)gf_malloc(sizeof(char)*ptr->data_length); + if (!ptr->data) return GF_OUT_OF_MEM; + gf_bs_read_data(bs, ptr->data, ptr->data_length); + return GF_OK; +} + +#ifndef GPAC_DISABLE_ISOM_WRITE +GF_Err bxml_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_BinaryXMLBox *ptr = (GF_BinaryXMLBox *)s; + if (!s) return GF_BAD_PARAM; + e = gf_isom_full_box_write(s, bs); + if (e) return e; + if (ptr->data_length) gf_bs_write_data(bs, ptr->data, ptr->data_length); + return GF_OK; +} + +GF_Err bxml_Size(GF_Box *s) +{ + GF_Err e; + GF_BinaryXMLBox *ptr = (GF_BinaryXMLBox *)s; + if (!s) return GF_BAD_PARAM; + e = gf_isom_full_box_get_size(s); + if (e) return e; + ptr->size += ptr->data_length; + return GF_OK; +} +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + +GF_Box *iloc_New() +{ + GF_ItemLocationBox *tmp = (GF_ItemLocationBox *) gf_malloc(sizeof(GF_ItemLocationBox)); + if (tmp == NULL) return NULL; + memset(tmp, 0, sizeof(GF_ItemLocationBox)); + gf_isom_full_box_init((GF_Box *)tmp); + tmp->type = GF_ISOM_BOX_TYPE_ILOC; + tmp->location_entries = gf_list_new(); + return (GF_Box *)tmp; +} + +void iloc_del(GF_Box *s) +{ + u32 i, j, item_count, extent_count; + GF_ItemLocationBox *ptr = (GF_ItemLocationBox *)s; + if (ptr == NULL) return; + item_count = gf_list_count(ptr->location_entries); + if (item_count) { + for (i = 0; i < item_count; i++) { + GF_ItemLocationEntry *location = (GF_ItemLocationEntry *)gf_list_get(ptr->location_entries, i); + extent_count = gf_list_count(location->extent_entries); + for (j = 0; j < extent_count; j++) { + GF_ItemExtentEntry *extent = (GF_ItemExtentEntry *)gf_list_get(location->extent_entries, j); + gf_free(extent); + } + gf_list_del(location->extent_entries); + gf_free(location); + } + gf_list_del(ptr->location_entries); + } + gf_free(ptr); +} + +GF_Err iloc_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + u32 item_count, extent_count, i, j; + GF_ItemLocationBox *ptr = (GF_ItemLocationBox *)s; + e = gf_isom_full_box_read(s, bs); + if (e) return e; + ptr->offset_size = gf_bs_read_int(bs, 4); + ptr->length_size = gf_bs_read_int(bs, 4); + ptr->base_offset_size = gf_bs_read_int(bs, 4); + gf_bs_read_int(bs, 4); + item_count = gf_bs_read_u16(bs); + for (i = 0; i < item_count; i++) { + GF_ItemLocationEntry *location_entry = (GF_ItemLocationEntry *)gf_malloc(sizeof(GF_ItemLocationEntry)); + gf_list_add(ptr->location_entries, location_entry); + location_entry->item_ID = gf_bs_read_u16(bs); + location_entry->data_reference_index = gf_bs_read_u16(bs); + location_entry->base_offset = gf_bs_read_int(bs, 8*ptr->base_offset_size); +#ifndef GPAC_DISABLE_ISOM_WRITE + location_entry->original_base_offset = location_entry->base_offset; +#endif + + extent_count = gf_bs_read_u16(bs); + location_entry->extent_entries = gf_list_new(); + for (j = 0; j < extent_count; j++) { + GF_ItemExtentEntry *extent_entry = (GF_ItemExtentEntry *)gf_malloc(sizeof(GF_ItemExtentEntry)); + gf_list_add(location_entry->extent_entries, extent_entry); + extent_entry->extent_offset = gf_bs_read_int(bs, 8*ptr->offset_size); + extent_entry->extent_length = gf_bs_read_int(bs, 8*ptr->length_size); +#ifndef GPAC_DISABLE_ISOM_WRITE + extent_entry->original_extent_offset = extent_entry->extent_offset; +#endif + } + } + return GF_OK; +} + +#ifndef GPAC_DISABLE_ISOM_WRITE +GF_Err iloc_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + u32 i, j, item_count, extent_count; + GF_ItemLocationBox *ptr = (GF_ItemLocationBox *)s; + if (!s) return GF_BAD_PARAM; + e = gf_isom_full_box_write(s, bs); + if (e) return e; + gf_bs_write_int(bs, ptr->offset_size, 4); + gf_bs_write_int(bs, ptr->length_size, 4); + gf_bs_write_int(bs, ptr->base_offset_size, 4); + gf_bs_write_int(bs, 0, 4); + item_count = gf_list_count(ptr->location_entries); + gf_bs_write_u16(bs, item_count); + for (i = 0; i < item_count; i++) { + GF_ItemLocationEntry *location = (GF_ItemLocationEntry *)gf_list_get(ptr->location_entries, i); + gf_bs_write_u16(bs, location->item_ID); + gf_bs_write_u16(bs, location->data_reference_index); + gf_bs_write_long_int(bs, location->base_offset, 8*ptr->base_offset_size); + extent_count = gf_list_count(location->extent_entries); + gf_bs_write_u16(bs, extent_count); + for (j=0; jextent_entries, j); + gf_bs_write_long_int(bs, extent->extent_offset, 8*ptr->offset_size); + gf_bs_write_long_int(bs, extent->extent_length, 8*ptr->length_size); + } + } + return GF_OK; +} + +GF_Err iloc_Size(GF_Box *s) +{ + GF_Err e; + u32 i, item_count, extent_count; + GF_ItemLocationBox *ptr = (GF_ItemLocationBox *)s; + if (!s) return GF_BAD_PARAM; + e = gf_isom_full_box_get_size(s); + if (e) return e; + ptr->size += 4; + item_count = gf_list_count(ptr->location_entries); + for (i = 0; i < item_count; i++) { + GF_ItemLocationEntry *location = (GF_ItemLocationEntry *)gf_list_get(ptr->location_entries, i); + extent_count = gf_list_count(location->extent_entries); + ptr->size += 6 + ptr->base_offset_size + extent_count*(ptr->offset_size + ptr->length_size); + } + return GF_OK; +} +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + +GF_Box *pitm_New() +{ + GF_PrimaryItemBox *tmp = (GF_PrimaryItemBox *) gf_malloc(sizeof(GF_PrimaryItemBox)); + if (tmp == NULL) return NULL; + memset(tmp, 0, sizeof(GF_PrimaryItemBox)); + gf_isom_full_box_init((GF_Box *)tmp); + tmp->type = GF_ISOM_BOX_TYPE_PITM; + return (GF_Box *)tmp; +} + +void pitm_del(GF_Box *s) +{ + GF_PrimaryItemBox *ptr = (GF_PrimaryItemBox *)s; + if (ptr == NULL) return; + gf_free(ptr); +} + +GF_Err pitm_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_PrimaryItemBox *ptr = (GF_PrimaryItemBox *)s; + e = gf_isom_full_box_read(s, bs); + if (e) return e; + ptr->item_ID = gf_bs_read_u16(bs); + return GF_OK; +} + +#ifndef GPAC_DISABLE_ISOM_WRITE +GF_Err pitm_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_PrimaryItemBox *ptr = (GF_PrimaryItemBox *)s; + if (!s) return GF_BAD_PARAM; + e = gf_isom_full_box_write(s, bs); + if (e) return e; + gf_bs_write_u16(bs, ptr->item_ID); + return GF_OK; +} + +GF_Err pitm_Size(GF_Box *s) +{ + GF_Err e; + GF_ItemLocationBox *ptr = (GF_ItemLocationBox *)s; + if (!s) return GF_BAD_PARAM; + e = gf_isom_full_box_get_size(s); + if (e) return e; + ptr->size += 16; + return GF_OK; +} +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + +GF_Box *ipro_New() +{ + GF_ItemProtectionBox *tmp = (GF_ItemProtectionBox *) gf_malloc(sizeof(GF_ItemProtectionBox)); + if (tmp == NULL) return NULL; + memset(tmp, 0, sizeof(GF_ItemProtectionBox)); + gf_isom_full_box_init((GF_Box *)tmp); + tmp->type = GF_ISOM_BOX_TYPE_IPRO; + tmp->protection_information = gf_list_new(); + return (GF_Box *)tmp; +} + +void ipro_del(GF_Box *s) +{ + u32 count, i; + GF_ItemProtectionBox *ptr = (GF_ItemProtectionBox *)s; + if (ptr == NULL) return; + count = gf_list_count(ptr->protection_information); + for (i = 0; i < count; i++) { + GF_Box *a = (GF_Box *)gf_list_get(ptr->protection_information, i); + gf_isom_box_del(a); + } + gf_list_del(ptr->protection_information); + gf_free(ptr); +} + +GF_Err ipro_AddBox(GF_Box *s, GF_Box *a) +{ + GF_ItemProtectionBox *ptr = (GF_ItemProtectionBox *)s; + if (a->type == GF_ISOM_BOX_TYPE_SINF) + gf_list_add(ptr->protection_information, a); + else + gf_isom_box_del(a); + return GF_OK; +} +GF_Err ipro_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + e = gf_isom_full_box_read(s, bs); + if (e) return e; + return gf_isom_read_box_list(s, bs, ipro_AddBox); +} + +#ifndef GPAC_DISABLE_ISOM_WRITE +GF_Err ipro_Write(GF_Box *s, GF_BitStream *bs) +{ + u32 count, i; + GF_Err e; + GF_ItemProtectionBox *ptr = (GF_ItemProtectionBox *)s; + if (!s) return GF_BAD_PARAM; + e = gf_isom_full_box_write(s, bs); + if (e) return e; + count = gf_list_count(ptr->protection_information); + gf_bs_write_u16(bs, count); + if (count) { + for (i = 0; i < count; i++) { + GF_Box *a = (GF_Box *)gf_list_get(ptr->protection_information, i); + e = gf_isom_box_write(a, bs); + if (e) return e; + } + } + return GF_OK; +} + +GF_Err ipro_Size(GF_Box *s) +{ + u32 i, count; + GF_Err e; + GF_ItemProtectionBox *ptr = (GF_ItemProtectionBox *)s; + if (!s) return GF_BAD_PARAM; + e = gf_isom_full_box_get_size(s); + if (e) return e; + ptr->size += 2; + if ((count = gf_list_count(ptr->protection_information))) { + for (i = 0; i < count; i++) { + GF_Box *a = (GF_Box *)gf_list_get(ptr->protection_information, i); + e = gf_isom_box_size(a); + if (e) return e; + ptr->size += a->size; + } + } + return GF_OK; +} +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + +GF_Box *infe_New() +{ + GF_ItemInfoEntryBox *tmp = (GF_ItemInfoEntryBox *) gf_malloc(sizeof(GF_ItemInfoEntryBox)); + if (tmp == NULL) return NULL; + memset(tmp, 0, sizeof(GF_ItemInfoEntryBox)); + gf_isom_full_box_init((GF_Box *)tmp); + tmp->type = GF_ISOM_BOX_TYPE_INFE; + return (GF_Box *)tmp; +} + +void infe_del(GF_Box *s) +{ + GF_ItemInfoEntryBox *ptr = (GF_ItemInfoEntryBox *)s; + if (ptr == NULL) return; + if (ptr->item_name) gf_free(ptr->item_name); + if (ptr->full_path) gf_free(ptr->full_path); + if (ptr->content_type) gf_free(ptr->content_type); + if (ptr->content_encoding) gf_free(ptr->content_encoding); + gf_free(ptr); +} + +GF_Err infe_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + char *buf; + u32 buf_len, i, string_len, string_start; + GF_ItemInfoEntryBox *ptr = (GF_ItemInfoEntryBox *)s; + if (ptr == NULL) return GF_BAD_PARAM; + e = gf_isom_full_box_read(s, bs); + if (e) return e; + + ptr->item_ID = gf_bs_read_u16(bs); + ptr->item_protection_index = gf_bs_read_u16(bs); + ptr->size -= 4; + buf_len = (u32) (ptr->size); + buf = (char*)gf_malloc(buf_len); + if (buf_len != gf_bs_read_data(bs, buf, buf_len)) { + gf_free(buf); + return GF_ISOM_INVALID_FILE; + } + string_len = 1; + string_start = 0; + for (i = 0; i < buf_len; i++) { + if (buf[i] == 0) { + if (!ptr->item_name) { + ptr->item_name = (char*)gf_malloc(sizeof(char)*string_len); + memcpy(ptr->item_name, buf+string_start, string_len); + } else if (!ptr->content_type) { + ptr->content_type = (char*)gf_malloc(sizeof(char)*string_len); + memcpy(ptr->content_type, buf+string_start, string_len); + } else { + ptr->content_encoding = (char*)gf_malloc(sizeof(char)*string_len); + memcpy(ptr->content_encoding, buf+string_start, string_len); + } + string_start += string_len; + string_len = 0; + } + string_len++; + } + gf_free(buf); + if (!ptr->item_name || !ptr->content_type) return GF_ISOM_INVALID_FILE; + return GF_OK; +} + +#ifndef GPAC_DISABLE_ISOM_WRITE +GF_Err infe_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + u32 len; + GF_ItemInfoEntryBox *ptr = (GF_ItemInfoEntryBox *)s; + if (!s) return GF_BAD_PARAM; + e = gf_isom_full_box_write(s, bs); + if (e) return e; + gf_bs_write_u16(bs, ptr->item_ID); + gf_bs_write_u16(bs, ptr->item_protection_index); + if (ptr->item_name) { + len = strlen(ptr->item_name)+1; + gf_bs_write_data(bs, ptr->item_name, len); + } + if (ptr->content_type) { + len = strlen(ptr->content_type)+1; + gf_bs_write_data(bs, ptr->content_type, len); + } + if (ptr->content_encoding) { + len = strlen(ptr->content_encoding)+1; + gf_bs_write_data(bs, ptr->content_encoding, len); + } + return GF_OK; +} + +GF_Err infe_Size(GF_Box *s) +{ + GF_Err e; + GF_ItemInfoEntryBox *ptr = (GF_ItemInfoEntryBox *)s; + if (!s) return GF_BAD_PARAM; + e = gf_isom_full_box_get_size(s); + if (e) return e; + ptr->size += 4; + if (ptr->item_name) ptr->size += strlen(ptr->item_name)+1; + if (ptr->content_type) ptr->size += strlen(ptr->content_type)+1; + if (ptr->content_encoding) ptr->size += strlen(ptr->content_encoding)+1; + return GF_OK; +} +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + +GF_Box *iinf_New() +{ + GF_ItemInfoBox *tmp = (GF_ItemInfoBox *) gf_malloc(sizeof(GF_ItemInfoBox)); + if (tmp == NULL) return NULL; + memset(tmp, 0, sizeof(GF_ItemInfoBox)); + gf_isom_full_box_init((GF_Box *)tmp); + tmp->type = GF_ISOM_BOX_TYPE_IINF; + tmp->item_infos = gf_list_new(); + return (GF_Box *)tmp; +} + +void iinf_del(GF_Box *s) +{ + u32 count, i; + GF_ItemInfoBox *ptr = (GF_ItemInfoBox *)s; + if (ptr == NULL) return; + count = gf_list_count(ptr->item_infos); + for (i = 0; i < count; i++) { + GF_Box *a = (GF_Box *)gf_list_get(ptr->item_infos, i); + gf_isom_box_del(a); + } + gf_list_del(ptr->item_infos); + gf_free(ptr); +} + +GF_Err iinf_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_Box *a; + u32 count; + GF_ItemInfoBox *ptr = (GF_ItemInfoBox *)s; + + e = gf_isom_full_box_read(s, bs); + if (e) return e; + count = gf_bs_read_u16(bs); + + while (count) { + e = gf_isom_parse_box(&a, bs); + if (e) return e; + if (ptr->sizesize) return GF_ISOM_INVALID_FILE; + + if (a->type == GF_ISOM_BOX_TYPE_INFE) + gf_list_add(ptr->item_infos, a); + else + gf_isom_box_del(a); + count --; + } + return GF_OK; +} + +#ifndef GPAC_DISABLE_ISOM_WRITE +GF_Err iinf_Write(GF_Box *s, GF_BitStream *bs) +{ + u32 count, i; + GF_Err e; + GF_ItemInfoBox *ptr = (GF_ItemInfoBox *)s; + if (!s) return GF_BAD_PARAM; + e = gf_isom_full_box_write(s, bs); + if (e) return e; + count = gf_list_count(ptr->item_infos); + gf_bs_write_u16(bs, count); + if (count) { + for (i = 0; i < count; i++) { + GF_Box *a = (GF_Box *)gf_list_get(ptr->item_infos, i); + e = gf_isom_box_write(a, bs); + if (e) return e; + } + } + return GF_OK; +} + +GF_Err iinf_Size(GF_Box *s) +{ + u32 i, count; + GF_Err e; + GF_ItemInfoBox *ptr = (GF_ItemInfoBox *)s; + if (!s) return GF_BAD_PARAM; + e = gf_isom_full_box_get_size(s); + if (e) return e; + ptr->size += 2; + if ((count = gf_list_count(ptr->item_infos))) { + for (i = 0; i < count; i++) { + GF_Box *a = (GF_Box *)gf_list_get(ptr->item_infos, i); + e = gf_isom_box_size(a); + if (e) return e; + ptr->size += a->size; + } + } + return GF_OK; +} +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + + +#endif /*GPAC_DISABLE_ISOM*/ + diff --git a/src/gpacmp4/box_funcs.c b/src/gpacmp4/box_funcs.c new file mode 100644 index 00000000..f5fdf264 --- /dev/null +++ b/src/gpacmp4/box_funcs.c @@ -0,0 +1,1520 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / ISO Media File Format sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include + +#ifndef GPAC_DISABLE_ISOM + +//Add this funct to handle incomplete files... +//bytesExpected is 0 most of the time. If the file is incomplete, bytesExpected +//is the number of bytes missing to parse the box... +GF_Err gf_isom_parse_root_box(GF_Box **outBox, GF_BitStream *bs, u64 *bytesExpected, Bool progressive_mode) +{ + GF_Err ret; + u64 start; + //first make sure we can at least get the box size and type... + //18 = size (int32) + type (int32) + if (gf_bs_available(bs) < 8) { + *bytesExpected = 8; + return GF_ISOM_INCOMPLETE_FILE; + } + start = gf_bs_get_position(bs); + ret = gf_isom_parse_box(outBox, bs); + if (ret == GF_ISOM_INCOMPLETE_FILE) { + *bytesExpected = (*outBox)->size; + GF_LOG(progressive_mode ? GF_LOG_DEBUG : GF_LOG_ERROR, GF_LOG_CONTAINER, ("[iso file] Incomplete box %s\n", gf_4cc_to_str( (*outBox)->type) )); + gf_bs_seek(bs, start); + gf_isom_box_del(*outBox); + *outBox = NULL; + } + return ret; +} + + +GF_Err gf_isom_parse_box_ex(GF_Box **outBox, GF_BitStream *bs, u32 parent_type) +{ + u32 type, hdr_size; + u64 size, start, end; + char uuid[16]; + GF_Err e; + GF_Box *newBox; + e = GF_OK; + if ((bs == NULL) || (outBox == NULL) ) return GF_BAD_PARAM; + *outBox = NULL; + + start = gf_bs_get_position(bs); + + size = (u64) gf_bs_read_u32(bs); + hdr_size = 4; + /*fix for some boxes found in some old hinted files*/ + if ((size >= 2) && (size <= 4)) { + size = 4; + type = GF_ISOM_BOX_TYPE_VOID; + } else { + /*now here's a bad thing: some files use size 0 for void atoms, some for "till end of file" indictaion..*/ + if (!size) { + type = gf_bs_peek_bits(bs, 32, 0); + if (!isalnum((type>>24)&0xFF) || !isalnum((type>>16)&0xFF) || !isalnum((type>>8)&0xFF) || !isalnum(type&0xFF)) { + size = 4; + type = GF_ISOM_BOX_TYPE_VOID; + } else { + goto proceed_box; + } + } else { +proceed_box: + type = gf_bs_read_u32(bs); + hdr_size += 4; + /*no size means till end of file - EXCEPT FOR some old QuickTime boxes...*/ + if (type == GF_ISOM_BOX_TYPE_TOTL) + size = 12; + if (!size) { + GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[iso file] Warning Read Box type %s size 0 reading till the end of file\n", gf_4cc_to_str(type))); + size = gf_bs_available(bs) + 8; + } + } + } + /*handle uuid*/ + memset(uuid, 0, 16); + if (type == GF_ISOM_BOX_TYPE_UUID ) { + gf_bs_read_data(bs, uuid, 16); + hdr_size += 16; + } + + //handle large box + if (size == 1) { + size = gf_bs_read_u64(bs); + hdr_size += 8; + } + GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[iso file] Read Box type %s size "LLD" start "LLD"\n", gf_4cc_to_str(type), LLD_CAST size, LLD_CAST start)); + + if ( size < hdr_size ) { + GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[iso file] Box size "LLD" less than box header size %d\n", LLD_CAST size, hdr_size)); + return GF_ISOM_INVALID_FILE; + } + + if (parent_type && (parent_type==GF_ISOM_BOX_TYPE_TREF)) { + newBox = gf_isom_box_new(GF_ISOM_BOX_TYPE_REFT); + if (!newBox) return GF_OUT_OF_MEM; + ((GF_TrackReferenceTypeBox*)newBox)->reference_type = type; + } else { + //OK, create the box based on the type + newBox = gf_isom_box_new(type); + if (!newBox) return GF_OUT_OF_MEM; + } + + //OK, init and read this box + if (type==GF_ISOM_BOX_TYPE_UUID) memcpy(((GF_UUIDBox *)newBox)->uuid, uuid, 16); + + if (!newBox->type) newBox->type = type; + + end = gf_bs_available(bs); + if (size - hdr_size > end ) { + newBox->size = size - hdr_size - end; + *outBox = newBox; + return GF_ISOM_INCOMPLETE_FILE; + } + //we need a special reading for these boxes... + if ((newBox->type == GF_ISOM_BOX_TYPE_STDP) || (newBox->type == GF_ISOM_BOX_TYPE_SDTP)) { + newBox->size = size; + *outBox = newBox; + return GF_OK; + } + + newBox->size = size - hdr_size; + e = gf_isom_box_read(newBox, bs); + newBox->size = size; + end = gf_bs_get_position(bs); + + if (e && (e != GF_ISOM_INCOMPLETE_FILE)) { + gf_isom_box_del(newBox); + *outBox = NULL; + GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[iso file] Read Box \"%s\" failed (%s)\n", gf_4cc_to_str(type), gf_error_to_string(e))); + return e; + } + + if (end-start > size) { + GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[iso file] Box \"%s\" size "LLU" invalid (read "LLU")\n", gf_4cc_to_str(type), LLU_CAST size, LLU_CAST (end-start) )); + /*let's still try to load the file since no error was notified*/ + gf_bs_seek(bs, start+size); + } else if (end-start < size) { + u32 to_skip = (u32) (size-(end-start)); + GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[iso file] Box \"%s\" has %d extra bytes\n", gf_4cc_to_str(type), to_skip)); + gf_bs_skip_bytes(bs, to_skip); + } + *outBox = newBox; + return e; +} + +GF_Err gf_isom_parse_box(GF_Box **outBox, GF_BitStream *bs) +{ + return gf_isom_parse_box_ex(outBox, bs, 0); +} + +GF_Err gf_isom_full_box_read(GF_Box *ptr, GF_BitStream *bs) +{ + GF_FullBox *self = (GF_FullBox *) ptr; + if (ptr->size<4) return GF_ISOM_INVALID_FILE; + self->version = gf_bs_read_u8(bs); + self->flags = gf_bs_read_u24(bs); + ptr->size -= 4; + return GF_OK; +} + +void gf_isom_full_box_init(GF_Box *a) +{ + GF_FullBox *ptr = (GF_FullBox *)a; + if (! ptr) return; + ptr->flags = 0; + ptr->version = 0; +} + + +void gf_isom_box_array_del(GF_List *boxList) +{ + u32 count, i; + GF_Box *a; + if (!boxList) return; + count = gf_list_count(boxList); + for (i = 0; i < count; i++) { + a = (GF_Box *)gf_list_get(boxList, i); + if (a) gf_isom_box_del(a); + } + gf_list_del(boxList); +} + +GF_Err gf_isom_read_box_list_ex(GF_Box *parent, GF_BitStream *bs, GF_Err (*add_box)(GF_Box *par, GF_Box *b), u32 parent_type) +{ + GF_Err e; + GF_Box *a; + while (parent->size) { + e = gf_isom_parse_box_ex(&a, bs, parent_type); + if (e) { + if (a) gf_isom_box_del(a); + return e; + } + if (parent->size < a->size) { + if (a) gf_isom_box_del(a); + return GF_OK; + //return GF_ISOM_INVALID_FILE; + } + parent->size -= a->size; + e = add_box(parent, a); + if (e) { + gf_isom_box_del(a); + return e; + } + } + return GF_OK; +} + +GF_Err gf_isom_read_box_list(GF_Box *parent, GF_BitStream *bs, GF_Err (*add_box)(GF_Box *par, GF_Box *b)) +{ + return gf_isom_read_box_list_ex(parent, bs, add_box, 0); +} + +#ifndef GPAC_DISABLE_ISOM_WRITE + +GF_Err gf_isom_box_get_size(GF_Box *ptr) +{ + ptr->size = 8; + + if (ptr->type == GF_ISOM_BOX_TYPE_UUID) { + ptr->size += 16; + } + //the large size is handled during write, cause at this stage we don't know the size + return GF_OK; +} + +GF_Err gf_isom_full_box_get_size(GF_Box *ptr) +{ + GF_Err e; + e = gf_isom_box_get_size(ptr); + if (e) return e; + ptr->size += 4; + return GF_OK; +} + + +GF_Err gf_isom_box_write_header(GF_Box *ptr, GF_BitStream *bs) +{ + if (! bs || !ptr) return GF_BAD_PARAM; + if (!ptr->size) return GF_ISOM_INVALID_FILE; + + if (ptr->size > 0xFFFFFFFF) { + gf_bs_write_u32(bs, 1); + } else { + gf_bs_write_u32(bs, (u32) ptr->size); + } + gf_bs_write_u32(bs, ptr->type); + if (ptr->type == GF_ISOM_BOX_TYPE_UUID) + gf_bs_write_data(bs, (char*)((GF_UUIDBox*)ptr)->uuid, 16); + if (ptr->size > 0xFFFFFFFF) + gf_bs_write_u64(bs, ptr->size); + return GF_OK; +} + +GF_Err gf_isom_full_box_write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_FullBox *ptr = (GF_FullBox *)s; + e = gf_isom_box_write_header(s, bs); + if (e) return e; + gf_bs_write_u8(bs, ptr->version); + gf_bs_write_u24(bs, ptr->flags); + return GF_OK; +} + + +GF_Err gf_isom_box_array_write(GF_Box *parent, GF_List *list, GF_BitStream *bs) +{ + u32 count, i; + GF_Box *a; + GF_Err e; + if (!list) return GF_BAD_PARAM; + count = gf_list_count(list); + for (i = 0; i < count; i++) { + a = (GF_Box *)gf_list_get(list, i); + if (a) { + e = gf_isom_box_write(a, bs); + if (e) return e; + } + } + return GF_OK; +} + +GF_Err gf_isom_box_array_size(GF_Box *parent, GF_List *list) +{ + GF_Err e; + u32 count, i; + GF_Box *a; + if (! list) return GF_BAD_PARAM; + + count = gf_list_count(list); + for (i = 0; i < count; i++) { + a = (GF_Box *)gf_list_get(list, i); + if (a) { + e = gf_isom_box_size(a); + if (e) return e; + parent->size += a->size; + } + } + return GF_OK; +} + +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + +GF_Box *gf_isom_box_new(u32 boxType) +{ + GF_Box *a; + switch (boxType) { + case GF_ISOM_BOX_TYPE_REFT: + return reftype_New(); + case GF_ISOM_BOX_TYPE_FREE: return free_New(); + case GF_ISOM_BOX_TYPE_SKIP: + a = free_New(); + if (a) a->type = GF_ISOM_BOX_TYPE_SKIP; + return a; + case GF_ISOM_BOX_TYPE_MDAT: return mdat_New(); + case GF_ISOM_BOX_TYPE_MOOV: return moov_New(); + case GF_ISOM_BOX_TYPE_MVHD: return mvhd_New(); + case GF_ISOM_BOX_TYPE_MDHD: return mdhd_New(); + case GF_ISOM_BOX_TYPE_VMHD: return vmhd_New(); + case GF_ISOM_BOX_TYPE_SMHD: return smhd_New(); + case GF_ISOM_BOX_TYPE_HMHD: return hmhd_New(); + case GF_ISOM_BOX_TYPE_ODHD: + case GF_ISOM_BOX_TYPE_CRHD: + case GF_ISOM_BOX_TYPE_SDHD: + case GF_ISOM_BOX_TYPE_NMHD: + a = nmhd_New(); + if (a) a->type = boxType; + return a; + case GF_ISOM_BOX_TYPE_STBL: return stbl_New(); + case GF_ISOM_BOX_TYPE_DINF: return dinf_New(); + case GF_ISOM_BOX_TYPE_URL: return url_New(); + case GF_ISOM_BOX_TYPE_URN: return urn_New(); + case GF_ISOM_BOX_TYPE_CPRT: return cprt_New(); + case GF_ISOM_BOX_TYPE_CHPL: return chpl_New(); + case GF_ISOM_BOX_TYPE_HDLR: return hdlr_New(); + case GF_ISOM_BOX_TYPE_IODS: return iods_New(); + case GF_ISOM_BOX_TYPE_TRAK: return trak_New(); + case GF_ISOM_BOX_TYPE_MP4S: return mp4s_New(); + case GF_ISOM_BOX_TYPE_MP4V: return mp4v_New(); + case GF_ISOM_BOX_TYPE_MP4A: return mp4a_New(); + case GF_ISOM_BOX_TYPE_GNRM: return gnrm_New(); + case GF_ISOM_BOX_TYPE_GNRV: return gnrv_New(); + case GF_ISOM_BOX_TYPE_GNRA: return gnra_New(); + case GF_ISOM_BOX_TYPE_EDTS: return edts_New(); + case GF_ISOM_BOX_TYPE_UDTA: return udta_New(); + case GF_ISOM_BOX_TYPE_DREF: return dref_New(); + case GF_ISOM_BOX_TYPE_STSD: return stsd_New(); + case GF_ISOM_BOX_TYPE_STTS: return stts_New(); + case GF_ISOM_BOX_TYPE_CTTS: return ctts_New(); + case GF_ISOM_BOX_TYPE_STSH: return stsh_New(); + case GF_ISOM_BOX_TYPE_ELST: return elst_New(); + case GF_ISOM_BOX_TYPE_STSC: return stsc_New(); + case GF_ISOM_BOX_TYPE_STZ2: + case GF_ISOM_BOX_TYPE_STSZ: + a = stsz_New(); + if (a) a->type = boxType; + return a; + case GF_ISOM_BOX_TYPE_STCO: return stco_New(); + case GF_ISOM_BOX_TYPE_STSS: return stss_New(); + case GF_ISOM_BOX_TYPE_STDP: return stdp_New(); + case GF_ISOM_BOX_TYPE_SDTP: return sdtp_New(); + case GF_ISOM_BOX_TYPE_CO64: return co64_New(); + case GF_ISOM_BOX_TYPE_ESDS: return esds_New(); + case GF_ISOM_BOX_TYPE_MINF: return minf_New(); + case GF_ISOM_BOX_TYPE_TKHD: return tkhd_New(); + case GF_ISOM_BOX_TYPE_TREF: return tref_New(); + case GF_ISOM_BOX_TYPE_MDIA: return mdia_New(); + + case GF_ISOM_BOX_TYPE_FTYP: + case GF_ISOM_BOX_TYPE_STYP: + a = ftyp_New(); + if (a) a->type = boxType; + return a; + case GF_ISOM_BOX_TYPE_FADB: return padb_New(); + case GF_ISOM_BOX_TYPE_VOID: return void_New(); + case GF_ISOM_BOX_TYPE_STSF: return stsf_New(); + case GF_ISOM_BOX_TYPE_PDIN: return pdin_New(); + +#ifndef GPAC_DISABLE_ISOM_HINTING + case GF_ISOM_BOX_TYPE_RTP_STSD: + a = ghnt_New(); + if (a) a->type = boxType; + return a; + case GF_ISOM_BOX_TYPE_RTPO: return rtpo_New(); + case GF_ISOM_BOX_TYPE_HNTI: return hnti_New(); + case GF_ISOM_BOX_TYPE_SDP: return sdp_New(); + case GF_ISOM_BOX_TYPE_HINF: return hinf_New(); + case GF_ISOM_BOX_TYPE_RELY: return rely_New(); + case GF_ISOM_BOX_TYPE_TIMS: return tims_New(); + case GF_ISOM_BOX_TYPE_TSRO: return tsro_New(); + case GF_ISOM_BOX_TYPE_SNRO: return snro_New(); + case GF_ISOM_BOX_TYPE_TRPY: return trpy_New(); + case GF_ISOM_BOX_TYPE_NUMP: return nump_New(); + case GF_ISOM_BOX_TYPE_TOTL: return totl_New(); + case GF_ISOM_BOX_TYPE_NPCK: return npck_New(); + case GF_ISOM_BOX_TYPE_TPYL: return tpyl_New(); + case GF_ISOM_BOX_TYPE_TPAY: return tpay_New(); + case GF_ISOM_BOX_TYPE_MAXR: return maxr_New(); + case GF_ISOM_BOX_TYPE_DMED: return dmed_New(); + case GF_ISOM_BOX_TYPE_DIMM: return dimm_New(); + case GF_ISOM_BOX_TYPE_DREP: return drep_New(); + case GF_ISOM_BOX_TYPE_TMIN: return tmin_New(); + case GF_ISOM_BOX_TYPE_TMAX: return tmax_New(); + case GF_ISOM_BOX_TYPE_PMAX: return pmax_New(); + case GF_ISOM_BOX_TYPE_DMAX: return dmax_New(); + case GF_ISOM_BOX_TYPE_PAYT: return payt_New(); + case GF_ISOM_BOX_TYPE_NAME: return name_New(); +#endif /*GPAC_DISABLE_ISOM_HINTING*/ + +#ifndef GPAC_DISABLE_ISOM_FRAGMENTS + case GF_ISOM_BOX_TYPE_MVEX: return mvex_New(); + case GF_ISOM_BOX_TYPE_MEHD: return mehd_New(); + case GF_ISOM_BOX_TYPE_TREX: return trex_New(); + case GF_ISOM_BOX_TYPE_MOOF: return moof_New(); + case GF_ISOM_BOX_TYPE_MFHD: return mfhd_New(); + case GF_ISOM_BOX_TYPE_TRAF: return traf_New(); + case GF_ISOM_BOX_TYPE_TFHD: return tfhd_New(); + case GF_ISOM_BOX_TYPE_TRUN: return trun_New(); +#endif + + /*3GPP boxes*/ + case GF_ISOM_SUBTYPE_3GP_AMR: + case GF_ISOM_SUBTYPE_3GP_AMR_WB: + case GF_ISOM_SUBTYPE_3GP_EVRC: + case GF_ISOM_SUBTYPE_3GP_QCELP: + case GF_ISOM_SUBTYPE_3GP_SMV: + return gppa_New(boxType); + case GF_ISOM_SUBTYPE_3GP_H263: + return gppv_New(boxType); + case GF_ISOM_BOX_TYPE_DAMR: + case GF_ISOM_BOX_TYPE_DEVC: + case GF_ISOM_BOX_TYPE_DQCP: + case GF_ISOM_BOX_TYPE_DSMV: + case GF_ISOM_BOX_TYPE_D263: + return gppc_New(boxType); + + /*AVC boxes*/ + case GF_ISOM_BOX_TYPE_AVCC: + case GF_ISOM_BOX_TYPE_SVCC: + a = avcc_New(); + if (a) a->type = boxType; + return a; + + case GF_ISOM_BOX_TYPE_BTRT: return btrt_New(); + case GF_ISOM_BOX_TYPE_M4DS: return m4ds_New(); + case GF_ISOM_BOX_TYPE_AVC1: return avc1_New(); + case GF_ISOM_BOX_TYPE_AVC2: return avc2_New(); + case GF_ISOM_BOX_TYPE_SVC1: return svc1_New(); + + /*3GPP streaming text*/ + case GF_ISOM_BOX_TYPE_FTAB: return ftab_New(); + case GF_ISOM_BOX_TYPE_TX3G: return tx3g_New(); + case GF_ISOM_BOX_TYPE_TEXT: return text_New(); + case GF_ISOM_BOX_TYPE_STYL: return styl_New(); + case GF_ISOM_BOX_TYPE_HLIT: return hlit_New(); + case GF_ISOM_BOX_TYPE_HCLR: return hclr_New(); + case GF_ISOM_BOX_TYPE_KROK: return krok_New(); + case GF_ISOM_BOX_TYPE_DLAY: return dlay_New(); + case GF_ISOM_BOX_TYPE_HREF: return href_New(); + case GF_ISOM_BOX_TYPE_TBOX: return tbox_New(); + case GF_ISOM_BOX_TYPE_BLNK: return blnk_New(); + case GF_ISOM_BOX_TYPE_TWRP: return twrp_New(); + + /* ISMA 1.0 Encryption and Authentication V 1.0 */ + case GF_ISOM_BOX_TYPE_IKMS: return iKMS_New(); + case GF_ISOM_BOX_TYPE_ISFM: return iSFM_New(); + + /* ISO FF extensions for MPEG-21 */ + case GF_ISOM_BOX_TYPE_META: return meta_New(); + case GF_ISOM_BOX_TYPE_XML: return xml_New(); + case GF_ISOM_BOX_TYPE_BXML: return bxml_New(); + case GF_ISOM_BOX_TYPE_ILOC: return iloc_New(); + case GF_ISOM_BOX_TYPE_PITM: return pitm_New(); + case GF_ISOM_BOX_TYPE_IPRO: return ipro_New(); + case GF_ISOM_BOX_TYPE_INFE: return infe_New(); + case GF_ISOM_BOX_TYPE_IINF: return iinf_New(); + case GF_ISOM_BOX_TYPE_SINF: return sinf_New(); + case GF_ISOM_BOX_TYPE_FRMA: return frma_New(); + case GF_ISOM_BOX_TYPE_SCHM: return schm_New(); + case GF_ISOM_BOX_TYPE_SCHI: return schi_New(); + case GF_ISOM_BOX_TYPE_ENCA: return enca_New(); + case GF_ISOM_BOX_TYPE_ENCV: return encv_New(); + case GF_ISOM_BOX_TYPE_ENCS: return encs_New(); + case GF_ISOM_BOX_TYPE_UUID: return uuid_New(); + + /* Apple extensions */ + case GF_ISOM_BOX_TYPE_ILST: return ilst_New(); + + case GF_ISOM_BOX_TYPE_0xA9NAM: + case GF_ISOM_BOX_TYPE_0xA9CMT: + case GF_ISOM_BOX_TYPE_0xA9DAY: + case GF_ISOM_BOX_TYPE_0xA9ART: + case GF_ISOM_BOX_TYPE_0xA9TRK: + case GF_ISOM_BOX_TYPE_0xA9ALB: + case GF_ISOM_BOX_TYPE_0xA9COM: + case GF_ISOM_BOX_TYPE_0xA9WRT: + case GF_ISOM_BOX_TYPE_0xA9TOO: + case GF_ISOM_BOX_TYPE_0xA9CPY: + case GF_ISOM_BOX_TYPE_0xA9DES: + case GF_ISOM_BOX_TYPE_0xA9GEN: + case GF_ISOM_BOX_TYPE_0xA9GRP: + case GF_ISOM_BOX_TYPE_0xA9ENC: + case GF_ISOM_BOX_TYPE_aART: + case GF_ISOM_BOX_TYPE_GNRE: + case GF_ISOM_BOX_TYPE_DISK: + case GF_ISOM_BOX_TYPE_TRKN: + case GF_ISOM_BOX_TYPE_TMPO: + case GF_ISOM_BOX_TYPE_CPIL: + case GF_ISOM_BOX_TYPE_PGAP: + case GF_ISOM_BOX_TYPE_COVR: + return ListItem_New(boxType); + + case GF_ISOM_BOX_TYPE_DATA: return data_New(); + + case GF_ISOM_BOX_TYPE_OHDR: return ohdr_New(); + case GF_ISOM_BOX_TYPE_GRPI: return grpi_New(); + case GF_ISOM_BOX_TYPE_MDRI: return mdri_New(); + case GF_ISOM_BOX_TYPE_ODTT: return odtt_New(); + case GF_ISOM_BOX_TYPE_ODRB: return odrb_New(); + case GF_ISOM_BOX_TYPE_ODKM: return odkm_New(); + case GF_ISOM_BOX_TYPE_ODAF: + a = iSFM_New(); + a->type = GF_ISOM_BOX_TYPE_ODAF; + return a; + + case GF_ISOM_BOX_TYPE_PASP: return pasp_New(); + case GF_ISOM_BOX_TYPE_TSEL: return tsel_New(); + + case GF_ISOM_BOX_TYPE_DIMS: return dims_New(); + case GF_ISOM_BOX_TYPE_DIMC: return dimC_New(); + case GF_ISOM_BOX_TYPE_DIST: return diST_New(); + + case GF_ISOM_BOX_TYPE_METX: + case GF_ISOM_BOX_TYPE_METT: + return metx_New(boxType); + + case GF_ISOM_BOX_TYPE_AC3: return ac3_New(); + case GF_ISOM_BOX_TYPE_DAC3: return dac3_New(); + + case GF_ISOM_BOX_TYPE_LSRC: return lsrc_New(); + case GF_ISOM_BOX_TYPE_LSR1: return lsr1_New(); + + case GF_ISOM_BOX_TYPE_SIDX: return sidx_New(); + case GF_ISOM_BOX_TYPE_SUBS: return subs_New(); + case GF_ISOM_BOX_TYPE_TFDT: return tfdt_New(); + case GF_ISOM_BOX_TYPE_RVCC: return rvcc_New(); + + default: + a = defa_New(); + if (a) a->type = boxType; + return a; + } +} + + +void gf_isom_box_del(GF_Box *a) +{ + if (!a) return; + switch (a->type) { + case GF_ISOM_BOX_TYPE_REFT: + reftype_del(a); + return; + case GF_ISOM_BOX_TYPE_FREE: + case GF_ISOM_BOX_TYPE_SKIP: + free_del(a); + return; + case GF_ISOM_BOX_TYPE_MDAT: mdat_del(a); return; + case GF_ISOM_BOX_TYPE_MOOV: moov_del(a); return; + case GF_ISOM_BOX_TYPE_MVHD: mvhd_del(a); return; + case GF_ISOM_BOX_TYPE_MDHD: mdhd_del(a); return; + case GF_ISOM_BOX_TYPE_VMHD: vmhd_del(a); return; + case GF_ISOM_BOX_TYPE_SMHD: smhd_del(a); return; + case GF_ISOM_BOX_TYPE_HMHD: hmhd_del(a); return; + case GF_ISOM_BOX_TYPE_ODHD: + case GF_ISOM_BOX_TYPE_CRHD: + case GF_ISOM_BOX_TYPE_SDHD: + case GF_ISOM_BOX_TYPE_NMHD: + nmhd_del(a); + return; + case GF_ISOM_BOX_TYPE_STBL: stbl_del(a); return; + case GF_ISOM_BOX_TYPE_DINF: dinf_del(a); return; + case GF_ISOM_BOX_TYPE_URL: url_del(a); return; + case GF_ISOM_BOX_TYPE_URN: urn_del(a); return; + case GF_ISOM_BOX_TYPE_CHPL: chpl_del(a); return; + case GF_ISOM_BOX_TYPE_CPRT: cprt_del(a); return; + case GF_ISOM_BOX_TYPE_HDLR: hdlr_del(a); return; + case GF_ISOM_BOX_TYPE_IODS: iods_del(a); return; + case GF_ISOM_BOX_TYPE_TRAK: trak_del(a); return; + case GF_ISOM_BOX_TYPE_MP4S: mp4s_del(a); return; + case GF_4CC('2','6','4','b'): + case GF_ISOM_BOX_TYPE_MP4V: + mp4v_del(a); + return; + case GF_ISOM_BOX_TYPE_MP4A: mp4a_del(a); return; + case GF_ISOM_BOX_TYPE_GNRM: gnrm_del(a); return; + case GF_ISOM_BOX_TYPE_GNRV: gnrv_del(a); return; + case GF_ISOM_BOX_TYPE_GNRA: gnra_del(a); return; + case GF_ISOM_BOX_TYPE_EDTS: edts_del(a); return; + case GF_ISOM_BOX_TYPE_UDTA: udta_del(a); return; + case GF_ISOM_BOX_TYPE_DREF: dref_del(a); return; + case GF_ISOM_BOX_TYPE_STSD: stsd_del(a); return; + case GF_ISOM_BOX_TYPE_STTS: stts_del(a); return; + case GF_ISOM_BOX_TYPE_CTTS: ctts_del(a); return; + case GF_ISOM_BOX_TYPE_STSH: stsh_del(a); return; + case GF_ISOM_BOX_TYPE_ELST: elst_del(a); return; + case GF_ISOM_BOX_TYPE_STSC: stsc_del(a); return; + case GF_ISOM_BOX_TYPE_STZ2: + case GF_ISOM_BOX_TYPE_STSZ: + stsz_del(a); + return; + case GF_ISOM_BOX_TYPE_STCO: stco_del(a); return; + case GF_ISOM_BOX_TYPE_STSS: stss_del(a); return; + case GF_ISOM_BOX_TYPE_STDP: stdp_del(a); return; + case GF_ISOM_BOX_TYPE_SDTP: sdtp_del(a); return; + case GF_ISOM_BOX_TYPE_CO64: co64_del(a); return; + case GF_ISOM_BOX_TYPE_ESDS: esds_del(a); return; + case GF_ISOM_BOX_TYPE_MINF: minf_del(a); return; + case GF_ISOM_BOX_TYPE_TKHD: tkhd_del(a); return; + case GF_ISOM_BOX_TYPE_TREF: tref_del(a); return; + case GF_ISOM_BOX_TYPE_MDIA: mdia_del(a); return; + case GF_ISOM_BOX_TYPE_FTYP: + case GF_ISOM_BOX_TYPE_STYP: + ftyp_del(a); + return; + case GF_ISOM_BOX_TYPE_FADB: padb_del(a); return; + case GF_ISOM_BOX_TYPE_VOID: void_del(a); return; + case GF_ISOM_BOX_TYPE_STSF: stsf_del(a); return; + case GF_ISOM_BOX_TYPE_PDIN: pdin_del(a); return; + +#ifndef GPAC_DISABLE_ISOM_HINTING + case GF_ISOM_BOX_TYPE_RTP_STSD: ghnt_del(a); return; + case GF_ISOM_BOX_TYPE_RTPO: rtpo_del(a); return; + case GF_ISOM_BOX_TYPE_HNTI: hnti_del(a); return; + case GF_ISOM_BOX_TYPE_SDP: sdp_del(a); return; + case GF_ISOM_BOX_TYPE_HINF: hinf_del(a); return; + case GF_ISOM_BOX_TYPE_RELY: rely_del(a); return; + case GF_ISOM_BOX_TYPE_TIMS: tims_del(a); return; + case GF_ISOM_BOX_TYPE_TSRO: tsro_del(a); return; + case GF_ISOM_BOX_TYPE_SNRO: snro_del(a); return; + case GF_ISOM_BOX_TYPE_TRPY: trpy_del(a); return; + case GF_ISOM_BOX_TYPE_NUMP: nump_del(a); return; + case GF_ISOM_BOX_TYPE_TOTL: totl_del(a); return; + case GF_ISOM_BOX_TYPE_NPCK: npck_del(a); return; + case GF_ISOM_BOX_TYPE_TPYL: tpyl_del(a); return; + case GF_ISOM_BOX_TYPE_TPAY: tpay_del(a); return; + case GF_ISOM_BOX_TYPE_MAXR: maxr_del(a); return; + case GF_ISOM_BOX_TYPE_DMED: dmed_del(a); return; + case GF_ISOM_BOX_TYPE_DIMM: dimm_del(a); return; + case GF_ISOM_BOX_TYPE_DREP: drep_del(a); return; + case GF_ISOM_BOX_TYPE_TMIN: tmin_del(a); return; + case GF_ISOM_BOX_TYPE_TMAX: tmax_del(a); return; + case GF_ISOM_BOX_TYPE_PMAX: pmax_del(a); return; + case GF_ISOM_BOX_TYPE_DMAX: dmax_del(a); return; + case GF_ISOM_BOX_TYPE_PAYT: payt_del(a); return; + case GF_ISOM_BOX_TYPE_NAME: name_del(a); return; +#endif /*GPAC_DISABLE_ISOM_HINTING*/ + +#ifndef GPAC_DISABLE_ISOM_FRAGMENTS + case GF_ISOM_BOX_TYPE_MVEX: mvex_del(a); return; + case GF_ISOM_BOX_TYPE_MEHD: mehd_del(a); return; + case GF_ISOM_BOX_TYPE_TREX: trex_del(a); return; + case GF_ISOM_BOX_TYPE_MOOF: moof_del(a); return; + case GF_ISOM_BOX_TYPE_MFHD: mfhd_del(a); return; + case GF_ISOM_BOX_TYPE_TRAF: traf_del(a); return; + case GF_ISOM_BOX_TYPE_TFHD: tfhd_del(a); return; + case GF_ISOM_BOX_TYPE_TRUN: trun_del(a); return; +#endif + + case GF_ISOM_SUBTYPE_3GP_AMR: + case GF_ISOM_SUBTYPE_3GP_AMR_WB: + case GF_ISOM_SUBTYPE_3GP_EVRC: + case GF_ISOM_SUBTYPE_3GP_QCELP: + case GF_ISOM_SUBTYPE_3GP_SMV: + gppa_del(a); + return; + case GF_ISOM_SUBTYPE_3GP_H263: gppv_del(a); return; + case GF_ISOM_BOX_TYPE_DAMR: + case GF_ISOM_BOX_TYPE_DEVC: + case GF_ISOM_BOX_TYPE_DQCP: + case GF_ISOM_BOX_TYPE_DSMV: + case GF_ISOM_BOX_TYPE_D263: + gppc_del(a); return; + + /*AVC boxes*/ + case GF_ISOM_BOX_TYPE_AVCC: + case GF_ISOM_BOX_TYPE_SVCC: + avcc_del(a); return; + case GF_ISOM_BOX_TYPE_BTRT: btrt_del(a); return; + case GF_ISOM_BOX_TYPE_M4DS: m4ds_del(a); return; + case GF_ISOM_BOX_TYPE_AVC1: + case GF_ISOM_BOX_TYPE_AVC2: + case GF_ISOM_BOX_TYPE_SVC1: + mp4v_del(a); return; + + /*3GPP streaming text*/ + case GF_ISOM_BOX_TYPE_FTAB: ftab_del(a); return; + case GF_ISOM_BOX_TYPE_TX3G: tx3g_del(a); return; + case GF_ISOM_BOX_TYPE_TEXT: text_del(a); return; + case GF_ISOM_BOX_TYPE_STYL: styl_del(a); return; + case GF_ISOM_BOX_TYPE_HLIT: hlit_del(a); return; + case GF_ISOM_BOX_TYPE_HCLR: hclr_del(a); return; + case GF_ISOM_BOX_TYPE_KROK: krok_del(a); return; + case GF_ISOM_BOX_TYPE_DLAY: dlay_del(a); return; + case GF_ISOM_BOX_TYPE_HREF: href_del(a); return; + case GF_ISOM_BOX_TYPE_TBOX: tbox_del(a); return; + case GF_ISOM_BOX_TYPE_BLNK: blnk_del(a); return; + case GF_ISOM_BOX_TYPE_TWRP: twrp_del(a); return; + + /* ISMA 1.0 Encryption and Authentication V 1.0 */ + case GF_ISOM_BOX_TYPE_IKMS: iKMS_del(a); return; + case GF_ISOM_BOX_TYPE_ISFM: iSFM_del(a); return; + + /* ISO FF extensions for MPEG-21 */ + case GF_ISOM_BOX_TYPE_META: meta_del(a); return; + case GF_ISOM_BOX_TYPE_XML: xml_del(a); return; + case GF_ISOM_BOX_TYPE_BXML: bxml_del(a); return; + case GF_ISOM_BOX_TYPE_ILOC: iloc_del(a); return; + case GF_ISOM_BOX_TYPE_PITM: pitm_del(a); return; + case GF_ISOM_BOX_TYPE_IPRO: ipro_del(a); return; + case GF_ISOM_BOX_TYPE_INFE: infe_del(a); return; + case GF_ISOM_BOX_TYPE_IINF: iinf_del(a); return; + case GF_ISOM_BOX_TYPE_SINF: sinf_del(a); return; + case GF_ISOM_BOX_TYPE_FRMA: frma_del(a); return; + case GF_ISOM_BOX_TYPE_SCHM: schm_del(a); return; + case GF_ISOM_BOX_TYPE_SCHI: schi_del(a); return; + + case GF_ISOM_BOX_TYPE_ENCA: + case GF_ISOM_BOX_TYPE_ENCV: + case GF_ISOM_BOX_TYPE_ENCS: + a->type = ((GF_SampleEntryBox *)a)->protection_info->original_format->data_format; + gf_isom_box_del(a); + return; + case GF_ISOM_BOX_TYPE_UUID: + uuid_del(a); + return; + + /* Apple extensions */ + case GF_ISOM_BOX_TYPE_ILST: ilst_del(a); return; + + case GF_ISOM_BOX_TYPE_0xA9NAM: + case GF_ISOM_BOX_TYPE_0xA9CMT: + case GF_ISOM_BOX_TYPE_0xA9DAY: + case GF_ISOM_BOX_TYPE_0xA9ART: + case GF_ISOM_BOX_TYPE_0xA9TRK: + case GF_ISOM_BOX_TYPE_0xA9ALB: + case GF_ISOM_BOX_TYPE_0xA9COM: + case GF_ISOM_BOX_TYPE_0xA9WRT: + case GF_ISOM_BOX_TYPE_0xA9TOO: + case GF_ISOM_BOX_TYPE_0xA9CPY: + case GF_ISOM_BOX_TYPE_0xA9DES: + case GF_ISOM_BOX_TYPE_0xA9GEN: + case GF_ISOM_BOX_TYPE_0xA9GRP: + case GF_ISOM_BOX_TYPE_0xA9ENC: + case GF_ISOM_BOX_TYPE_aART: + case GF_ISOM_BOX_TYPE_GNRE: + case GF_ISOM_BOX_TYPE_DISK: + case GF_ISOM_BOX_TYPE_TRKN: + case GF_ISOM_BOX_TYPE_TMPO: + case GF_ISOM_BOX_TYPE_CPIL: + case GF_ISOM_BOX_TYPE_PGAP: + case GF_ISOM_BOX_TYPE_COVR: + ListItem_del(a); + return; + + case GF_ISOM_BOX_TYPE_DATA: data_del(a); return; + + case GF_ISOM_BOX_TYPE_OHDR: ohdr_del(a); return; + case GF_ISOM_BOX_TYPE_GRPI: grpi_del(a); return; + case GF_ISOM_BOX_TYPE_MDRI: mdri_del(a); return; + case GF_ISOM_BOX_TYPE_ODTT: odtt_del(a); return; + case GF_ISOM_BOX_TYPE_ODRB: odrb_del(a); return; + case GF_ISOM_BOX_TYPE_ODKM: odkm_del(a); return; + case GF_ISOM_BOX_TYPE_ODAF: iSFM_del(a); return; + + case GF_ISOM_BOX_TYPE_PASP: pasp_del(a); return; + case GF_ISOM_BOX_TYPE_TSEL: tsel_del(a); return; + + case GF_ISOM_BOX_TYPE_METX: + case GF_ISOM_BOX_TYPE_METT: + metx_del(a); + return; + + case GF_ISOM_BOX_TYPE_DIMS: dims_del(a); return; + case GF_ISOM_BOX_TYPE_DIMC: dimC_del(a); return; + case GF_ISOM_BOX_TYPE_DIST: diST_del(a); return; + + case GF_ISOM_BOX_TYPE_AC3: ac3_del(a); return; + case GF_ISOM_BOX_TYPE_DAC3: dac3_del(a); return; + + case GF_ISOM_BOX_TYPE_LSRC: lsrc_del(a); return; + case GF_ISOM_BOX_TYPE_LSR1: lsr1_del(a); return; + + case GF_ISOM_BOX_TYPE_SIDX: sidx_del(a); return; + case GF_ISOM_BOX_TYPE_SUBS: subs_del(a); return; + case GF_ISOM_BOX_TYPE_TFDT: tfdt_del(a); return; + case GF_ISOM_BOX_TYPE_RVCC: rvcc_del(a); return; + + default: + defa_del(a); + return; + } +} + + + + +GF_Err gf_isom_box_read(GF_Box *a, GF_BitStream *bs) +{ + switch (a->type) { + case GF_ISOM_BOX_TYPE_REFT: + return reftype_Read(a, bs); + case GF_ISOM_BOX_TYPE_FREE: + case GF_ISOM_BOX_TYPE_SKIP: + return free_Read(a, bs); + case GF_ISOM_BOX_TYPE_MDAT: return mdat_Read(a, bs); + case GF_ISOM_BOX_TYPE_MOOV: return moov_Read(a, bs); + case GF_ISOM_BOX_TYPE_MVHD: return mvhd_Read(a, bs); + case GF_ISOM_BOX_TYPE_MDHD: return mdhd_Read(a, bs); + case GF_ISOM_BOX_TYPE_VMHD: return vmhd_Read(a, bs); + case GF_ISOM_BOX_TYPE_SMHD: return smhd_Read(a, bs); + case GF_ISOM_BOX_TYPE_HMHD: return hmhd_Read(a, bs); + case GF_ISOM_BOX_TYPE_ODHD: + case GF_ISOM_BOX_TYPE_CRHD: + case GF_ISOM_BOX_TYPE_SDHD: + case GF_ISOM_BOX_TYPE_NMHD: + return nmhd_Read(a, bs); + case GF_ISOM_BOX_TYPE_STBL: return stbl_Read(a, bs); + case GF_ISOM_BOX_TYPE_DINF: return dinf_Read(a, bs); + case GF_ISOM_BOX_TYPE_URL: return url_Read(a, bs); + case GF_ISOM_BOX_TYPE_URN: return urn_Read(a, bs); + case GF_ISOM_BOX_TYPE_CPRT: return cprt_Read(a, bs); + case GF_ISOM_BOX_TYPE_HDLR: return hdlr_Read(a, bs); + case GF_ISOM_BOX_TYPE_IODS: return iods_Read(a, bs); + case GF_ISOM_BOX_TYPE_TRAK: return trak_Read(a, bs); + case GF_ISOM_BOX_TYPE_MP4S: return mp4s_Read(a, bs); + case GF_ISOM_BOX_TYPE_MP4V: return mp4v_Read(a, bs); + case GF_ISOM_BOX_TYPE_MP4A: return mp4a_Read(a, bs); + case GF_ISOM_BOX_TYPE_EDTS: return edts_Read(a, bs); + case GF_ISOM_BOX_TYPE_UDTA: return udta_Read(a, bs); + case GF_ISOM_BOX_TYPE_DREF: return dref_Read(a, bs); + case GF_ISOM_BOX_TYPE_STSD: return stsd_Read(a, bs); + case GF_ISOM_BOX_TYPE_STTS: return stts_Read(a, bs); + case GF_ISOM_BOX_TYPE_CTTS: return ctts_Read(a, bs); + case GF_ISOM_BOX_TYPE_STSH: return stsh_Read(a, bs); + case GF_ISOM_BOX_TYPE_ELST: return elst_Read(a, bs); + case GF_ISOM_BOX_TYPE_STSC: return stsc_Read(a, bs); + case GF_ISOM_BOX_TYPE_STZ2: + case GF_ISOM_BOX_TYPE_STSZ: + return stsz_Read(a, bs); + case GF_ISOM_BOX_TYPE_STCO: return stco_Read(a, bs); + case GF_ISOM_BOX_TYPE_STSS: return stss_Read(a, bs); + case GF_ISOM_BOX_TYPE_STDP: return stdp_Read(a, bs); + case GF_ISOM_BOX_TYPE_SDTP: return sdtp_Read(a, bs); + case GF_ISOM_BOX_TYPE_CO64: return co64_Read(a, bs); + case GF_ISOM_BOX_TYPE_ESDS: return esds_Read(a, bs); + case GF_ISOM_BOX_TYPE_MINF: return minf_Read(a, bs); + case GF_ISOM_BOX_TYPE_TKHD: return tkhd_Read(a, bs); + case GF_ISOM_BOX_TYPE_TREF: return tref_Read(a, bs); + case GF_ISOM_BOX_TYPE_MDIA: return mdia_Read(a, bs); + case GF_ISOM_BOX_TYPE_CHPL: return chpl_Read(a, bs); + case GF_ISOM_BOX_TYPE_FTYP: + case GF_ISOM_BOX_TYPE_STYP: + return ftyp_Read(a, bs); + case GF_ISOM_BOX_TYPE_FADB: return padb_Read(a, bs); + case GF_ISOM_BOX_TYPE_VOID: return void_Read(a, bs); + case GF_ISOM_BOX_TYPE_STSF: return stsf_Read(a, bs); + case GF_ISOM_BOX_TYPE_PDIN: return pdin_Read(a, bs); + +#ifndef GPAC_DISABLE_ISOM_HINTING + case GF_ISOM_BOX_TYPE_RTP_STSD: return ghnt_Read(a, bs); + case GF_ISOM_BOX_TYPE_RTPO: return rtpo_Read(a, bs); + case GF_ISOM_BOX_TYPE_HNTI: return hnti_Read(a, bs); + case GF_ISOM_BOX_TYPE_SDP: return sdp_Read(a, bs); + case GF_ISOM_BOX_TYPE_HINF: return hinf_Read(a, bs); + case GF_ISOM_BOX_TYPE_RELY: return rely_Read(a, bs); + case GF_ISOM_BOX_TYPE_TIMS: return tims_Read(a, bs); + case GF_ISOM_BOX_TYPE_TSRO: return tsro_Read(a, bs); + case GF_ISOM_BOX_TYPE_SNRO: return snro_Read(a, bs); + case GF_ISOM_BOX_TYPE_TRPY: return trpy_Read(a, bs); + case GF_ISOM_BOX_TYPE_NUMP: return nump_Read(a, bs); + case GF_ISOM_BOX_TYPE_TOTL: return totl_Read(a, bs); + case GF_ISOM_BOX_TYPE_NPCK: return npck_Read(a, bs); + case GF_ISOM_BOX_TYPE_TPYL: return tpyl_Read(a, bs); + case GF_ISOM_BOX_TYPE_TPAY: return tpay_Read(a, bs); + case GF_ISOM_BOX_TYPE_MAXR: return maxr_Read(a, bs); + case GF_ISOM_BOX_TYPE_DMED: return dmed_Read(a, bs); + case GF_ISOM_BOX_TYPE_DIMM: return dimm_Read(a, bs); + case GF_ISOM_BOX_TYPE_DREP: return drep_Read(a, bs); + case GF_ISOM_BOX_TYPE_TMIN: return tmin_Read(a, bs); + case GF_ISOM_BOX_TYPE_TMAX: return tmax_Read(a, bs); + case GF_ISOM_BOX_TYPE_PMAX: return pmax_Read(a, bs); + case GF_ISOM_BOX_TYPE_DMAX: return dmax_Read(a, bs); + case GF_ISOM_BOX_TYPE_PAYT: return payt_Read(a, bs); + case GF_ISOM_BOX_TYPE_NAME: return name_Read(a, bs); +#endif /*GPAC_DISABLE_ISOM_HINTING*/ + +#ifndef GPAC_DISABLE_ISOM_FRAGMENTS + case GF_ISOM_BOX_TYPE_MVEX: return mvex_Read(a, bs); + case GF_ISOM_BOX_TYPE_MEHD: return mehd_Read(a, bs); + case GF_ISOM_BOX_TYPE_TREX: return trex_Read(a, bs); + case GF_ISOM_BOX_TYPE_MOOF: return moof_Read(a, bs); + case GF_ISOM_BOX_TYPE_MFHD: return mfhd_Read(a, bs); + case GF_ISOM_BOX_TYPE_TRAF: return traf_Read(a, bs); + case GF_ISOM_BOX_TYPE_TFHD: return tfhd_Read(a, bs); + case GF_ISOM_BOX_TYPE_TRUN: return trun_Read(a, bs); +#endif + + + /*3GPP boxes*/ + case GF_ISOM_SUBTYPE_3GP_AMR: + case GF_ISOM_SUBTYPE_3GP_AMR_WB: + case GF_ISOM_SUBTYPE_3GP_EVRC: + case GF_ISOM_SUBTYPE_3GP_QCELP: + case GF_ISOM_SUBTYPE_3GP_SMV: + return gppa_Read(a, bs); + case GF_ISOM_SUBTYPE_3GP_H263: return gppv_Read(a, bs); + case GF_ISOM_BOX_TYPE_DAMR: + case GF_ISOM_BOX_TYPE_DEVC: + case GF_ISOM_BOX_TYPE_DQCP: + case GF_ISOM_BOX_TYPE_DSMV: + case GF_ISOM_BOX_TYPE_D263: + return gppc_Read(a, bs); + + case GF_ISOM_BOX_TYPE_AVCC: + case GF_ISOM_BOX_TYPE_SVCC: + return avcc_Read(a, bs); + case GF_ISOM_BOX_TYPE_BTRT: return btrt_Read(a, bs); + case GF_ISOM_BOX_TYPE_M4DS: return m4ds_Read(a, bs); + case GF_ISOM_BOX_TYPE_AVC1: + case GF_ISOM_BOX_TYPE_AVC2: + case GF_ISOM_BOX_TYPE_SVC1: + return mp4v_Read(a, bs); + + /*3GPP streaming text*/ + case GF_ISOM_BOX_TYPE_FTAB: return ftab_Read(a, bs); + case GF_ISOM_BOX_TYPE_TEXT: return text_Read(a, bs); + case GF_ISOM_BOX_TYPE_TX3G: return tx3g_Read(a, bs); + case GF_ISOM_BOX_TYPE_STYL: return styl_Read(a, bs); + case GF_ISOM_BOX_TYPE_HLIT: return hlit_Read(a, bs); + case GF_ISOM_BOX_TYPE_HCLR: return hclr_Read(a, bs); + case GF_ISOM_BOX_TYPE_KROK: return krok_Read(a, bs); + case GF_ISOM_BOX_TYPE_DLAY: return dlay_Read(a, bs); + case GF_ISOM_BOX_TYPE_HREF: return href_Read(a, bs); + case GF_ISOM_BOX_TYPE_TBOX: return tbox_Read(a, bs); + case GF_ISOM_BOX_TYPE_BLNK: return blnk_Read(a, bs); + case GF_ISOM_BOX_TYPE_TWRP: return twrp_Read(a, bs); + + /* ISMA 1.0 Encryption and Authentication V 1.0 */ + case GF_ISOM_BOX_TYPE_IKMS: return iKMS_Read(a, bs); + case GF_ISOM_BOX_TYPE_ISFM: return iSFM_Read(a, bs); + + /* ISO FF extensions for MPEG-21 */ + case GF_ISOM_BOX_TYPE_META: return meta_Read(a, bs); + case GF_ISOM_BOX_TYPE_XML: return xml_Read(a, bs); + case GF_ISOM_BOX_TYPE_BXML: return bxml_Read(a, bs); + case GF_ISOM_BOX_TYPE_ILOC: return iloc_Read(a, bs); + case GF_ISOM_BOX_TYPE_PITM: return pitm_Read(a, bs); + case GF_ISOM_BOX_TYPE_IPRO: return ipro_Read(a, bs); + case GF_ISOM_BOX_TYPE_INFE: return infe_Read(a, bs); + case GF_ISOM_BOX_TYPE_IINF: return iinf_Read(a, bs); + case GF_ISOM_BOX_TYPE_SINF: return sinf_Read(a, bs); + case GF_ISOM_BOX_TYPE_FRMA: return frma_Read(a, bs); + case GF_ISOM_BOX_TYPE_SCHM: return schm_Read(a, bs); + case GF_ISOM_BOX_TYPE_SCHI: return schi_Read(a, bs); + case GF_ISOM_BOX_TYPE_ENCA: return mp4a_Read(a, bs); + case GF_ISOM_BOX_TYPE_ENCV: return mp4v_Read(a, bs); + case GF_ISOM_BOX_TYPE_ENCS: return mp4s_Read(a, bs); + case GF_ISOM_BOX_TYPE_UUID: return uuid_Read(a, bs); + + /* Apple extensions */ + case GF_ISOM_BOX_TYPE_ILST: return ilst_Read(a, bs); + + case GF_ISOM_BOX_TYPE_0xA9NAM: + case GF_ISOM_BOX_TYPE_0xA9CMT: + case GF_ISOM_BOX_TYPE_0xA9DAY: + case GF_ISOM_BOX_TYPE_0xA9ART: + case GF_ISOM_BOX_TYPE_0xA9TRK: + case GF_ISOM_BOX_TYPE_0xA9ALB: + case GF_ISOM_BOX_TYPE_0xA9COM: + case GF_ISOM_BOX_TYPE_0xA9WRT: + case GF_ISOM_BOX_TYPE_0xA9TOO: + case GF_ISOM_BOX_TYPE_0xA9CPY: + case GF_ISOM_BOX_TYPE_0xA9DES: + case GF_ISOM_BOX_TYPE_0xA9GEN: + case GF_ISOM_BOX_TYPE_0xA9GRP: + case GF_ISOM_BOX_TYPE_0xA9ENC: + case GF_ISOM_BOX_TYPE_aART: + case GF_ISOM_BOX_TYPE_GNRE: + case GF_ISOM_BOX_TYPE_DISK: + case GF_ISOM_BOX_TYPE_TRKN: + case GF_ISOM_BOX_TYPE_TMPO: + case GF_ISOM_BOX_TYPE_CPIL: + case GF_ISOM_BOX_TYPE_PGAP: + case GF_ISOM_BOX_TYPE_COVR: + return ListItem_Read(a, bs); + + case GF_ISOM_BOX_TYPE_DATA: return data_Read(a, bs); + + case GF_ISOM_BOX_TYPE_OHDR: return ohdr_Read(a, bs); + case GF_ISOM_BOX_TYPE_GRPI: return grpi_Read(a, bs); + case GF_ISOM_BOX_TYPE_MDRI: return mdri_Read(a, bs); + case GF_ISOM_BOX_TYPE_ODTT: return odtt_Read(a, bs); + case GF_ISOM_BOX_TYPE_ODRB: return odrb_Read(a, bs); + case GF_ISOM_BOX_TYPE_ODKM: return odkm_Read(a, bs); + case GF_ISOM_BOX_TYPE_ODAF: return iSFM_Read(a, bs); + + case GF_ISOM_BOX_TYPE_PASP: return pasp_Read(a, bs); + case GF_ISOM_BOX_TYPE_TSEL: return tsel_Read(a, bs); + + case GF_ISOM_BOX_TYPE_METX: + case GF_ISOM_BOX_TYPE_METT: + return metx_Read(a, bs); + + case GF_ISOM_BOX_TYPE_DIMS: return dims_Read(a, bs); + case GF_ISOM_BOX_TYPE_DIMC: return dimC_Read(a, bs); + case GF_ISOM_BOX_TYPE_DIST: return diST_Read(a, bs); + + case GF_ISOM_BOX_TYPE_AC3: return ac3_Read(a, bs); + case GF_ISOM_BOX_TYPE_DAC3: return dac3_Read(a, bs); + + case GF_ISOM_BOX_TYPE_LSRC: return lsrc_Read(a, bs); + case GF_ISOM_BOX_TYPE_LSR1: return lsr1_Read(a, bs); + + case GF_ISOM_BOX_TYPE_SIDX: return sidx_Read(a, bs); + case GF_ISOM_BOX_TYPE_SUBS: return subs_Read(a, bs); + case GF_ISOM_BOX_TYPE_TFDT: return tfdt_Read(a, bs); + case GF_ISOM_BOX_TYPE_RVCC: return rvcc_Read(a, bs); + + default: + return defa_Read(a, bs); + } +} + +#ifndef GPAC_DISABLE_ISOM_WRITE + +GF_Err gf_isom_box_write(GF_Box *a, GF_BitStream *bs) +{ + switch (a->type) { + case GF_ISOM_BOX_TYPE_REFT: + return reftype_Write(a, bs); + case GF_ISOM_BOX_TYPE_FREE: + case GF_ISOM_BOX_TYPE_SKIP: + return free_Write(a, bs); + case GF_ISOM_BOX_TYPE_MDAT: return mdat_Write(a, bs); + case GF_ISOM_BOX_TYPE_MOOV: return moov_Write(a, bs); + case GF_ISOM_BOX_TYPE_MVHD: return mvhd_Write(a, bs); + case GF_ISOM_BOX_TYPE_MDHD: return mdhd_Write(a, bs); + case GF_ISOM_BOX_TYPE_VMHD: return vmhd_Write(a, bs); + case GF_ISOM_BOX_TYPE_SMHD: return smhd_Write(a, bs); + case GF_ISOM_BOX_TYPE_HMHD: return hmhd_Write(a, bs); + case GF_ISOM_BOX_TYPE_ODHD: + case GF_ISOM_BOX_TYPE_CRHD: + case GF_ISOM_BOX_TYPE_SDHD: + case GF_ISOM_BOX_TYPE_NMHD: + return nmhd_Write(a, bs); + case GF_ISOM_BOX_TYPE_STBL: return stbl_Write(a, bs); + case GF_ISOM_BOX_TYPE_DINF: return dinf_Write(a, bs); + case GF_ISOM_BOX_TYPE_URL: return url_Write(a, bs); + case GF_ISOM_BOX_TYPE_URN: return urn_Write(a, bs); + case GF_ISOM_BOX_TYPE_CHPL: return chpl_Write(a, bs); + case GF_ISOM_BOX_TYPE_CPRT: return cprt_Write(a, bs); + case GF_ISOM_BOX_TYPE_HDLR: return hdlr_Write(a, bs); + case GF_ISOM_BOX_TYPE_IODS: return iods_Write(a, bs); + case GF_ISOM_BOX_TYPE_TRAK: return trak_Write(a, bs); + case GF_ISOM_BOX_TYPE_MP4S: return mp4s_Write(a, bs); + case GF_ISOM_BOX_TYPE_MP4V: return mp4v_Write(a, bs); + case GF_ISOM_BOX_TYPE_MP4A: return mp4a_Write(a, bs); + case GF_ISOM_BOX_TYPE_GNRM: return gnrm_Write(a, bs); + case GF_ISOM_BOX_TYPE_GNRV: return gnrv_Write(a, bs); + case GF_ISOM_BOX_TYPE_GNRA: return gnra_Write(a, bs); + case GF_ISOM_BOX_TYPE_EDTS: return edts_Write(a, bs); + case GF_ISOM_BOX_TYPE_UDTA: return udta_Write(a, bs); + case GF_ISOM_BOX_TYPE_DREF: return dref_Write(a, bs); + case GF_ISOM_BOX_TYPE_STSD: return stsd_Write(a, bs); + case GF_ISOM_BOX_TYPE_STTS: return stts_Write(a, bs); + case GF_ISOM_BOX_TYPE_CTTS: return ctts_Write(a, bs); + case GF_ISOM_BOX_TYPE_STSH: return stsh_Write(a, bs); + case GF_ISOM_BOX_TYPE_ELST: return elst_Write(a, bs); + case GF_ISOM_BOX_TYPE_STSC: return stsc_Write(a, bs); + case GF_ISOM_BOX_TYPE_STZ2: + case GF_ISOM_BOX_TYPE_STSZ: + return stsz_Write(a, bs); + case GF_ISOM_BOX_TYPE_STCO: return stco_Write(a, bs); + case GF_ISOM_BOX_TYPE_STSS: return stss_Write(a, bs); + case GF_ISOM_BOX_TYPE_STDP: return stdp_Write(a, bs); + case GF_ISOM_BOX_TYPE_SDTP: return sdtp_Write(a, bs); + case GF_ISOM_BOX_TYPE_CO64: return co64_Write(a, bs); + case GF_ISOM_BOX_TYPE_ESDS: return esds_Write(a, bs); + case GF_ISOM_BOX_TYPE_MINF: return minf_Write(a, bs); + case GF_ISOM_BOX_TYPE_TKHD: return tkhd_Write(a, bs); + case GF_ISOM_BOX_TYPE_TREF: return tref_Write(a, bs); + case GF_ISOM_BOX_TYPE_MDIA: return mdia_Write(a, bs); + case GF_ISOM_BOX_TYPE_FTYP: + case GF_ISOM_BOX_TYPE_STYP: + return ftyp_Write(a, bs); + case GF_ISOM_BOX_TYPE_FADB: return padb_Write(a, bs); + case GF_ISOM_BOX_TYPE_VOID: return void_Write(a, bs); + case GF_ISOM_BOX_TYPE_STSF: return stsf_Write(a, bs); + case GF_ISOM_BOX_TYPE_PDIN: return pdin_Write(a, bs); + +#ifndef GPAC_DISABLE_ISOM_HINTING + case GF_ISOM_BOX_TYPE_RTP_STSD: return ghnt_Write(a, bs); + case GF_ISOM_BOX_TYPE_RTPO: return rtpo_Write(a, bs); + case GF_ISOM_BOX_TYPE_HNTI: return hnti_Write(a, bs); + case GF_ISOM_BOX_TYPE_SDP: return sdp_Write(a, bs); + case GF_ISOM_BOX_TYPE_HINF: return hinf_Write(a, bs); + case GF_ISOM_BOX_TYPE_RELY: return rely_Write(a, bs); + case GF_ISOM_BOX_TYPE_TIMS: return tims_Write(a, bs); + case GF_ISOM_BOX_TYPE_TSRO: return tsro_Write(a, bs); + case GF_ISOM_BOX_TYPE_SNRO: return snro_Write(a, bs); + case GF_ISOM_BOX_TYPE_TRPY: return trpy_Write(a, bs); + case GF_ISOM_BOX_TYPE_NUMP: return nump_Write(a, bs); + case GF_ISOM_BOX_TYPE_TOTL: return totl_Write(a, bs); + case GF_ISOM_BOX_TYPE_NPCK: return npck_Write(a, bs); + case GF_ISOM_BOX_TYPE_TPYL: return tpyl_Write(a, bs); + case GF_ISOM_BOX_TYPE_TPAY: return tpay_Write(a, bs); + case GF_ISOM_BOX_TYPE_MAXR: return maxr_Write(a, bs); + case GF_ISOM_BOX_TYPE_DMED: return dmed_Write(a, bs); + case GF_ISOM_BOX_TYPE_DIMM: return dimm_Write(a, bs); + case GF_ISOM_BOX_TYPE_DREP: return drep_Write(a, bs); + case GF_ISOM_BOX_TYPE_TMIN: return tmin_Write(a, bs); + case GF_ISOM_BOX_TYPE_TMAX: return tmax_Write(a, bs); + case GF_ISOM_BOX_TYPE_PMAX: return pmax_Write(a, bs); + case GF_ISOM_BOX_TYPE_DMAX: return dmax_Write(a, bs); + case GF_ISOM_BOX_TYPE_PAYT: return payt_Write(a, bs); + case GF_ISOM_BOX_TYPE_NAME: return name_Write(a, bs); +#endif /*GPAC_DISABLE_ISOM_HINTING*/ + +#ifndef GPAC_DISABLE_ISOM_FRAGMENTS + case GF_ISOM_BOX_TYPE_MVEX: return mvex_Write(a, bs); + case GF_ISOM_BOX_TYPE_MEHD: return mehd_Write(a, bs); + case GF_ISOM_BOX_TYPE_TREX: return trex_Write(a, bs); + case GF_ISOM_BOX_TYPE_MOOF: return moof_Write(a, bs); + case GF_ISOM_BOX_TYPE_MFHD: return mfhd_Write(a, bs); + case GF_ISOM_BOX_TYPE_TRAF: return traf_Write(a, bs); + case GF_ISOM_BOX_TYPE_TFHD: return tfhd_Write(a, bs); + case GF_ISOM_BOX_TYPE_TRUN: return trun_Write(a, bs); +#endif + + /*3GPP boxes*/ + case GF_ISOM_SUBTYPE_3GP_AMR: + case GF_ISOM_SUBTYPE_3GP_AMR_WB: + case GF_ISOM_SUBTYPE_3GP_EVRC: + case GF_ISOM_SUBTYPE_3GP_QCELP: + case GF_ISOM_SUBTYPE_3GP_SMV: + return gppa_Write(a, bs); + case GF_ISOM_SUBTYPE_3GP_H263: + return gppv_Write(a, bs); + case GF_ISOM_BOX_TYPE_DAMR: + case GF_ISOM_BOX_TYPE_DEVC: + case GF_ISOM_BOX_TYPE_DQCP: + case GF_ISOM_BOX_TYPE_DSMV: + case GF_ISOM_BOX_TYPE_D263: + return gppc_Write(a, bs); + + case GF_ISOM_BOX_TYPE_AVCC: + case GF_ISOM_BOX_TYPE_SVCC: + return avcc_Write(a, bs); + case GF_ISOM_BOX_TYPE_BTRT: return btrt_Write(a, bs); + case GF_ISOM_BOX_TYPE_M4DS: return m4ds_Write(a, bs); + case GF_ISOM_BOX_TYPE_AVC1: + case GF_ISOM_BOX_TYPE_AVC2: + case GF_ISOM_BOX_TYPE_SVC1: + return mp4v_Write(a, bs); + + /*3GPP streaming text*/ + case GF_ISOM_BOX_TYPE_FTAB: return ftab_Write(a, bs); + case GF_ISOM_BOX_TYPE_TX3G: return tx3g_Write(a, bs); + case GF_ISOM_BOX_TYPE_TEXT: return text_Write(a, bs); + case GF_ISOM_BOX_TYPE_STYL: return styl_Write(a, bs); + case GF_ISOM_BOX_TYPE_HLIT: return hlit_Write(a, bs); + case GF_ISOM_BOX_TYPE_HCLR: return hclr_Write(a, bs); + case GF_ISOM_BOX_TYPE_KROK: return krok_Write(a, bs); + case GF_ISOM_BOX_TYPE_DLAY: return dlay_Write(a, bs); + case GF_ISOM_BOX_TYPE_HREF: return href_Write(a, bs); + case GF_ISOM_BOX_TYPE_TBOX: return tbox_Write(a, bs); + case GF_ISOM_BOX_TYPE_BLNK: return blnk_Write(a, bs); + case GF_ISOM_BOX_TYPE_TWRP: return twrp_Write(a, bs); + + /* ISMA 1.0 Encryption and Authentication V 1.0 */ + case GF_ISOM_BOX_TYPE_IKMS: return iKMS_Write(a, bs); + case GF_ISOM_BOX_TYPE_ISFM: return iSFM_Write(a, bs); + + /* ISO FF extensions for MPEG-21 */ + case GF_ISOM_BOX_TYPE_META: return meta_Write(a, bs); + case GF_ISOM_BOX_TYPE_XML: return xml_Write(a, bs); + case GF_ISOM_BOX_TYPE_BXML: return bxml_Write(a, bs); + case GF_ISOM_BOX_TYPE_ILOC: return iloc_Write(a, bs); + case GF_ISOM_BOX_TYPE_PITM: return pitm_Write(a, bs); + case GF_ISOM_BOX_TYPE_IPRO: return ipro_Write(a, bs); + case GF_ISOM_BOX_TYPE_INFE: return infe_Write(a, bs); + case GF_ISOM_BOX_TYPE_IINF: return iinf_Write(a, bs); + case GF_ISOM_BOX_TYPE_SINF: return sinf_Write(a, bs); + case GF_ISOM_BOX_TYPE_FRMA: return frma_Write(a, bs); + case GF_ISOM_BOX_TYPE_SCHM: return schm_Write(a, bs); + case GF_ISOM_BOX_TYPE_SCHI: return schi_Write(a, bs); + case GF_ISOM_BOX_TYPE_ENCA: return mp4a_Write(a, bs); + case GF_ISOM_BOX_TYPE_ENCV: return mp4v_Write(a, bs); + case GF_ISOM_BOX_TYPE_ENCS: return mp4s_Write(a, bs); + case GF_ISOM_BOX_TYPE_UUID: return uuid_Write(a, bs); + + /* Apple extensions */ + case GF_ISOM_BOX_TYPE_ILST: return ilst_Write(a, bs); + + case GF_ISOM_BOX_TYPE_0xA9NAM: + case GF_ISOM_BOX_TYPE_0xA9CMT: + case GF_ISOM_BOX_TYPE_0xA9DAY: + case GF_ISOM_BOX_TYPE_0xA9ART: + case GF_ISOM_BOX_TYPE_0xA9TRK: + case GF_ISOM_BOX_TYPE_0xA9ALB: + case GF_ISOM_BOX_TYPE_0xA9COM: + case GF_ISOM_BOX_TYPE_0xA9WRT: + case GF_ISOM_BOX_TYPE_0xA9TOO: + case GF_ISOM_BOX_TYPE_0xA9CPY: + case GF_ISOM_BOX_TYPE_0xA9DES: + case GF_ISOM_BOX_TYPE_0xA9GEN: + case GF_ISOM_BOX_TYPE_0xA9GRP: + case GF_ISOM_BOX_TYPE_0xA9ENC: + case GF_ISOM_BOX_TYPE_aART: + case GF_ISOM_BOX_TYPE_GNRE: + case GF_ISOM_BOX_TYPE_DISK: + case GF_ISOM_BOX_TYPE_TRKN: + case GF_ISOM_BOX_TYPE_TMPO: + case GF_ISOM_BOX_TYPE_CPIL: + case GF_ISOM_BOX_TYPE_PGAP: + case GF_ISOM_BOX_TYPE_COVR: + return ListItem_Write(a, bs); + + case GF_ISOM_BOX_TYPE_DATA: return data_Write(a, bs); + + case GF_ISOM_BOX_TYPE_OHDR: return ohdr_Write(a, bs); + case GF_ISOM_BOX_TYPE_GRPI: return grpi_Write(a, bs); + case GF_ISOM_BOX_TYPE_MDRI: return mdri_Write(a, bs); + case GF_ISOM_BOX_TYPE_ODTT: return odtt_Write(a, bs); + case GF_ISOM_BOX_TYPE_ODRB: return odrb_Write(a, bs); + case GF_ISOM_BOX_TYPE_ODKM: return odkm_Write(a, bs); + case GF_ISOM_BOX_TYPE_ODAF: return iSFM_Write(a, bs); + + case GF_ISOM_BOX_TYPE_PASP: return pasp_Write(a, bs); + case GF_ISOM_BOX_TYPE_TSEL: return tsel_Write(a, bs); + + case GF_ISOM_BOX_TYPE_METX: + case GF_ISOM_BOX_TYPE_METT: + return metx_Write(a, bs); + + case GF_ISOM_BOX_TYPE_DIMS: return dims_Write(a, bs); + case GF_ISOM_BOX_TYPE_DIMC: return dimC_Write(a, bs); + case GF_ISOM_BOX_TYPE_DIST: return diST_Write(a, bs); + + case GF_ISOM_BOX_TYPE_AC3: return ac3_Write(a, bs); + case GF_ISOM_BOX_TYPE_DAC3: return dac3_Write(a, bs); + + case GF_ISOM_BOX_TYPE_LSRC: return lsrc_Write(a, bs); + case GF_ISOM_BOX_TYPE_LSR1: return lsr1_Write(a, bs); + + case GF_ISOM_BOX_TYPE_SIDX: return sidx_Write(a, bs); + case GF_ISOM_BOX_TYPE_SUBS: return subs_Write(a, bs); + case GF_ISOM_BOX_TYPE_TFDT: return tfdt_Write(a, bs); + case GF_ISOM_BOX_TYPE_RVCC: return rvcc_Write(a, bs); + + default: + return defa_Write(a, bs); + } +} + + +GF_Err gf_isom_box_size(GF_Box *a) +{ + switch (a->type) { + case GF_ISOM_BOX_TYPE_REFT: + return reftype_Size(a); + case GF_ISOM_BOX_TYPE_FREE: + case GF_ISOM_BOX_TYPE_SKIP: + return free_Size(a); + case GF_ISOM_BOX_TYPE_MDAT: return mdat_Size(a); + case GF_ISOM_BOX_TYPE_MOOV: return moov_Size(a); + case GF_ISOM_BOX_TYPE_MVHD: return mvhd_Size(a); + case GF_ISOM_BOX_TYPE_MDHD: return mdhd_Size(a); + case GF_ISOM_BOX_TYPE_VMHD: return vmhd_Size(a); + case GF_ISOM_BOX_TYPE_SMHD: return smhd_Size(a); + case GF_ISOM_BOX_TYPE_HMHD: return hmhd_Size(a); + case GF_ISOM_BOX_TYPE_ODHD: + case GF_ISOM_BOX_TYPE_CRHD: + case GF_ISOM_BOX_TYPE_SDHD: + case GF_ISOM_BOX_TYPE_NMHD: + return nmhd_Size(a); + case GF_ISOM_BOX_TYPE_STBL: return stbl_Size(a); + case GF_ISOM_BOX_TYPE_DINF: return dinf_Size(a); + case GF_ISOM_BOX_TYPE_URL: return url_Size(a); + case GF_ISOM_BOX_TYPE_URN: return urn_Size(a); + case GF_ISOM_BOX_TYPE_CHPL: return chpl_Size(a); + case GF_ISOM_BOX_TYPE_CPRT: return cprt_Size(a); + case GF_ISOM_BOX_TYPE_HDLR: return hdlr_Size(a); + case GF_ISOM_BOX_TYPE_IODS: return iods_Size(a); + case GF_ISOM_BOX_TYPE_TRAK: return trak_Size(a); + case GF_ISOM_BOX_TYPE_MP4S: return mp4s_Size(a); + case GF_ISOM_BOX_TYPE_MP4V: return mp4v_Size(a); + case GF_ISOM_BOX_TYPE_MP4A: return mp4a_Size(a); + case GF_ISOM_BOX_TYPE_GNRM: return gnrm_Size(a); + case GF_ISOM_BOX_TYPE_GNRV: return gnrv_Size(a); + case GF_ISOM_BOX_TYPE_GNRA: return gnra_Size(a); + case GF_ISOM_BOX_TYPE_EDTS: return edts_Size(a); + case GF_ISOM_BOX_TYPE_UDTA: return udta_Size(a); + case GF_ISOM_BOX_TYPE_DREF: return dref_Size(a); + case GF_ISOM_BOX_TYPE_STSD: return stsd_Size(a); + case GF_ISOM_BOX_TYPE_STTS: return stts_Size(a); + case GF_ISOM_BOX_TYPE_CTTS: return ctts_Size(a); + case GF_ISOM_BOX_TYPE_STSH: return stsh_Size(a); + case GF_ISOM_BOX_TYPE_ELST: return elst_Size(a); + case GF_ISOM_BOX_TYPE_STSC: return stsc_Size(a); + case GF_ISOM_BOX_TYPE_STZ2: + case GF_ISOM_BOX_TYPE_STSZ: + return stsz_Size(a); + case GF_ISOM_BOX_TYPE_STCO: return stco_Size(a); + case GF_ISOM_BOX_TYPE_STSS: return stss_Size(a); + case GF_ISOM_BOX_TYPE_SDTP: return sdtp_Size(a); + case GF_ISOM_BOX_TYPE_CO64: return co64_Size(a); + case GF_ISOM_BOX_TYPE_ESDS: return esds_Size(a); + case GF_ISOM_BOX_TYPE_MINF: return minf_Size(a); + case GF_ISOM_BOX_TYPE_TKHD: return tkhd_Size(a); + case GF_ISOM_BOX_TYPE_TREF: return tref_Size(a); + case GF_ISOM_BOX_TYPE_MDIA: return mdia_Size(a); + case GF_ISOM_BOX_TYPE_FTYP: + case GF_ISOM_BOX_TYPE_STYP: + return ftyp_Size(a); + case GF_ISOM_BOX_TYPE_FADB: return padb_Size(a); + case GF_ISOM_BOX_TYPE_VOID: return void_Size(a); + case GF_ISOM_BOX_TYPE_STSF: return stsf_Size(a); + case GF_ISOM_BOX_TYPE_PDIN: return pdin_Size(a); + +#ifndef GPAC_DISABLE_ISOM_HINTING + case GF_ISOM_BOX_TYPE_RTP_STSD: return ghnt_Size(a); + case GF_ISOM_BOX_TYPE_RTPO: return rtpo_Size(a); + case GF_ISOM_BOX_TYPE_HNTI: return hnti_Size(a); + case GF_ISOM_BOX_TYPE_SDP: return sdp_Size(a); + case GF_ISOM_BOX_TYPE_HINF: return hinf_Size(a); + case GF_ISOM_BOX_TYPE_RELY: return rely_Size(a); + case GF_ISOM_BOX_TYPE_TIMS: return tims_Size(a); + case GF_ISOM_BOX_TYPE_TSRO: return tsro_Size(a); + case GF_ISOM_BOX_TYPE_SNRO: return snro_Size(a); + case GF_ISOM_BOX_TYPE_TRPY: return trpy_Size(a); + case GF_ISOM_BOX_TYPE_NUMP: return nump_Size(a); + case GF_ISOM_BOX_TYPE_TOTL: return totl_Size(a); + case GF_ISOM_BOX_TYPE_NPCK: return npck_Size(a); + case GF_ISOM_BOX_TYPE_TPYL: return tpyl_Size(a); + case GF_ISOM_BOX_TYPE_TPAY: return tpay_Size(a); + case GF_ISOM_BOX_TYPE_MAXR: return maxr_Size(a); + case GF_ISOM_BOX_TYPE_DMED: return dmed_Size(a); + case GF_ISOM_BOX_TYPE_DIMM: return dimm_Size(a); + case GF_ISOM_BOX_TYPE_DREP: return drep_Size(a); + case GF_ISOM_BOX_TYPE_TMIN: return tmin_Size(a); + case GF_ISOM_BOX_TYPE_TMAX: return tmax_Size(a); + case GF_ISOM_BOX_TYPE_PMAX: return pmax_Size(a); + case GF_ISOM_BOX_TYPE_DMAX: return dmax_Size(a); + case GF_ISOM_BOX_TYPE_PAYT: return payt_Size(a); + case GF_ISOM_BOX_TYPE_NAME: return name_Size(a); +#endif /*GPAC_DISABLE_ISOM_HINTING*/ + + +#ifndef GPAC_DISABLE_ISOM_FRAGMENTS + case GF_ISOM_BOX_TYPE_MVEX: return mvex_Size(a); + case GF_ISOM_BOX_TYPE_MEHD: return mehd_Size(a); + case GF_ISOM_BOX_TYPE_TREX: return trex_Size(a); + case GF_ISOM_BOX_TYPE_MOOF: return moof_Size(a); + case GF_ISOM_BOX_TYPE_MFHD: return mfhd_Size(a); + case GF_ISOM_BOX_TYPE_TRAF: return traf_Size(a); + case GF_ISOM_BOX_TYPE_TFHD: return tfhd_Size(a); + case GF_ISOM_BOX_TYPE_TRUN: return trun_Size(a); +#endif + + /*3GPP boxes*/ + case GF_ISOM_SUBTYPE_3GP_AMR: + case GF_ISOM_SUBTYPE_3GP_AMR_WB: + case GF_ISOM_SUBTYPE_3GP_EVRC: + case GF_ISOM_SUBTYPE_3GP_QCELP: + case GF_ISOM_SUBTYPE_3GP_SMV: + return gppa_Size(a); + case GF_ISOM_SUBTYPE_3GP_H263: return gppv_Size(a); + case GF_ISOM_BOX_TYPE_DAMR: + case GF_ISOM_BOX_TYPE_DEVC: + case GF_ISOM_BOX_TYPE_DQCP: + case GF_ISOM_BOX_TYPE_DSMV: + case GF_ISOM_BOX_TYPE_D263: + return gppc_Size(a); + + case GF_ISOM_BOX_TYPE_AVCC: + case GF_ISOM_BOX_TYPE_SVCC: + return avcc_Size(a); + case GF_ISOM_BOX_TYPE_BTRT: return btrt_Size(a); + case GF_ISOM_BOX_TYPE_M4DS: return m4ds_Size(a); + case GF_ISOM_BOX_TYPE_AVC1: + case GF_ISOM_BOX_TYPE_AVC2: + case GF_ISOM_BOX_TYPE_SVC1: + return mp4v_Size(a); + + /*3GPP streaming text*/ + case GF_ISOM_BOX_TYPE_FTAB: return ftab_Size(a); + case GF_ISOM_BOX_TYPE_TX3G: return tx3g_Size(a); + case GF_ISOM_BOX_TYPE_TEXT: return text_Size(a); + case GF_ISOM_BOX_TYPE_STYL: return styl_Size(a); + case GF_ISOM_BOX_TYPE_HLIT: return hlit_Size(a); + case GF_ISOM_BOX_TYPE_HCLR: return hclr_Size(a); + case GF_ISOM_BOX_TYPE_KROK: return krok_Size(a); + case GF_ISOM_BOX_TYPE_DLAY: return dlay_Size(a); + case GF_ISOM_BOX_TYPE_HREF: return href_Size(a); + case GF_ISOM_BOX_TYPE_TBOX: return tbox_Size(a); + case GF_ISOM_BOX_TYPE_BLNK: return blnk_Size(a); + case GF_ISOM_BOX_TYPE_TWRP: return twrp_Size(a); + + /* ISMA 1.0 Encryption and Authentication V 1.0 */ + case GF_ISOM_BOX_TYPE_IKMS: return iKMS_Size(a); + case GF_ISOM_BOX_TYPE_ISFM: return iSFM_Size(a); + + /* ISO FF extensions for MPEG-21 */ + case GF_ISOM_BOX_TYPE_META: return meta_Size(a); + case GF_ISOM_BOX_TYPE_XML: return xml_Size(a); + case GF_ISOM_BOX_TYPE_BXML: return bxml_Size(a); + case GF_ISOM_BOX_TYPE_ILOC: return iloc_Size(a); + case GF_ISOM_BOX_TYPE_PITM: return pitm_Size(a); + case GF_ISOM_BOX_TYPE_IPRO: return ipro_Size(a); + case GF_ISOM_BOX_TYPE_INFE: return infe_Size(a); + case GF_ISOM_BOX_TYPE_IINF: return iinf_Size(a); + case GF_ISOM_BOX_TYPE_SINF: return sinf_Size(a); + case GF_ISOM_BOX_TYPE_FRMA: return frma_Size(a); + case GF_ISOM_BOX_TYPE_SCHM: return schm_Size(a); + case GF_ISOM_BOX_TYPE_SCHI: return schi_Size(a); + case GF_ISOM_BOX_TYPE_ENCA: return mp4a_Size(a); + case GF_ISOM_BOX_TYPE_ENCV: return mp4v_Size(a); + case GF_ISOM_BOX_TYPE_ENCS: return mp4s_Size(a); + case GF_ISOM_BOX_TYPE_UUID: return uuid_Size(a); + + /* Apple extensions */ + case GF_ISOM_BOX_TYPE_ILST: return ilst_Size(a); + + case GF_ISOM_BOX_TYPE_0xA9NAM: + case GF_ISOM_BOX_TYPE_0xA9CMT: + case GF_ISOM_BOX_TYPE_0xA9DAY: + case GF_ISOM_BOX_TYPE_0xA9ART: + case GF_ISOM_BOX_TYPE_0xA9TRK: + case GF_ISOM_BOX_TYPE_0xA9ALB: + case GF_ISOM_BOX_TYPE_0xA9COM: + case GF_ISOM_BOX_TYPE_0xA9WRT: + case GF_ISOM_BOX_TYPE_0xA9TOO: + case GF_ISOM_BOX_TYPE_0xA9CPY: + case GF_ISOM_BOX_TYPE_0xA9DES: + case GF_ISOM_BOX_TYPE_0xA9GEN: + case GF_ISOM_BOX_TYPE_0xA9GRP: + case GF_ISOM_BOX_TYPE_0xA9ENC: + case GF_ISOM_BOX_TYPE_aART: + case GF_ISOM_BOX_TYPE_GNRE: + case GF_ISOM_BOX_TYPE_DISK: + case GF_ISOM_BOX_TYPE_TRKN: + case GF_ISOM_BOX_TYPE_TMPO: + case GF_ISOM_BOX_TYPE_CPIL: + case GF_ISOM_BOX_TYPE_PGAP: + case GF_ISOM_BOX_TYPE_COVR: + return ListItem_Size(a); + + case GF_ISOM_BOX_TYPE_DATA: return data_Size(a); + + case GF_ISOM_BOX_TYPE_OHDR: return ohdr_Size(a); + case GF_ISOM_BOX_TYPE_GRPI: return grpi_Size(a); + case GF_ISOM_BOX_TYPE_MDRI: return mdri_Size(a); + case GF_ISOM_BOX_TYPE_ODTT: return odtt_Size(a); + case GF_ISOM_BOX_TYPE_ODRB: return odrb_Size(a); + case GF_ISOM_BOX_TYPE_ODKM: return odkm_Size(a); + case GF_ISOM_BOX_TYPE_ODAF: return iSFM_Size(a); + + case GF_ISOM_BOX_TYPE_PASP: return pasp_Size(a); + case GF_ISOM_BOX_TYPE_TSEL: return tsel_Size(a); + + case GF_ISOM_BOX_TYPE_METX: + case GF_ISOM_BOX_TYPE_METT: + return metx_Size(a); + + case GF_ISOM_BOX_TYPE_DIMS: return dims_Size(a); + case GF_ISOM_BOX_TYPE_DIMC: return dimC_Size(a); + case GF_ISOM_BOX_TYPE_DIST: return diST_Size(a); + + case GF_ISOM_BOX_TYPE_AC3: return ac3_Size(a); + case GF_ISOM_BOX_TYPE_DAC3: return dac3_Size(a); + + case GF_ISOM_BOX_TYPE_LSRC: return lsrc_Size(a); + case GF_ISOM_BOX_TYPE_LSR1: return lsr1_Size(a); + + case GF_ISOM_BOX_TYPE_SIDX: return sidx_Size(a); + case GF_ISOM_BOX_TYPE_SUBS: return subs_Size(a); + case GF_ISOM_BOX_TYPE_TFDT: return tfdt_Size(a); + case GF_ISOM_BOX_TYPE_RVCC: return rvcc_Size(a); + + default: return defa_Size(a); + } +} + +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + +#endif /*GPAC_DISABLE_ISOM*/ diff --git a/src/gpacmp4/data_map.c b/src/gpacmp4/data_map.c new file mode 100644 index 00000000..0d5c9ba1 --- /dev/null +++ b/src/gpacmp4/data_map.c @@ -0,0 +1,567 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / ISO Media File Format sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ +#include "../disable_warnings.h" +#include +#include + +#ifndef GPAC_DISABLE_ISOM + +void gf_isom_datamap_del(GF_DataMap *ptr) +{ + if (!ptr) return; + + //then delete the structure itself.... + switch (ptr->type) { + //file-based + case GF_ISOM_DATA_FILE: + gf_isom_fdm_del((GF_FileDataMap *)ptr); + break; + case GF_ISOM_DATA_FILE_MAPPING: + gf_isom_fmo_del((GF_FileMappingDataMap *)ptr); + break; + //not implemented + default: + break; + } +} + +//Close a data entry +void gf_isom_datamap_close(GF_MediaInformationBox *minf) +{ + GF_DataEntryBox *ent; + if (!minf || !minf->dataHandler) return; + + ent = (GF_DataEntryBox*)gf_list_get(minf->dataInformation->dref->boxList, minf->dataEntryIndex - 1); + + //if ent NULL, the data entry was not used (should never happen) + if (ent == NULL) return; + + //self contained, do nothing + switch (ent->type) { + case GF_ISOM_BOX_TYPE_URL: + case GF_ISOM_BOX_TYPE_URN: + if (ent->flags == 1) return; + break; + default: + return; + } + + //finally close it + gf_isom_datamap_del(minf->dataHandler); + minf->dataHandler = NULL; +} + +/*cf below, we disable filedatamap since it tricks mem usage on w32*/ +#if 0 +static Bool IsLargeFile(char *path) +{ +#ifndef _WIN32_WCE + FILE *stream; + s64 size; + stream = gf_f64_open(path, "rb"); + if (!stream) return 0; + gf_f64_seek(stream, 0, SEEK_END); + size = gf_f64_tell(stream); + fclose(stream); + if (size == -1L) return 0; + if (size > 0xFFFFFFFF) return 1; +#endif + return 0; +} +#endif + + +//Special constructor, we need some error feedback... + +GF_Err gf_isom_datamap_new(const char *location, const char *parentPath, u8 mode, GF_DataMap **outDataMap) +{ + Bool extern_file; + char *sPath; + *outDataMap = NULL; + + //if nothing specified, this is a MEMORY data map + if (!location) { + //not supported yet + return GF_NOT_SUPPORTED; + } + //we need a temp file ... + if (!strcmp(location, "mp4_tmp_edit")) { +#ifndef GPAC_DISABLE_ISOM_WRITE + *outDataMap = gf_isom_fdm_new_temp(parentPath); + if (! (*outDataMap)) return GF_IO_ERR; + return GF_OK; +#else + return GF_NOT_SUPPORTED; +#endif + } + + extern_file = !gf_url_is_local(location); + + if (mode == GF_ISOM_DATA_MAP_EDIT) { + //we need a local file for edition!!! + if (extern_file) return GF_ISOM_INVALID_MODE; + //OK, switch back to READ mode + mode = GF_ISOM_DATA_MAP_READ; + } + + //TEMP: however, only support for file right now (we'd have to add some callback functions at some point) + if (extern_file) return GF_NOT_SUPPORTED; + + sPath = gf_url_get_absolute_path(location, parentPath); + if (sPath == NULL) return GF_URL_ERROR; + + if (mode == GF_ISOM_DATA_MAP_READ_ONLY) { + mode = GF_ISOM_DATA_MAP_READ; + /*It seems win32 file mapping is reported in prog mem usage -> large increases of occupancy. Should not be a pb + but unless you want mapping, only regular IO will be used...*/ +#if 0 + if (IsLargeFile(sPath)) { + *outDataMap = gf_isom_fdm_new(sPath, mode); + } else { + *outDataMap = gf_isom_fmo_new(sPath, mode); + } +#else + *outDataMap = gf_isom_fdm_new(sPath, mode); +#endif + } else { + *outDataMap = gf_isom_fdm_new(sPath, mode); + } + + gf_free(sPath); + if (! (*outDataMap)) return GF_URL_ERROR; + return GF_OK; +} + +//Open a data entry of a track +//Edit is used to switch between original and edition file +GF_Err gf_isom_datamap_open(GF_MediaBox *mdia, u32 dataRefIndex, u8 Edit) +{ + GF_DataEntryBox *ent; + GF_MediaInformationBox *minf; + u32 SelfCont; + GF_Err e = GF_OK; + if ((mdia == NULL) || (! mdia->information) || !dataRefIndex) + return GF_ISOM_INVALID_MEDIA; + + minf = mdia->information; + + if (dataRefIndex > gf_list_count(minf->dataInformation->dref->boxList)) + return GF_BAD_PARAM; + + ent = (GF_DataEntryBox*)gf_list_get(minf->dataInformation->dref->boxList, dataRefIndex - 1); + if (ent == NULL) return GF_ISOM_INVALID_MEDIA; + + //if the current dataEntry is the desired one, and not self contained, return + if ((minf->dataEntryIndex == dataRefIndex) && (ent->flags != 1)) { + return GF_OK; + } + + //we need to open a new one + //first close the existing one + if (minf->dataHandler) gf_isom_datamap_close(minf); + + SelfCont = 0; + switch (ent->type) { + case GF_ISOM_BOX_TYPE_URL: + case GF_ISOM_BOX_TYPE_URN: + if (ent->flags == 1) SelfCont = 1; + break; + default: + SelfCont = 1; + break; + } + //if self-contained, assign the input file + if (SelfCont) { + //if no edit, open the input file + if (!Edit) { + if (mdia->mediaTrack->moov->mov->movieFileMap == NULL) return GF_ISOM_INVALID_FILE; + minf->dataHandler = mdia->mediaTrack->moov->mov->movieFileMap; + } else { +#ifndef GPAC_DISABLE_ISOM_WRITE + if (mdia->mediaTrack->moov->mov->editFileMap == NULL) return GF_ISOM_INVALID_FILE; + minf->dataHandler = mdia->mediaTrack->moov->mov->editFileMap; +#else + //this should never be the case in an read-only MP4 file + return GF_BAD_PARAM; +#endif + } + //else this is a URL (read mode only) + } else { + e = gf_isom_datamap_new(ent->location, mdia->mediaTrack->moov->mov->fileName, GF_ISOM_DATA_MAP_READ, & mdia->information->dataHandler); + if (e) return (e==GF_URL_ERROR) ? GF_ISOM_UNKNOWN_DATA_REF : e; + } + //OK, set the data entry index + minf->dataEntryIndex = dataRefIndex; + return GF_OK; +} + +//return the NB of bytes actually read (used for HTTP, ...) in case file is uncomplete +u32 gf_isom_datamap_get_data(GF_DataMap *map, char *buffer, u32 bufferLength, u64 Offset) +{ + if (!map || !buffer) return 0; + + switch (map->type) { + case GF_ISOM_DATA_FILE: + return gf_isom_fdm_get_data((GF_FileDataMap *)map, buffer, bufferLength, Offset); + + case GF_ISOM_DATA_FILE_MAPPING: + return gf_isom_fmo_get_data((GF_FileMappingDataMap *)map, buffer, bufferLength, Offset); + + default: + return 0; + } +} + + +#ifndef GPAC_DISABLE_ISOM_WRITE + +u64 FDM_GetTotalOffset(GF_FileDataMap *ptr); +GF_Err FDM_AddData(GF_FileDataMap *ptr, char *data, u32 dataSize); + +u64 gf_isom_datamap_get_offset(GF_DataMap *map) +{ + if (!map) return 0; + + switch (map->type) { + case GF_ISOM_DATA_FILE: + return FDM_GetTotalOffset((GF_FileDataMap *)map); + + default: + return 0; + } +} + + +GF_Err gf_isom_datamap_add_data(GF_DataMap *ptr, char *data, u32 dataSize) +{ + if (!ptr || !data|| !dataSize) return GF_BAD_PARAM; + + switch (ptr->type) { + case GF_ISOM_DATA_FILE: + return FDM_AddData((GF_FileDataMap *)ptr, data, dataSize); + default: + return GF_NOT_SUPPORTED; + } +} + +GF_DataMap *gf_isom_fdm_new_temp(const char *sPath) +{ + GF_FileDataMap *tmp = (GF_FileDataMap *) gf_malloc(sizeof(GF_FileDataMap)); + if (!tmp) return NULL; + memset(tmp, 0, sizeof(GF_FileDataMap)); + tmp->type = GF_ISOM_DATA_FILE; + tmp->mode = GF_ISOM_DATA_MAP_WRITE; + + if (!sPath) { + tmp->stream = gf_temp_file_new(); + } else { + char szPath[GF_MAX_PATH]; + if ((sPath[strlen(sPath)-1] != '\\') && (sPath[strlen(sPath)-1] != '/')) { + sprintf(szPath, "%s%c%p_isotmp", sPath, GF_PATH_SEPARATOR, (void*) tmp); + } else { + sprintf(szPath, "%s%p_isotmp", sPath, (void*) tmp); + } + tmp->stream = gf_f64_open(szPath, "w+b"); + tmp->temp_file = gf_strdup(szPath); + } + if (!tmp->stream) { + if (tmp->temp_file) gf_free(tmp->temp_file); + gf_free(tmp); + return NULL; + } + tmp->bs = gf_bs_from_file(tmp->stream, GF_BITSTREAM_WRITE); + if (!tmp->bs) { + fclose(tmp->stream); + gf_free(tmp); + return NULL; + } + return (GF_DataMap *)tmp; +} + +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + +GF_DataMap *gf_isom_fdm_new(const char *sPath, u8 mode) +{ + u8 bs_mode; + + GF_FileDataMap *tmp = (GF_FileDataMap *) gf_malloc(sizeof(GF_FileDataMap)); + if (!tmp) return NULL; + memset(tmp, 0, sizeof(GF_FileDataMap)); + tmp->type = GF_ISOM_DATA_FILE; + tmp->mode = mode; + +#ifndef GPAC_DISABLE_ISOM_WRITE + //open a temp file + if (!strcmp(sPath, "mp4_tmp_edit")) { + //create a temp file (that only occurs in EDIT/WRITE mode) + tmp->stream = gf_temp_file_new(); + bs_mode = GF_BITSTREAM_READ; + } +#endif + + switch (mode) { + case GF_ISOM_DATA_MAP_READ: + if (!tmp->stream) tmp->stream = gf_f64_open(sPath, "rb"); + bs_mode = GF_BITSTREAM_READ; + break; + ///we open the file in READ/WRITE mode, in case + case GF_ISOM_DATA_MAP_WRITE: + if (!tmp->stream) tmp->stream = gf_f64_open(sPath, "w+b"); + if (!tmp->stream) tmp->stream = gf_f64_open(sPath, "wb"); + bs_mode = GF_BITSTREAM_WRITE; + break; + ///we open the file in CAT mode, in case + case GF_ISOM_DATA_MAP_CAT: + if (!tmp->stream) tmp->stream = gf_f64_open(sPath, "a+b"); + if (tmp->stream) gf_f64_seek(tmp->stream, 0, SEEK_END); + bs_mode = GF_BITSTREAM_WRITE; + break; + default: + gf_free(tmp); + return NULL; + } + if (!tmp->stream) { + gf_free(tmp); + return NULL; + } + tmp->bs = gf_bs_from_file(tmp->stream, bs_mode); + if (!tmp->bs) { + fclose(tmp->stream); + gf_free(tmp); + return NULL; + } + return (GF_DataMap *)tmp; +} + +void gf_isom_fdm_del(GF_FileDataMap *ptr) +{ + if (!ptr || (ptr->type != GF_ISOM_DATA_FILE)) return; + if (ptr->bs) gf_bs_del(ptr->bs); + if (ptr->stream) fclose(ptr->stream); + +#ifndef GPAC_DISABLE_ISOM_WRITE + if (ptr->temp_file) { + gf_delete_file(ptr->temp_file); + gf_free(ptr->temp_file); + } +#endif + gf_free(ptr); +} + + + +u32 gf_isom_fdm_get_data(GF_FileDataMap *ptr, char *buffer, u32 bufferLength, u64 fileOffset) +{ + u32 bytesRead; + + //can we seek till that point ??? + if (fileOffset > gf_bs_get_size(ptr->bs)) return 0; + + //ouch, we are not at the previous location, do a seek + if (ptr->curPos != fileOffset) { + if (gf_bs_seek(ptr->bs, fileOffset) != GF_OK) return 0; + ptr->curPos = fileOffset; + } + //read our data. + bytesRead = gf_bs_read_data(ptr->bs, buffer, bufferLength); + //update our cache + if (bytesRead == bufferLength) { + ptr->curPos += bytesRead; + } else { + //rewind to original (if seek fails, return 0 cause this means: + //1- no support for seek on the platform + //2- corrupted file for the OS + fflush(ptr->stream); + gf_bs_seek(ptr->bs, ptr->curPos); + } + ptr->last_acces_was_read = 1; + return bytesRead; +} + + + +#ifndef GPAC_DISABLE_ISOM_WRITE + + +u64 FDM_GetTotalOffset(GF_FileDataMap *ptr) +{ + if (!ptr) return 0; + //the pos is not always at the end + //this function is called to set up the chunks + //so we need the next WRITE offset + return gf_bs_get_size(ptr->bs); +} + + + +GF_Err FDM_AddData(GF_FileDataMap *ptr, char *data, u32 dataSize) +{ + u32 ret; + u64 orig; + if (ptr->mode == GF_ISOM_DATA_MAP_READ) return GF_BAD_PARAM; + + orig = gf_bs_get_size(ptr->bs); + + /*last access was read, seek to end of file*/ + if (ptr->last_acces_was_read) { + gf_bs_seek(ptr->bs, orig); + ptr->last_acces_was_read = 0; + } + //OK, write our stuff to the datamap... + //we don't use bs here cause we want to know more about what has been written + ret = gf_bs_write_data(ptr->bs, data, dataSize); + if (ret != dataSize) { + ptr->curPos = orig; + gf_bs_seek(ptr->bs, orig); + return GF_IO_ERR; + } + ptr->curPos = gf_bs_get_position(ptr->bs); + //flush the stream !! + fflush(ptr->stream); + return GF_OK; +} + +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + + +#ifdef WIN32 + +#include +#include + +GF_DataMap *gf_isom_fmo_new(const char *sPath, u8 mode) +{ + GF_FileMappingDataMap *tmp; + HANDLE fileH, fileMapH; + DWORD err; +#ifdef _WIN32_WCE + unsigned short sWPath[MAX_PATH]; +#endif + + //only in read only + if (mode != GF_ISOM_DATA_MAP_READ) return NULL; + + tmp = (GF_FileMappingDataMap *) gf_malloc(sizeof(GF_FileMappingDataMap)); + if (!tmp) return NULL; + memset(tmp, 0, sizeof(GF_FileMappingDataMap)); + tmp->type = GF_ISOM_DATA_FILE_MAPPING; + tmp->mode = mode; + tmp->name = gf_strdup(sPath); + + // + // Open the file + // +#ifdef _WIN32_WCE + //convert to WIDE + CE_CharToWide((char *)sPath, sWPath); + + fileH = CreateFileForMapping(sWPath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, + (FILE_ATTRIBUTE_READONLY | FILE_FLAG_RANDOM_ACCESS), NULL ); +#else + fileH = CreateFile(sPath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, + (FILE_ATTRIBUTE_READONLY | FILE_FLAG_RANDOM_ACCESS), NULL ); +#endif + + + if (fileH == INVALID_HANDLE_VALUE) { + gf_free(tmp->name); + gf_free(tmp); + return NULL; + } + + tmp->file_size = GetFileSize(fileH, NULL); + if (tmp->file_size == 0xFFFFFFFF) { + CloseHandle(fileH); + gf_free(tmp->name); + gf_free(tmp); + return NULL; + } + + // + // Create the mapping + // + fileMapH = CreateFileMapping(fileH, NULL, PAGE_READONLY, 0, 0, NULL); + if (fileMapH == NULL) { + CloseHandle(fileH); + gf_free(tmp->name); + gf_free(tmp); + err = GetLastError(); + return NULL; + } + + tmp->byte_map = MapViewOfFile(fileMapH, FILE_MAP_READ, 0, 0, 0); + if (tmp->byte_map == NULL) { + CloseHandle(fileMapH); + CloseHandle(fileH); + gf_free(tmp->name); + gf_free(tmp); + return NULL; + } + + CloseHandle(fileH); + CloseHandle(fileMapH); + + //finaly open our bitstream (from buffer) + tmp->bs = gf_bs_new(tmp->byte_map, tmp->file_size, GF_BITSTREAM_READ); + return (GF_DataMap *)tmp; +} + +void gf_isom_fmo_del(GF_FileMappingDataMap *ptr) +{ + if (!ptr || (ptr->type != GF_ISOM_DATA_FILE_MAPPING)) return; + + if (ptr->bs) gf_bs_del(ptr->bs); + if (ptr->byte_map) UnmapViewOfFile(ptr->byte_map); + gf_free(ptr->name); + gf_free(ptr); +} + + +u32 gf_isom_fmo_get_data(GF_FileMappingDataMap *ptr, char *buffer, u32 bufferLength, u64 fileOffset) +{ + u32 size; + + //can we seek till that point ??? + if (fileOffset > ptr->file_size) return 0; + size = (u32) fileOffset; + + //we do only read operations, so trivial + memcpy(buffer, ptr->byte_map + fileOffset, bufferLength); + return bufferLength; +} + +#else + +GF_DataMap *gf_isom_fmo_new(const char *sPath, u8 mode) { return gf_isom_fdm_new(sPath, mode); } +void gf_isom_fmo_del(GF_FileMappingDataMap *ptr) { gf_isom_fdm_del((GF_FileDataMap *)ptr); } +u32 gf_isom_fmo_get_data(GF_FileMappingDataMap *ptr, char *buffer, u32 bufferLength, u64 fileOffset) +{ + return gf_isom_fdm_get_data((GF_FileDataMap *)ptr, buffer, bufferLength, fileOffset); +} + +#endif + +#endif /*GPAC_DISABLE_ISOM*/ + + diff --git a/src/gpacmp4/desc_private.c b/src/gpacmp4/desc_private.c new file mode 100644 index 00000000..cb3059b8 --- /dev/null +++ b/src/gpacmp4/desc_private.c @@ -0,0 +1,704 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / MPEG-4 ObjectDescriptor sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include + + +// +// CONSTRUCTORS +// +GF_Descriptor *gf_odf_create_descriptor(u8 tag) +{ + GF_Descriptor *desc; + + switch (tag) { + case GF_ODF_IOD_TAG: + return gf_odf_new_iod(); + case GF_ODF_OD_TAG: + return gf_odf_new_od(); + case GF_ODF_ESD_TAG: + return gf_odf_new_esd(); + case GF_ODF_DCD_TAG: + return gf_odf_new_dcd(); + case GF_ODF_SLC_TAG: + //default : we create it without any predefinition... + return gf_odf_new_slc(0); + case GF_ODF_MUXINFO_TAG: + return gf_odf_new_muxinfo(); + case GF_ODF_BIFS_CFG_TAG: + return gf_odf_new_bifs_cfg(); + case GF_ODF_UI_CFG_TAG: + return gf_odf_new_ui_cfg(); + case GF_ODF_TEXT_CFG_TAG: + return gf_odf_new_text_cfg(); + case GF_ODF_TX3G_TAG: + return gf_odf_new_tx3g(); + case GF_ODF_ELEM_MASK_TAG: + return gf_odf_New_ElemMask(); + case GF_ODF_LASER_CFG_TAG: + return gf_odf_new_laser_cfg(); + + case GF_ODF_DSI_TAG: + desc = gf_odf_new_default(); + if (!desc) return desc; + desc->tag = GF_ODF_DSI_TAG; + return desc; + + case GF_ODF_AUX_VIDEO_DATA: + return gf_odf_new_auxvid(); + + case GF_ODF_SEGMENT_TAG: + return gf_odf_new_segment(); + case GF_ODF_MEDIATIME_TAG: + return gf_odf_new_mediatime(); + + //File Format Specific + case GF_ODF_ISOM_IOD_TAG: + return gf_odf_new_isom_iod(); + case GF_ODF_ISOM_OD_TAG: + return gf_odf_new_isom_od(); + case GF_ODF_ESD_INC_TAG: + return gf_odf_new_esd_inc(); + case GF_ODF_ESD_REF_TAG: + return gf_odf_new_esd_ref(); + case GF_ODF_LANG_TAG: + return gf_odf_new_lang(); + +#ifndef GPAC_MINIMAL_ODF + + case GF_ODF_CI_TAG: + return gf_odf_new_ci(); + case GF_ODF_SCI_TAG: + return gf_odf_new_sup_cid(); + case GF_ODF_IPI_PTR_TAG: + return gf_odf_new_ipi_ptr(); + //special case for the file format + case GF_ODF_ISOM_IPI_PTR_TAG: + desc = gf_odf_new_ipi_ptr(); + if (!desc) return desc; + desc->tag = GF_ODF_ISOM_IPI_PTR_TAG; + return desc; + + case GF_ODF_IPMP_PTR_TAG: + return gf_odf_new_ipmp_ptr(); + case GF_ODF_IPMP_TAG: + return gf_odf_new_ipmp(); + case GF_ODF_QOS_TAG: + return gf_odf_new_qos(); + case GF_ODF_REG_TAG: + return gf_odf_new_reg(); + case GF_ODF_CC_TAG: + return gf_odf_new_cc(); + case GF_ODF_KW_TAG: + return gf_odf_new_kw(); + case GF_ODF_RATING_TAG: + return gf_odf_new_rating(); + case GF_ODF_SHORT_TEXT_TAG: + return gf_odf_new_short_text(); + case GF_ODF_TEXT_TAG: + return gf_odf_new_exp_text(); + case GF_ODF_CC_NAME_TAG: + return gf_odf_new_cc_name(); + case GF_ODF_CC_DATE_TAG: + return gf_odf_new_cc_date(); + case GF_ODF_OCI_NAME_TAG: + return gf_odf_new_oci_name(); + case GF_ODF_OCI_DATE_TAG: + return gf_odf_new_oci_date(); + case GF_ODF_SMPTE_TAG: + return gf_odf_new_smpte_camera(); + case GF_ODF_EXT_PL_TAG: + return gf_odf_new_pl_ext(); + case GF_ODF_PL_IDX_TAG: + return gf_odf_new_pl_idx(); + + case GF_ODF_IPMP_TL_TAG: + return gf_odf_new_ipmp_tool_list(); + case GF_ODF_IPMP_TOOL_TAG: + return gf_odf_new_ipmp_tool(); + + case 0: + case 0xFF: + return NULL; +#endif /*GPAC_MINIMAL_ODF*/ + default: + //ISO Reserved + if ( (tag >= GF_ODF_ISO_RES_BEGIN_TAG) && + (tag <= GF_ODF_ISO_RES_END_TAG) ) { + return NULL; + } + desc = gf_odf_new_default(); + if (!desc) return desc; + desc->tag = tag; + return desc; + } +} + +// +// DESTRUCTORS +// +GF_Err gf_odf_delete_descriptor(GF_Descriptor *desc) +{ + switch (desc->tag) { + case GF_ODF_IOD_TAG : + return gf_odf_del_iod((GF_InitialObjectDescriptor *)desc); + case GF_ODF_OD_TAG: + return gf_odf_del_od((GF_ObjectDescriptor *)desc); + case GF_ODF_ESD_TAG : + return gf_odf_del_esd((GF_ESD *)desc); + case GF_ODF_DCD_TAG : + return gf_odf_del_dcd((GF_DecoderConfig *)desc); + case GF_ODF_SLC_TAG: + return gf_odf_del_slc((GF_SLConfig *)desc); + + case GF_ODF_ISOM_IOD_TAG: + return gf_odf_del_isom_iod((GF_IsomInitialObjectDescriptor *)desc); + case GF_ODF_ISOM_OD_TAG: + return gf_odf_del_isom_od((GF_IsomObjectDescriptor *)desc); + + case GF_ODF_SEGMENT_TAG: + return gf_odf_del_segment((GF_Segment *) desc); + case GF_ODF_MEDIATIME_TAG: + return gf_odf_del_mediatime((GF_MediaTime *) desc); + + case GF_ODF_MUXINFO_TAG: + return gf_odf_del_muxinfo((GF_MuxInfo *)desc); + case GF_ODF_BIFS_CFG_TAG: + return gf_odf_del_bifs_cfg((GF_BIFSConfig *)desc); + case GF_ODF_UI_CFG_TAG: + return gf_odf_del_ui_cfg((GF_UIConfig *)desc); + case GF_ODF_TEXT_CFG_TAG: + return gf_odf_del_text_cfg((GF_TextConfig *)desc); + case GF_ODF_TX3G_TAG: + return gf_odf_del_tx3g((GF_TextSampleDescriptor*)desc); + case GF_ODF_LASER_CFG_TAG: + return gf_odf_del_laser_cfg((GF_LASERConfig *)desc); + + case GF_ODF_AUX_VIDEO_DATA: + return gf_odf_del_auxvid((GF_AuxVideoDescriptor *)desc); + + case GF_ODF_LANG_TAG: + return gf_odf_del_lang((GF_Language *)desc); + +#ifndef GPAC_MINIMAL_ODF + + case GF_ODF_CC_TAG: + return gf_odf_del_cc((GF_CCDescriptor *)desc); + case GF_ODF_CC_DATE_TAG: + return gf_odf_del_cc_date((GF_CC_Date *)desc); + case GF_ODF_CC_NAME_TAG: + return gf_odf_del_cc_name((GF_CC_Name *)desc); + case GF_ODF_CI_TAG: + return gf_odf_del_ci((GF_CIDesc *)desc); + case GF_ODF_ESD_INC_TAG: + return gf_odf_del_esd_inc((GF_ES_ID_Inc *)desc); + case GF_ODF_ESD_REF_TAG: + return gf_odf_del_esd_ref((GF_ES_ID_Ref *)desc); + case GF_ODF_TEXT_TAG: + return gf_odf_del_exp_text((GF_ExpandedTextual *)desc); + case GF_ODF_EXT_PL_TAG: + return gf_odf_del_pl_ext((GF_PLExt *)desc); + case GF_ODF_IPI_PTR_TAG: + case GF_ODF_ISOM_IPI_PTR_TAG: + return gf_odf_del_ipi_ptr((GF_IPIPtr *)desc); + case GF_ODF_IPMP_TAG: + return gf_odf_del_ipmp((GF_IPMP_Descriptor *)desc); + case GF_ODF_IPMP_PTR_TAG: + return gf_odf_del_ipmp_ptr((GF_IPMPPtr *)desc); + case GF_ODF_KW_TAG: + return gf_odf_del_kw((GF_KeyWord *)desc); + case GF_ODF_OCI_DATE_TAG: + return gf_odf_del_oci_date((GF_OCI_Data *)desc); + case GF_ODF_OCI_NAME_TAG: + return gf_odf_del_oci_name((GF_OCICreators *)desc); + case GF_ODF_PL_IDX_TAG: + return gf_odf_del_pl_idx((GF_PL_IDX *)desc); + case GF_ODF_QOS_TAG: + return gf_odf_del_qos((GF_QoS_Descriptor *)desc); + case GF_ODF_RATING_TAG: + return gf_odf_del_rating((GF_Rating *)desc); + case GF_ODF_REG_TAG: + return gf_odf_del_reg((GF_Registration *)desc); + case GF_ODF_SHORT_TEXT_TAG: + return gf_odf_del_short_text((GF_ShortTextual *)desc); + case GF_ODF_SMPTE_TAG: + return gf_odf_del_smpte_camera((GF_SMPTECamera *)desc); + case GF_ODF_SCI_TAG: + return gf_odf_del_sup_cid((GF_SCIDesc *)desc); + + case GF_ODF_IPMP_TL_TAG: + return gf_odf_del_ipmp_tool_list((GF_IPMP_ToolList *)desc); + case GF_ODF_IPMP_TOOL_TAG: + return gf_odf_del_ipmp_tool((GF_IPMP_Tool *)desc); + +#endif /*GPAC_MINIMAL_ODF*/ + + default: + return gf_odf_del_default((GF_DefaultDescriptor *)desc); + } + return GF_OK; +} + + + + +// +// READERS +// +GF_Err gf_odf_read_descriptor(GF_BitStream *bs, GF_Descriptor *desc, u32 DescSize) +{ + switch (desc->tag) { + case GF_ODF_IOD_TAG : + return gf_odf_read_iod(bs, (GF_InitialObjectDescriptor *)desc, DescSize); + case GF_ODF_ESD_TAG : + return gf_odf_read_esd(bs, (GF_ESD *)desc, DescSize); + case GF_ODF_DCD_TAG : + return gf_odf_read_dcd(bs, (GF_DecoderConfig *)desc, DescSize); + case GF_ODF_SLC_TAG : + return gf_odf_read_slc(bs, (GF_SLConfig *)desc, DescSize); + case GF_ODF_OD_TAG: + return gf_odf_read_od(bs, (GF_ObjectDescriptor *)desc, DescSize); + + //MP4 File Format + case GF_ODF_ISOM_IOD_TAG: + return gf_odf_read_isom_iod(bs, (GF_IsomInitialObjectDescriptor *)desc, DescSize); + case GF_ODF_ISOM_OD_TAG: + return gf_odf_read_isom_od(bs, (GF_IsomObjectDescriptor *)desc, DescSize); + case GF_ODF_ESD_INC_TAG: + return gf_odf_read_esd_inc(bs, (GF_ES_ID_Inc *)desc, DescSize); + case GF_ODF_ESD_REF_TAG: + return gf_odf_read_esd_ref(bs, (GF_ES_ID_Ref *)desc, DescSize); + + case GF_ODF_SEGMENT_TAG: + return gf_odf_read_segment(bs, (GF_Segment *) desc, DescSize); + case GF_ODF_MEDIATIME_TAG: + return gf_odf_read_mediatime(bs, (GF_MediaTime *) desc, DescSize); + case GF_ODF_MUXINFO_TAG: + return gf_odf_read_muxinfo(bs, (GF_MuxInfo *) desc, DescSize); + + case GF_ODF_AUX_VIDEO_DATA: + return gf_odf_read_auxvid(bs, (GF_AuxVideoDescriptor *)desc, DescSize); + + case GF_ODF_LANG_TAG: + return gf_odf_read_lang(bs, (GF_Language *)desc, DescSize); + +#ifndef GPAC_MINIMAL_ODF + case GF_ODF_IPMP_TAG: + return gf_odf_read_ipmp(bs, (GF_IPMP_Descriptor *)desc, DescSize); + case GF_ODF_IPMP_PTR_TAG: + return gf_odf_read_ipmp_ptr(bs, (GF_IPMPPtr *)desc, DescSize); + + case GF_ODF_CC_TAG: + return gf_odf_read_cc(bs, (GF_CCDescriptor *)desc, DescSize); + case GF_ODF_CC_DATE_TAG: + return gf_odf_read_cc_date(bs, (GF_CC_Date *)desc, DescSize); + case GF_ODF_CC_NAME_TAG: + return gf_odf_read_cc_name(bs, (GF_CC_Name *)desc, DescSize); + case GF_ODF_CI_TAG: + return gf_odf_read_ci(bs, (GF_CIDesc *)desc, DescSize); + case GF_ODF_TEXT_TAG: + return gf_odf_read_exp_text(bs, (GF_ExpandedTextual *)desc, DescSize); + case GF_ODF_EXT_PL_TAG: + return gf_odf_read_pl_ext(bs, (GF_PLExt *)desc, DescSize); + case GF_ODF_IPI_PTR_TAG: + case GF_ODF_ISOM_IPI_PTR_TAG: + return gf_odf_read_ipi_ptr(bs, (GF_IPIPtr *)desc, DescSize); + case GF_ODF_KW_TAG: + return gf_odf_read_kw(bs, (GF_KeyWord *)desc, DescSize); + case GF_ODF_OCI_DATE_TAG: + return gf_odf_read_oci_date(bs, (GF_OCI_Data *)desc, DescSize); + case GF_ODF_OCI_NAME_TAG: + return gf_odf_read_oci_name(bs, (GF_OCICreators *)desc, DescSize); + case GF_ODF_PL_IDX_TAG: + return gf_odf_read_pl_idx(bs, (GF_PL_IDX *)desc, DescSize); + case GF_ODF_QOS_TAG: + return gf_odf_read_qos(bs, (GF_QoS_Descriptor *)desc, DescSize); + case GF_ODF_RATING_TAG: + return gf_odf_read_rating(bs, (GF_Rating *)desc, DescSize); + case GF_ODF_REG_TAG: + return gf_odf_read_reg(bs, (GF_Registration *)desc, DescSize); + case GF_ODF_SHORT_TEXT_TAG: + return gf_odf_read_short_text(bs, (GF_ShortTextual *)desc, DescSize); + case GF_ODF_SMPTE_TAG: + return gf_odf_read_smpte_camera(bs, (GF_SMPTECamera *)desc, DescSize); + case GF_ODF_SCI_TAG: + return gf_odf_read_sup_cid(bs, (GF_SCIDesc *)desc, DescSize); + + case GF_ODF_IPMP_TL_TAG: + return gf_odf_read_ipmp_tool_list(bs, (GF_IPMP_ToolList *)desc, DescSize); + case GF_ODF_IPMP_TOOL_TAG: + return gf_odf_read_ipmp_tool(bs, (GF_IPMP_Tool *)desc, DescSize); + +#endif /*GPAC_MINIMAL_ODF*/ + //default: + case GF_ODF_DSI_TAG: + default: + return gf_odf_read_default(bs, (GF_DefaultDescriptor *)desc, DescSize); + } + return GF_OK; +} + + + + + +// +// SIZE FUNCTION +// +GF_Err gf_odf_size_descriptor(GF_Descriptor *desc, u32 *outSize) +{ + switch(desc->tag) { + case GF_ODF_IOD_TAG : + return gf_odf_size_iod((GF_InitialObjectDescriptor *)desc, outSize); + case GF_ODF_ESD_TAG : + return gf_odf_size_esd((GF_ESD *)desc, outSize); + case GF_ODF_DCD_TAG : + return gf_odf_size_dcd((GF_DecoderConfig *)desc, outSize); + case GF_ODF_SLC_TAG : + return gf_odf_size_slc((GF_SLConfig *)desc, outSize); + + case GF_ODF_OD_TAG: + return gf_odf_size_od((GF_ObjectDescriptor *)desc, outSize); + case GF_ODF_ISOM_IOD_TAG: + return gf_odf_size_isom_iod((GF_IsomInitialObjectDescriptor *)desc, outSize); + case GF_ODF_ISOM_OD_TAG: + return gf_odf_size_isom_od((GF_IsomObjectDescriptor *)desc, outSize); + case GF_ODF_ESD_INC_TAG: + return gf_odf_size_esd_inc((GF_ES_ID_Inc *)desc, outSize); + case GF_ODF_ESD_REF_TAG: + return gf_odf_size_esd_ref((GF_ES_ID_Ref *)desc, outSize); + + case GF_ODF_SEGMENT_TAG: + return gf_odf_size_segment((GF_Segment *) desc, outSize); + case GF_ODF_MEDIATIME_TAG: + return gf_odf_size_mediatime((GF_MediaTime *) desc, outSize); + case GF_ODF_MUXINFO_TAG: + return gf_odf_size_muxinfo((GF_MuxInfo *) desc, outSize); + + case GF_ODF_AUX_VIDEO_DATA: + return gf_odf_size_auxvid((GF_AuxVideoDescriptor *)desc, outSize); + + case GF_ODF_LANG_TAG: + return gf_odf_size_lang((GF_Language *)desc, outSize); + +#ifndef GPAC_MINIMAL_ODF + case GF_ODF_CC_TAG: + return gf_odf_size_cc((GF_CCDescriptor *)desc, outSize); + case GF_ODF_CC_DATE_TAG: + return gf_odf_size_cc_date((GF_CC_Date *)desc, outSize); + case GF_ODF_CC_NAME_TAG: + return gf_odf_size_cc_name((GF_CC_Name *)desc, outSize); + case GF_ODF_CI_TAG: + return gf_odf_size_ci((GF_CIDesc *)desc, outSize); + case GF_ODF_TEXT_TAG: + return gf_odf_size_exp_text((GF_ExpandedTextual *)desc, outSize); + case GF_ODF_EXT_PL_TAG: + return gf_odf_size_pl_ext((GF_PLExt *)desc, outSize); + case GF_ODF_IPI_PTR_TAG: + case GF_ODF_ISOM_IPI_PTR_TAG: + return gf_odf_size_ipi_ptr((GF_IPIPtr *)desc, outSize); + case GF_ODF_IPMP_TAG: + return gf_odf_size_ipmp((GF_IPMP_Descriptor *)desc, outSize); + case GF_ODF_IPMP_PTR_TAG: + return gf_odf_size_ipmp_ptr((GF_IPMPPtr *)desc, outSize); + case GF_ODF_KW_TAG: + return gf_odf_size_kw((GF_KeyWord *)desc, outSize); + case GF_ODF_OCI_DATE_TAG: + return gf_odf_size_oci_date((GF_OCI_Data *)desc, outSize); + case GF_ODF_OCI_NAME_TAG: + return gf_odf_size_oci_name((GF_OCICreators *)desc, outSize); + case GF_ODF_PL_IDX_TAG: + return gf_odf_size_pl_idx((GF_PL_IDX *)desc, outSize); + case GF_ODF_QOS_TAG: + return gf_odf_size_qos((GF_QoS_Descriptor *)desc, outSize); + case GF_ODF_RATING_TAG: + return gf_odf_size_rating((GF_Rating *)desc, outSize); + case GF_ODF_REG_TAG: + return gf_odf_size_reg((GF_Registration *)desc, outSize); + case GF_ODF_SHORT_TEXT_TAG: + return gf_odf_size_short_text((GF_ShortTextual *)desc, outSize); + case GF_ODF_SMPTE_TAG: + return gf_odf_size_smpte_camera((GF_SMPTECamera *)desc, outSize); + case GF_ODF_SCI_TAG: + return gf_odf_size_sup_cid((GF_SCIDesc *)desc, outSize); + + + case GF_ODF_IPMP_TL_TAG: + return gf_odf_size_ipmp_tool_list((GF_IPMP_ToolList *)desc, outSize); + case GF_ODF_IPMP_TOOL_TAG: + return gf_odf_size_ipmp_tool((GF_IPMP_Tool *)desc, outSize); + +#endif /*GPAC_MINIMAL_ODF*/ + default: + /*don't write out l descriptors*/ + if ((desc->tag>=GF_ODF_MUXINFO_TAG) && (desc->tag<=GF_ODF_LASER_CFG_TAG)) { + *outSize = 0; + return GF_OK; + } + return gf_odf_size_default((GF_DefaultDescriptor *)desc, outSize); + } + return GF_OK; +} + + +// +// WRITERS +// +GF_Err gf_odf_write_descriptor(GF_BitStream *bs, GF_Descriptor *desc) +{ + switch(desc->tag) { + case GF_ODF_IOD_TAG : + return gf_odf_write_iod(bs, (GF_InitialObjectDescriptor *)desc); + case GF_ODF_ESD_TAG : + return gf_odf_write_esd(bs, (GF_ESD *)desc); + case GF_ODF_DCD_TAG : + return gf_odf_write_dcd(bs, (GF_DecoderConfig *)desc); + case GF_ODF_SLC_TAG : + return gf_odf_write_slc(bs, (GF_SLConfig *)desc); + case GF_ODF_ESD_INC_TAG: + return gf_odf_write_esd_inc(bs, (GF_ES_ID_Inc *)desc); + case GF_ODF_ESD_REF_TAG: + return gf_odf_write_esd_ref(bs, (GF_ES_ID_Ref *)desc); + + + case GF_ODF_ISOM_IOD_TAG: + return gf_odf_write_isom_iod(bs, (GF_IsomInitialObjectDescriptor *)desc); + case GF_ODF_ISOM_OD_TAG: + return gf_odf_write_isom_od(bs, (GF_IsomObjectDescriptor *)desc); + case GF_ODF_OD_TAG: + return gf_odf_write_od(bs, (GF_ObjectDescriptor *)desc); + case GF_ODF_SEGMENT_TAG: + return gf_odf_write_segment(bs, (GF_Segment *) desc); + case GF_ODF_MEDIATIME_TAG: + return gf_odf_write_mediatime(bs, (GF_MediaTime *) desc); + case GF_ODF_MUXINFO_TAG: + return gf_odf_write_muxinfo(bs, (GF_MuxInfo *) desc); + + case GF_ODF_AUX_VIDEO_DATA: + return gf_odf_write_auxvid(bs, (GF_AuxVideoDescriptor *)desc); + + case GF_ODF_LANG_TAG: + return gf_odf_write_lang(bs, (GF_Language *)desc); + +#ifndef GPAC_MINIMAL_ODF + case GF_ODF_CC_TAG: + return gf_odf_write_cc(bs, (GF_CCDescriptor *)desc); + case GF_ODF_CC_DATE_TAG: + return gf_odf_write_cc_date(bs, (GF_CC_Date *)desc); + case GF_ODF_CC_NAME_TAG: + return gf_odf_write_cc_name(bs, (GF_CC_Name *)desc); + case GF_ODF_CI_TAG: + return gf_odf_write_ci(bs, (GF_CIDesc *)desc); + + case GF_ODF_TEXT_TAG: + return gf_odf_write_exp_text(bs, (GF_ExpandedTextual *)desc); + case GF_ODF_EXT_PL_TAG: + return gf_odf_write_pl_ext(bs, (GF_PLExt *)desc); + case GF_ODF_IPI_PTR_TAG: + case GF_ODF_ISOM_IPI_PTR_TAG: + return gf_odf_write_ipi_ptr(bs, (GF_IPIPtr *)desc); + case GF_ODF_IPMP_TAG: + return gf_odf_write_ipmp(bs, (GF_IPMP_Descriptor *)desc); + case GF_ODF_IPMP_PTR_TAG: + return gf_odf_write_ipmp_ptr(bs, (GF_IPMPPtr *)desc); + case GF_ODF_KW_TAG: + return gf_odf_write_kw(bs, (GF_KeyWord *)desc); + case GF_ODF_OCI_DATE_TAG: + return gf_odf_write_oci_date(bs, (GF_OCI_Data *)desc); + case GF_ODF_OCI_NAME_TAG: + return gf_odf_write_oci_name(bs, (GF_OCICreators *)desc); + case GF_ODF_PL_IDX_TAG: + return gf_odf_write_pl_idx(bs, (GF_PL_IDX *)desc); + case GF_ODF_QOS_TAG: + return gf_odf_write_qos(bs, (GF_QoS_Descriptor *)desc); + case GF_ODF_RATING_TAG: + return gf_odf_write_rating(bs, (GF_Rating *)desc); + case GF_ODF_REG_TAG: + return gf_odf_write_reg(bs, (GF_Registration *)desc); + case GF_ODF_SHORT_TEXT_TAG: + return gf_odf_write_short_text(bs, (GF_ShortTextual *)desc); + case GF_ODF_SMPTE_TAG: + return gf_odf_write_smpte_camera(bs, (GF_SMPTECamera *)desc); + case GF_ODF_SCI_TAG: + return gf_odf_write_sup_cid(bs, (GF_SCIDesc *)desc); + + case GF_ODF_IPMP_TL_TAG: + return gf_odf_write_ipmp_tool_list(bs, (GF_IPMP_ToolList *)desc); + case GF_ODF_IPMP_TOOL_TAG: + return gf_odf_write_ipmp_tool(bs, (GF_IPMP_Tool *)desc); +#endif /*GPAC_MINIMAL_ODF*/ + default: + /*don't write out internal descriptors*/ + if ((desc->tag>=GF_ODF_MUXINFO_TAG) && (desc->tag<=GF_ODF_LASER_CFG_TAG)) + return GF_OK; + return gf_odf_write_default(bs, (GF_DefaultDescriptor *)desc); + } + return GF_OK; +} + +// +// CONSTRUCTORS +// +GF_ODCom *gf_odf_create_command(u8 tag) +{ + GF_ODCom *com; + switch (tag) { + case GF_ODF_OD_UPDATE_TAG: + return gf_odf_new_od_update(); + case GF_ODF_OD_REMOVE_TAG: + return gf_odf_new_od_remove(); + case GF_ODF_ESD_UPDATE_TAG: + return gf_odf_new_esd_update(); + case GF_ODF_ESD_REMOVE_TAG: + return gf_odf_new_esd_remove(); + //special case for ESDRemove in the file format... + case GF_ODF_ESD_REMOVE_REF_TAG: + com = gf_odf_new_esd_remove(); + if (!com) return com; + com->tag = GF_ODF_ESD_REMOVE_REF_TAG; + return com; + + case GF_ODF_IPMP_UPDATE_TAG: + return gf_odf_new_ipmp_update(); + case GF_ODF_IPMP_REMOVE_TAG: + return gf_odf_new_ipmp_remove(); + + default: + if ( (tag >= GF_ODF_COM_ISO_BEGIN_TAG) && + ( tag <= GF_ODF_COM_ISO_END_TAG) ) { + return NULL; + } + com = gf_odf_new_base_command(); + if (!com) return com; + com->tag = tag; + return com; + } +} + + +// +// DESTRUCTORS +// +GF_Err gf_odf_delete_command(GF_ODCom *com) +{ + switch (com->tag) { + case GF_ODF_OD_UPDATE_TAG: + return gf_odf_del_od_update((GF_ODUpdate *)com); + case GF_ODF_OD_REMOVE_TAG: + return gf_odf_del_od_remove((GF_ODRemove *)com); + + case GF_ODF_ESD_UPDATE_TAG: + return gf_odf_del_esd_update((GF_ESDUpdate *)com); + case GF_ODF_ESD_REMOVE_TAG: + case GF_ODF_ESD_REMOVE_REF_TAG: + return gf_odf_del_esd_remove((GF_ESDRemove *)com); + case GF_ODF_IPMP_UPDATE_TAG: + return gf_odf_del_ipmp_update((GF_IPMPUpdate *)com); + case GF_ODF_IPMP_REMOVE_TAG: + return gf_odf_del_ipmp_remove((GF_IPMPRemove *)com); + + default: + return gf_odf_del_base_command((GF_BaseODCom *)com); + } +} + + +// +// READERS +// +GF_Err gf_odf_read_command(GF_BitStream *bs, GF_ODCom *com, u32 gf_odf_size_command) +{ + switch (com->tag) { + case GF_ODF_OD_UPDATE_TAG: + return gf_odf_read_od_update(bs, (GF_ODUpdate *)com, gf_odf_size_command); + case GF_ODF_OD_REMOVE_TAG: + return gf_odf_read_od_remove(bs, (GF_ODRemove *)com, gf_odf_size_command); + case GF_ODF_ESD_UPDATE_TAG: + return gf_odf_read_esd_update(bs, (GF_ESDUpdate *)com, gf_odf_size_command); + case GF_ODF_ESD_REMOVE_TAG: + case GF_ODF_ESD_REMOVE_REF_TAG: + return gf_odf_read_esd_remove(bs, (GF_ESDRemove *)com, gf_odf_size_command); + case GF_ODF_IPMP_UPDATE_TAG: + return gf_odf_read_ipmp_update(bs, (GF_IPMPUpdate *)com, gf_odf_size_command); + case GF_ODF_IPMP_REMOVE_TAG: + return gf_odf_read_ipmp_remove(bs, (GF_IPMPRemove *)com, gf_odf_size_command); + default: + return gf_odf_read_base_command(bs, (GF_BaseODCom *)com, gf_odf_size_command); + } +} + + + +// +// SIZE FUNCTION +// +GF_Err gf_odf_size_command(GF_ODCom *com, u32 *outSize) +{ + switch (com->tag) { + case GF_ODF_OD_UPDATE_TAG: + return gf_odf_size_od_update((GF_ODUpdate *)com, outSize); + case GF_ODF_OD_REMOVE_TAG: + return gf_odf_size_od_remove((GF_ODRemove *)com, outSize); + + case GF_ODF_ESD_UPDATE_TAG: + return gf_odf_size_esd_update((GF_ESDUpdate *)com, outSize); + case GF_ODF_ESD_REMOVE_TAG: + case GF_ODF_ESD_REMOVE_REF_TAG: + return gf_odf_size_esd_remove((GF_ESDRemove *)com, outSize); + case GF_ODF_IPMP_UPDATE_TAG: + return gf_odf_size_ipmp_update((GF_IPMPUpdate *)com, outSize); + case GF_ODF_IPMP_REMOVE_TAG: + return gf_odf_size_ipmp_remove((GF_IPMPRemove *)com, outSize); + + default: + return gf_odf_size_base_command((GF_BaseODCom *)com, outSize); + } +} + + +// +// WRITERS +// +GF_Err gf_odf_write_command(GF_BitStream *bs, GF_ODCom *com) +{ + switch (com->tag) { + case GF_ODF_OD_UPDATE_TAG: + return gf_odf_write_od_update(bs, (GF_ODUpdate *)com); + case GF_ODF_OD_REMOVE_TAG: + return gf_odf_write_od_remove(bs, (GF_ODRemove *)com); + + case GF_ODF_ESD_UPDATE_TAG: + return gf_odf_write_esd_update(bs, (GF_ESDUpdate *)com); + case GF_ODF_ESD_REMOVE_TAG: + case GF_ODF_ESD_REMOVE_REF_TAG: + return gf_odf_write_esd_remove(bs, (GF_ESDRemove *)com); + case GF_ODF_IPMP_UPDATE_TAG: + return gf_odf_write_ipmp_update(bs, (GF_IPMPUpdate *)com); + case GF_ODF_IPMP_REMOVE_TAG: + return gf_odf_write_ipmp_remove(bs, (GF_IPMPRemove *)com); + + default: + return gf_odf_write_base_command(bs, (GF_BaseODCom *)com); + } +} diff --git a/src/gpacmp4/descriptors.c b/src/gpacmp4/descriptors.c new file mode 100644 index 00000000..03038df3 --- /dev/null +++ b/src/gpacmp4/descriptors.c @@ -0,0 +1,963 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / MPEG-4 ObjectDescriptor sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ +#include "../disable_warnings.h" +#include +#include + +#ifndef GPAC_DISABLE_AV_PARSERS +#include +#endif + +GF_EXPORT +const char *gf_odf_stream_type_name(u32 streamType) +{ + switch (streamType) { + case GF_STREAM_OD: return "ObjectDescriptor"; + case GF_STREAM_OCR: return "ClockReference"; + case GF_STREAM_SCENE: return "SceneDescription"; + case GF_STREAM_VISUAL: return "Visual"; + case GF_STREAM_AUDIO: return "Audio"; + case GF_STREAM_MPEG7: return "MPEG7"; + case GF_STREAM_IPMP: return "IPMP"; + case GF_STREAM_OCI: return "OCI"; + case GF_STREAM_MPEGJ: return "MPEGJ"; + case GF_STREAM_INTERACT: return "Interaction"; + case GF_STREAM_TEXT: return "Text"; + case GF_STREAM_ND_SUBPIC: return "NeroDigital Subpicture"; + default: return "Unknown"; + } +} + +GF_EXPORT +u32 gf_odf_stream_type_by_name(const char *streamType) +{ + if (!streamType) return 0; + if (!stricmp(streamType, "ObjectDescriptor")) return GF_STREAM_OD; + if (!stricmp(streamType, "ClockReference")) return GF_STREAM_OCR; + if (!stricmp(streamType, "SceneDescription")) return GF_STREAM_SCENE; + if (!stricmp(streamType, "Visual")) return GF_STREAM_VISUAL; + if (!stricmp(streamType, "Audio")) return GF_STREAM_AUDIO; + if (!stricmp(streamType, "MPEG7")) return GF_STREAM_MPEG7; + if (!stricmp(streamType, "IPMP")) return GF_STREAM_IPMP; + if (!stricmp(streamType, "OCI")) return GF_STREAM_OCI; + if (!stricmp(streamType, "MPEGJ")) return GF_STREAM_MPEGJ; + if (!stricmp(streamType, "Interaction")) return GF_STREAM_INTERACT; + if (!stricmp(streamType, "Text")) return GF_STREAM_TEXT; + return 0; +} + + +s32 gf_odf_size_field_size(u32 size_desc) +{ + if (size_desc < 0x00000080) { + return 1 + 1; + } else if (size_desc < 0x00004000) { + return 2 + 1; + } else if (size_desc < 0x00200000) { + return 3 + 1; + } else if (size_desc < 0x10000000) { + return 4 + 1; + } else { + return -1; + } + +} + + +GF_EXPORT +GF_Err gf_odf_parse_descriptor(GF_BitStream *bs, GF_Descriptor **desc, u32 *desc_size) +{ + u32 val, size, sizeHeader; + u8 tag; + GF_Err err; + GF_Descriptor *newDesc; + if (!bs) return GF_BAD_PARAM; + + *desc_size = 0; + + //tag + tag = (u8) gf_bs_read_int(bs, 8); + sizeHeader = 1; + + //size + size = 0; + do { + val = gf_bs_read_int(bs, 8); + sizeHeader++; + size <<= 7; + size |= val & 0x7F; + } while ( val & 0x80); + *desc_size = size; + + GF_LOG(GF_LOG_DEBUG, GF_LOG_CODEC, ("[ODF] Reading descriptor (tag %d size %d)\n", tag, size )); + + newDesc = gf_odf_create_descriptor(tag); + if (! newDesc) { + *desc = NULL; + *desc_size = sizeHeader; + if ( (tag >= GF_ODF_ISO_RES_BEGIN_TAG) && + (tag <= GF_ODF_ISO_RES_END_TAG) ) { + return GF_ODF_FORBIDDEN_DESCRIPTOR; + } + else if (!tag || (tag == 0xFF)) { + return GF_ODF_INVALID_DESCRIPTOR; + } +#ifndef GPAC_MINIMAL_ODF + return GF_OUT_OF_MEM; +#else + gf_bs_skip_bytes(bs, size); + *desc_size = size + sizeHeader - gf_odf_size_field_size(*desc_size); + return GF_OK; +#endif + } + + newDesc->tag = tag; + err = gf_odf_read_descriptor(bs, newDesc, *desc_size); + + /*FFMPEG fix*/ + if ((tag==GF_ODF_SLC_TAG) && (((GF_SLConfig*)newDesc)->predefined==2)) { + if (*desc_size==3) { + *desc_size = 1; + err = GF_OK; + } + } + + //little trick to handle lazy bitstreams that encode + //SizeOfInstance on a fix number of bytes + //This nb of bytes is added in Read methods + *desc_size += sizeHeader - gf_odf_size_field_size(*desc_size); + *desc = newDesc; + if (err) { + GF_LOG(GF_LOG_ERROR, GF_LOG_CODEC, ("[ODF] Error reading descriptor (tag %d size %d): %s\n", tag, size, gf_error_to_string(err) )); + gf_odf_delete_descriptor(newDesc); + *desc = NULL; + } + return err; +} + + + +GF_Err gf_odf_delete_descriptor_list(GF_List *descList) +{ + GF_Err e; + GF_Descriptor*tmp; + u32 i; + //no error if NULL chain... + if (! descList) return GF_OK; + i=0; + while ((tmp = (GF_Descriptor*)gf_list_enum(descList, &i))) { + e = gf_odf_delete_descriptor(tmp); + if (e) return e; + } + gf_list_del(descList); + return GF_OK; +} + +GF_Err gf_odf_write_base_descriptor(GF_BitStream *bs, u8 tag, u32 size) +{ + u32 length; + unsigned char vals[4]; + + if (!tag ) return GF_BAD_PARAM; + + length = size; + vals[3] = (unsigned char) (length & 0x7f); + length >>= 7; + vals[2] = (unsigned char) ((length & 0x7f) | 0x80); + length >>= 7; + vals[1] = (unsigned char) ((length & 0x7f) | 0x80); + length >>= 7; + vals[0] = (unsigned char) ((length & 0x7f) | 0x80); + + gf_bs_write_int(bs, tag, 8); + if (size < 0x00000080) { + gf_bs_write_int(bs, vals[3], 8); + } else if (size < 0x00004000) { + gf_bs_write_int(bs, vals[2], 8); + gf_bs_write_int(bs, vals[3], 8); + } else if (size < 0x00200000) { + gf_bs_write_int(bs, vals[1], 8); + gf_bs_write_int(bs, vals[2], 8); + gf_bs_write_int(bs, vals[3], 8); + } else if (size < 0x10000000) { + gf_bs_write_int(bs, vals[0], 8); + gf_bs_write_int(bs, vals[1], 8); + gf_bs_write_int(bs, vals[2], 8); + gf_bs_write_int(bs, vals[3], 8); + } else { + return GF_ODF_INVALID_DESCRIPTOR; + } + return GF_OK; +} + + +GF_Err gf_odf_size_descriptor_list(GF_List *descList, u32 *outSize) +{ + GF_Err e; + GF_Descriptor *tmp; + u32 tmpSize, count, i; + if (! descList) return GF_OK; + + count = gf_list_count(descList); + for ( i = 0; i < count; i++ ) { + tmp = (GF_Descriptor*)gf_list_get(descList, i); + if (tmp) { + e = gf_odf_size_descriptor(tmp, &tmpSize); + if (e) return e; + if (tmpSize) *outSize += tmpSize + gf_odf_size_field_size(tmpSize); + } + } + return GF_OK; +} + +GF_Err gf_odf_write_descriptor_list(GF_BitStream *bs, GF_List *descList) +{ + GF_Err e; + u32 count, i; + GF_Descriptor *tmp; + + if (! descList) return GF_OK; + count = gf_list_count(descList); + for ( i = 0; i < count; i++ ) { + tmp = (GF_Descriptor*)gf_list_get(descList, i); + if (tmp) { + e = gf_odf_write_descriptor(bs, tmp); + if (e) return e; + } + } + return GF_OK; +} + +GF_Err gf_odf_write_descriptor_list_filter(GF_BitStream *bs, GF_List *descList, u8 only_tag) +{ + GF_Err e; + u32 count, i; + GF_Descriptor *tmp; + + if (! descList) return GF_OK; + count = gf_list_count(descList); + for ( i = 0; i < count; i++ ) { + tmp = (GF_Descriptor*)gf_list_get(descList, i); + if (tmp && (tmp->tag==only_tag) ) { + e = gf_odf_write_descriptor(bs, tmp); + if (e) return e; + } + } + return GF_OK; +} +#ifndef GPAC_DISABLE_ODF + + +u32 gf_ipmpx_array_size(GF_BitStream *bs, u32 *array_size) +{ + u32 val, size, io_size; + + io_size = size = 0; + do { + val = gf_bs_read_int(bs, 8); + io_size ++; + size <<= 7; + size |= val & 0x7F; + } while ( val & 0x80 ); + *array_size = size; + return io_size; +} + +void gf_ipmpx_write_array(GF_BitStream *bs, char *data, u32 data_len) +{ + u32 length; + unsigned char vals[4]; + + if (!data || !data_len) return; + + length = data_len; + vals[3] = (unsigned char) (length & 0x7f); length >>= 7; + vals[2] = (unsigned char) ((length & 0x7f) | 0x80); length >>= 7; + vals[1] = (unsigned char) ((length & 0x7f) | 0x80); length >>= 7; + vals[0] = (unsigned char) ((length & 0x7f) | 0x80); + + if (data_len < 0x00000080) { + gf_bs_write_int(bs, vals[3], 8); + } else if (data_len < 0x00004000) { + gf_bs_write_int(bs, vals[2], 8); + gf_bs_write_int(bs, vals[3], 8); + } else if (data_len < 0x00200000) { + gf_bs_write_int(bs, vals[1], 8); + gf_bs_write_int(bs, vals[2], 8); + gf_bs_write_int(bs, vals[3], 8); + } else if (data_len < 0x10000000) { + gf_bs_write_int(bs, vals[0], 8); + gf_bs_write_int(bs, vals[1], 8); + gf_bs_write_int(bs, vals[2], 8); + gf_bs_write_int(bs, vals[3], 8); + } else { + return; + } + gf_bs_write_data(bs, data, data_len); +} + + +#endif /*GPAC_MINIMAL_ODF*/ + +/*special authoring functions*/ +GF_EXPORT +GF_BIFSConfig *gf_odf_get_bifs_config(GF_DefaultDescriptor *dsi, u8 oti) +{ + Bool hasSize, cmd_stream; + GF_BitStream *bs; + GF_BIFSConfig *cfg; + + if (oti>=GPAC_OTI_SCENE_BIFS_EXTENDED) return NULL; + + if (!dsi || !dsi->data || !dsi->dataLength ) { + /* Hack for T-DMB non compliant streams (OnTimeTek ?) */ + cfg = (GF_BIFSConfig *) gf_odf_desc_new(GF_ODF_BIFS_CFG_TAG); + cfg->pixelMetrics = 1; + cfg->version = 1; + return cfg; + } + bs = gf_bs_new(dsi->data, dsi->dataLength, GF_BITSTREAM_READ); + + cfg = (GF_BIFSConfig *) gf_odf_desc_new(GF_ODF_BIFS_CFG_TAG); + if (oti==2) { + /*3D Mesh Coding*/ + gf_bs_read_int(bs, 1); + /*PMF*/ + gf_bs_read_int(bs, 1); + } + cfg->nodeIDbits = gf_bs_read_int(bs, 5); + cfg->routeIDbits = gf_bs_read_int(bs, 5); + if (oti==2) cfg->protoIDbits = gf_bs_read_int(bs, 5); + + cmd_stream = gf_bs_read_int(bs, 1); + if (!cmd_stream) { + cfg->elementaryMasks = gf_list_new(); + while (1) { + GF_ElementaryMask* em = (GF_ElementaryMask* ) gf_odf_New_ElemMask(); + em->node_id = gf_bs_read_int(bs, cfg->nodeIDbits); + gf_list_add(cfg->elementaryMasks, em); + /*this assumes only FDP, BDP and IFS2D (no elem mask)*/ + if (gf_bs_read_int(bs, 1) == 0) break; + } + gf_bs_align(bs); + if (gf_bs_get_size(bs) != gf_bs_get_position(bs)) { + GF_LOG(GF_LOG_WARNING, GF_LOG_CODEC, ("[ODF] Reading bifs config: shift in sizes (not supported)\n")); + } + } else { + cfg->pixelMetrics = gf_bs_read_int(bs, 1); + hasSize = gf_bs_read_int(bs, 1); + if (hasSize) { + cfg->pixelWidth = gf_bs_read_int(bs, 16); + cfg->pixelHeight = gf_bs_read_int(bs, 16); + } + gf_bs_align(bs); + if (gf_bs_get_size(bs) != gf_bs_get_position(bs)) + GF_LOG(GF_LOG_WARNING, GF_LOG_CODEC, ("[ODF] Reading bifs config: shift in sizes (invalid descriptor)\n")); + } + gf_bs_del(bs); + return cfg; +} + +/*special function for authoring - convert DSI to LASERConfig*/ +GF_EXPORT +GF_Err gf_odf_get_laser_config(GF_DefaultDescriptor *dsi, GF_LASERConfig *cfg) +{ + u32 to_skip; + GF_BitStream *bs; + if (!dsi || !dsi->data || !dsi->dataLength || !cfg) return GF_BAD_PARAM; + bs = gf_bs_new(dsi->data, dsi->dataLength, GF_BITSTREAM_READ); + memset(cfg, 0, sizeof(GF_LASERConfig)); + cfg->tag = GF_ODF_LASER_CFG_TAG; + cfg->profile = gf_bs_read_int(bs, 8); + cfg->level = gf_bs_read_int(bs, 8); + /*cfg->reserved = */gf_bs_read_int(bs, 3); + cfg->pointsCodec = gf_bs_read_int(bs, 2); + cfg->pathComponents = gf_bs_read_int(bs, 4); + cfg->fullRequestHost = gf_bs_read_int(bs, 1); + if (gf_bs_read_int(bs, 1)) cfg->time_resolution = gf_bs_read_int(bs, 16); + else cfg->time_resolution = 1000; + cfg->colorComponentBits = 1 + gf_bs_read_int(bs, 4); + cfg->resolution = gf_bs_read_int(bs, 4); + if (cfg->resolution>7) cfg->resolution -= 16; + cfg->coord_bits = gf_bs_read_int(bs, 5); + cfg->scale_bits_minus_coord_bits = gf_bs_read_int(bs, 4); + cfg->newSceneIndicator = gf_bs_read_int(bs, 1); + /*reserved2*/ gf_bs_read_int(bs, 3); + cfg->extensionIDBits = gf_bs_read_int(bs, 4); + /*hasExtConfig - we just ignore it*/ + if (gf_bs_read_int(bs, 1)) { + to_skip = gf_bs_read_vluimsbf5(bs); + while (to_skip) { + gf_bs_read_int(bs, 8); + to_skip--; + } + } + /*hasExtension - we just ignore it*/ + if (gf_bs_read_int(bs, 1)) { + to_skip = gf_bs_read_vluimsbf5(bs); + while (to_skip) { + gf_bs_read_int(bs, 8); + to_skip--; + } + } + gf_bs_del(bs); + return GF_OK; +} + +GF_EXPORT +GF_Err gf_odf_get_ui_config(GF_DefaultDescriptor *dsi, GF_UIConfig *cfg) +{ + u32 len, i; + GF_BitStream *bs; + if (!dsi || !dsi->data || !dsi->dataLength || !cfg) return GF_BAD_PARAM; + memset(cfg, 0, sizeof(GF_UIConfig)); + cfg->tag = GF_ODF_UI_CFG_TAG; + bs = gf_bs_new(dsi->data, dsi->dataLength, GF_BITSTREAM_READ); + len = gf_bs_read_int(bs, 8); + cfg->deviceName = (char*)gf_malloc(sizeof(char) * (len+1)); + for (i=0; ideviceName[i] = gf_bs_read_int(bs, 8); + cfg->deviceName[i] = 0; + + if (!stricmp(cfg->deviceName, "StringSensor") && gf_bs_available(bs)) { + cfg->termChar = gf_bs_read_int(bs, 8); + cfg->delChar = gf_bs_read_int(bs, 8); + } + gf_bs_del(bs); + return GF_OK; +} + +GF_EXPORT +GF_Err gf_odf_encode_ui_config(GF_UIConfig *cfg, GF_DefaultDescriptor **out_dsi) +{ + u32 i, len; + GF_BitStream *bs; + GF_DefaultDescriptor *dsi; + if (!out_dsi || (cfg->tag != GF_ODF_UI_CFG_TAG)) return GF_BAD_PARAM; + + *out_dsi = NULL; + if (!cfg->deviceName) return GF_OK; + + bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); + len = strlen(cfg->deviceName); + gf_bs_write_int(bs, len, 8); + for (i=0; ideviceName[i], 8); + if (!stricmp(cfg->deviceName, "StringSensor")) { + /*fixme - this should be UTF-8 chars*/ + if (cfg->delChar || cfg->termChar) { + gf_bs_write_int(bs, cfg->termChar, 8); + gf_bs_write_int(bs, cfg->delChar, 8); + } + } + if (cfg->ui_data) gf_bs_write_data(bs, cfg->ui_data, cfg->ui_data_length); + + dsi = (GF_DefaultDescriptor *) gf_odf_desc_new(GF_ODF_DSI_TAG); + gf_bs_get_content(bs, &dsi->data, &dsi->dataLength); + gf_bs_del(bs); + *out_dsi = dsi; + return GF_OK; +} + + +GF_EXPORT +GF_AVCConfig *gf_odf_avc_cfg_new() +{ + GF_AVCConfig *cfg; + GF_SAFEALLOC(cfg, GF_AVCConfig); + if (!cfg) return NULL; + cfg->sequenceParameterSets = gf_list_new(); + cfg->pictureParameterSets = gf_list_new(); + return cfg; +} + +GF_EXPORT +void gf_odf_avc_cfg_del(GF_AVCConfig *cfg) +{ + if (!cfg) return; + while (gf_list_count(cfg->sequenceParameterSets)) { + GF_AVCConfigSlot *sl = (GF_AVCConfigSlot *)gf_list_get(cfg->sequenceParameterSets, 0); + gf_list_rem(cfg->sequenceParameterSets, 0); + if (sl->data) gf_free(sl->data); + gf_free(sl); + } + gf_list_del(cfg->sequenceParameterSets); + while (gf_list_count(cfg->pictureParameterSets)) { + GF_AVCConfigSlot *sl = (GF_AVCConfigSlot *)gf_list_get(cfg->pictureParameterSets, 0); + gf_list_rem(cfg->pictureParameterSets, 0); + if (sl->data) gf_free(sl->data); + gf_free(sl); + } + gf_list_del(cfg->pictureParameterSets); + gf_free(cfg); +} + +GF_EXPORT +GF_Err gf_odf_avc_cfg_write(GF_AVCConfig *cfg, char **outData, u32 *outSize) +{ + u32 i, count; + GF_BitStream *bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); + gf_bs_write_int(bs, cfg->configurationVersion, 8); + gf_bs_write_int(bs, cfg->AVCProfileIndication , 8); + gf_bs_write_int(bs, cfg->profile_compatibility, 8); + gf_bs_write_int(bs, cfg->AVCLevelIndication, 8); + gf_bs_write_int(bs, 0x3F, 6); + gf_bs_write_int(bs, cfg->nal_unit_size - 1, 2); + gf_bs_write_int(bs, 0x7, 3); + count = gf_list_count(cfg->sequenceParameterSets); + gf_bs_write_int(bs, count, 5); + for (i=0; isequenceParameterSets, i); + gf_bs_write_int(bs, sl->size, 16); + gf_bs_write_data(bs, sl->data, sl->size); + } + count = gf_list_count(cfg->pictureParameterSets); + gf_bs_write_int(bs, count, 8); + for (i=0; ipictureParameterSets, i); + gf_bs_write_int(bs, sl->size, 16); + gf_bs_write_data(bs, sl->data, sl->size); + } + + *outSize = 0; + *outData = NULL; + gf_bs_get_content(bs, outData, outSize); + gf_bs_del(bs); + return GF_OK; +} + +GF_EXPORT +GF_AVCConfig *gf_odf_avc_cfg_read(char *dsi, u32 dsi_size) +{ + u32 i, count; + GF_AVCConfig *avcc = gf_odf_avc_cfg_new(); + GF_BitStream *bs = gf_bs_new(dsi, dsi_size, GF_BITSTREAM_READ); + avcc->configurationVersion = gf_bs_read_int(bs, 8); + avcc->AVCProfileIndication = gf_bs_read_int(bs, 8); + avcc->profile_compatibility = gf_bs_read_int(bs, 8); + avcc->AVCLevelIndication = gf_bs_read_int(bs, 8); + gf_bs_read_int(bs, 6); + avcc->nal_unit_size = 1 + gf_bs_read_int(bs, 2); + gf_bs_read_int(bs, 3); + count = gf_bs_read_int(bs, 5); + for (i=0; isize = gf_bs_read_int(bs, 16); + sl->data = (char*)gf_malloc(sizeof(char)*sl->size); + gf_bs_read_data(bs, sl->data, sl->size); + gf_list_add(avcc->sequenceParameterSets, sl); + } + count = gf_bs_read_int(bs, 8); + for (i=0; isize = gf_bs_read_int(bs, 16); + sl->data = (char*)gf_malloc(sizeof(char)*sl->size); + gf_bs_read_data(bs, sl->data, sl->size); + gf_list_add(avcc->pictureParameterSets, sl); + } + gf_bs_del(bs); + return avcc; +} + + +GF_Descriptor *gf_odf_new_tx3g() +{ + GF_TextSampleDescriptor *newDesc = (GF_TextSampleDescriptor*) gf_malloc(sizeof(GF_TextSampleDescriptor)); + if (!newDesc) return NULL; + memset(newDesc, 0, sizeof(GF_TextSampleDescriptor)); + newDesc->tag = GF_ODF_TX3G_TAG; + return (GF_Descriptor *) newDesc; +} +GF_Err gf_odf_del_tx3g(GF_TextSampleDescriptor *sd) +{ + u32 i; + for (i=0; ifont_count; i++) + if (sd->fonts[i].fontName) gf_free(sd->fonts[i].fontName); + gf_free(sd->fonts); + gf_free(sd); + return GF_OK; +} + +/*TextConfig*/ +GF_Descriptor *gf_odf_new_text_cfg() +{ + GF_TextConfig *newDesc = (GF_TextConfig*) gf_malloc(sizeof(GF_TextConfig)); + if (!newDesc) return NULL; + memset(newDesc, 0, sizeof(GF_TextConfig)); + newDesc->tag = GF_ODF_TEXT_CFG_TAG; + newDesc->sample_descriptions = gf_list_new(); + newDesc->Base3GPPFormat = 0x10; + newDesc->MPEGExtendedFormat = 0x10; + newDesc->profileLevel = 0x10; + newDesc->timescale = 1000; + return (GF_Descriptor *) newDesc; +} + +void ResetTextConfig(GF_TextConfig *desc) +{ + GF_List *bck; + while (gf_list_count(desc->sample_descriptions)) { + GF_TextSampleDescriptor *sd = (GF_TextSampleDescriptor *)gf_list_get(desc->sample_descriptions, 0); + gf_list_rem(desc->sample_descriptions, 0); + gf_odf_del_tx3g(sd); + } + bck = desc->sample_descriptions; + memset(desc, 0, sizeof(GF_TextConfig)); + desc->tag = GF_ODF_TEXT_CFG_TAG; + desc->sample_descriptions = bck; +} + +GF_Err gf_odf_del_text_cfg(GF_TextConfig *desc) +{ + ResetTextConfig(desc); + gf_list_del(desc->sample_descriptions); + gf_free(desc); + return GF_OK; +} + +/*we need box parsing*/ +#include +GF_EXPORT +GF_Err gf_odf_get_text_config(GF_DefaultDescriptor *dsi, u8 oti, GF_TextConfig *cfg) +{ + u32 i, j; + Bool has_alt_format, has_sd; + GF_Err e; + GF_BitStream *bs; + if (!dsi || !dsi->data || !dsi->dataLength || !cfg) return GF_BAD_PARAM; + if (oti != 0x08) return GF_NOT_SUPPORTED; + + /*reset*/ + ResetTextConfig(cfg); + bs = gf_bs_new(dsi->data, dsi->dataLength, GF_BITSTREAM_READ); + + e = GF_OK; + cfg->Base3GPPFormat = gf_bs_read_int(bs, 8); + cfg->MPEGExtendedFormat = gf_bs_read_int(bs, 8); + cfg->profileLevel = gf_bs_read_int(bs, 8); + cfg->timescale = gf_bs_read_int(bs, 24); + has_alt_format = gf_bs_read_int(bs, 1); + cfg->sampleDescriptionFlags = gf_bs_read_int(bs, 2); + has_sd = gf_bs_read_int(bs, 1); + cfg->has_vid_info = gf_bs_read_int(bs, 1); + gf_bs_read_int(bs, 3); + cfg->layer = gf_bs_read_int(bs, 8); + cfg->text_width = gf_bs_read_int(bs, 16); + cfg->text_height = gf_bs_read_int(bs, 16); + if (has_alt_format) { + cfg->nb_compatible_formats = gf_bs_read_int(bs, 8); + for (i=0; inb_compatible_formats; i++) cfg->compatible_formats[i] = gf_bs_read_int(bs, 8); + } +#ifndef GPAC_DISABLE_ISOM + if (has_sd) { + u8 sample_index; + GF_TextSampleDescriptor *txdesc; + GF_Tx3gSampleEntryBox *a; + s64 avail; + u32 nb_desc = gf_bs_read_int(bs, 8); + + /*parse TTU[5]s*/ + avail = (s64) gf_bs_available(bs); + for (i=0; isize; + + if (avail<0) { + e = GF_NON_COMPLIANT_BITSTREAM; + goto exit; + } + txdesc = (GF_TextSampleDescriptor *)gf_malloc(sizeof(GF_TextSampleDescriptor)); + txdesc->sample_index = sample_index; + txdesc->displayFlags = a->displayFlags; + txdesc->back_color = a->back_color; + txdesc->default_pos = a->default_box; + txdesc->default_style = a->default_style; + txdesc->vert_justif = a->vertical_justification; + txdesc->horiz_justif = a->horizontal_justification; + txdesc->font_count = a->font_table ? a->font_table->entry_count : 0; + if (txdesc->font_count) { + txdesc->fonts = (GF_FontRecord*)gf_malloc(sizeof(GF_FontRecord)*txdesc->font_count); + for (j=0; jfont_count; j++) { + txdesc->fonts[j].fontID = a->font_table->fonts[j].fontID; + txdesc->fonts[j].fontName = a->font_table->fonts[j].fontName ? gf_strdup(a->font_table->fonts[j].fontName) : NULL; + } + } + gf_list_add(cfg->sample_descriptions, txdesc); + gf_isom_box_del((GF_Box *)a); + } + } +#endif + + if (cfg->has_vid_info) { + cfg->video_width = gf_bs_read_int(bs, 16); + cfg->video_height = gf_bs_read_int(bs, 16); + cfg->horiz_offset = gf_bs_read_int(bs, 16); + cfg->vert_offset = gf_bs_read_int(bs, 16); + } + +exit: + gf_bs_del(bs); + if (e) ResetTextConfig(cfg); + return e; +} + +GF_EXPORT +const char *gf_afx_get_type_description(u8 afx_code) +{ + switch (afx_code) { + case GPAC_AFX_3DMC: + return "AFX 3D Mesh Compression"; + case GPAC_AFX_WAVELET_SUBDIVISION: + return "AFX Wavelet Subdivision Surface"; + case GPAC_AFX_MESHGRID: + return "AFX Mesh Grid"; + case GPAC_AFX_COORDINATE_INTERPOLATOR: + return "AFX Coordinate Interpolator"; + case GPAC_AFX_ORIENTATION_INTERPOLATOR: + return "AFX Orientation Interpolator"; + case GPAC_AFX_POSITION_INTERPOLATOR: + return "AFX Position Interpolator"; + case GPAC_AFX_OCTREE_IMAGE: + return "AFX Octree Image"; + case GPAC_AFX_BBA: + return "AFX BBA"; + case GPAC_AFX_POINT_TEXTURE: + return "AFX Point Texture"; + case GPAC_AFX_3DMC_EXT: + return "AFX 3D Mesh Compression Extension"; + case GPAC_AFX_FOOTPRINT: + return "AFX FootPrint Representation"; + case GPAC_AFX_ANIMATED_MESH: + return "AFX Animated Mesh Compression"; + case GPAC_AFX_SCALABLE_COMPLEXITY: + return "AFX Scalable Complexity Representation"; + default: + break; + } + return "AFX Unknown"; +} + + +GF_EXPORT +const char *gf_esd_get_textual_description(GF_ESD *esd) +{ + if (!esd || !esd->decoderConfig) return "Bad parameter"; + + switch (esd->decoderConfig->streamType) { + case GF_STREAM_OD: + return "MPEG-4 Object Descriptor"; + case GF_STREAM_OCR: + return "MPEG-4 Object Clock Reference"; + case GF_STREAM_SCENE: + switch (esd->decoderConfig->objectTypeIndication) { + case 0x0: + case 0x1: + case 0x2: + case 0x3: + case 0xFF: + return "MPEG-4 BIFS Scene Description"; + case GPAC_OTI_SCENE_BIFS_EXTENDED: + return "MPEG-4 Extended BIFS Scene Description"; + case GPAC_OTI_SCENE_AFX: + if (!esd->decoderConfig->decoderSpecificInfo || !esd->decoderConfig->decoderSpecificInfo->data) + return "AFX Unknown"; + return gf_afx_get_type_description(esd->decoderConfig->decoderSpecificInfo->data[0]); + case GPAC_OTI_SCENE_LASER: + { + GF_LASERConfig l_cfg; + gf_odf_get_laser_config(esd->decoderConfig->decoderSpecificInfo, &l_cfg); + if (! l_cfg.newSceneIndicator ) return "LASeR Scene Segment Description"; + } + return "LASeR Scene Description"; + case GPAC_OTI_SCENE_SYNTHESIZED_TEXTURE: + return "MPEG-4 Synthesized Texture"; + case GPAC_OTI_SCENE_SAF: + return "MPEG-4 SAF"; + case GPAC_OTI_3GPP2_CMF: + return "3GPP2 CMF"; + default: + return "Unknown Scene Type"; + } + break; + case GF_STREAM_VISUAL: + switch (esd->decoderConfig->objectTypeIndication) { + case GPAC_OTI_VIDEO_MPEG2_SIMPLE: + return "MPEG-2 Visual Simple Profile"; + case GPAC_OTI_VIDEO_MPEG2_MAIN: + return "MPEG-2 Visual Main Profile"; + case GPAC_OTI_VIDEO_MPEG2_SNR: + return "MPEG-2 Visual SNR Profile"; + case GPAC_OTI_VIDEO_MPEG2_SPATIAL: + return "MPEG-2 Visual SNR Profile"; + case GPAC_OTI_VIDEO_MPEG2_HIGH: + return "MPEG-2 Visual SNR Profile"; + case GPAC_OTI_VIDEO_MPEG2_422: + return "MPEG-2 Visual SNR Profile"; + case GPAC_OTI_VIDEO_MPEG1: + return "MPEG-1 Video"; + case GPAC_OTI_IMAGE_JPEG: + return "JPEG Image"; + case GPAC_OTI_IMAGE_PNG: + return "PNG Image"; + case GPAC_OTI_IMAGE_JPEG_2000: + return "JPEG2000 Image"; + case GPAC_OTI_VIDEO_MPEG4_PART2: + return "MPEG-4 Part 2 Video"; + case GPAC_OTI_VIDEO_AVC: + return "MPEG-4 AVC|H264 Video"; + case GPAC_OTI_VIDEO_AVC_PS: + return "MPEG-4 AVC|H264 Parameter Set"; + case GPAC_OTI_MEDIA_FFMPEG: + return "GPAC FFMPEG Private Video"; + case GPAC_OTI_VIDEO_SMPTE_VC1: + return "SMPTE VC-1 Video"; + case GPAC_OTI_VIDEO_DIRAC: + return "Dirac Video"; + default: + return "Unknown Video type"; + } + break; + case GF_STREAM_AUDIO: + switch (esd->decoderConfig->objectTypeIndication) { + case GPAC_OTI_AUDIO_AAC_MPEG2_MP: + return "MPEG-2 AAC Main Profile"; + case GPAC_OTI_AUDIO_AAC_MPEG2_LCP: + return "MPEG-2 AAC Low Complexity Profile"; + case GPAC_OTI_AUDIO_AAC_MPEG2_SSRP: + return "MPEG-2 AAC Scaleable Sampling Rate Profile"; + case GPAC_OTI_AUDIO_MPEG2_PART3: + return "MPEG-2 Audio Part 3"; + case GPAC_OTI_AUDIO_MPEG1: + return "MPEG-1 Audio"; + case GPAC_OTI_AUDIO_AAC_MPEG4: + { +#ifdef GPAC_DISABLE_AV_PARSERS + return "MPEG-4 AAC"; +#else + GF_M4ADecSpecInfo a_cfg; + if (!esd->decoderConfig->decoderSpecificInfo) return "MPEG-4 AAC"; + gf_m4a_get_config(esd->decoderConfig->decoderSpecificInfo->data, esd->decoderConfig->decoderSpecificInfo->dataLength, &a_cfg); + return gf_m4a_object_type_name(a_cfg.base_object_type); +#endif + } + break; + case GPAC_OTI_MEDIA_FFMPEG: + return "GPAC FFMPEG Private Audio"; + case GPAC_OTI_AUDIO_EVRC_VOICE: + return "EVRC Voice"; + case GPAC_OTI_AUDIO_SMV_VOICE: + return "SMV Voice"; + case GPAC_OTI_AUDIO_AC3: + return "AC-3 audio"; + case GPAC_OTI_AUDIO_AC3_ENHANCED: + return "Enhanced AC-3 Audio"; + case GPAC_OTI_AUDIO_DRA: + return "DRA Audio"; + case GPAC_OTI_AUDIO_ITU_G719: + return "ITU G719 Audio"; + case GPAC_OTI_AUDIO_DTS_CA: + return "DTS Coherent Acoustics audio"; + case GPAC_OTI_AUDIO_DTS_HD_HR: + return "DTS-HD High Resolution audio"; + case GPAC_OTI_AUDIO_DTS_HD_MASTER: + return "DTS-HD Master audios"; + default: + return "Unknown Audio Type"; + } + break; + case GF_STREAM_MPEG7: + return "MPEG-7 Description"; + case GF_STREAM_IPMP: + return "MPEG-4 IPMP"; + case GF_STREAM_OCI: + return "MPEG-4 OCI"; + case GF_STREAM_MPEGJ: + return "MPEG-4 MPEG-J"; + case GF_STREAM_INTERACT: + return "MPEG-4 User Interaction"; + case GF_STREAM_IPMP_TOOL: + return "MPEG-4 IPMP Tool"; + case GF_STREAM_FONT: + return "MPEG-4 Font Data"; + case GF_STREAM_TEXT: + return "MPEG-4 Streaming Text"; + case GF_STREAM_ND_SUBPIC: + return "Nero Digital Subpicture"; + + case GF_STREAM_PRIVATE_SCENE: + switch (esd->decoderConfig->objectTypeIndication) { + case GPAC_OTI_PRIVATE_SCENE_GENERIC: + { + char *ext = strchr(esd->decoderConfig->decoderSpecificInfo->data + 4, '.'); + if (!ext) return "GPAC Internal Scene Description"; + ext += 1; + if (!strnicmp(ext, "bt", 2)) + return "BT Scene Description"; + if (!strnicmp(ext, "xmt", 2)) + return "XMT Scene Description"; + if (!strnicmp(ext, "wrl", 3)) + return "VRML Scene Description"; + if (!strnicmp(ext, "x3d", 3)) + return "W3D Scene Description"; + if (!strnicmp(ext, "x3dv", 4)) + return "X3D Scene Description"; + if (!strnicmp(ext, "swf", 3)) + return "Flash (SWF) Scene Description"; + if (!strnicmp(ext, "xsr", 3)) + return "LASeR-ML Scene Description"; + if (!strnicmp(ext, "wgt", 3)) + return "W3C Widget Package"; + if (!strnicmp(ext, "mgt", 3)) + return "MPEG-U Widget Package"; + } + return "GPAC Internal Scene Description"; + case GPAC_OTI_PRIVATE_SCENE_SVG: + return "SVG"; + case GPAC_OTI_PRIVATE_SCENE_LASER: + return "LASeR (XML)"; + case GPAC_OTI_PRIVATE_SCENE_XBL: + return "XBL"; + case GPAC_OTI_PRIVATE_SCENE_EPG: + return "DVB Event Information"; + case GPAC_OTI_PRIVATE_SCENE_WGT: + return "W3C/MPEG-U Widget"; + case GPAC_OTI_SCENE_SVG: + return "SVG over RTP"; + case GPAC_OTI_SCENE_SVG_GZ: + return "SVG+gz over RTP"; + case GPAC_OTI_SCENE_DIMS: + return "3GPP DIMS"; + default: + return "Unknown Scene Description"; + } + break; + case GF_STREAM_PRIVATE_MEDIA: + return "Opaque Decoder"; + case GF_STREAM_4CC: + return gf_4cc_to_str(esd->decoderConfig->objectTypeIndication); + default: + return "Unknown Media Type"; + } +} diff --git a/src/gpacmp4/error.c b/src/gpacmp4/error.c new file mode 100644 index 00000000..c199a9f8 --- /dev/null +++ b/src/gpacmp4/error.c @@ -0,0 +1,685 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / common tools sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ +#include "../disable_warnings.h" +#include + + +static char szTYPE[5]; + + +GF_EXPORT +const char *gf_4cc_to_str(u32 type) +{ + u32 ch, i; + char *ptr, *name = (char *)szTYPE; + ptr = name; + for (i = 0; i < 4; i++, name++) { + ch = type >> (8 * (3-i) ) & 0xff; + if ( ch >= 0x20 && ch <= 0x7E ) { + *name = ch; + } else { + *name = '.'; + } + } + *name = 0; + return (const char *) ptr; +} + + +static const char *szProg[] = +{ + " ", + "= ", + "== ", + "=== ", + "==== ", + "===== ", + "====== ", + "======= ", + "======== ", + "========= ", + "========== ", + "=========== ", + "============ ", + "============= ", + "============== ", + "=============== ", + "================ ", + "================= ", + "================== ", + "=================== ", + "====================", +}; + +static u64 prev_pos = 0; +static u64 prev_pc = 0; +static void gf_on_progress_stdout(const char *_title, u64 done, u64 total) +{ + Double prog; + u32 pos; + const char *szT = _title ? (char *)_title : (char *) ""; + prog = (double) done; + prog /= total; + pos = MIN((u32) (20 * prog), 20); + + if (pos>prev_pos) { + prev_pos = 0; + prev_pc = 0; + } + if (done==total) { + u32 len = strlen(szT) + 40; + while (len) { fprintf(stdout, " "); len--; }; + fprintf(stdout, "\r"); + } + else { + u32 pc = (u32) ( 100 * prog); + if ((pos!=prev_pos) || (pc!=prev_pc)) { + prev_pos = pos; + prev_pc = pc; + fprintf(stdout, "%s: |%s| (%02d/100)\r", szT, szProg[pos], pc); + fflush(stdout); + } + } +} + +static gf_on_progress_cbk prog_cbk = NULL; +static void *user_cbk; + +GF_EXPORT +void gf_set_progress(const char *title, u64 done, u64 total) +{ + if (prog_cbk) { + prog_cbk(user_cbk, title, done, total); + } +#ifndef _WIN32_WCE + else { + gf_on_progress_stdout(title, done, total); + } +#endif +} + +GF_EXPORT +void gf_set_progress_callback(void *_user_cbk, gf_on_progress_cbk _prog_cbk) +{ + prog_cbk = _prog_cbk; + user_cbk = _user_cbk; +} + +/*ENTRIES MUST BE IN THE SAME ORDER AS LOG_TOOL DECLARATION IN */ +static struct log_tool_info {u32 type; const char *name; u32 level; } global_log_tools [] = +{ + { GF_LOG_CORE, "core", GF_LOG_WARNING }, + { GF_LOG_CODING, "coding", GF_LOG_WARNING }, + { GF_LOG_CONTAINER, "container", GF_LOG_WARNING }, + { GF_LOG_NETWORK, "network", GF_LOG_WARNING }, + { GF_LOG_RTP, "rtp", GF_LOG_WARNING }, + { GF_LOG_AUTHOR, "author", GF_LOG_WARNING }, + { GF_LOG_SYNC, "sync", GF_LOG_WARNING }, + { GF_LOG_CODEC, "codec", GF_LOG_WARNING }, + { GF_LOG_PARSER, "parser", GF_LOG_WARNING }, + { GF_LOG_MEDIA, "media", GF_LOG_WARNING }, + { GF_LOG_SCENE, "scene", GF_LOG_WARNING }, + { GF_LOG_SCRIPT, "script", GF_LOG_WARNING }, + { GF_LOG_INTERACT, "interact", GF_LOG_WARNING }, + { GF_LOG_COMPOSE, "compose", GF_LOG_WARNING }, + { GF_LOG_CACHE, "cache", GF_LOG_WARNING }, + { GF_LOG_MMIO, "mmio", GF_LOG_WARNING }, + { GF_LOG_RTI, "rti", GF_LOG_WARNING }, + { GF_LOG_SMIL, "smil", GF_LOG_WARNING }, + { GF_LOG_MEMORY, "mem", GF_LOG_WARNING }, + { GF_LOG_AUDIO, "audio", GF_LOG_WARNING }, + { GF_LOG_MODULE, "module", GF_LOG_WARNING }, + { GF_LOG_MUTEX, "mutex", GF_LOG_WARNING }, + { GF_LOG_CONSOLE, "console", GF_LOG_INFO } +}; + +GF_EXPORT +GF_Err gf_log_modify_tools_levels(const char *val) +{ +#ifndef GPAC_DISABLE_LOG + u32 level; + char *sep, *sep_level; + while (val && strlen(val)) { + const char *next_val = NULL; + const char *tools = NULL; + /*look for log level*/ + sep_level = (char *) strchr(val, '@'); + if (!sep_level) { + fprintf(stderr, "Unrecognized log format %s - expecting logTool@logLevel\n", val); + return GF_BAD_PARAM; + } + + level = 0; + if (!strnicmp(sep_level+1, "error", 5)) { + level = GF_LOG_ERROR; + next_val = sep_level+1 + 5; + } + else if (!strnicmp(sep_level+1, "warning", 7)) { + level = GF_LOG_WARNING; + next_val = sep_level+1 + 7; + } + else if (!strnicmp(sep_level+1, "info", 4)) { + level = GF_LOG_INFO; + next_val = sep_level+1 + 4; + } + else if (!strnicmp(sep_level+1, "debug", 5)) { + level = GF_LOG_DEBUG; + next_val = sep_level+1 + 5; + } + else if (!strnicmp(sep_level+1, "quiet", 5)) { + level = GF_LOG_QUIET; + next_val = sep_level+1 + 5; + } + else { + fprintf(stderr, "Unknown log level specified: %s\n", sep_level+1); + return GF_BAD_PARAM; + } + + sep_level[0] = 0; + tools = val; + while (tools) { + u32 i; + + sep = (char *) strchr(tools, ':'); + if (sep) sep[0] = 0; + + if (!stricmp(tools, "all")) { + for (i=0; iGF_LOG_TOOL_MAX/2) { + strcpy(szLogs, szLogTools); + strcpy(szLogTools, "all"); + strcat(szLogTools, levelstr); + if (strlen(szLogs)) { + strcat(szLogTools, ":"); + strcat(szLogTools, szLogs); + } + } else { + if (strlen(szLogTools)) { + strcat(szLogTools, ":"); + } + /*remove last ':' from tool*/ + szLogs[ strlen(szLogs) - 1 ] = 0; + strcat(szLogTools, szLogs); + strcat(szLogTools, levelstr); + } + } + level++; + } + len = strlen(szLogTools); + if (len) { + /*remove last ':' from level*/ + if (szLogTools[ len-1 ] == ':') szLogTools[ len-1 ] = 0; + return gf_strdup(szLogTools); + } +#endif + return gf_strdup("all@quiet"); +} + +#ifndef GPAC_DISABLE_LOG +u32 call_lev = 0; +u32 call_tool = 0; + +GF_EXPORT +Bool gf_log_tool_level_on(u32 log_tool, u32 log_level) +{ + assert(log_tool= log_level) return 1; + return 0; +} + +void default_log_callback(void *cbck, u32 level, u32 tool, const char* fmt, va_list vlist) +{ +#ifndef _WIN32_WCE + vfprintf(stdout, fmt, vlist); +#endif +} + +static void *user_log_cbk = NULL; +static gf_log_cbk log_cbk = default_log_callback; +static Bool log_exit_on_error = 0; + +GF_EXPORT +void gf_log(const char *fmt, ...) +{ + va_list vl; + va_start(vl, fmt); + log_cbk(user_log_cbk, call_lev, call_tool, fmt, vl); + va_end(vl); + if (log_exit_on_error && call_lev==GF_LOG_ERROR) + exit(1); +} + +GF_EXPORT +void gf_log_set_strict_error(Bool strict) +{ + log_exit_on_error = strict; +} + +GF_EXPORT +void gf_log_set_tool_level(u32 tool, u32 level) +{ + assert(tool<=GF_LOG_TOOL_MAX); + if (tool==GF_LOG_ALL) { + u32 i; + for (i=0; i> 24) ^ *data++) & 0xff]; + + return crc; +} + +#define CHECK_MAC(_a) "#_a :" ? (_a) ? "yes":"no" + +GF_EXPORT +const char *gpac_features() +{ + const char *features = "" +#ifdef GPAC_FIXED_POINT + "GPAC_FIXED_POINT " +#endif +#ifdef GPAC_MEMORY_TRACKING + "GPAC_MEMORY_TRACKING " +#endif +#ifdef GPAC_BIG_ENDIAN + "GPAC_BIG_ENDIAN " +#endif +#ifdef GPAC_HAS_SSL + "GPAC_HAS_SSL " +#endif +#ifdef GPAC_HAS_SPIDERMONKEY + "GPAC_HAS_SPIDERMONKEY " +#endif +#ifdef GPAC_HAS_JPEG + "GPAC_HAS_JPEG " +#endif +#ifdef GPAC_HAS_PNG + "GPAC_HAS_PNG " +#endif +#ifdef GPAC_DISABLE_3D + "GPAC_DISABLE_3D " +#endif +#ifdef GPAC_USE_TINYGL + "GPAC_USE_TINYGL " +#endif +#ifdef GPAC_USE_OGL_ES + "GPAC_USE_OGL_ES " +#endif +#if defined(_WIN32_WCE) +#ifdef GPAC_USE_IGPP + "GPAC_USE_IGPP " +#endif +#ifdef GPAC_USE_IGPP_HP + "GPAC_USE_IGPP_HP " +#endif +#endif +#ifdef GPAC_DISABLE_SVG + "GPAC_DISABLE_SVG " +#endif +#ifdef GPAC_DISABLE_VRML + "GPAC_DISABLE_VRML " +#endif +#ifdef GPAC_MINIMAL_ODF + "GPAC_MINIMAL_ODF " +#endif +#ifdef GPAC_DISABLE_BIFS + "GPAC_DISABLE_BIFS " +#endif +#ifdef GPAC_DISABLE_QTVR + "GPAC_DISABLE_QTVR " +#endif +#ifdef GPAC_DISABLE_AVILIB + "GPAC_DISABLE_AVILIB " +#endif +#ifdef GPAC_DISABLE_OGG + "GPAC_DISABLE_OGG " +#endif +#ifdef GPAC_DISABLE_MPEG2PS + "GPAC_DISABLE_MPEG2PS " +#endif +#ifdef GPAC_DISABLE_MPEG2PS + "GPAC_DISABLE_MPEG2TS " +#endif +#ifdef GPAC_DISABLE_SENG + "GPAC_DISABLE_SENG " +#endif +#ifdef GPAC_DISABLE_MEDIA_IMPORT + "GPAC_DISABLE_MEDIA_IMPORT " +#endif +#ifdef GPAC_DISABLE_AV_PARSERS + "GPAC_DISABLE_AV_PARSERS " +#endif +#ifdef GPAC_DISABLE_MEDIA_EXPORT + "GPAC_DISABLE_MEDIA_EXPORT " +#endif +#ifdef GPAC_DISABLE_SWF_IMPORT + "GPAC_DISABLE_SWF_IMPORT " +#endif +#ifdef GPAC_DISABLE_SCENE_STATS + "GPAC_DISABLE_SCENE_STATS " +#endif +#ifdef GPAC_DISABLE_SCENE_DUMP + "GPAC_DISABLE_SCENE_DUMP " +#endif +#ifdef GPAC_DISABLE_SCENE_ENCODER + "GPAC_DISABLE_SCENE_ENCODER " +#endif +#ifdef GPAC_DISABLE_LOADER_ISOM + "GPAC_DISABLE_LOADER_ISOM " +#endif +#ifdef GPAC_DISABLE_OD_DUMP + "GPAC_DISABLE_OD_DUMP " +#endif +#ifdef GPAC_DISABLE_MCRYPT + "GPAC_DISABLE_MCRYPT " +#endif +#ifdef GPAC_DISABLE_ISOM + "GPAC_DISABLE_MCRYPT " +#endif +#ifdef GPAC_DISABLE_ISOM_HINTING + "GPAC_DISABLE_ISOM_HINTING " +#endif +#ifdef GPAC_DISABLE_ISOM_WRITE + "GPAC_DISABLE_ISOM_WRITE " +#endif +#ifdef GPAC_DISABLE_ISOM_FRAGMENTS + "GPAC_DISABLE_ISOM_FRAGMENTS " +#endif +#ifdef GPAC_DISABLE_LASER + "GPAC_DISABLE_LASER " +#endif +#ifdef GPAC_DISABLE_STREAMING + "GPAC_DISABLE_STREAMING " +#endif + + ; + return features; +} diff --git a/src/gpacmp4/gpac/avparse.h b/src/gpacmp4/gpac/avparse.h new file mode 100644 index 00000000..eeb1afa0 --- /dev/null +++ b/src/gpacmp4/gpac/avparse.h @@ -0,0 +1,245 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / Authoring Tools sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef _GF_PARSERS_AV_H_ +#define _GF_PARSERS_AV_H_ + + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +/*basic MPEG (1,2,4) visual object parser (DSI extraction and timing/framing)*/ +typedef struct +{ + /*video PL*/ + u8 VideoPL; + u8 RAP_stream, objectType, has_shape, enh_layer; + /*video resolution*/ + u16 width, height; + /*pixel aspect ratio*/ + u8 par_num, par_den; + + u16 clock_rate; + u8 NumBitsTimeIncrement; + u32 time_increment; + /*for MPEG 1/2*/ + Double fps; +} GF_M4VDecSpecInfo; + + +typedef struct __tag_m4v_parser GF_M4VParser; + +#ifndef GPAC_DISABLE_AV_PARSERS + +GF_M4VParser *gf_m4v_parser_new(char *data, u64 data_size, Bool mpeg12video); +GF_M4VParser *gf_m4v_parser_bs_new(GF_BitStream *bs, Bool mpeg12video); +void gf_m4v_parser_del(GF_M4VParser *m4v); +GF_Err gf_m4v_parse_config(GF_M4VParser *m4v, GF_M4VDecSpecInfo *dsi); + +/*get a frame (can contain GOP). The parser ALWAYS resync on the next object in the bitstream +thus you can seek the bitstream to copy the payload without re-seeking it */ +GF_Err gf_m4v_parse_frame(GF_M4VParser *m4v, GF_M4VDecSpecInfo dsi, u8 *frame_type, u32 *time_inc, u64 *size, u64 *start, Bool *is_coded); +/*returns current object start in bitstream*/ +u64 gf_m4v_get_object_start(GF_M4VParser *m4v); +/*returns 1 if current object is a valid MPEG-4 Visual object*/ +Bool gf_m4v_is_valid_object_type(GF_M4VParser *m4v); +/*decodes DSI*/ +GF_Err gf_m4v_get_config(char *rawdsi, u32 rawdsi_size, GF_M4VDecSpecInfo *dsi); +/*rewrites PL code in DSI*/ +void gf_m4v_rewrite_pl(char **io_dsi, u32 *io_dsi_len, u8 PL); +/*rewrites PAR code in DSI. Negative values will remove the par*/ +GF_Err gf_m4v_rewrite_par(char **o_data, u32 *o_dataLen, s32 par_n, s32 par_d); + +#endif /*GPAC_DISABLE_AV_PARSERS*/ + +/*returns readable description of profile*/ +const char *gf_m4v_get_profile_name(u8 video_pl); + +#ifndef GPAC_DISABLE_AV_PARSERS +s32 gf_mv12_next_start_code(unsigned char *pbuffer, u32 buflen, u32 *optr, u32 *scode); +s32 gf_mv12_next_slice_start(unsigned char *pbuffer, u32 startoffset, u32 buflen, u32 *slice_offset); + +#endif /* GPAC_DISABLE_AV_PARSERS*/ + +#ifndef GPAC_DISABLE_AV_PARSERS + +/*MP3 tools*/ +u8 gf_mp3_num_channels(u32 hdr); +u16 gf_mp3_sampling_rate(u32 hdr); +u16 gf_mp3_window_size(u32 hdr); +u32 gf_mp3_bit_rate(u32 hdr); +u8 gf_mp3_object_type_indication(u32 hdr); +u8 gf_mp3_layer(u32 hdr); +u16 gf_mp3_frame_size(u32 hdr); +u32 gf_mp3_get_next_header(FILE* in); +u32 gf_mp3_get_next_header_mem(const char *buffer, u32 size, u32 *pos); + +#endif /*GPAC_DISABLE_AV_PARSERS*/ + +u8 gf_mp3_version(u32 hdr); +const char *gf_mp3_version_name(u32 hdr); + + + +#if !defined(GPAC_DISABLE_AV_PARSERS) && !defined (GPAC_DISABLE_OGG) + +/*vorbis tools*/ +typedef struct +{ + u32 sample_rate, channels, version; + s32 max_r, avg_r, low_r; + u32 min_block, max_block; + + /*do not touch, parser private*/ + Bool is_init; + u32 modebits; + Bool mode_flag[64]; +} GF_VorbisParser; + +/*call with vorbis header packets - you MUST initialize the structure to 0 before!! +returns 1 if success, 0 if error.*/ +Bool gf_vorbis_parse_header(GF_VorbisParser *vp, char *data, u32 data_len); +/*returns 0 if init error or not a vorbis frame, otherwise returns the number of audio samples +in this frame*/ +u32 gf_vorbis_check_frame(GF_VorbisParser *vp, char *data, u32 data_length); + +#endif /*!defined(GPAC_DISABLE_AV_PARSERS) && !defined (GPAC_DISABLE_OGG)*/ + + +enum +{ + GF_M4A_AAC_MAIN = 1, + GF_M4A_AAC_LC = 2, + GF_M4A_AAC_SSR = 3, + GF_M4A_AAC_LTP = 4, + GF_M4A_AAC_SBR = 5, + GF_M4A_AAC_SCALABLE = 6, + GF_M4A_TWINVQ = 7, + GF_M4A_CELP = 8, + GF_M4A_HVXC = 9, + GF_M4A_TTSI = 12, + GF_M4A_MAIN_SYNTHETIC = 13, + GF_M4A_WAVETABLE_SYNTHESIS = 14, + GF_M4A_GENERAL_MIDI = 15, + GF_M4A_ALGO_SYNTH_AUDIO_FX = 16, + GF_M4A_ER_AAC_LC = 17, + GF_M4A_ER_AAC_LTP = 19, + GF_M4A_ER_AAC_SCALABLE = 20, + GF_M4A_ER_TWINVQ = 21, + GF_M4A_ER_BSAC = 22, + GF_M4A_ER_AAC_LD = 23, + GF_M4A_ER_CELP = 24, + GF_M4A_ER_HVXC = 25, + GF_M4A_ER_HILN = 26, + GF_M4A_ER_PARAMETRIC = 27, + GF_M4A_SSC = 28, + GF_M4A_AAC_PS = 29, + GF_M4A_LAYER1 = 32, + GF_M4A_LAYER2 = 33, + GF_M4A_LAYER3 = 34, + GF_M4A_DST = 35, + GF_M4A_ALS = 36 +}; + +#ifndef GPAC_DISABLE_AV_PARSERS + +static const u32 GF_M4ASampleRates[] = +{ + 96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, + 16000, 12000, 11025, 8000, 7350, 0, 0, 0 +}; + +/*get Audio type from dsi. return audio codec type:*/ +typedef struct +{ + u32 nb_chan; + u32 base_object_type, base_sr, base_sr_index; + /*SBR*/ + Bool has_sbr; + u32 sbr_object_type, sbr_sr, sbr_sr_index; + /*PS*/ + Bool has_ps; + /*PL indication*/ + u8 audioPL; +} GF_M4ADecSpecInfo; +/*parses dsi and updates audioPL*/ +GF_Err gf_m4a_get_config(char *dsi, u32 dsi_size, GF_M4ADecSpecInfo *cfg); +/*gets audioPL for given cfg*/ +u32 gf_m4a_get_profile(GF_M4ADecSpecInfo *cfg); + +GF_Err gf_m4a_write_config(GF_M4ADecSpecInfo *cfg, char **dsi, u32 *dsi_size); +GF_Err gf_m4a_write_config_bs(GF_BitStream *bs, GF_M4ADecSpecInfo *cfg); +GF_Err gf_m4a_parse_config(GF_BitStream *bs, GF_M4ADecSpecInfo *cfg, Bool size_known); + +#endif /*GPAC_DISABLE_AV_PARSERS*/ + +const char *gf_m4a_object_type_name(u32 objectType); +const char *gf_m4a_get_profile_name(u8 audio_pl); + +#ifndef GPAC_DISABLE_AV_PARSERS + + +typedef struct +{ + u32 bitrate; + u32 sample_rate; + u32 framesize; + u32 channels; + /*only set if full parse*/ + u8 fscod, bsid, bsmod, acmod, lfon, brcode; +} GF_AC3Header; + +Bool gf_ac3_parser(u8 *buffer, u32 buffer_size, u32 *pos, GF_AC3Header *out_hdr, Bool full_parse); +Bool gf_ac3_parser_bs(GF_BitStream *bs, GF_AC3Header *hdr, Bool full_parse); +u32 gf_ac3_get_channels(u32 acmod); +u32 gf_ac3_get_bitrate(u32 brcode); + +GF_Err gf_avc_get_sps_info(char *sps, u32 sps_size, u32 *sps_id, u32 *width, u32 *height, s32 *par_n, s32 *par_d); +const char *gf_avc_get_profile_name(u8 video_prof); + +#endif /*GPAC_DISABLE_AV_PARSERS*/ + + + +/*gets image size (bs must contain the whole image) +@OTI: image type (JPEG=0x6C, PNG=0x6D) +@width, height: image resolution - for jpeg max size if thumbnail included*/ +void gf_img_parse(GF_BitStream *bs, u8 *OTI, u32 *mtype, u32 *width, u32 *height, char **dsi, u32 *dsi_len); + +GF_Err gf_img_jpeg_dec(char *jpg, u32 jpg_size, u32 *width, u32 *height, u32 *pixel_format, char *dst, u32 *dst_size, u32 dst_nb_comp); + +GF_Err gf_img_png_dec(char *png, u32 png_size, u32 *width, u32 *height, u32 *pixel_format, char *dst, u32 *dst_size); +GF_Err gf_img_file_dec(char *png_file, u32 *oti, u32 *width, u32 *height, u32 *pixel_format, char **dst, u32 *dst_size); +GF_Err gf_img_png_enc(char *data, u32 width, u32 height, s32 stride, u32 pixel_format, char *dst, u32 *dst_size); + +#ifdef __cplusplus +} +#endif + + +#endif /*_GF_PARSERS_AV_H_*/ + diff --git a/src/gpacmp4/gpac/base_coding.h b/src/gpacmp4/gpac/base_coding.h new file mode 100644 index 00000000..ee02e9c3 --- /dev/null +++ b/src/gpacmp4/gpac/base_coding.h @@ -0,0 +1,105 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / common tools sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef _GF_BASE_CODING_H_ +#define _GF_BASE_CODING_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/*! + * \file + * \brief Base coding functions. + */ + +/*! + * \addtogroup bascod_grp base coding + * \ingroup utils_grp + * \brief Base Coding functions + * + * This section documents the base encoding and decoding functions of the GPAC framework. + * @{ + */ + +#include + +/*! + *\brief base64 encoder + * + *Encodes a data buffer to Base64 + *\param in_buffer input data buffer + *\param in_buffer_size input data buffer size + *\param out_buffer output Base64 buffer location + *\param out_buffer_size output Base64 buffer allocated size + *\return size of the encoded Base64 buffer + *\note the encoded data buffer is not NULL-terminated. + */ +u32 gf_base64_encode(char *in_buffer, u32 in_buffer_size, char *out_buffer, u32 out_buffer_size); +/*! + *\brief base64 decoder + * + *Decodes a Base64 buffer to data + *\param in_buffer input Base64 buffer + *\param in_buffer_size input Base64 buffer size + *\param out_buffer output data buffer location + *\param out_buffer_size output data buffer allocated size + *\return size of the decoded buffer + */ +u32 gf_base64_decode(char *in_buffer, u32 in_buffer_size, char *out_buffer, u32 out_buffer_size); + +/*! + *\brief base16 encoder + * + *Encodes a data buffer to Base16 + *\param in_buffer input data buffer + *\param in_buffer_size input data buffer size + *\param out_buffer output Base16 buffer location + *\param out_buffer_size output Base16 buffer allocated size + *\return size of the encoded Base16 buffer + *\note the encoded data buffer is not NULL-terminated. + */ +u32 gf_base16_encode(char *in_buffer, u32 in_buffer_size, char *out_buffer, u32 out_buffer_size); + +/*! + *\brief base16 decoder + * + *Decodes a Base16 buffer to data + *\param in_buffer input Base16 buffer + *\param in_buffer_size input Base16 buffer size + *\param out_buffer output data buffer location + *\param out_buffer_size output data buffer allocated size + *\return size of the decoded buffer + */ +u32 gf_base16_decode(char *in_buffer, u32 in_buffer_size, char *out_buffer, u32 out_buffer_size); + +/*! @} */ + +#ifdef __cplusplus +} +#endif + + +#endif /*_GF_BASE_CODING_H_*/ + diff --git a/src/gpacmp4/gpac/bitstream.h b/src/gpacmp4/gpac/bitstream.h new file mode 100644 index 00000000..5186288b --- /dev/null +++ b/src/gpacmp4/gpac/bitstream.h @@ -0,0 +1,469 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / common tools sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef _GF_BITSTREAM_H_ +#define _GF_BITSTREAM_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/*! + * \file + * \brief bitstream functions. + */ + +/*! + * \addtogroup bs_grp bitstream + * \ingroup utils_grp + * \brief BitStream object + * + * This section documents the bitstream object of the GPAC framework. + * \note Unless specified, all functions assume Big-Endian ordering of data in the bitstream. + * @{ + */ + +#include + + +enum +{ + GF_BITSTREAM_READ = 0, + GF_BITSTREAM_WRITE +}; + +typedef struct __tag_bitstream GF_BitStream; + +/*! + * \brief bitstream constructor + * + * Constructs a bitstream from a buffer (read or write mode) + * \param buffer buffer to read or write. In WRITE mode, this can be NULL to let the bitstream object dynamically allocate memory, in which case the size param is ignored. + * \param size size of the buffer given. + * \param mode operation mode for this bitstream: GF_BITSTREAM_READ for read, GF_BITSTREAM_WRITE for write. + * \return new bitstream object + * \note In write mode on an existing data buffer, data overflow is never signaled but simply ignored, it is the caller responsability to ensure it + * does not write more than possible. + */ +GF_BitStream *gf_bs_new(const char *buffer, u64 size, u32 mode); +/*! + * \brief bitstream constructor from file handle + * + * Creates a bitstream from a file handle. + * \param f handle of the file to use. This handle must be created with binary mode. + * \param mode operation mode for this bitstream: GF_BITSTREAM_READ for read, GF_BITSTREAM_WRITE for write. + * \return new bitstream object + * \note - You have to open your file in the appropriated mode:\n + * - GF_BITSTREAM_READ: bitstream is constructed for reading\n + * - GF_BITSTREAM_WRITE: bitstream is constructed for writing\n + * \note - you may use any of these modes for a file with read/write access. + * \warning RESULTS ARE UNEXPECTED IF YOU TOUCH THE FILE WHILE USING THE BITSTREAM. + */ +GF_BitStream *gf_bs_from_file(FILE *f, u32 mode); +/*! + * \brief bitstream constructor from file handle + * + * Deletes the bitstream object. If the buffer was created by the bitstream, it is deleted if still present. + */ +void gf_bs_del(GF_BitStream *bs); + +/*! + * \brief integer reading + * + * Reads an integer coded on a number of bit. + * \param bs the target bitstream + * \param nBits the number of bits to read + * \return the integer value read. + */ +u32 gf_bs_read_int(GF_BitStream *bs, u32 nBits); +/*! + * \brief large integer reading + * + * Reads a large integer coded on a number of bit bigger than 32. + * \param bs the target bitstream + * \param nBits the number of bits to read + * \return the large integer value read. + */ +u64 gf_bs_read_long_int(GF_BitStream *bs, u32 nBits); +/*! + * \brief float reading + * + * Reads a float coded as IEEE 32 bit format. + * \param bs the target bitstream + * \return the float value read. + */ +Float gf_bs_read_float(GF_BitStream *bs); +/*! + * \brief double reading + * + * Reads a double coded as IEEE 64 bit format. + * \param bs the target bitstream + * \return the double value read. + */ +Double gf_bs_read_double(GF_BitStream *bs); +/*! + * \brief data reading + * + * Reads a data buffer + * \param bs the target bitstream + * \param data the data buffer to be filled + * \param nbBytes the amount of bytes to read + * \return the number of bytes actually read. + * \warning the data buffer passed must be large enough to hold the desired amount of bytes. + */ +u32 gf_bs_read_data(GF_BitStream *bs, char *data, u32 nbBytes); + +/*! + * \brief align char reading + * + * Reads an integer coded on 8 bits starting at a byte boundary in the bitstream. + * \warning you must not use this function if the bitstream is not aligned + * \param bs the target bitstream + * \return the char value read. + */ +u32 gf_bs_read_u8(GF_BitStream *bs); +/*! + * \brief align short reading + * + * Reads an integer coded on 16 bits starting at a byte boundary in the bitstream. + * \warning you must not use this function if the bitstream is not aligned + * \param bs the target bitstream + * \return the short value read. + */ +u32 gf_bs_read_u16(GF_BitStream *bs); +/*! + * \brief align 24-bit integer reading + * + * Reads an integer coded on 24 bits starting at a byte boundary in the bitstream. + * \warning you must not use this function if the bitstream is not aligned + * \param bs the target bitstream + * \return the integer value read. + */ +u32 gf_bs_read_u24(GF_BitStream *bs); +/*! + * \brief align integer reading + * + * Reads an integer coded on 32 bits starting at a byte boundary in the bitstream. + * \warning you must not use this function if the bitstream is not aligned + * \param bs the target bitstream + * \return the integer value read. + */ +u32 gf_bs_read_u32(GF_BitStream *bs); +/*! + * \brief align large integer reading + * + * Reads an integer coded on 64 bits starting at a byte boundary in the bitstream. + * \warning you must not use this function if the bitstream is not aligned + * \param bs the target bitstream + * \return the large integer value read. + */ +u64 gf_bs_read_u64(GF_BitStream *bs); +/*! + * \brief little endian integer reading + * + * Reads an integer coded on 32 bits in little-endian order. + * \param bs the target bitstream + * \return the integer value read. + */ +u32 gf_bs_read_u32_le(GF_BitStream *bs); +/*! + * \brief little endian integer reading + * + * Reads an integer coded on 16 bits in little-endian order. + * \param bs the target bitstream + * \return the integer value read. + */ +u16 gf_bs_read_u16_le(GF_BitStream *bs); + + +/*! + * \brief variable length integer reading + * + * Reads an integer coded on a variable number of 4-bits chunks. The number of chunks is given by the number of non-0 bits at the begining. + * \param bs the target bitstream + * \return the integer value read. + */ +u32 gf_bs_read_vluimsbf5(GF_BitStream *bs); + +/*! + * \brief bit position + * + * Returns current bit position in the bitstream - only works in memory mode. + * \param bs the target bitstream + * \return the integer value read. + */ +u32 gf_bs_get_bit_offset(GF_BitStream *bs); + +/*! + * \brief current bit position + * + * Returns bit position in the current byte of the bitstream - only works in memory mode. + * \param bs the target bitstream + * \return the integer value read. + */ +u32 gf_bs_get_bit_position(GF_BitStream *bs); + + +/*! + * \brief integer writing + * + * Writes an integer on a given number of bits. + * \param bs the target bitstream + * \param value the integer to write + * \param nBits number of bits used to code the integer + */ +void gf_bs_write_int(GF_BitStream *bs, s32 value, s32 nBits); +/*! + * \brief large integer writing + * + * Writes an integer on a given number of bits greater than 32. + * \param bs the target bitstream + * \param value the large integer to write + * \param nBits number of bits used to code the integer + */ +void gf_bs_write_long_int(GF_BitStream *bs, s64 value, s32 nBits); +/*! + * \brief float writing + * + * Writes a float in IEEE 32 bits format. + * \param bs the target bitstream + * \param value the float to write + */ +void gf_bs_write_float(GF_BitStream *bs, Float value); +/*! + * \brief double writing + * + * Writes a double in IEEE 64 bits format. + * \param bs the target bitstream + * \param value the double to write + */ +void gf_bs_write_double(GF_BitStream *bs, Double value); +/*! + * \brief data writing + * + * Writes a data buffer. + * \param bs the target bitstream + * \param data the data to write + * \param nbBytes number of data bytes to write + */ +u32 gf_bs_write_data(GF_BitStream *bs, const char *data, u32 nbBytes); + +/*! + * \brief align char writing + * + * Writes an integer on 8 bits starting at a byte boundary in the bitstream. + * \warning you must not use this function if the bitstream is not aligned + * \param bs the target bitstream + * \param value the char value to write + */ +void gf_bs_write_u8(GF_BitStream *bs, u32 value); +/*! + * \brief align short writing + * + * Writes an integer on 16 bits starting at a byte boundary in the bitstream. + * \warning you must not use this function if the bitstream is not aligned + * \param bs the target bitstream + * \param value the short value to write + */ +void gf_bs_write_u16(GF_BitStream *bs, u32 value); +/*! + * \brief align 24-bits integer writing + * + * Writes an integer on 24 bits starting at a byte boundary in the bitstream. + * \warning you must not use this function if the bitstream is not aligned + * \param bs the target bitstream + * \param value the integer value to write + */ +void gf_bs_write_u24(GF_BitStream *bs, u32 value); +/*! + * \brief align integer writing + * + * Writes an integer on 32 bits starting at a byte boundary in the bitstream. + * \warning you must not use this function if the bitstream is not aligned + * \param bs the target bitstream + * \param value the integer value to write + */ +void gf_bs_write_u32(GF_BitStream *bs, u32 value); +/*! + * \brief align large integer writing + * + * Writes an integer on 64 bits starting at a byte boundary in the bitstream. + * \warning you must not use this function if the bitstream is not aligned + * \param bs the target bitstream + * \param value the large integer value to write + */ +void gf_bs_write_u64(GF_BitStream *bs, u64 value); +/*! + * \brief little endian integer writing + * + * Writes an integer on 32 bits in little-endian order. + * \param bs the target bitstream + * \param value the integer value to write + */ +void gf_bs_write_u32_le(GF_BitStream *bs, u32 value); +/*! + * \brief little endian short writing + * + * Writes an integer on 16 bits in little-endian order. + * \param bs the target bitstream + * \param value the short value to write + */ +void gf_bs_write_u16_le(GF_BitStream *bs, u32 value); + +/*! + * \brief write byte multiple times + * + * Writes a give byte multiple times. + * \param bs the target bitstream + * \param byte the byte value to write + * \param count the number of times the byte should be written + * \return the number of bytes written + */ +u32 gf_bs_write_byte(GF_BitStream *bs, u8 byte, u32 count); + +/*! + * \brief end of bitstream management + * + * Assigns a notification callback function for end of stream signaling in read mode + * \param bs the target bitstream + * \param EndOfStream the notification function to use + * \param par opaque user data passed to the bitstream + */ +void gf_bs_set_eos_callback(GF_BitStream *bs, void (*EndOfStream)(void *par), void *par); + +/*! + * \brief bitstream alignment + * + * Aligns bitstream to next byte boundary. In write mode, this will write 0 bit values until alignment. + * \param bs the target bitstream + * \return the number of bits read/written until alignment + */ +u8 gf_bs_align(GF_BitStream *bs); +/*! + * \brief capacity query + * + * Returns the number of bytes still available in the bitstream in read mode. + * \param bs the target bitstream + * \return the number of bytes still available in read mode, -1 in write modes. + */ +u64 gf_bs_available(GF_BitStream *bs); +/*! + * \brief buffer fetching + * + * Fetches the internal bitstream buffer in write mode. If a buffer was given at the bitstream construction, or if the bitstream is in read mode, this does nothing. + * \param bs the target bitstream + * \param output address of a memory block to be allocated for bitstream data. + * \param outSize set to the size of the allocated memory block. + * \note + * It is the user responsability to destroy the allocated buffer + * Once this function has been called, the internal bitstream buffer is reseted. + */ +void gf_bs_get_content(GF_BitStream *bs, char **output, u32 *outSize); +/*! + * \brief byte skipping + * + * Skips bytes in the bitstream. In Write mode, this will write the 0 integer value for memory-based bitstreams or seek the stream + for file-based bitstream. + * \param bs the target bitstream + * \param nbBytes the number of bytes to skip + */ +void gf_bs_skip_bytes(GF_BitStream *bs, u64 nbBytes); + +/*! + *\brief bitstream seeking + * + *Seeks the bitstream to a given offset after the begining of the stream. This will perform alignment of the bitstream in all modes. + *\warning Results are unpredictable if seeking beyond the bitstream end is performed. + *\param bs the target bitstream + *\param offset buffer/file offset to seek to + */ +GF_Err gf_bs_seek(GF_BitStream *bs, u64 offset); + +/*! + *\brief bitstream truncation + * + *Truncates the bitstream at the current position + *\param bs the target bitstream + */ +void gf_bs_truncate(GF_BitStream *bs); + +/*! + *\brief bit peeking + * + *Peeks a given number of bits (read without moving the position indicator) for read modes only. + *\param bs the target bitstream + *\param numBits the number of bits to peek + *\param byte_offset + * if set, bitstream is aligned and moved from byte_offset before peeking (byte-aligned picking) + * otherwise, bitstream is not aligned and bits are peeked from current state + *\return the integer value read +*/ +u32 gf_bs_peek_bits(GF_BitStream *bs, u32 numBits, u32 byte_offset); + +/*! + *\brief bit reservoir query + * + * Queries the number of bits available in read mode. + *\param bs the target bitstream + *\return number of available bits if position is in the last byte of the buffer/stream, 8 otherwise + */ +u8 gf_bs_bits_available(GF_BitStream *bs); +/*! + *\brief position query + * + *Returns the reading/writting position in the buffer/file. + *\param bs the target bitstream + *\return the read/write position of the bitstream + */ +u64 gf_bs_get_position(GF_BitStream *bs); +/*! + *\brief size query + * + *Returns the size of the associated buffer/file. + *\param bs the target bitstream + *\return the size of the bitstream + */ +u64 gf_bs_get_size(GF_BitStream *bs); +/*! + *\brief file-based size query + * + *Returns the size of a file-based bitstream and force a seek to end of file. This is used in case the file handle + *describes a file being constructed on disk while being read? + * + *\param bs the target bitstream + *\return the disk size of the associated file + */ +u64 gf_bs_get_refreshed_size(GF_BitStream *bs); + + + +/*! @} */ + +#ifdef __cplusplus +} +#endif + + +#endif /*_GF_BITSTREAM_H_*/ + diff --git a/src/gpacmp4/gpac/configuration.h b/src/gpacmp4/gpac/configuration.h new file mode 100644 index 00000000..c4f6eb10 --- /dev/null +++ b/src/gpacmp4/gpac/configuration.h @@ -0,0 +1,219 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) ENST 2008 - + * All rights reserved + * + * This file is part of GPAC + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +#ifndef _GF_CONFIG_H_ +#define _GF_CONFIG_H_ + +#define GPAC_CONFIGURATION "(static configuration file)" + +/*this file defines all common macros for libgpac compilation + except for symbian32 which uses .mmp directives ... */ +#if defined(WIN32) || defined(_WIN32_WCE) || defined(GPAC_CONFIG_DARWIN) /*visual studio and xcode*/ + +/*enables GPAC fixed point*/ +//#define GPAC_FIXED_POINT + +/*automatic tracking is disabled by default, causes too many problems when reusing parts of gpac*/ +#if 0 +/*enables GPAC memory tracking*/ +#if defined(DEBUG) || defined(_DEBUG) +#define GPAC_MEMORY_TRACKING +#endif +#endif + +/*platform is big endian*/ +//#define GPAC_BIG_ENDIAN + +/*SSL enabled*/ +#ifdef WIN32 +#define GPAC_HAS_SSL +#endif + +/*spidermonkey enabled*/ +#define GPAC_HAS_SPIDERMONKEY +#ifdef GPAC_CONFIG_DARWIN +#define MOZILLA_1_8_BRANCH +#endif + +/*libjpeg enabled*/ +#define GPAC_HAS_JPEG + +/*pnj enabled*/ +#define GPAC_HAS_PNG + +/*IPv6 enabled - for win32, this is evaluated at compile time, !! do not uncomment !!*/ +//#define GPAC_HAS_IPV6 + +/*3D compositor disabled*/ +#ifdef GPAC_CONFIG_DARWIN +#define GPAC_DISABLE_3D +#endif + +/*use TinyGL instead of OpenGL*/ +//#define GPAC_USE_TINYGL + +/*use OpenGL ES instead of OpenGL*/ +#ifdef GPAC_CONFIG_DARWIN +#define GPAC_USE_OGL_ES +#endif + + +#if defined(_WIN32_WCE) + +#ifndef GPAC_FIXED_POINT +#define GPAC_FIXED_POINT +#endif + +/*use intel fixed-point*/ +//#define GPAC_USE_IGPP +/*use intel fixed-point with high precision*/ +//#define GPAC_USE_IGPP_HP + +#if defined(GPAC_USE_IGPP) && defined(GPAC_USE_IGPP_HP) +#error "Only one of GPAC_USE_IGPP and GPAC_USE_IGPP_HP can be defined" +#endif + +#if !defined(GPAC_DISABLE_3D) && !defined(GPAC_USE_TINYGL) && !defined(GPAC_USE_OGL_ES) +#define GPAC_USE_OGL_ES +#endif + +#endif /*_WIN32_WCE*/ + + + +#endif /*defined(WIN32) || defined(_WIN32_WCE)*/ + + +#if defined(__SYMBIAN32__) + +#ifndef GPAC_FIXED_POINT +#define GPAC_FIXED_POINT +#endif + +#endif + +#if defined(_WIN32_WCE) +/*comment this line if you don't have a GLU32 version for Windows Mobile*/ +//#define GPAC_HAS_GLU +#elif defined(WIN32) +#define GPAC_HAS_GLU +#endif + + +/*disables SVG scene graph*/ +//#define GPAC_DISABLE_SVG + +/*disables VRML/BIFS scene graphs*/ +//#define GPAC_DISABLE_VRML + +/*disables X3D scene graphs*/ +//#define GPAC_DISABLE_X3D + +/*disables MPEG-4 OD Framework - this only minimalize the set of OD features used, however all cannot be removed*/ +//#define GPAC_MINIMAL_ODF + +/*disables BIFS coding*/ +//#define GPAC_DISABLE_BIFS + +/*disables LASeR coder*/ +//#define GPAC_DISABLE_LASER + +/*disables BIFS Engine support - TODO - merge DIMS and LASeR into BENG and rename it*/ +//#define GPAC_DISABLE_SENG + +/*disables Cubic QTVR importing*/ +//#define GPAC_DISABLE_QTVR + +/*disables AVILib support*/ +//#define GPAC_DISABLE_AVILIB + +/*disables OGG support*/ +//#define GPAC_DISABLE_OGG + +/*disables MPEG2 PS support*/ +//#define GPAC_DISABLE_MPEG2PS + +/*disables MPEG2 TS demux support*/ +//#define GPAC_DISABLE_MPEG2TS + +/*disables MPEG2 TS Mux support*/ +//#define GPAC_DISABLE_MPEG2TS_MUX + +/*disables all media import functions*/ +//#define GPAC_DISABLE_MEDIA_IMPORT + +/*disable all AV parsing functions*/ +//#define GPAC_DISABLE_AV_PARSERS + +/*disables all media export functions*/ +//#define GPAC_DISABLE_MEDIA_EXPORT + +/*disables SWF importer*/ +//#define GPAC_DISABLE_SWF_IMPORT + +/*disables all media export functions*/ +//#define GPAC_DISABLE_SCENE_STATS + +/*disables scene -> MP4 encoder*/ +//#define GPAC_DISABLE_SCENE_ENCODER + +/*disables ISOM -> scene decoder*/ +//#define GPAC_DISABLE_LOADER_ISOM + +/*disables BT/WRL/X3DV -> scene decoder*/ +//#define GPAC_DISABLE_LOADER_BT + +/*disables XMTA/X3D -> scene decoder*/ +//#define GPAC_DISABLE_LOADER_XMT + +/*disables mcrypt*/ +//#define GPAC_DISABLE_MCRYPT + +/*disables all ISO FF*/ +//#define GPAC_DISABLE_ISOM + +/*disables ISO FF hint tracks*/ +//#define GPAC_DISABLE_ISOM_HINTING + +/*disables ISO FF writing*/ +//#define GPAC_DISABLE_ISOM_WRITE + +/*disables ISO FF fragments*/ +//#define GPAC_DISABLE_ISOM_FRAGMENTS + +/*disables scene graph textual dump*/ +//#define GPAC_DISABLE_SCENE_DUMP + +/*disables OD graph textual dump*/ +//#define GPAC_DISABLE_OD_DUMP + +/*disables OD graph textual dump*/ +//#define GPAC_DISABLE_ISOM_DUMP + +/*disables IETF RTP/SDP/RTSP*/ +//#define GPAC_DISABLE_STREAMING + +#endif /*_GF_CONFIG_H_*/ + diff --git a/src/gpacmp4/gpac/constants.h b/src/gpacmp4/gpac/constants.h new file mode 100644 index 00000000..601fe935 --- /dev/null +++ b/src/gpacmp4/gpac/constants.h @@ -0,0 +1,587 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / exported constants + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef _GF_CONSTANTS_H_ +#define _GF_CONSTANTS_H_ + +#ifdef __cplusplus +extern "C" { +#endif + + +/*! \addtogroup cst_grp constants + * \brief Constants used within GPAC + * + * This section documents some constants used in the GPAC framework which are not related to + * any specific sub-project. + * \ingroup utils_grp + * @{ + */ + + +/*! + * \brief Supported media stream types + * \hideinitializer + * + * Supported media stream types for media objects. +*/ +enum +{ + /*!MPEG-4 Object Descriptor Stream*/ + GF_STREAM_OD = 0x01, + /*!MPEG-4 Object Clock Reference Stream*/ + GF_STREAM_OCR = 0x02, + /*!MPEG-4 Scene Description Stream*/ + GF_STREAM_SCENE = 0x03, + /*!Visual Stream (Video, Image or MPEG-4 SNHC Tools)*/ + GF_STREAM_VISUAL = 0x04, + /*!Audio Stream (Audio, MPEG-4 Structured-Audio Tools)*/ + GF_STREAM_AUDIO = 0x05, + /*!MPEG-7 Description Stream*/ + GF_STREAM_MPEG7 = 0x06, + /*!MPEG-4 Intellectual Property Management and Protection Stream*/ + GF_STREAM_IPMP = 0x07, + /*!MPEG-4 Object Content Information Stream*/ + GF_STREAM_OCI = 0x08, + /*!MPEG-4 MPEGlet Stream*/ + GF_STREAM_MPEGJ = 0x09, + /*!MPEG-4 User Interaction Stream*/ + GF_STREAM_INTERACT = 0x0A, + /*!MPEG-4 IPMP Tool Stream*/ + GF_STREAM_IPMP_TOOL = 0x0B, + /*!MPEG-4 Font Data Stream*/ + GF_STREAM_FONT = 0x0C, + /*!MPEG-4 Streaming Text Stream*/ + GF_STREAM_TEXT = 0x0D, + /*!Nero Digital Subpicture Stream*/ + GF_STREAM_ND_SUBPIC = 0x38, + + /*GPAC internal stream types*/ + + + /*!GPAC Private Scene streams\n + *\n\note + *this stream type (MPEG-4 user-private) is reserved for streams only used to create a scene decoder + *handling the scene without input streams, as is the case for file readers (BT/VRML/XML..).\n + *The decoderSpecificInfo carried is as follows: + \code + u32 file_size: total file size + char file_name[dsi_size - sizeof(u32)]: local file name. + \n\note: File may be a cache file, it is the decoder responsability to check if the file is completely + downloaded before parsing if needed. + \endcode + *The inBufferLength param for decoders using these streams is the stream clock in ms (no input data is given).\n + *The "dummy_in" module is available to generate these streams for common files, and also takes care of proper + clock init in case of seeking.\n + *This is a reentrant stream type: if any media object with this streamtype also exist in the scene, they will be + *attached to the scene decoder (except when a new inline scene is detected, in which case a new decoder will + *be created). This allows for animation/sprite usage along with the systems timing/stream management.\n + *\n + *the objectTypeIndication currently in use for these streams are documented below\n + */ + GF_STREAM_PRIVATE_SCENE = 0x20, + + /*!GPAC Private Media streams\n + *\n\note + *this stream type (MPEG-4 user-private) is reserved for media streams bypassing GPAC for decoding + and composition. The media decoder is only in charge of repositioning the video output, and the compositor will + draw an empty rectangle if using alpha composition + + *The decoderSpecificInfo carried only contains an opaque pointer in the data field, which depends on the underlying InputServce provider + + *the objectTypeIndication currently in use for these streams are documented below\n + */ + GF_STREAM_PRIVATE_MEDIA = 0x21, + + /*used internally to signal the the OTI carries a 4CC code, typically media subtype (stsd entry in file format)*/ + GF_STREAM_4CC = 0xF0 +}; + + +/*! + * Media Object types + * + * This type provides a hint to network modules which may have to generate an service descriptor on the fly. + * They occur only if objects/services used in the scene are not referenced through ObjectDescriptors (MPEG-4) + * but direct through URL +*/ +enum +{ + /*!service descriptor expected is of undefined type. This should be treated like GF_MEDIA_OBJECT_SCENE*/ + GF_MEDIA_OBJECT_UNDEF = 0, + /*!service descriptor expected is of SCENE type and shall contain a scene stream and OD one if needed*/ + GF_MEDIA_OBJECT_SCENE, + /*!service descriptor expected is of SCENE UPDATES type (animation streams)*/ + GF_MEDIA_OBJECT_UPDATES, + /*!service descriptor expected is of VISUAL type*/ + GF_MEDIA_OBJECT_VIDEO, + /*!service descriptor expected is of AUDIO type*/ + GF_MEDIA_OBJECT_AUDIO, + /*!service descriptor expected is of TEXT type (3GPP/MPEG4)*/ + GF_MEDIA_OBJECT_TEXT, + /*!service descriptor expected is of UserInteraction type (MPEG-4 InputSensor)*/ + GF_MEDIA_OBJECT_INTERACT +}; + +/*! All Media Objects inserted through URLs and not MPEG-4 OD Framework use this ODID*/ +#define GF_MEDIA_EXTERNAL_ID 1050 + + +/*! + * \brief Pixel Formats + * + * Supported pixel formats for everything using video +*/ +#ifndef GF_4CC +#define GF_4CC(a,b,c,d) (((a)<<24)|((b)<<16)|((c)<<8)|(d)) +#endif +typedef enum +{ + /*!8 bit GREY */ + GF_PIXEL_GREYSCALE = GF_4CC('G','R','E','Y'), + /*!16 bit greyscale*/ + GF_PIXEL_ALPHAGREY = GF_4CC('G','R','A','L'), + /*!12 bit RGB on 16 bits (4096 colors)*/ + GF_PIXEL_RGB_444 = GF_4CC('R','4','4','4'), + /*!15 bit RGB*/ + GF_PIXEL_RGB_555 = GF_4CC('R','5','5','5'), + /*!16 bit RGB*/ + GF_PIXEL_RGB_565 = GF_4CC('R','5','6','5'), + /*!24 bit RGB*/ + GF_PIXEL_RGB_24 = GF_4CC('R','G','B','3'), + /*!24 bit BGR*/ + GF_PIXEL_BGR_24 = GF_4CC('B','G','R','3'), + /*!32 bit RGB. Component ordering in bytes is B-G-R-X.*/ + GF_PIXEL_RGB_32 = GF_4CC('R','G','B','4'), + /*!32 bit BGR. Component ordering in bytes is R-G-B-X.*/ + GF_PIXEL_BGR_32 = GF_4CC('B','G','R','4'), + + /*!32 bit ARGB. Component ordering in bytes is B-G-R-A.*/ + GF_PIXEL_ARGB = GF_4CC('A','R','G','B'), + /*!32 bit RGBA (openGL like). Component ordering in bytes is R-G-B-A.*/ + GF_PIXEL_RGBA = GF_4CC('R','G','B', 'A'), + /*!RGB24 + depth plane. Component ordering in bytes is R-G-B-D.*/ + GF_PIXEL_RGBD = GF_4CC('R', 'G', 'B', 'D'), + /*!RGB24 + depth plane (7 lower bits) + shape mask. Component ordering in bytes is R-G-B-(S+D).*/ + GF_PIXEL_RGBDS = GF_4CC('3', 'C', 'D', 'S'), + /*!Stereo RGB24 */ + GF_PIXEL_RGBS = GF_4CC('R', 'G', 'B', 'S'), + /*!Stereo RGBA. Component ordering in bytes is R-G-B-A. */ + GF_PIXEL_RGBAS = GF_4CC('R', 'G', 'A', 'S'), + + /*internal format for OpenGL using pachek RGB 24 bit plus planaer depth plane at the end of the image*/ + GF_PIXEL_RGB_24_DEPTH = GF_4CC('R', 'G', 'B', 'd'), + + /*!YUV packed format*/ + GF_PIXEL_YUY2 = GF_4CC('Y','U','Y','2'), + /*!YUV packed format*/ + GF_PIXEL_YVYU = GF_4CC('Y','V','Y','U'), + /*!YUV packed format*/ + GF_PIXEL_UYVY = GF_4CC('U','Y','V','Y'), + /*!YUV packed format*/ + GF_PIXEL_VYUY = GF_4CC('V','Y','U','Y'), + /*!YUV packed format*/ + GF_PIXEL_Y422 = GF_4CC('Y','4','2','2'), + /*!YUV packed format*/ + GF_PIXEL_UYNV = GF_4CC('U','Y','N','V'), + /*!YUV packed format*/ + GF_PIXEL_YUNV = GF_4CC('Y','U','N','V'), + /*!YUV packed format*/ + GF_PIXEL_V422 = GF_4CC('V','4','2','2'), + + /*!YUV planar format*/ + GF_PIXEL_YV12 = GF_4CC('Y','V','1','2'), + /*!YUV planar format*/ + GF_PIXEL_IYUV = GF_4CC('I','Y','U','V'), + /*!YUV planar format*/ + GF_PIXEL_I420 = GF_4CC('I','4','2','0'), + /*!YUV planar format*/ + GF_PIXEL_NV21 = GF_4CC('N','V','2','1'), + + /*!YV12 + Alpha plane*/ + GF_PIXEL_YUVA = GF_4CC('Y', 'U', 'V', 'A'), + + /*!YV12 + Depth plane*/ + GF_PIXEL_YUVD = GF_4CC('Y', 'U', 'V', 'D') + +} GF_PixelFormat; + + +/*! + * \brief Scene ObjectTypeIndication Formats + * + * Supported ObjectTypeIndication for scene description streams. *_FILE_* are only used with private scene streams + * and only carry the file name for the scene. Other internal stream types can be used in a real streaming environment +*/ +enum +{ + /*!OTI for BIFS v1*/ + GPAC_OTI_SCENE_BIFS = 0x01, + /*!OTI for OD v1*/ + GPAC_OTI_OD_V1 = 0x01, + /*!OTI for BIFS v2*/ + GPAC_OTI_SCENE_BIFS_V2 = 0x02, + /*!OTI for OD v2*/ + GPAC_OTI_OD_V2 = 0x02, + /*!OTI for BIFS InputSensor streams*/ + GPAC_OTI_SCENE_INTERACT = 0x03, + /*!OTI for streams with extended BIFS config*/ + GPAC_OTI_SCENE_BIFS_EXTENDED = 0x04, + /*!OTI for AFX streams with AFXConfig*/ + GPAC_OTI_SCENE_AFX = 0x05, + /*!OTI for Font data streams */ + GPAC_OTI_FONT = 0x06, + /*!OTI for synthesized texture streams */ + GPAC_OTI_SCENE_SYNTHESIZED_TEXTURE = 0x07, + /*!OTI for streaming text streams */ + GPAC_OTI_TEXT_MPEG4 = 0x08, + /*!OTI for LASeR streams*/ + GPAC_OTI_SCENE_LASER = 0x09, + /*!OTI for SAF streams*/ + GPAC_OTI_SCENE_SAF = 0x0A, + + /*!OTI for MPEG-4 Video Part 2 streams*/ + GPAC_OTI_VIDEO_MPEG4_PART2 = 0x20, + /*!OTI for MPEG-4 Video Part 10 (H.264 | AVC ) streams*/ + GPAC_OTI_VIDEO_AVC = 0x21, + /*!OTI for AVC Parameter sets streams*/ + GPAC_OTI_VIDEO_AVC_PS = 0x22, + /*!OTI for MPEG-4 AAC streams*/ + GPAC_OTI_AUDIO_AAC_MPEG4 = 0x40, + + /*!OTI for MPEG-2 Visual Simple Profile streams*/ + GPAC_OTI_VIDEO_MPEG2_SIMPLE = 0x60, + /*!OTI for MPEG-2 Visual Main Profile streams*/ + GPAC_OTI_VIDEO_MPEG2_MAIN = 0x61, + /*!OTI for MPEG-2 Visual SNR Profile streams*/ + GPAC_OTI_VIDEO_MPEG2_SNR = 0x62, + /*!OTI for MPEG-2 Visual SNR Profile streams*/ + GPAC_OTI_VIDEO_MPEG2_SPATIAL = 0x63, + /*!OTI for MPEG-2 Visual SNR Profile streams*/ + GPAC_OTI_VIDEO_MPEG2_HIGH = 0x64, + /*!OTI for MPEG-2 Visual SNR Profile streams*/ + GPAC_OTI_VIDEO_MPEG2_422 = 0x65, + + + /*!OTI for MPEG-2 AAC Main Profile streams*/ + GPAC_OTI_AUDIO_AAC_MPEG2_MP = 0x66, + /*!OTI for MPEG-2 AAC Low Complexity Profile streams*/ + GPAC_OTI_AUDIO_AAC_MPEG2_LCP = 0x67, + /*!OTI for MPEG-2 AAC Scaleable Sampling Rate Profile streams*/ + GPAC_OTI_AUDIO_AAC_MPEG2_SSRP = 0x68, + /*!OTI for MPEG-2 Audio Part 3 streams*/ + GPAC_OTI_AUDIO_MPEG2_PART3 = 0x69, + /*!OTI for MPEG-1 Video streams*/ + GPAC_OTI_VIDEO_MPEG1 = 0x6A, + /*!OTI for MPEG-1 Audio streams*/ + GPAC_OTI_AUDIO_MPEG1 = 0x6B, + /*!OTI for JPEG streams*/ + GPAC_OTI_IMAGE_JPEG = 0x6C, + /*!OTI for PNG streams*/ + GPAC_OTI_IMAGE_PNG = 0x6D, + /*!OTI for JPEG-2000 streams*/ + GPAC_OTI_IMAGE_JPEG_2000 = 0x6E, + +/*! + * \brief Extra ObjectTypeIndication + * + * ObjectTypeIndication for media (audio/video) codecs not defined in MPEG-4. Since GPAC signals streams through MPEG-4 Descriptions, + * it needs extensions for non-MPEG-4 streams such as AMR, H263 , etc.\n + *\note The decoder specific info for such streams is always carried encoded, with the following syntax:\n + * DSI Syntax for audio streams + \code + * u32 codec_four_cc: the codec 4CC reg code / codec id for ffmpeg + * u32 sample_rate: sampling rate or 0 if unknown + * u16 nb_channels: num channels or 0 if unknown + * u16 frame_size: num audio samples per frame or 0 if unknown + * u8 nb_bits_per_sample: nb bits or 0 if unknown + * u8 num_frames_per_au: num audio frames per AU (used in 3GPP, max 15), 0 if unknown + * char *data: per-codec extensions till end of DSI bitstream + \endcode + \n + * DSI Syntax for video streams + \code + * u32 codec_four_cc: the codec 4CC reg code / codec id for ffmpeg + * u16 width: video width or 0 if unknown + * u16 height: video height or 0 if unknown + * char *data: per-codec extensions till end of DSI bitstream + \endcode +*/ + GPAC_OTI_MEDIA_GENERIC = 0x80, +/*! + * \brief FFMPEG ObjectTypeIndication + * + * ObjectTypeIndication for FFMPEG codecs not defined in MPEG-4. FFMPEG uses the base GPAC_OTI_MEDIA_GENERIC specific info formats, and extends it as follows: + \code + * u32 bit_rate: the stream rate or 0 if unknown + * u32 codec_tag: FFMPEG codec tag as defined in libavcodec + * char *data: codec extensions till end of DSI bitstream + \endcode + */ + GPAC_OTI_MEDIA_FFMPEG = 0x81, + + /*!OTI for EVRC Voice streams*/ + GPAC_OTI_AUDIO_EVRC_VOICE = 0xA0, + /*!OTI for SMV Voice streams*/ + GPAC_OTI_AUDIO_SMV_VOICE = 0xA1, + /*!OTI for 3GPP2 CMF streams*/ + GPAC_OTI_3GPP2_CMF = 0xA2, + /*!OTI for SMPTE VC-1 Video streams*/ + GPAC_OTI_VIDEO_SMPTE_VC1 = 0xA3, + /*!OTI for Dirac Video streams*/ + GPAC_OTI_VIDEO_DIRAC = 0xA4, + /*!OTI for AC-3 audio streams*/ + GPAC_OTI_AUDIO_AC3 = 0xA5, + /*!OTI for enhanced AC-3 audio streams*/ + GPAC_OTI_AUDIO_AC3_ENHANCED = 0xA6, + /*!OTI for DRA audio streams*/ + GPAC_OTI_AUDIO_DRA = 0xA7, + /*!OTI for ITU G719 audio streams*/ + GPAC_OTI_AUDIO_ITU_G719 = 0xA8, + /*!OTI for DTS Coherent Acoustics audio streams*/ + GPAC_OTI_AUDIO_DTS_CA = 0xA9, + /*!OTI for DTS-HD High Resolution audio streams*/ + GPAC_OTI_AUDIO_DTS_HD_HR = 0xAA, + /*!OTI for DTS-HD Master audio streams*/ + GPAC_OTI_AUDIO_DTS_HD_MASTER = 0xAB, + + /*!OTI for dummy streams (dsi = file name) using the generic context loader (BIFS/VRML/SWF/...) - GPAC internal*/ + GPAC_OTI_PRIVATE_SCENE_GENERIC = 0xC0, + /*!OTI for SVG dummy stream (dsi = file name) - GPAC internal*/ + GPAC_OTI_PRIVATE_SCENE_SVG = 0xC1, + /*!OTI for LASeR/SAF+XML dummy stream (dsi = file name) - GPAC internal*/ + GPAC_OTI_PRIVATE_SCENE_LASER = 0xC2, + /*!OTI for XBL dummy streams (dsi = file name) - GPAC internal*/ + GPAC_OTI_PRIVATE_SCENE_XBL = 0xC3, + /*!OTI for EPG dummy streams (dsi = null) - GPAC internal*/ + GPAC_OTI_PRIVATE_SCENE_EPG = 0xC4, + /*!OTI for WGT dummy streams (dsi = null) - GPAC internal*/ + GPAC_OTI_PRIVATE_SCENE_WGT = 0xC5, + + /*!OTI for streaming SVG - GPAC internal*/ + GPAC_OTI_SCENE_SVG = 0xD0, + /*!OTI for streaming SVG + gz - GPAC internal*/ + GPAC_OTI_SCENE_SVG_GZ = 0xD1, + /*!OTI for DIMS (dsi = 3GPP DIMS configuration) - GPAC internal*/ + GPAC_OTI_SCENE_DIMS = 0xD2, + +/*! + * \brief OGG ObjectTypeIndication + * + * Object type indication for all OGG media. The DSI contains all intitialization ogg packets for the codec + * and is formated as follows:\n + *\code + while (dsi_size) { + bit(16) packet_size; + char packet[packet_size]; + dsi_size -= packet_size; + }\endcode +*/ + GPAC_OTI_MEDIA_OGG = 0xDD, + GPAC_OTI_MEDIA_THEORA = 0xDF, + + GPAC_OTI_MEDIA_SUBPIC = 0xE0, + + /*!OTI for 13K Voice / QCELP audio streams*/ + GPAC_OTI_AUDIO_13K_VOICE = 0xE1, + + /*!OTI for RAW media streams. Input data is directly dispatched to the composition memory. The DSI contains is formated (MSBF) as follows:\n + * DSI Syntax for audio streams + \code + * u32 sample_rate: sampling rate + * u16 nb_channels: num channels + * u16 nb_bits_per_sample: num of bits per audio sample + * u32 frame_size: max size of audio frame in byte + * u32 channel_config: GPAC mask of GF_AUDIO_CH_ constants, or 0 if unknown + \endcode + \n + * DSI Syntax for video streams + \code + * u32 codec_four_cc: the codec 4CC reg code / codec id for ffmpeg + * u16 width: video width or 0 if unknown + * u16 height: video height or 0 if unknown + * u32 frame_size: size of the video frame + * u32 stride: horizontal stride of the video frame + \endcode + */ + GPAC_OTI_RAW_MEDIA_STREAM = 0x101, + + /*!OTI for LIBPLAYER private streams. The data pointer in the DSI is the libplayer handle object*/ + GPAC_OTI_PRIVATE_MEDIA_LIBPLAYER = 0xF1 +}; + + +/*! + * \brief AFX Object Code +*/ +enum +{ + /*!3D Mesh Compression*/ + GPAC_AFX_3DMC = 0x00, + /*!Wavelet Subdivision Surface*/ + GPAC_AFX_WAVELET_SUBDIVISION = 0x01, + /*!MeshGrid*/ + GPAC_AFX_MESHGRID = 0x02, + /*!Coordinate Interpolator*/ + GPAC_AFX_COORDINATE_INTERPOLATOR = 0x03, + /*!Orientation Interpolator*/ + GPAC_AFX_ORIENTATION_INTERPOLATOR = 0x04, + /*!Position Interpolator*/ + GPAC_AFX_POSITION_INTERPOLATOR = 0x05, + /*!Octree Image*/ + GPAC_AFX_OCTREE_IMAGE = 0x06, + /*!BBA*/ + GPAC_AFX_BBA = 0x07, + /*!PointTexture*/ + GPAC_AFX_POINT_TEXTURE = 0x08, + /*!3DMC Extension*/ + GPAC_AFX_3DMC_EXT = 0x09, + /*!FootPrint representation*/ + GPAC_AFX_FOOTPRINT = 0x0A, + /*!Animated Mesh Compression*/ + GPAC_AFX_ANIMATED_MESH = 0x0B, + /*!Scalable Complexity*/ + GPAC_AFX_SCALABLE_COMPLEXITY = 0x0C, +}; + + +/*channel cfg flags - DECODERS MUST OUTPUT STEREO/MULTICHANNEL IN THIS ORDER*/ +/*! + * \brief Audio Channel Configuration + * + * Audio channel flags for spatialization. + \note Decoders must output stereo/multichannel audio channels in this order in the decoded audio frame. + */ +enum +{ + /*!Left Audio Channel*/ + GF_AUDIO_CH_FRONT_LEFT = (1), + /*!Right Audio Channel*/ + GF_AUDIO_CH_FRONT_RIGHT = (1<<1), + /*!Center Audio Channel - may also be used to signal monophonic audio*/ + GF_AUDIO_CH_FRONT_CENTER = (1<<2), + /*!LFE Audio Channel*/ + GF_AUDIO_CH_LFE = (1<<3), + /*!Back Left Audio Channel*/ + GF_AUDIO_CH_BACK_LEFT = (1<<4), + /*!Back Right Audio Channel*/ + GF_AUDIO_CH_BACK_RIGHT = (1<<5), + /*!Back Center Audio Channel*/ + GF_AUDIO_CH_BACK_CENTER = (1<<6), + /*!Side Left Audio Channel*/ + GF_AUDIO_CH_SIDE_LEFT = (1<<7), + /*!Side Right Audio Channel*/ + GF_AUDIO_CH_SIDE_RIGHT = (1<<8) +}; + + + +/*DIMS unit flags */ +/*! + * \brief DIMS Unit header flags + * + * DIMS Unit header flags as 3GPP TS 26.142. + */ +enum +{ + /*!S: is-Scene: DIMS unit contains a complete document (*/ + GF_DIMS_UNIT_S = 1, + /*!M: is-RAP: DIMS unit is a random access point*/ + GF_DIMS_UNIT_M = 1<<1, + /*!I: is-Redundant: DIMS unit is made of redundant data*/ + GF_DIMS_UNIT_I = 1<<2, + /*!D: redundant-exit: DIMS unit is the end of redundant data*/ + GF_DIMS_UNIT_D = 1<<3, + /*!P: priority: DIMS unit is high priority*/ + GF_DIMS_UNIT_P = 1<<4, + /*!C: compressed: DIMS unit is compressed*/ + GF_DIMS_UNIT_C = 1<<5 +}; + + +/*! + \cond DUMMY_DOXY_SECTION +*/ + +/*AVC NAL unit types*/ +#define GF_AVC_NALU_NON_IDR_SLICE 1 +#define GF_AVC_NALU_DP_A_SLICE 2 +#define GF_AVC_NALU_DP_B_SLICE 3 +#define GF_AVC_NALU_DP_C_SLICE 4 +#define GF_AVC_NALU_IDR_SLICE 5 +#define GF_AVC_NALU_SEI 6 +#define GF_AVC_NALU_SEQ_PARAM 7 +#define GF_AVC_NALU_PIC_PARAM 8 +#define GF_AVC_NALU_ACCESS_UNIT 9 +#define GF_AVC_NALU_END_OF_SEQ 10 +#define GF_AVC_NALU_END_OF_STREAM 11 +#define GF_AVC_NALU_FILLER_DATA 12 +#define GF_AVC_NALU_SEQ_PARAM_EXT 13 + +#define GF_AVC_NALU_SVC_PREFIX_NALU 14 +#define GF_AVC_NALU_SVC_SUBSEQ_PARAM 15 +#define GF_AVC_NALU_SLICE_AUX 19 + +#define GF_AVC_NALU_SVC_SLICE 0x14 + +/*#define GF_SVC_NALU_SLICE 0x14 +#define GF_SVC_NALU_NAL_EXT_PARAM 14 +#define GF_SVC_NALU_SEQ_EXT_PARAM 15*/ + +#define GF_AVC_TYPE_P 0 +#define GF_AVC_TYPE_B 1 +#define GF_AVC_TYPE_I 2 +#define GF_AVC_TYPE_SP 3 +#define GF_AVC_TYPE_SI 4 +#define GF_AVC_TYPE2_P 5 +#define GF_AVC_TYPE2_B 6 +#define GF_AVC_TYPE2_I 7 +#define GF_AVC_TYPE2_SP 8 +#define GF_AVC_TYPE2_SI 9 + + +/*rate sizes - note that these sizes INCLUDE the rate_type header byte*/ +static const unsigned int GF_QCELP_RATE_TO_SIZE [] = {0, 1, 1, 4, 2, 8, 3, 17, 4, 35, 5, 8, 14, 1}; +static const unsigned int GF_QCELP_RATE_TO_SIZE_NB = 7; +static const unsigned int GF_SMV_EVRC_RATE_TO_SIZE [] = {0, 1, 1, 3, 2, 6, 3, 11, 4, 23, 5, 1}; +static const unsigned int GF_SMV_EVRC_RATE_TO_SIZE_NB = 6; +static const unsigned int GF_AMR_FRAME_SIZE[16] = { 12, 13, 15, 17, 19, 20, 26, 31, 5, 0, 0, 0, 0, 0, 0, 0 }; +static const unsigned int GF_AMR_WB_FRAME_SIZE[16] = { 17, 23, 32, 36, 40, 46, 50, 58, 60, 5, 5, 0, 0, 0, 0, 0 }; + + +/*out-of-band sample desc (128 and 255 reserved in RFC)*/ +#define GF_RTP_TX3G_SIDX_OFFSET 129 + +/*! + \endcond +*/ + + +/*! @} */ + +#ifdef __cplusplus +} +#endif + +#endif /*_GF_CONSTANTS_H_*/ diff --git a/src/gpacmp4/gpac/ietf.h b/src/gpacmp4/gpac/ietf.h new file mode 100644 index 00000000..78180520 --- /dev/null +++ b/src/gpacmp4/gpac/ietf.h @@ -0,0 +1,1332 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / IETF RTP/RTSP/SDP sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +#ifndef _GF_IETF_H_ +#define _GF_IETF_H_ + + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#ifndef GPAC_DISABLE_STREAMING + +#include +#include +#include + + +/**************************************************************************** + + RTSP VERSION 1.0 LIBRARY EXPORTS + +****************************************************************************/ + +#define GF_RTSP_VERSION "RTSP/1.0" + + +/* + * RTSP NOTIF CODES + */ +enum +{ + NC_RTSP_Continue = 100, + NC_RTSP_OK = 200, + NC_RTSP_Created = 201, + NC_RTSP_Low_on_Storage_Space = 250, + + NC_RTSP_Multiple_Choice = 300, + NC_RTSP_Moved_Permanently = 301, + NC_RTSP_Moved_Temporarily = 302, + NC_RTSP_See_Other = 303, + NC_RTSP_Use_Proxy = 305, + + NC_RTSP_Bad_Request = 400, + NC_RTSP_Unauthorized = 401, + NC_RTSP_Payment_Required = 402, + NC_RTSP_Forbidden = 403, + NC_RTSP_Not_Found = 404, + NC_RTSP_Method_Not_Allowed = 405, + NC_RTSP_Not_Acceptable = 406, + NC_RTSP_Proxy_Authentication_Required = 407, + NC_RTSP_Request_Timeout = 408, + NC_RTSP_Gone = 410, + NC_RTSP_Length_Required = 411, + NC_RTSP_Precondition_Failed = 412, + NC_RTSP_Request_Entity_Too_Large = 413, + NC_RTSP_Request_URI_Too_Long = 414, + NC_RTSP_Unsupported_Media_Type = 415, + + NC_RTSP_Invalid_parameter = 451, + NC_RTSP_Illegal_Conference_Identifier = 452, + NC_RTSP_Not_Enough_Bandwidth = 453, + NC_RTSP_Session_Not_Found = 454, + NC_RTSP_Method_Not_Valid_In_This_State = 455, + NC_RTSP_Header_Field_Not_Valid = 456, + NC_RTSP_Invalid_Range = 457, + NC_RTSP_Parameter_Is_ReadOnly = 458, + NC_RTSP_Aggregate_Operation_Not_Allowed = 459, + NC_RTSP_Only_Aggregate_Operation_Allowed = 460, + NC_RTSP_Unsupported_Transport = 461, + NC_RTSP_Destination_Unreachable = 462, + + NC_RTSP_Internal_Server_Error = 500, + NC_RTSP_Not_Implemented = 501, + NC_RTSP_Bad_Gateway = 502, + NC_RTSP_Service_Unavailable = 503, + NC_RTSP_Gateway_Timeout = 504, + NC_RTSP_RTSP_Version_Not_Supported = 505, + + NC_RTSP_Option_not_support = 551, +}; + +const char *gf_rtsp_nc_to_string(u32 ErrCode); + +/* + Common structures between commands and responses +*/ + +/* + RTSP Range information - RTSP Session level only (though this is almost the same + format as an SDP range, this is not used in the SDP lib as "a=range" is not part of SDP + but part of RTSP +*/ +typedef struct { + /* start and end range. If end is -1, the range is open (from start to unknown) */ + Double start, end; + /* use SMPTE range (Start and End specify the number of frames) (currently not supported) */ + u32 UseSMPTE; + /* framerate for SMPTE range */ + Double FPS; +} GF_RTSPRange; + +/* +parses a Range line and returns range header structure. can be used for RTSP extension of SDP +NB: Only support for npt for now +*/ +GF_RTSPRange *gf_rtsp_range_parse(char *range_buf); + +GF_RTSPRange *gf_rtsp_range_new(); +void gf_rtsp_range_del(GF_RTSPRange *range); + +/* + Transport structure + contains all network info for RTSP sessions (ports, uni/multi-cast, ...) +*/ + +/* + Transport Profiles as defined in RFC 2326 +*/ +#define GF_RTSP_PROFILE_RTP_AVP "RTP/AVP" +#define GF_RTSP_PROFILE_RTP_AVP_TCP "RTP/AVP/TCP" +#define GF_RTSP_PROFILE_UDP "udp" + + +typedef struct +{ + /* set to 1 if unicast */ + Bool IsUnicast; + /* for multicast */ + char *destination; + /* for redirections internal to servers */ + char *source; + /*IsRecord is usually 0 (PLAY) . If set, Append specify that the stream should + be concatenated to existing resources */ + Bool IsRecord, Append; + /* in case transport is on TCP/RTSP, If only 1 ID is specified, it is stored in rtpID (this + is not RTP interleaving) */ + Bool IsInterleaved, rtpID, rtcpID; + /* Multicast specific */ + u32 MulticastLayers; + u8 TTL; + /*RTP specific*/ + + /*port for multicast*/ + /*server port in unicast - RTP implies low is even , and last is low+1*/ + u16 port_first, port_last; + /*client port in unicast - RTP implies low is even , and last is low+1*/ + u16 client_port_first, client_port_last; + u32 SSRC; + + /*Transport protocol. In this version we only support RTP/AVP, the following flag tells + us if this is RTP/AVP/TCP or RTP/AVP (default)*/ + char *Profile; +} GF_RTSPTransport; + + +GF_RTSPTransport *gf_rtsp_transport_clone(GF_RTSPTransport *original); +void gf_rtsp_transport_del(GF_RTSPTransport *transp); + + +/* + RTSP Command + the RTSP Response is sent by a client / received by a server + text Allocation is done by the lib when parsing a command, and + is automatically freed when calling reset / delete. Therefore you must + set/allocate the fields yourself when writing a command (client) + +*/ + +/*ALL RTSP METHODS - all other methods will be ignored*/ +#define GF_RTSP_DESCRIBE "DESCRIBE" +#define GF_RTSP_SETUP "SETUP" +#define GF_RTSP_PLAY "PLAY" +#define GF_RTSP_PAUSE "PAUSE" +#define GF_RTSP_RECORD "RECORD" +#define GF_RTSP_TEARDOWN "TEARDOWN" +#define GF_RTSP_GET_PARAMETER "GET_PARAMETER" +#define GF_RTSP_SET_PARAMETER "SET_PARAMETER" +#define GF_RTSP_OPTIONS "OPTIONS" +#define GF_RTSP_ANNOUNCE "ANNOUNCE" +#define GF_RTSP_REDIRECTE "REDIRECT" + + +typedef struct +{ + char *Accept; + char *Accept_Encoding; + char *Accept_Language; + char *Authorization; + u32 Bandwidth; + u32 Blocksize; + char *Cache_Control; + char *Conference; + char *Connection; + u32 Content_Length; + u32 CSeq; + char *From; + char *Proxy_Authorization; + char *Proxy_Require; + GF_RTSPRange *Range; + char *Referer; + Double Scale; + char *Session; + Double Speed; + /*nota : RTSP allows several configurations for a single channel (multicast and + unicast , ...). Usually only 1*/ + GF_List *Transports; + char *User_Agent; + + /*type of the command, one of the described above*/ + char *method; + + /*Header extensions*/ + GF_List *Xtensions; + + /*body of the command, size is Content-Length (auto computed when sent). It is not + terminated by a NULL char*/ + char *body; + + /* + Specify ControlString if your request targets + a specific media stream in the service. If null, the service name only will be used + for control (for ex, both A and V streams in a single file) + If the request is GF_RTSP_OPTIONS, you must provide a control string containing the options + you want to query + */ + char *ControlString; + + /*user data: this is never touched by the lib, its intend is to help stacking + RTSP commands in your app*/ + void *user_data; + + + /* + Server side Extensions + */ + + /*full URL of the command. Not used at client side, as the URL is ALWAYS relative + to the server / service of the RTSP session + On the server side however redirections are up to the server, so we cannot decide for it */ + char *service_name; + /*RTSP status code of the command as parsed. One of the above RTSP StatusCode*/ + u32 StatusCode; +} GF_RTSPCommand; + + +GF_RTSPCommand *gf_rtsp_command_new(); +void gf_rtsp_command_del(GF_RTSPCommand *com); +void gf_rtsp_command_reset(GF_RTSPCommand *com); + + + +/* + RTSP Response + the RTSP Response is received by a client / sent by a server + text Allocation is done by the lib when parsing a response, and + is automatically freed when calling reset / delete. Therefore you must + allocate the fields yourself when writing a response (server) + +*/ + +/* + RTP-Info for RTP channels. There may be several RTP-Infos in one response + based on the server implementation (DSS/QTSS begaves this way) +*/ +typedef struct +{ + /*control string of the channel*/ + char *url; + /*seq num for asociated rtp_time*/ + u32 seq; + /*rtp TimeStamp corresponding to the Range start specified in the PLAY request*/ + u32 rtp_time; + /*ssrc of sender if known, 0 otherwise*/ + u32 ssrc; +} GF_RTPInfo; + + + +/* + RTSP Response +*/ +typedef struct +{ + /* response code*/ + u32 ResponseCode; + /* comment from the server */ + char *ResponseInfo; + + /* Header Fields */ + char *Accept; + char *Accept_Encoding; + char *Accept_Language; + char *Allow; + char *Authorization; + u32 Bandwidth; + u32 Blocksize; + char *Cache_Control; + char *Conference; + char *Connection; + char *Content_Base; + char *Content_Encoding; + char *Content_Language; + u32 Content_Length; + char *Content_Location; + char *Content_Type; + u32 CSeq; + char *Date; + char *Expires; + char *From; + char *Host; + char *If_Match; + char *If_Modified_Since; + char *Last_Modified; + char *Location; + char *Proxy_Authenticate; + char *Proxy_Require; + char *Public; + GF_RTSPRange *Range; + char *Referer; + char *Require; + char *Retry_After; + GF_List *RTP_Infos; + Double Scale; + char *Server; + char *Session; + u32 SessionTimeOut; + Double Speed; + char *Timestamp; + /*nota : RTSP allows several configurations for a single channel (multicast and + unicast , ...). Usually only 1*/ + GF_List *Transports; + char *Unsupported; + char *User_Agent; + char *Vary; + char *Via; + char *WWW_Authenticate; + + /*Header extensions*/ + GF_List *Xtensions; + + /*body of the response, size is Content-Length (auto computed when sent). It is not + terminated by a NULL char when response is parsed but must be null-terminated when + response is being sent*/ + char *body; +} GF_RTSPResponse; + + +GF_RTSPResponse *gf_rtsp_response_new(); +void gf_rtsp_response_del(GF_RTSPResponse *rsp); +void gf_rtsp_response_reset(GF_RTSPResponse *rsp); + + + +typedef struct _tag_rtsp_session GF_RTSPSession; + +GF_RTSPSession *gf_rtsp_session_new(char *sURL, u16 DefaultPort); +void gf_rtsp_session_del(GF_RTSPSession *sess); + +GF_Err gf_rtsp_set_buffer_size(GF_RTSPSession *sess, u32 BufferSize); + +/*force the IP address the client is using*/ +void gf_rtsp_set_mobile_ip(GF_RTSPSession *sess, char *MobileIP); + + +/*Reset state machine, invalidate SessionID +NOTE: RFC2326 requires that the session is reseted when all RTP streams +are closed. As this lib doesn't maintain the number of valid streams +you MUST call reset when all your streams are shutdown (either requested through +TEARDOWN or signaled through RTCP BYE packets for RTP, or any other signaling means +for other protocols) +reset connection will destroy the socket - this is isefull in case of timeouts, because +some servers do not restart with the right CSeq...*/ +void gf_rtsp_session_reset(GF_RTSPSession *sess, Bool ResetConnection); + +u32 gf_rtsp_is_my_session(GF_RTSPSession *sess, char *url); +const char *gf_rtsp_get_last_session_id(GF_RTSPSession *sess); +char *gf_rtsp_get_server_name(GF_RTSPSession *sess); +char *gf_rtsp_get_service_name(GF_RTSPSession *sess); +u16 gf_rtsp_get_session_port(GF_RTSPSession *sess); + +/*Fetch an RTSP response from the server the GF_RTSPResponse will be reseted before fetch*/ +GF_Err gf_rtsp_get_response(GF_RTSPSession *sess, GF_RTSPResponse *rsp); + + +/*RTSP State Machine. The only non blocking mode is GF_RTSP_STATE_WAIT_FOR_CONTROL*/ +enum +{ + /*Initialized (connection might be off, but all structures are in place) + This is the default state between # requests (aka, DESCRIBE and SETUP + or SETUP and PLAY ...)*/ + GF_RTSP_STATE_INIT = 0, + /*Waiting*/ + GF_RTSP_STATE_WAITING, + /*PLAY, PAUSE, RECORD. Aggregation is allowed for the same type, you can send several command + in a row. However the session will return GF_SERVICE_ERROR if you do not have + a valid SessionID in the command + You cannot issue a SETUP / DESCRIBE while in this state*/ + GF_RTSP_STATE_WAIT_FOR_CONTROL, + + /*FATAL ERROR: session is invalidated by server. Call reset and restart from SETUP if needed*/ + GF_RTSP_STATE_INVALIDATED +}; + +u32 gf_rtsp_get_session_state(GF_RTSPSession *sess); +/*aggregate command state-machine: the PLAY/PAUSE can be aggregated +(sent before the reply is received). This function gets the last command sent*/ +char *gf_rtsp_get_last_request(GF_RTSPSession *sess); +/*foce a reset in case of pbs*/ +void gf_rtsp_reset_aggregation(GF_RTSPSession *sess); + +/* + Send an RTSP request to the server. +*/ +GF_Err gf_rtsp_send_command(GF_RTSPSession *sess, GF_RTSPCommand *com); + + +GF_Err gf_rtsp_set_interleave_callback(GF_RTSPSession *sess, + GF_Err (*SignalData)(GF_RTSPSession *sess, void *cbk_ptr, char *buffer, u32 bufferSize, Bool IsRTCP) + ); + + +GF_Err gf_rtsp_session_read(GF_RTSPSession *sess); + +GF_Err gf_rtsp_register_interleave(GF_RTSPSession *sess, void *the_ch, u8 LowInterID, u8 HighInterID); +u32 gf_rtsp_unregister_interleave(GF_RTSPSession *sess, u8 LowInterID); + + + +/* + Server side session constructor + create a new RTSP session from an existing socket in listen state. If no pending connection + is detected, return NULL +*/ +GF_RTSPSession *gf_rtsp_session_new_server(GF_Socket *rtsp_listener); + +/*fetch an RTSP request. The GF_RTSPCommand will be reseted before fetch*/ +GF_Err gf_rtsp_get_command(GF_RTSPSession *sess, GF_RTSPCommand *com); + +/*unpack the URL, check the service name / server. Typically used when a client sends a + DESCRIBE || SETUP url RTSP/1.0. Server / service name check must be performed by your app as redirection +or services available are unknown here.*/ +GF_Err gf_rtsp_load_service_name(GF_RTSPSession *sess, char *URL); + +/*geenrates a session ID fpor the given session*/ +char *gf_rtsp_generate_session_id(GF_RTSPSession *sess); + +/*send the RTSP response*/ +GF_Err gf_rtsp_send_response(GF_RTSPSession *sess, GF_RTSPResponse *rsp); + +/*gets the IP address of the local host running the session +buffer shall be GF_MAX_IP_NAME_LEN long*/ +GF_Err gf_rtsp_get_session_ip(GF_RTSPSession *sess, char *buffer); + +/*returns the next available ID for interleaving. It is recommended that you use 2 +consecutive IDs for RTP/RTCP interleaving*/ +u8 gf_rtsp_get_next_interleave_id(GF_RTSPSession *sess); + +/*gets the IP address of the connected peer - buffer shall be GF_MAX_IP_NAME_LEN long*/ +GF_Err gf_rtsp_get_remote_address(GF_RTSPSession *sess, char *buffer); + + +/* + RTP LIB EXPORTS +*/ + + +typedef struct tagRTP_HEADER { + /*version, must be 2*/ + u8 Version; + /*padding bits in the payload*/ + u8 Padding; + /*header extension is defined*/ + u8 Extension; + /*number of CSRC (<=15)*/ + u8 CSRCCount; + /*Marker Bit*/ + u8 Marker; + /*payload type on 7 bits*/ + u8 PayloadType; + /*packet seq number*/ + u16 SequenceNumber; + /*packet time stamp*/ + u32 TimeStamp; + /*sync source identifier*/ + u32 SSRC; + /*in our basic client, CSRC should always be NULL*/ + u32 CSRC[16]; +} GF_RTPHeader; + + + + +/* + structure containing the rtpmap information +*/ +typedef struct +{ + /*dynamic payload type of this map*/ + u32 PayloadType; + /*registered payload name of this map*/ + char *payload_name; + /*RTP clock rate (TS resolution) of this map*/ + u32 ClockRate; + /*optional parameters for audio, specifying number of channels. Unused for other media types.*/ + u32 AudioChannels; +} GF_RTPMap; + + + +typedef struct __tag_rtp_channel GF_RTPChannel; + +GF_RTPChannel *gf_rtp_new(); +void gf_rtp_del(GF_RTPChannel *ch); + +/*you configure a server channel through the transport structure, with the same info as a +client channel, the client_port_* info designing the REMOTE client and port_* designing +your server channel*/ +GF_Err gf_rtp_setup_transport(GF_RTPChannel *ch, GF_RTSPTransport *trans_info, const char *remote_address); + +/*auto-setup of rtp/rtcp transport ports - only effective in unicast, non interleaved cases. +for multicast port setup MUST be done through the above gf_rtp_setup_transport function +this will take care of port reuse*/ +GF_Err gf_rtp_set_ports(GF_RTPChannel *ch, u16 first_port); + +/*init of payload information. only ONE payload per sync source is supported in this +version of the library (a sender cannot switch payload types on a single media)*/ +GF_Err gf_rtp_setup_payload(GF_RTPChannel *ch, GF_RTPMap *map); + +/*enables sending of NAT keep-alive packets for NAT traversal + @nat_timeout: specifies the inactivity period in ms after which NAT keepalive packets are sent. + If 0, disables NAT keep-alive packets +*/ +void gf_rtp_enable_nat_keepalive(GF_RTPChannel *ch, u32 nat_timeout); + + +/*initialize the RTP channel. + +UDPBufferSize: UDP stack buffer size if configurable by OS/ISP - ignored otherwise +NOTE: on WinCE devices, this is not configurable on an app bases but for the whole OS +you must update the device registry with: + [HKEY_LOCAL_MACHINE\Comm\Afd] + DgramBuffer=dword:N + + where N is the number of UDP datagrams a socket should be able to buffer. For multimedia +app you should set N as large as possible. The device MUST be reseted for the param to take effect + +ReorederingSize: max number of packets to queue for reordering. 0 means no reordering +MaxReorderDelay: max time to wait in ms before releasing first packet in reoderer when only one packet is present. +If 0 and reordering size is specified, defaults to 200 ms (usually enough). +IsSource: if true, the channel is a sender (media data, sender report, Receiver report processing) +if source, you must specify the Path MTU size. The RTP lib won't send any packet bigger than this size +your application shall perform payload size splitting if needed +local_interface_ip: local interface address to use for multicast. If NULL, default address is used +*/ +GF_Err gf_rtp_initialize(GF_RTPChannel *ch, u32 UDPBufferSize, Bool IsSource, u32 PathMTU, u32 ReorederingSize, u32 MaxReorderDelay, char *local_interface_ip); + +/*init the RTP info after a PLAY or PAUSE, rtp_time is the rtp TimeStamp of the RTP packet +with seq_num sequence number. This info is needed to compute the CurrentTime of the RTP channel +ssrc may not be known if sender hasn't indicated it (use 0 then)*/ +GF_Err gf_rtp_set_info_rtp(GF_RTPChannel *ch, u32 seq_num, u32 rtp_time, u32 ssrc); + +/*retrieve current RTP time in sec. If rtp_time was unknown (not on demand media) the time is absolute. +Otherwise this is the time in ms elapsed since the last PLAY range start value +Not supported yet if played without RTSP (aka RTCP time not supported)*/ +Double gf_rtp_get_current_time(GF_RTPChannel *ch); + + +void gf_rtp_reset_buffers(GF_RTPChannel *ch); + +/*read any data on UDP only (not valid for TCP). Performs re-ordering if configured for it +returns amount of data read (raw UDP packet size)*/ +u32 gf_rtp_read_rtp(GF_RTPChannel *ch, char *buffer, u32 buffer_size); +u32 gf_rtp_read_rtcp(GF_RTPChannel *ch, char *buffer, u32 buffer_size); + +/*decodes an RTP packet and gets the begining of the RTP payload*/ +GF_Err gf_rtp_decode_rtp(GF_RTPChannel *ch, char *pck, u32 pck_size, GF_RTPHeader *rtp_hdr, u32 *PayloadStart); + +/*decodes an RTCP packet and update timing info, send RR too*/ +GF_Err gf_rtp_decode_rtcp(GF_RTPChannel *ch, char *pck, u32 pck_size, Bool *has_sr); + +/*computes and send Receiver report. If the channel is a TCP channel, you must specify +the callback function. NOTE: many RTP implementation do NOT process RTCP info received on TCP... +the lib will decide whether the report shall be sent or not, therefore you should call +this function at regular times*/ +GF_Err gf_rtp_send_rtcp_report(GF_RTPChannel *ch, + GF_Err (*RTP_TCPCallback)(void *cbk, char *pck, u32 pck_size), + void *rtsp_cbk); + +/*send a BYE info (leaving the session)*/ +GF_Err gf_rtp_send_bye(GF_RTPChannel *ch, + GF_Err (*RTP_TCPCallback)(void *cbk, char *pck, u32 pck_size), + void *rtsp_cbk); + + +/*send RTP packet. In fast_send mode, user passes a pck pointer with 12 bytes available BEFORE pck to +write the header in place*/ +GF_Err gf_rtp_send_packet(GF_RTPChannel *ch, GF_RTPHeader *rtp_hdr, char *pck, u32 pck_size, Bool fast_send); + +enum +{ + GF_RTCP_INFO_NAME = 0, + GF_RTCP_INFO_EMAIL, + GF_RTCP_INFO_PHONE, + GF_RTCP_INFO_LOCATION, + GF_RTCP_INFO_TOOL, + GF_RTCP_INFO_NOTE, + GF_RTCP_INFO_PRIV +}; + +/*sets RTCP info sent in RTCP reports. info_string shall NOT exceed 255 chars*/ +GF_Err gf_rtp_set_info_rtcp(GF_RTPChannel *ch, u32 InfoCode, char *info_string); + +u32 gf_rtp_is_unicast(GF_RTPChannel *ch); +u32 gf_rtp_is_interleaved(GF_RTPChannel *ch); +u32 gf_rtp_get_clockrate(GF_RTPChannel *ch); +u32 gf_rtp_is_active(GF_RTPChannel *ch); +u8 gf_rtp_get_low_interleave_id(GF_RTPChannel *ch); +u8 gf_rtp_get_hight_interleave_id(GF_RTPChannel *ch); +GF_RTSPTransport *gf_rtp_get_transport(GF_RTPChannel *ch); +u32 gf_rtp_get_local_ssrc(GF_RTPChannel *ch); + +Float gf_rtp_get_loss(GF_RTPChannel *ch); +u32 gf_rtp_get_tcp_bytes_sent(GF_RTPChannel *ch); +void gf_rtp_get_ports(GF_RTPChannel *ch, u16 *rtp_port, u16 *rtcp_port); + + + + + +/**************************************************************************** + + SDP LIBRARY EXPORTS + + Note: SDP is mainly a text protocol with + well defined containers. The following structures are used to write / read + SDP informations, and the library also provides consistency checking + + When reading SDP, all text items/structures are allocated by the lib, and you + must call gf_sdp_info_reset(GF_SDPInfo *sdp) or gf_sdp_info_del(GF_SDPInfo *sdp) to release the memory + + When writing the SDP from a GF_SDPInfo, the output buffer is allocated by the library, + and you must release it yourself + + Some quick constructors are available for GF_SDPConnection and GF_SDPMedia in order to set up + some specific parameters to their default value + + An extra function gf_sdp_info_check(GF_SDPInfo *sdp) is provided for compliency check + with RFC2327: all requested fields are checked as well as conflicting information forbidden + in RFC 2327 +****************************************************************************/ + +/* + All attributes x-ZZZZ are considered as extensions attributes. If no "x-" is found + the attributes in the RTSP response is SKIPPED. The "x-" radical is removed in the structure + when parsing commands / responses +*/ +typedef struct +{ + char *Name; + char *Value; +} GF_X_Attribute; + + +/* + Structure for bandwidth info +*/ +typedef struct +{ + /*"CT", "AS" are defined. Private extensions must be "X-*" ( * "are recommended to be short")*/ + char *name; + /*in kBitsPerSec*/ + u32 value; +} GF_SDPBandwidth; + +/* + Structure for Time info +*/ +/*we do not support more than ... time offsets / zone adjustment +if more are needed, RFC recommends to use several entries rather than a big*/ +#define GF_SDP_MAX_TIMEOFFSET 10 + +typedef struct +{ + /*NPT time in sec*/ + u32 StartTime; + /*if 0, session is unbound. NPT time in sec*/ + u32 StopTime; + /*if 0 session is not repeated. Expressed in sec. + Session is signaled repeated every repeatInterval*/ + u32 RepeatInterval; + /*active duration of the session in sec*/ + u32 ActiveDuration; + + /*time offsets to use with repeat. Specify a non-regular repeat time from the Start time*/ + u32 OffsetFromStart[GF_SDP_MAX_TIMEOFFSET]; + /*Number of offsets*/ + u32 NbRepeatOffsets; + + /*EX of repeat: + a session happens 3 times a week, on mon 1PM, thu 3PM and fri 10AM + 1- StartTime should be NPT for the session on the very first monday, StopTime + the end of this session + 2- the repeatInterval should be 1 week, ActiveDuration the length of the session + 3- 3 offsets: 0 (for monday) (3*24+2)*3600 for thu and (4*24-3) for fri + */ + + + /*timezone adjustments, to cope with #timezones, daylight saving countries and co ... + Ex: adjTime = [2882844526 2898848070] adjOffset=[-1h 0] + [0]: at 2882844526 the time base by which the session's repeat times are calculated + is shifted back by 1 hour + [1]: at time 2898848070 the session's original time base is restored + */ + + /*Adjustment time at which the corresponding time offset is to be applied to the + session time line (time used to compute the "repeat session"). + All Expressed in NPT*/ + u32 AdjustmentTime[GF_SDP_MAX_TIMEOFFSET]; + /* Offset with the session time line, ALWAYS ABSOLUTE OFFSET TO the specified StartTime*/ + s32 AdjustmentOffset[GF_SDP_MAX_TIMEOFFSET]; + /*Number of offsets.*/ + u32 NbZoneOffsets; +} GF_SDPTiming; + + +typedef struct +{ + /*only "IN" currently defined*/ + char *net_type; + /*"IP4","IP6"*/ + char *add_type; + /*hex IPv6 address or doted IPv4 address*/ + char *host; + /*TTL - MUST BE PRESENT if IP is multicast - -1 otherwise*/ + s32 TTL; + /*multiple address counts - ONLY in media descriptions if needed. This + is used for content scaling, when # quality of the same media are multicasted on + # IP addresses*/ + u32 add_count; +} GF_SDPConnection; + +/* + FMTP: description of dynamic payload types. This is opaque at the SDP level. + Each attributes is assumed to be formatted as + If not the case the attribute will have an empty value string and only the + parameter name. +*/ +typedef struct +{ + /*payload type of the format described*/ + u32 PayloadType; + /*list of GF_X_Attribute elements. The Value field may be NULL*/ + GF_List *Attributes; +} GF_SDP_FMTP; + +typedef struct +{ + /*m= + 0: application - 1:video - 2: audio - 3: text - 4:data - 5: control*/ + u32 Type; + /*Port Number - For transports based on UDP, the value should be in the range 1024 + to 65535 inclusive. For RTP compliance it should be an even number*/ + u32 PortNumber; + /*number of ports described. If >= 2, the next media(s) in the SDP will be configured + to use the next tuple (for RTP). If 0 or 1, ignored + Note: this is used for scalable media: PortNumber indicates the port of the base + media and NumPorts the ports||total number of the upper layers*/ + u32 NumPorts; + /*currently ony "RTP/AVP" and "udp" defined*/ + char *Profile; + + /*list of GF_SDPConnection's. A media can have several connection in case of scalable content*/ + GF_List *Connections; + + /*RTPMaps contains a list SDPRTPMaps*/ + GF_List *RTPMaps; + + /*FMTP contains a list of FMTP structures*/ + GF_List *FMTP; + + /*for RTP this is PayloadType, but can be opaque (string) depending on the app. + Formated as XX WW QQ FF + When reading the SDP, the payloads defined in RTPMap are removed from this list + When writing the SDP for RTP, you should only specify static payload types here, + as dynamic ones are stored in RTPMaps and automatically written*/ + char *fmt_list; + + /*all attributes not defined in RFC 2327 for the media*/ + GF_List *Attributes; + + /*Other SDP attributes for media desc*/ + + /*k= + method is 'clear' (key follows), 'base64' (key in base64), 'uri' (key is the URI) + or 'prompt' (key not included)*/ + char *k_method, *k_key; + + GF_List *Bandwidths; + + /*0 if not present*/ + u32 PacketTime; + /*0: none - 1: recv, 2: send, 3 both*/ + u32 SendReceive; + char *orientation, *sdplang, *lang; + /*for video only, 0.0 if not present*/ + Double FrameRate; + /*between 0 and 10, -1 if not present*/ + s32 Quality; +} GF_SDPMedia; + +typedef struct +{ + /*v=*/ + u32 Version; + /*o=*/ + char *o_username, *o_session_id, *o_version, *o_address; + /*"IN" for Net, "IP4" or "IP6" for address are currently valid*/ + char *o_net_type, *o_add_type; + + /*s=*/ + char *s_session_name; + /*i=*/ + char *i_description; + /*u=*/ + char *u_uri; + /*e=*/ + char *e_email; + /*p=*/ + char *p_phone; + /*c= either 1 or 0 GF_SDPConnection */ + GF_SDPConnection *c_connection; + /*b=*/ + GF_List *b_bandwidth; + /*All time info (t, r, z)*/ + GF_List *Timing; + /*k= + method is 'clear' (key follows), 'base64' (key in base64), 'uri' (key is the URI) + or 'prompt' (key not included)*/ + char *k_method, *k_key; + /*all possible attributes (a=), session level*/ + char *a_cat, *a_keywds, *a_tool; + /*0: none, 1: recv, 2: send, 3 both*/ + u32 a_SendReceive; + /*should be `broadcast', `meeting', `moderated', `test' or `H332'*/ + char *a_type; + char *a_charset; + char *a_sdplang, *a_lang; + + /*all attributes not defined in RFC 2327 for the presentation*/ + GF_List *Attributes; + + /*list of media in the SDP*/ + GF_List *media_desc; +} GF_SDPInfo; + + +/* + Memory Consideration: the destructors free all non-NULL string. You should therefore + be carefull while (de-)assigning the strings. The function gf_sdp_info_parse() performs a complete + reset of the GF_SDPInfo +*/ +/*constructor*/ +GF_SDPInfo *gf_sdp_info_new(); +/*destructor*/ +void gf_sdp_info_del(GF_SDPInfo *sdp); +/*reset all structures (destroys substructure too)*/ +void gf_sdp_info_reset(GF_SDPInfo *sdp); +/*Parses a memory SDP buffer*/ +GF_Err gf_sdp_info_parse(GF_SDPInfo *sdp, char *sdp_text, u32 text_size); +/*check the consistency of the GF_SDPInfo*/ +GF_Err gf_sdp_info_check(GF_SDPInfo *sdp); +/*write the SDP to a new buffer and returns it. Automatically checks the SDP before calling*/ +GF_Err gf_sdp_info_write(GF_SDPInfo *sdp, char **out_str_buf); + + +/* + Const/dest for GF_SDPMedia +*/ +GF_SDPMedia *gf_sdp_media_new(); +void gf_sdp_media_del(GF_SDPMedia *media); + +/* + Const/dest for GF_SDPConnection +*/ +GF_SDPConnection *gf_sdp_conn_new(); +void gf_sdp_conn_del(GF_SDPConnection *conn); + +/* + Const/dest for SDP FMTP +*/ +GF_SDP_FMTP *gf_sdp_fmtp_new(); +void gf_sdp_fmtp_del(GF_SDP_FMTP *fmtp); + + + +/* + RTP packetizer +*/ + + +/*RTP<->SL mapping*/ +typedef struct +{ + /*1 - required options*/ + + /*mode, or "" if no mode ("generic" should be used instead)*/ + char mode[30]; + + /*config of the stream if carried in SDP*/ + char *config; + u32 configSize; + /* Stream Type*/ + u8 StreamType; + /* stream profile and level indication - for AVC/H264, 0xPPCCLL, with PP:profile, CC:compatibility, LL:level*/ + u32 PL_ID; + + /*rvc config of the stream if carried in SDP*/ + u16 rvc_predef; + char *rvc_config; + u32 rvc_config_size; + + /*2 - optional options*/ + + /*size of AUs if constant*/ + u32 ConstantSize; + /*duration of AUs if constant, in RTP timescale*/ + u32 ConstantDuration; + + /* Object Type Indication */ + u8 ObjectTypeIndication; + /*audio max displacement when interleaving (eg, de-interleaving window buffer max length) in RTP timescale*/ + u32 maxDisplacement; + /*de-interleaveBufferSize if not recomputable from maxDisplacement*/ + u32 deinterleaveBufferSize; + + /*The number of bits on which the AU-size field is encoded in the AU-header*/ + u32 SizeLength; + /*The number of bits on which the AU-Index is encoded in the first AU-header*/ + u32 IndexLength; + /*The number of bits on which the AU-Index-delta field is encoded in any non-first AU-header*/ + u32 IndexDeltaLength; + + /*The number of bits on which the DTS-delta field is encoded in the AU-header*/ + u32 DTSDeltaLength; + /*The number of bits on which the CTS-delta field is encoded in the AU-header*/ + u32 CTSDeltaLength; + /*random access point flag present*/ + Bool RandomAccessIndication; + + /*The number of bits on which the Stream-state field is encoded in the AU-header (systems only)*/ + u32 StreamStateIndication; + /*The number of bits that is used to encode the auxiliary-data-size field + (no normative usage of this section)*/ + u32 AuxiliaryDataSizeLength; + + /*ISMACryp stuff*/ + u8 IV_length, IV_delta_length; + u8 KI_length; + + /*internal stuff*/ + /*len of first AU header in an RTP payload*/ + u32 auh_first_min_len; + u32 auh_min_len; +} GP_RTPSLMap; + + +/*packetizer config flags - some flags are dynamically re-assigned when detecting multiSL / B-Frames / ...*/ +enum +{ + /*forces MPEG-4 generic transport if MPEG-4 systems mapping is available*/ + GP_RTP_PCK_FORCE_MPEG4 = (1), + /*Enables AUs concatenation in an RTP packet (if payload supports it) - this forces GP_RTP_PCK_SIGNAL_SIZE for MPEG-4*/ + GP_RTP_PCK_USE_MULTI = (1<<1), + /*if set, audio interleaving is used if payload supports it (forces GP_RTP_PCK_USE_MULTI flag) + THIS IS CURRENTLY NOT IMPLEMENTED*/ + GP_RTP_PCK_USE_INTERLEAVING = (1<<2), + /*uses static RTP payloadID if any defined*/ + GP_RTP_PCK_USE_STATIC_ID = (1<<3), + + /*MPEG-4 generic transport option*/ + /*if flag set, RAP flag is signaled in RTP payload*/ + GP_RTP_PCK_SIGNAL_RAP = (1<<4), + /*if flag set, AU indexes are signaled in RTP payload - only usable for AU interleaving (eg audio)*/ + GP_RTP_PCK_SIGNAL_AU_IDX = (1<<5), + /*if flag set, AU size is signaled in RTP payload*/ + GP_RTP_PCK_SIGNAL_SIZE = (1<<6), + /*if flag set, CTS is signaled in RTP payload - DTS is automatically set if needed*/ + GP_RTP_PCK_SIGNAL_TS = (1<<7), + + /*setup payload for carouseling of systems streams*/ + GP_RTP_PCK_SYSTEMS_CAROUSEL = (1<<8), + + /*use LATM payload for AAC-LC*/ + GP_RTP_PCK_USE_LATM_AAC = (1<<9), + + /*ISMACryp options*/ + /*signals that input data is selectively encrypted (eg not all input frames are encrypted) + - this is usually automatically set by hinter*/ + GP_RTP_PCK_SELECTIVE_ENCRYPTION = (1<<10), + /*signals that each sample will have its own key indicator - ignored in non-multi modes + if not set and key indicator changes, a new RTP packet will be forced*/ + GP_RTP_PCK_KEY_IDX_PER_AU = (1<<11), + + /*is zip compression used in DIMS unit ?*/ + GP_RTP_DIMS_COMPRESSED = (1<<12), +}; + + + +/* + Generic packetization tools - used by track hinters and future live tools +*/ + +/*currently supported payload types*/ +enum +{ + /*not defined*/ + GF_RTP_PAYT_UNKNOWN, + /*use generic MPEG-4 transport - RFC 3016 and RFC 3640*/ + GF_RTP_PAYT_MPEG4, + /*use generic MPEG-1/2 video transport - RFC 2250*/ + GF_RTP_PAYT_MPEG12_VIDEO, + /*use generic MPEG-1/2 audio transport - RFC 2250*/ + GF_RTP_PAYT_MPEG12_AUDIO, + /*use H263 transport - RFC 2429*/ + GF_RTP_PAYT_H263, + /*use AMR transport - RFC 3267*/ + GF_RTP_PAYT_AMR, + /*use AMR-WB transport - RFC 3267*/ + GF_RTP_PAYT_AMR_WB, + /*use QCELP transport - RFC 2658*/ + GF_RTP_PAYT_QCELP, + /*use EVRC/SMV transport - RFC 3558*/ + GF_RTP_PAYT_EVRC_SMV, + /*use 3GPP Text transport - no RFC yet, only draft*/ + GF_RTP_PAYT_3GPP_TEXT, + /*use H264 transport - no RFC yet, only draft*/ + GF_RTP_PAYT_H264_AVC, + /*use LATM for AAC-LC*/ + GF_RTP_PAYT_LATM, + /*use 3GPP DIMS format*/ + GF_RTP_PAYT_3GPP_DIMS, + /*use AC3 audio format*/ + GF_RTP_PAYT_AC3, +}; + + + +/* + RTP packetizer +*/ + + + +/* + RTP -> SL packetization tool + You should ONLY modify the GF_SLHeader while packetizing, all the rest is private + to the tool. + Also note that AU start/end is automatically updated, therefore you should only + set CTS-DTS-OCR-sequenceNumber (which is automatically incremented when spliting a payload) + -padding-idle infos + SL flags are computed on the fly, but you may wish to modify them in case of + packet drop/... at the encoder side + +*/ +struct __tag_rtp_packetizer +{ + /*input packet sl header cfg. modify only if needed*/ + GF_SLHeader sl_header; + + /* + + PRIVATE _ DO NOT TOUCH + */ + + /*RTP payload type (RFC type, NOT the RTP hdr payT)*/ + u32 rtp_payt; + /*packetization flags*/ + u32 flags; + /*Path MTU size without 12-bytes RTP header*/ + u32 Path_MTU; + /*max packet duration in RTP TS*/ + u32 max_ptime; + + /*payload type of RTP packets - only one payload type can be used in GPAC*/ + u8 PayloadType; + + /*RTP header of current packet*/ + GF_RTPHeader rtp_header; + + /*RTP packet handling callbacks*/ + void (*OnNewPacket)(void *cbk_obj, GF_RTPHeader *header); + void (*OnPacketDone)(void *cbk_obj, GF_RTPHeader *header); + void (*OnDataReference)(void *cbk_obj, u32 payload_size, u32 offset_from_orig); + void (*OnData)(void *cbk_obj, char *data, u32 data_size, Bool is_header); + void *cbk_obj; + + /********************************* + MPEG-4 Generic hinting + *********************************/ + + /*SL to RTP map*/ + GP_RTPSLMap slMap; + /*SL conf and state*/ + GF_SLConfig sl_config; + + /*set to 1 if firstSL in RTP packet*/ + Bool first_sl_in_rtp; + Bool has_AU_header; + /*current info writers*/ + GF_BitStream *pck_hdr, *payload; + + /*AU SN of last au*/ + u32 last_au_sn; + + /*info for the current packet*/ + u32 auh_size, bytesInPacket; + + /********************************* + ISMACryp info + *********************************/ + Bool force_flush, is_encrypted; + u64 IV, first_AU_IV; + char *key_indicator; + + /********************************* + AVC-H264 info + *********************************/ + /*AVC non-IDR flag: set if all NAL in current packet are non-IDR (disposable)*/ + Bool avc_non_idr; + + /********************************* + AC3 info + *********************************/ + /*ac3 ft flags*/ + u8 ac3_ft; + +}; + +/*generic rtp builder (packetizer)*/ +typedef struct __tag_rtp_packetizer GP_RTPPacketizer; + +/*creates a new builder + @rtp_payt: rtp payload format, one of the above GF_RTP_PAYT_* + @flags: packetizer flags, one of the above GP_RTP_PCK_* + @slc: user-given SL config to use. If none specified, default RFC config is used + @cbk_obj: callback object passed back in functions + @OnNewPacket: callback function starting new RTP packet + @header: rtp header for new packet - note that RTP header flags are not used until PacketDone is called + @OnPacketDone: callback function closing current RTP packet + @header: final rtp header for packet + @OnDataReference: optional, to call each time data from input buffer is added to current RTP packet. + If not set, data must be added through OnData + @payload_size: size of reference data + @offset_from_orig: start offset in input buffer + @OnData: to call each time data is added to current RTP packet (either extra data from payload or + data from input when not using referencing) + @is_head: signal the data added MUST be inserted at the begining of the payload. Otherwise data + is concatenated as received +*/ +GP_RTPPacketizer *gf_rtp_builder_new(u32 rtp_payt, + GF_SLConfig *slc, + u32 flags, + void *cbk_obj, + void (*OnNewPacket)(void *cbk, GF_RTPHeader *header), + void (*OnPacketDone)(void *cbk, GF_RTPHeader *header), + void (*OnDataReference)(void *cbk, u32 payload_size, u32 offset_from_orig), + void (*OnData)(void *cbk, char *data, u32 data_size, Bool is_head) + ); + +/*destroy builder*/ +void gf_rtp_builder_del(GP_RTPPacketizer *builder); + +/* + init the builder + @MaxPayloadSize: maximum payload size of RTP packets (eg MTU minus IP/UDP/RTP headers) + @max_ptime: maximum packet duration IN RTP TIMESCALE + @StreamType: MPEG-4 system stream type - MUST always be provided for payloads format specifying + audio or video streams + @OTI : MPEG-4 system objectTypeIndication - may be 0 if stream is not mpeg4 systems + + *** all other params are for MultiSL draft *** + + @avgSize: average size of an AU. This is not always known (real-time encoding). +In this case you should specify a rough compute indicating how many packets could be +stored per RTP packet. for ex AAC stereo at 44100 k / 64kbps , one AU ~= 380 bytes +so 3 AUs for 1500 MTU is ok - BE CAREFULL: MultiSL adds some SL info on top of the 12 +byte RTP header so you should specify a smaller size +The packetizer will ALWAYS make sure there's no pb storing the packets so specifying +more will result in a slight overhead in the SL mapping but the gain to singleSL +will still be worth it. + -Nota: at init, the packetizer can decide to switch to SingleSL if the average size +specified is too close to the PathMTU + + @maxSize: max size of an AU. If unknown (real-time) set to 0 + @avgTS: average CTS progression (1000/FPS for video) + @maxDTS: maximum DTS offset in case of bidirectional coding. + @IV_length: size (in bytes) of IV when ISMACrypted + @KI_length: size (in bytes) of key indicator when ISMACrypted + @pref_mode: MPEG-4 generic only, specifies the payload mode - can be NULL (mode generic) +*/ + +void gf_rtp_builder_init(GP_RTPPacketizer *builder, u8 PayloadType, u32 MaxPayloadSize, u32 max_ptime, + u32 StreamType, u32 OTI, u32 PL_ID, + u32 avgSize, u32 maxSize, + u32 avgTS, u32 maxDTS, + u32 IV_length, u32 KI_length, + char *pref_mode); + +/*set frame crypto info*/ +void gp_rtp_builder_set_cryp_info(GP_RTPPacketizer *builder, u64 IV, char *key_indicator, Bool is_encrypted); +/*packetize input buffer +@data, @data_size: input buffer +@IsAUEnd: set to one if this buffer is the last of the AU +@FullAUSize: complete access unit size if known, 0 otherwise +@duration: sample duration in rtp timescale (only needed for 3GPP text streams) +@descIndex: sample description index (only needed for 3GPP text streams) +*/ +GF_Err gf_rtp_builder_process(GP_RTPPacketizer *builder, char *data, u32 data_size, u8 IsAUEnd, u32 FullAUSize, u32 duration, u8 descIndex); + +/*format the "fmtp: " attribute for the MPEG-4 generic packetizer. sdpline shall be at least 2000 char*/ +GF_Err gf_rtp_builder_format_sdp(GP_RTPPacketizer *builder, char *payload_name, char *sdpLine, char *dsi, u32 dsi_size); +/*formats SDP payload name and media name - both MUST be at least 20 bytes*/ +Bool gf_rtp_builder_get_payload_name(GP_RTPPacketizer *builder, char *szPayloadName, char *szMediaName); + + + + + +/*rtp payload flags*/ +enum +{ + /*AU end was detected (eg next packet is AU start)*/ + GF_RTP_NEW_AU = (1), + /*AMR config*/ + GF_RTP_AMR_ALIGN = (1<<1), + /*for RFC3016, signals bitstream inspection for RAP discovery*/ + GF_RTP_M4V_CHECK_RAP = (1<<2), + /*flag set when unreliable usage of the M bit is detected*/ + GF_RTP_UNRELIABLE_M = (1<<3), + + /*AWFULL hack at rtp level to cope with ffmpeg h264 crashes when jumping in stream without IDR*/ + GF_RTP_AVC_WAIT_RAP = (1<<4), + /*ISMACryp stuff*/ + GF_RTP_HAS_ISMACRYP = (1<<5), + GF_RTP_ISMA_SEL_ENC = (1<<6), + GF_RTP_ISMA_HAS_KEY_IDX = (1<<7) +}; + +/* + SL -> RTP packetization tool + +*/ +struct __tag_rtp_depacketizer +{ + /*depacketize routine*/ + void (*depacketize)(struct __tag_rtp_depacketizer *rtp, GF_RTPHeader *hdr, char *payload, u32 size); + + /*output packet sl header cfg*/ + GF_SLHeader sl_hdr; + + /*RTP payload type (RFC type, NOT the RTP hdr payT)*/ + u32 payt; + /*depacketization flags*/ + u32 flags; + + /*callback routine*/ + void (*on_sl_packet)(void *udta, char *payload, u32 size, GF_SLHeader *hdr, GF_Err e); + void *udta; + + /*SL <-> RTP map*/ + GP_RTPSLMap sl_map; + u32 clock_rate; + + /*inter-packet reconstruction bitstream (for 3GP text and H264)*/ + GF_BitStream *inter_bs; + + /*H264/AVC config*/ + u32 h264_pck_mode; + + /*3GP text reassembler state*/ + u8 nb_txt_frag, cur_txt_frag, sidx, txt_len, nb_mod_frag; + + /*ISMACryp*/ + u32 isma_scheme; + char *key; +}; + +/*generic rtp builder (packetizer)*/ +typedef struct __tag_rtp_depacketizer GF_RTPDepacketizer; + +GF_RTPDepacketizer *gf_rtp_depacketizer_new(GF_SDPMedia *media, void (*sl_packet_cbk)(void *udta, char *payload, u32 size, GF_SLHeader *hdr, GF_Err e), void *udta); +void gf_rtp_depacketizer_del(GF_RTPDepacketizer *rtp); +void gf_rtp_depacketizer_reset(GF_RTPDepacketizer *rtp, Bool full_reset); +void gf_rtp_depacketizer_process(GF_RTPDepacketizer *rtp, GF_RTPHeader *hdr, char *payload, u32 size); + +void gf_rtp_depacketizer_get_slconfig(GF_RTPDepacketizer *rtp, GF_SLConfig *sl); + + +#endif /*GPAC_DISABLE_STREAMING*/ + +#ifdef __cplusplus +} +#endif + +#endif /*_GF_IETF_H_*/ + diff --git a/src/gpacmp4/gpac/internal/avilib.h b/src/gpacmp4/gpac/internal/avilib.h new file mode 100644 index 00000000..7697cc33 --- /dev/null +++ b/src/gpacmp4/gpac/internal/avilib.h @@ -0,0 +1,439 @@ +/* + * avilib.h + * + * Copyright (C) Thomas Östreich - June 2001 + * multiple audio track support Copyright (C) 2002 Thomas Östreich + * + * Original code: + * Copyright (C) 1999 Rainer Johanni + * + * This file is part of transcode, a linux video stream processing tool + * + * transcode is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * transcode 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef _GF_AVILIB_H_ +#define _GF_AVILIB_H_ + +#include + +#ifndef GPAC_DISABLE_AVILIB + +#define AVI_MAX_TRACKS 8 + +typedef struct +{ + u64 key; + u64 pos; + u64 len; +} video_index_entry; + +typedef struct +{ + u64 pos; + u64 len; + u64 tot; +} audio_index_entry; + + +// Index types + + +#define AVI_INDEX_OF_INDEXES 0x00 // when each entry in aIndex + // array points to an index chunk +#define AVI_INDEX_OF_CHUNKS 0x01 // when each entry in aIndex + // array points to a chunk in the file +#define AVI_INDEX_IS_DATA 0x80 // when each entry is aIndex is + // really the data +// bIndexSubtype codes for INDEX_OF_CHUNKS +// +#define AVI_INDEX_2FIELD 0x01 // when fields within frames + // are also indexed + + + +typedef struct _avisuperindex_entry { + u64 qwOffset; // absolute file offset + u32 dwSize; // size of index chunk at this offset + u32 dwDuration; // time span in stream ticks +} avisuperindex_entry; + +typedef struct _avistdindex_entry { + u32 dwOffset; // qwBaseOffset + this is absolute file offset + u32 dwSize; // bit 31 is set if this is NOT a keyframe +} avistdindex_entry; + +// Standard index +typedef struct _avistdindex_chunk { + char fcc[4]; // ix## + u32 dwSize; // size of this chunk + u16 wLongsPerEntry; // must be sizeof(aIndex[0])/sizeof(DWORD) + u8 bIndexSubType; // must be 0 + u8 bIndexType; // must be AVI_INDEX_OF_CHUNKS + u32 nEntriesInUse; // + char dwChunkId[4]; // '##dc' or '##db' or '##wb' etc.. + u64 qwBaseOffset; // all dwOffsets in aIndex array are relative to this + u32 dwReserved3; // must be 0 + avistdindex_entry *aIndex; +} avistdindex_chunk; + + +// Base Index Form 'indx' +typedef struct _avisuperindex_chunk { + char fcc[4]; + u32 dwSize; // size of this chunk + u16 wLongsPerEntry; // size of each entry in aIndex array (must be 8 for us) + u8 bIndexSubType; // future use. must be 0 + u8 bIndexType; // one of AVI_INDEX_* codes + u32 nEntriesInUse; // index of first unused member in aIndex array + char dwChunkId[4]; // fcc of what is indexed + u32 dwReserved[3]; // meaning differs for each index type/subtype. + // 0 if unused + avisuperindex_entry *aIndex; // where are the ix## chunks + avistdindex_chunk **stdindex; // the ix## chunks itself (array) +} avisuperindex_chunk; + + + +typedef struct track_s +{ + + long a_fmt; /* Audio format, see #defines below */ + long a_chans; /* Audio channels, 0 for no audio */ + long a_rate; /* Rate in Hz */ + long a_bits; /* bits per audio sample */ + long mp3rate; /* mp3 bitrate kbs*/ + long a_vbr; /* 0 == no Variable BitRate */ + long padrate; /* byte rate used for zero padding */ + + long audio_strn; /* Audio stream number */ + u64 audio_bytes; /* Total number of bytes of audio data */ + long audio_chunks; /* Chunks of audio data in the file */ + + char audio_tag[4]; /* Tag of audio data */ + long audio_posc; /* Audio position: chunk */ + long audio_posb; /* Audio position: byte within chunk */ + + u64 a_codech_off; /* absolut offset of audio codec information */ + u64 a_codecf_off; /* absolut offset of audio codec information */ + + audio_index_entry *audio_index; + avisuperindex_chunk *audio_superindex; + +} track_t; + +typedef struct +{ + u32 bi_size; + u32 bi_width; + u32 bi_height; + u16 bi_planes; + u16 bi_bit_count; + u32 bi_compression; + u32 bi_size_image; + u32 bi_x_pels_per_meter; + u32 bi_y_pels_per_meter; + u32 bi_clr_used; + u32 bi_clr_important; +} alBITMAPINFOHEADER; + +typedef struct +{ + u16 w_format_tag; + u16 n_channels; + u32 n_samples_per_sec; + u32 n_avg_bytes_per_sec; + u16 n_block_align; + u16 w_bits_per_sample; + u16 cb_size; +} alWAVEFORMATEX; + +typedef struct +{ + u32 fcc_type; + u32 fcc_handler; + u32 dw_flags; + u32 dw_caps; + u16 w_priority; + u16 w_language; + u32 dw_scale; + u32 dw_rate; + u32 dw_start; + u32 dw_length; + u32 dw_initial_frames; + u32 dw_suggested_buffer_size; + u32 dw_quality; + u32 dw_sample_size; + u16 dw_left; + u16 dw_top; + u16 dw_right; + u16 dw_bottom; +} alAVISTREAMHEADER; + +typedef struct +{ + + FILE *fdes; /* File descriptor of AVI file */ + long mode; /* 0 for reading, 1 for writing */ + + long width; /* Width of a video frame */ + long height; /* Height of a video frame */ + double fps; /* Frames per second */ + char compressor[8]; /* Type of compressor, 4 bytes + padding for 0 byte */ + char compressor2[8]; /* Type of compressor, 4 bytes + padding for 0 byte */ + u32 video_strn; /* Video stream number */ + long video_frames; /* Number of video frames */ + char video_tag[4]; /* Tag of video data */ + long video_pos; /* Number of next frame to be read + (if index present) */ + alAVISTREAMHEADER video_stream_header; + + u32 max_len; /* maximum video chunk present */ + + track_t track[AVI_MAX_TRACKS]; // up to AVI_MAX_TRACKS audio tracks supported + + s64 pos; /* position in file */ + long n_idx; /* number of index entries actually filled */ + long max_idx; /* number of index entries actually allocated */ + + s64 v_codech_off; /* absolut offset of video codec (strh) info */ + s64 v_codecf_off; /* absolut offset of video codec (strf) info */ + + u8 (*idx)[16]; /* index entries (AVI idx1 tag) */ + + video_index_entry *video_index; + avisuperindex_chunk *video_superindex; /* index of indices */ + int is_opendml; /* set to 1 if this is an odml file with multiple index chunks */ + + s64 last_pos; /* Position of last frame written */ + u32 last_len; /* Length of last frame written */ + int must_use_index; /* Flag if frames are duplicated */ + s64 movi_start; + int total_frames; /* total number of frames if dmlh is present */ + + u32 anum; // total number of audio tracks + u32 aptr; // current audio working track + char *index_file; // read the avi index from this file + + alBITMAPINFOHEADER *bitmap_info_header; + alWAVEFORMATEX *wave_format_ex[AVI_MAX_TRACKS]; + alAVISTREAMHEADER stream_headers[AVI_MAX_TRACKS]; + + void* extradata; + unsigned long extradata_size; +} avi_t; + +#define AVI_MODE_WRITE 0 +#define AVI_MODE_READ 1 + +/* The error codes delivered by avi_open_input_file */ + +#define AVI_ERR_SIZELIM 1 /* The write of the data would exceed + the maximum size of the AVI file. + This is more a warning than an error + since the file may be closed safely */ + +#define AVI_ERR_OPEN 2 /* Error opening the AVI file - wrong path + name or file nor readable/writable */ + +#define AVI_ERR_READ 3 /* Error reading from AVI File */ + +#define AVI_ERR_WRITE 4 /* Error writing to AVI File, + disk full ??? */ + +#define AVI_ERR_WRITE_INDEX 5 /* Could not write index to AVI file + during close, file may still be + usable */ + +#define AVI_ERR_CLOSE 6 /* Could not write header to AVI file + or not truncate the file during close, + file is most probably corrupted */ + +#define AVI_ERR_NOT_PERM 7 /* Operation not permitted: + trying to read from a file open + for writing or vice versa */ + +#define AVI_ERR_NO_MEM 8 /* malloc failed */ + +#define AVI_ERR_NO_AVI 9 /* Not an AVI file */ + +#define AVI_ERR_NO_HDRL 10 /* AVI file has no has no header list, + corrupted ??? */ + +#define AVI_ERR_NO_MOVI 11 /* AVI file has no has no MOVI list, + corrupted ??? */ + +#define AVI_ERR_NO_VIDS 12 /* AVI file contains no video data */ + +#define AVI_ERR_NO_IDX 13 /* The file has been opened with + getIndex==0, but an operation has been + performed that needs an index */ + +/* Possible Audio formats */ + +#ifndef WAVE_FORMAT_PCM +#define WAVE_FORMAT_UNKNOWN (0x0000) +#ifndef WAVE_FORMAT_PCM +#define WAVE_FORMAT_PCM (0x0001) +#endif +#define WAVE_FORMAT_ADPCM (0x0002) +#define WAVE_FORMAT_IBM_CVSD (0x0005) +#define WAVE_FORMAT_ALAW (0x0006) +#define WAVE_FORMAT_MULAW (0x0007) +#define WAVE_FORMAT_OKI_ADPCM (0x0010) +#define WAVE_FORMAT_DVI_ADPCM (0x0011) +#define WAVE_FORMAT_DIGISTD (0x0015) +#define WAVE_FORMAT_DIGIFIX (0x0016) +#define WAVE_FORMAT_YAMAHA_ADPCM (0x0020) +#define WAVE_FORMAT_DSP_TRUESPEECH (0x0022) +#define WAVE_FORMAT_GSM610 (0x0031) +#define IBM_FORMAT_MULAW (0x0101) +#define IBM_FORMAT_ALAW (0x0102) +#define IBM_FORMAT_ADPCM (0x0103) +#endif + +avi_t* AVI_open_output_file(char * filename); +void AVI_set_video(avi_t *AVI, int width, int height, double fps, char *compressor); +void AVI_set_audio(avi_t *AVI, int channels, long rate, int bits, int format, long mp3rate); +int AVI_write_frame(avi_t *AVI, char *data, long bytes, int keyframe); +int AVI_dup_frame(avi_t *AVI); +int AVI_write_audio(avi_t *AVI, char *data, long bytes); +int AVI_append_audio(avi_t *AVI, char *data, long bytes); +u64 AVI_bytes_remain(avi_t *AVI); +int AVI_close(avi_t *AVI); +u64 AVI_bytes_written(avi_t *AVI); + +avi_t *AVI_open_input_file(char *filename, int getIndex); +avi_t *AVI_open_input_indexfile(char *filename, int getIndex, char *indexfile); +avi_t *AVI_open_fd(FILE *fd, int getIndex); +avi_t *AVI_open_indexfd(FILE *fd, int getIndex, char *indexfile); +int avi_parse_input_file(avi_t *AVI, int getIndex); +int avi_parse_index_from_file(avi_t *AVI, char *filename); +long AVI_audio_mp3rate(avi_t *AVI); +long AVI_audio_padrate(avi_t *AVI); +long AVI_video_frames(avi_t *AVI); +int AVI_video_width(avi_t *AVI); +int AVI_video_height(avi_t *AVI); +double AVI_frame_rate(avi_t *AVI); +char* AVI_video_compressor(avi_t *AVI); + +int AVI_audio_channels(avi_t *AVI); +int AVI_audio_bits(avi_t *AVI); +int AVI_audio_format(avi_t *AVI); +long AVI_audio_rate(avi_t *AVI); +u64 AVI_audio_bytes(avi_t *AVI); +long AVI_audio_chunks(avi_t *AVI); +int AVI_can_read_audio(avi_t *AVI); + +long AVI_max_video_chunk(avi_t *AVI); + +long AVI_frame_size(avi_t *AVI, long frame); +long AVI_audio_size(avi_t *AVI, long frame); +int AVI_seek_start(avi_t *AVI); +int AVI_set_video_position(avi_t *AVI, long frame); +u64 AVI_get_video_position(avi_t *AVI, long frame); +long AVI_read_frame(avi_t *AVI, char *vidbuf, int *keyframe); + +int AVI_set_audio_position(avi_t *AVI, long byte); +int AVI_set_audio_bitrate(avi_t *AVI, long bitrate); + +long AVI_get_audio_position_index(avi_t *AVI); +int AVI_set_audio_position_index(avi_t *AVI, long indexpos); + +long AVI_read_audio(avi_t *AVI, char *audbuf, long bytes, int *continuous); + +int AVI_read_data(avi_t *AVI, char *vidbuf, long max_vidbuf, + char *audbuf, long max_audbuf, + long *len); + +int AVI_scan(char *name); +int AVI_dump(char *name, int mode); + +char *AVI_codec2str(short cc); +int AVI_file_check(char *import_file); + +void AVI_info(avi_t *avifile); +u64 AVI_max_size(void); +int avi_update_header(avi_t *AVI); + +int AVI_set_audio_track(avi_t *AVI, u32 track); +int AVI_get_audio_track(avi_t *AVI); +int AVI_audio_tracks(avi_t *AVI); + +void AVI_set_audio_vbr(avi_t *AVI, long is_vbr); +long AVI_get_audio_vbr(avi_t *AVI); + +void AVI_set_comment_fd(avi_t *AVI, int fd); +int AVI_get_comment_fd(avi_t *AVI); + +struct riff_struct +{ + u8 id[4]; /* RIFF */ + u32 len; + u8 wave_id[4]; /* WAVE */ +}; + + +struct chunk_struct +{ + u8 id[4]; + u32 len; +}; + +struct common_struct +{ + u16 wFormatTag; + u16 wChannels; + u32 dwSamplesPerSec; + u32 dwAvgBytesPerSec; + u16 wBlockAlign; + u16 wBitsPerSample; /* Only for PCM */ +}; + +struct wave_header +{ + struct riff_struct riff; + struct chunk_struct format; + struct common_struct common; + struct chunk_struct data; +}; + +// Simple WAV IO +int AVI_read_wave_header( int fd, struct wave_header * wave ); +int AVI_write_wave_header( int fd, const struct wave_header * wave ); +size_t AVI_read_wave_pcm_data( int fd, void * buffer, size_t buflen ); +size_t AVI_write_wave_pcm_data( int fd, const void * buffer, size_t buflen ); + + +struct AVIStreamHeader { + long fccType; + long fccHandler; + long dwFlags; + long dwPriority; + long dwInitialFrames; + long dwScale; + long dwRate; + long dwStart; + long dwLength; + long dwSuggestedBufferSize; + long dwQuality; + long dwSampleSize; +}; + +#endif /*GPAC_DISABLE_AVILIB*/ + +#endif /*_GF_AVILIB_H_*/ + diff --git a/src/gpacmp4/gpac/internal/isomedia_dev.h b/src/gpacmp4/gpac/internal/isomedia_dev.h new file mode 100644 index 00000000..1bb47cbb --- /dev/null +++ b/src/gpacmp4/gpac/internal/isomedia_dev.h @@ -0,0 +1,3375 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / ISO Media File Format sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef _GF_ISOMEDIA_DEV_H_ +#define _GF_ISOMEDIA_DEV_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#ifndef GPAC_DISABLE_ISOM + + +//the default size is 64, cause we need to handle large boxes... +#define GF_ISOM_BOX \ + u32 type; \ + u64 size; \ + +#define GF_ISOM_FULL_BOX \ + GF_ISOM_BOX \ + u8 version; \ + u32 flags; \ + +#define GF_ISOM_UUID_BOX \ + GF_ISOM_BOX \ + u8 uuid[16]; \ + +typedef struct +{ + GF_ISOM_BOX +} GF_Box; + +typedef struct +{ + GF_ISOM_FULL_BOX +} GF_FullBox; + +typedef struct +{ + GF_ISOM_UUID_BOX +} GF_UUIDBox; + +/*constructor*/ +GF_Box *gf_isom_box_new(u32 boxType); + +GF_Err gf_isom_box_write(GF_Box *ptr, GF_BitStream *bs); +GF_Err gf_isom_box_read(GF_Box *ptr, GF_BitStream *bs); +void gf_isom_box_del(GF_Box *ptr); +GF_Err gf_isom_box_size(GF_Box *ptr); + +GF_Err gf_isom_parse_box(GF_Box **outBox, GF_BitStream *bs); +GF_Err gf_isom_read_box_list(GF_Box *s, GF_BitStream *bs, GF_Err (*add_box)(GF_Box *par, GF_Box *b)); +GF_Err gf_isom_read_box_list_ex(GF_Box *parent, GF_BitStream *bs, GF_Err (*add_box)(GF_Box *par, GF_Box *b), u32 parent_type); + +GF_Err gf_isom_box_get_size(GF_Box *ptr); +GF_Err gf_isom_full_box_get_size(GF_Box *ptr); +GF_Err gf_isom_box_write_header(GF_Box *ptr, GF_BitStream *bs); +GF_Err gf_isom_full_box_read(GF_Box *ptr, GF_BitStream *bs); +GF_Err gf_isom_full_box_write(GF_Box *s, GF_BitStream *bs); +void gf_isom_full_box_init(GF_Box *ptr); +void gf_isom_box_array_del(GF_List *boxList); +GF_Err gf_isom_box_array_write(GF_Box *parent, GF_List *list, GF_BitStream *bs); +GF_Err gf_isom_box_array_size(GF_Box *parent, GF_List *list); + + +enum +{ + GF_ISOM_BOX_TYPE_CO64 = GF_4CC( 'c', 'o', '6', '4' ), + GF_ISOM_BOX_TYPE_STCO = GF_4CC( 's', 't', 'c', 'o' ), + GF_ISOM_BOX_TYPE_CRHD = GF_4CC( 'c', 'r', 'h', 'd' ), + GF_ISOM_BOX_TYPE_CTTS = GF_4CC( 'c', 't', 't', 's' ), + GF_ISOM_BOX_TYPE_CPRT = GF_4CC( 'c', 'p', 'r', 't' ), + GF_ISOM_BOX_TYPE_CHPL = GF_4CC( 'c', 'h', 'p', 'l' ), + GF_ISOM_BOX_TYPE_URL = GF_4CC( 'u', 'r', 'l', ' ' ), + GF_ISOM_BOX_TYPE_URN = GF_4CC( 'u', 'r', 'n', ' ' ), + GF_ISOM_BOX_TYPE_DINF = GF_4CC( 'd', 'i', 'n', 'f' ), + GF_ISOM_BOX_TYPE_DREF = GF_4CC( 'd', 'r', 'e', 'f' ), + GF_ISOM_BOX_TYPE_STDP = GF_4CC( 's', 't', 'd', 'p' ), + GF_ISOM_BOX_TYPE_EDTS = GF_4CC( 'e', 'd', 't', 's' ), + GF_ISOM_BOX_TYPE_ELST = GF_4CC( 'e', 'l', 's', 't' ), + GF_ISOM_BOX_TYPE_UUID = GF_4CC( 'u', 'u', 'i', 'd' ), + GF_ISOM_BOX_TYPE_FREE = GF_4CC( 'f', 'r', 'e', 'e' ), + GF_ISOM_BOX_TYPE_HDLR = GF_4CC( 'h', 'd', 'l', 'r' ), + GF_ISOM_BOX_TYPE_GMHD = GF_4CC( 'g', 'm', 'h', 'd' ), + GF_ISOM_BOX_TYPE_HMHD = GF_4CC( 'h', 'm', 'h', 'd' ), + GF_ISOM_BOX_TYPE_HINT = GF_4CC( 'h', 'i', 'n', 't' ), + GF_ISOM_BOX_TYPE_MDIA = GF_4CC( 'm', 'd', 'i', 'a' ), + GF_ISOM_BOX_TYPE_MDAT = GF_4CC( 'm', 'd', 'a', 't' ), + GF_ISOM_BOX_TYPE_MDHD = GF_4CC( 'm', 'd', 'h', 'd' ), + GF_ISOM_BOX_TYPE_MINF = GF_4CC( 'm', 'i', 'n', 'f' ), + GF_ISOM_BOX_TYPE_MOOV = GF_4CC( 'm', 'o', 'o', 'v' ), + GF_ISOM_BOX_TYPE_MVHD = GF_4CC( 'm', 'v', 'h', 'd' ), + GF_ISOM_BOX_TYPE_STSD = GF_4CC( 's', 't', 's', 'd' ), + GF_ISOM_BOX_TYPE_STSZ = GF_4CC( 's', 't', 's', 'z' ), + GF_ISOM_BOX_TYPE_STZ2 = GF_4CC( 's', 't', 'z', '2' ), + GF_ISOM_BOX_TYPE_STBL = GF_4CC( 's', 't', 'b', 'l' ), + GF_ISOM_BOX_TYPE_STSC = GF_4CC( 's', 't', 's', 'c' ), + GF_ISOM_BOX_TYPE_STSH = GF_4CC( 's', 't', 's', 'h' ), + GF_ISOM_BOX_TYPE_SKIP = GF_4CC( 's', 'k', 'i', 'p' ), + GF_ISOM_BOX_TYPE_SMHD = GF_4CC( 's', 'm', 'h', 'd' ), + GF_ISOM_BOX_TYPE_STSS = GF_4CC( 's', 't', 's', 's' ), + GF_ISOM_BOX_TYPE_STTS = GF_4CC( 's', 't', 't', 's' ), + GF_ISOM_BOX_TYPE_TRAK = GF_4CC( 't', 'r', 'a', 'k' ), + GF_ISOM_BOX_TYPE_TKHD = GF_4CC( 't', 'k', 'h', 'd' ), + GF_ISOM_BOX_TYPE_TREF = GF_4CC( 't', 'r', 'e', 'f' ), + GF_ISOM_BOX_TYPE_UDTA = GF_4CC( 'u', 'd', 't', 'a' ), + GF_ISOM_BOX_TYPE_VMHD = GF_4CC( 'v', 'm', 'h', 'd' ), + GF_ISOM_BOX_TYPE_FTYP = GF_4CC( 'f', 't', 'y', 'p' ), + GF_ISOM_BOX_TYPE_FADB = GF_4CC( 'p', 'a', 'd', 'b' ), + GF_ISOM_BOX_TYPE_PDIN = GF_4CC( 'p', 'd', 'i', 'n' ), + GF_ISOM_BOX_TYPE_SDTP = GF_4CC( 's', 'd', 't', 'p' ), + +#ifndef GPAC_DISABLE_ISOM_FRAGMENTS + /*Movie Fragments*/ + GF_ISOM_BOX_TYPE_MVEX = GF_4CC( 'm', 'v', 'e', 'x' ), + GF_ISOM_BOX_TYPE_MEHD = GF_4CC( 'm', 'e', 'h', 'd' ), + GF_ISOM_BOX_TYPE_TREX = GF_4CC( 't', 'r', 'e', 'x' ), + GF_ISOM_BOX_TYPE_MOOF = GF_4CC( 'm', 'o', 'o', 'f' ), + GF_ISOM_BOX_TYPE_MFHD = GF_4CC( 'm', 'f', 'h', 'd' ), + GF_ISOM_BOX_TYPE_TRAF = GF_4CC( 't', 'r', 'a', 'f' ), + GF_ISOM_BOX_TYPE_TFHD = GF_4CC( 't', 'f', 'h', 'd' ), + GF_ISOM_BOX_TYPE_TRUN = GF_4CC( 't', 'r', 'u', 'n' ), +#endif + + /*MP4 extensions*/ + GF_ISOM_BOX_TYPE_DPND = GF_4CC( 'd', 'p', 'n', 'd' ), + GF_ISOM_BOX_TYPE_IODS = GF_4CC( 'i', 'o', 'd', 's' ), + GF_ISOM_BOX_TYPE_ESDS = GF_4CC( 'e', 's', 'd', 's' ), + GF_ISOM_BOX_TYPE_MPOD = GF_4CC( 'm', 'p', 'o', 'd' ), + GF_ISOM_BOX_TYPE_SYNC = GF_4CC( 's', 'y', 'n', 'c' ), + GF_ISOM_BOX_TYPE_IPIR = GF_4CC( 'i', 'p', 'i', 'r' ), + GF_ISOM_BOX_TYPE_SDHD = GF_4CC( 's', 'd', 'h', 'd' ), + GF_ISOM_BOX_TYPE_ODHD = GF_4CC( 'o', 'd', 'h', 'd' ), + GF_ISOM_BOX_TYPE_NMHD = GF_4CC( 'n', 'm', 'h', 'd' ), + GF_ISOM_BOX_TYPE_MP4S = GF_4CC( 'm', 'p', '4', 's' ), + GF_ISOM_BOX_TYPE_MP4A = GF_4CC( 'm', 'p', '4', 'a' ), + GF_ISOM_BOX_TYPE_MP4V = GF_4CC( 'm', 'p', '4', 'v' ), + + /*AVC / H264 extension*/ + GF_ISOM_BOX_TYPE_AVCC = GF_4CC( 'a', 'v', 'c', 'C' ), + GF_ISOM_BOX_TYPE_BTRT = GF_4CC( 'b', 't', 'r', 't' ), + GF_ISOM_BOX_TYPE_M4DS = GF_4CC( 'm', '4', 'd', 's' ), + GF_ISOM_BOX_TYPE_PASP = GF_4CC( 'p', 'a', 's', 'p' ), + GF_ISOM_BOX_TYPE_AVC1 = GF_4CC( 'a', 'v', 'c', '1' ), + GF_ISOM_BOX_TYPE_AVC2 = GF_4CC( 'a', 'v', 'c', '2' ), + GF_ISOM_BOX_TYPE_SVCC = GF_4CC( 's', 'v', 'c', 'C' ), + GF_ISOM_BOX_TYPE_SVC1 = GF_4CC( 's', 'v', 'c', '1' ), + + /*LASeR extension*/ + GF_ISOM_BOX_TYPE_LSRC = GF_4CC( 'l', 's', 'r', 'C' ), + GF_ISOM_BOX_TYPE_LSR1 = GF_4CC( 'l', 's', 'r', '1' ), + + /*3GPP extensions*/ + GF_ISOM_BOX_TYPE_DAMR = GF_4CC( 'd', 'a', 'm', 'r' ), + GF_ISOM_BOX_TYPE_D263 = GF_4CC( 'd', '2', '6', '3' ), + GF_ISOM_BOX_TYPE_DEVC = GF_4CC( 'd', 'e', 'v', 'c' ), + GF_ISOM_BOX_TYPE_DQCP = GF_4CC( 'd', 'q', 'c', 'p' ), + GF_ISOM_BOX_TYPE_DSMV = GF_4CC( 'd', 's', 'm', 'v' ), + GF_ISOM_BOX_TYPE_TSEL = GF_4CC( 't', 's', 'e', 'l' ), + + /* 3GPP Adaptive Streaming extensions */ + GF_ISOM_BOX_TYPE_STYP = GF_4CC( 's', 't', 'y', 'p' ), + GF_ISOM_BOX_TYPE_TFDT = GF_4CC( 't', 'f', 'd', 't' ), + GF_ISOM_BOX_TYPE_SIDX = GF_4CC( 's', 'i', 'd', 'x' ), + + /*3GPP text / MPEG-4 StreamingText*/ + GF_ISOM_BOX_TYPE_FTAB = GF_4CC( 'f', 't', 'a', 'b' ), + GF_ISOM_BOX_TYPE_TX3G = GF_4CC( 't', 'x', '3', 'g' ), + GF_ISOM_BOX_TYPE_STYL = GF_4CC( 's', 't', 'y', 'l' ), + GF_ISOM_BOX_TYPE_HLIT = GF_4CC( 'h', 'l', 'i', 't' ), + GF_ISOM_BOX_TYPE_HCLR = GF_4CC( 'h', 'c', 'l', 'r' ), + GF_ISOM_BOX_TYPE_KROK = GF_4CC( 'k', 'r', 'o', 'k' ), + GF_ISOM_BOX_TYPE_DLAY = GF_4CC( 'd', 'l', 'a', 'y' ), + GF_ISOM_BOX_TYPE_HREF = GF_4CC( 'h', 'r', 'e', 'f' ), + GF_ISOM_BOX_TYPE_TBOX = GF_4CC( 't', 'b', 'o', 'x' ), + GF_ISOM_BOX_TYPE_BLNK = GF_4CC( 'b', 'l', 'n', 'k' ), + GF_ISOM_BOX_TYPE_TWRP = GF_4CC( 't', 'w', 'r', 'p' ), + + /* ISO Base Media File Format Extensions for MPEG-21 */ + GF_ISOM_BOX_TYPE_META = GF_4CC( 'm', 'e', 't', 'a' ), + GF_ISOM_BOX_TYPE_XML = GF_4CC( 'x', 'm', 'l', ' ' ), + GF_ISOM_BOX_TYPE_BXML = GF_4CC( 'b', 'x', 'm', 'l' ), + GF_ISOM_BOX_TYPE_ILOC = GF_4CC( 'i', 'l', 'o', 'c' ), + GF_ISOM_BOX_TYPE_PITM = GF_4CC( 'p', 'i', 't', 'm' ), + GF_ISOM_BOX_TYPE_IPRO = GF_4CC( 'i', 'p', 'r', 'o' ), + GF_ISOM_BOX_TYPE_INFE = GF_4CC( 'i', 'n', 'f', 'e' ), + GF_ISOM_BOX_TYPE_IINF = GF_4CC( 'i', 'i', 'n', 'f' ), + GF_ISOM_BOX_TYPE_ENCA = GF_4CC( 'e', 'n', 'c', 'a' ), + GF_ISOM_BOX_TYPE_ENCV = GF_4CC( 'e', 'n', 'c', 'v' ), + GF_ISOM_BOX_TYPE_ENCT = GF_4CC( 'e', 'n', 'c', 't' ), + GF_ISOM_BOX_TYPE_ENCS = GF_4CC( 'e', 'n', 'c', 's' ), + GF_ISOM_BOX_TYPE_SINF = GF_4CC( 's', 'i', 'n', 'f' ), + GF_ISOM_BOX_TYPE_FRMA = GF_4CC( 'f', 'r', 'm', 'a' ), + GF_ISOM_BOX_TYPE_SCHM = GF_4CC( 's', 'c', 'h', 'm' ), + GF_ISOM_BOX_TYPE_SCHI = GF_4CC( 's', 'c', 'h', 'i' ), + + GF_ISOM_BOX_TYPE_METX = GF_4CC( 'm', 'e', 't', 'x' ), + GF_ISOM_BOX_TYPE_METT = GF_4CC( 'm', 'e', 't', 't' ), + + /* ISMA 1.0 Encryption and Authentication V 1.0 */ + GF_ISOM_BOX_TYPE_IKMS = GF_4CC( 'i', 'K', 'M', 'S' ), + GF_ISOM_BOX_TYPE_ISFM = GF_4CC( 'i', 'S', 'F', 'M' ), + + /* Hinting boxes */ + GF_ISOM_BOX_TYPE_RTP_STSD = GF_4CC( 'r', 't', 'p', ' ' ), + GF_ISOM_BOX_TYPE_HNTI = GF_4CC( 'h', 'n', 't', 'i' ), + GF_ISOM_BOX_TYPE_RTP = GF_4CC( 'r', 't', 'p', ' ' ), + GF_ISOM_BOX_TYPE_SDP = GF_4CC( 's', 'd', 'p', ' ' ), + GF_ISOM_BOX_TYPE_HINF = GF_4CC( 'h', 'i', 'n', 'f' ), + GF_ISOM_BOX_TYPE_NAME = GF_4CC( 'n', 'a', 'm', 'e' ), + GF_ISOM_BOX_TYPE_TRPY = GF_4CC( 't', 'r', 'p', 'y' ), + GF_ISOM_BOX_TYPE_NUMP = GF_4CC( 'n', 'u', 'm', 'p' ), + GF_ISOM_BOX_TYPE_TOTL = GF_4CC( 't', 'o', 't', 'l' ), + GF_ISOM_BOX_TYPE_NPCK = GF_4CC( 'n', 'p', 'c', 'k' ), + GF_ISOM_BOX_TYPE_TPYL = GF_4CC( 't', 'p', 'y', 'l' ), + GF_ISOM_BOX_TYPE_TPAY = GF_4CC( 't', 'p', 'a', 'y' ), + GF_ISOM_BOX_TYPE_MAXR = GF_4CC( 'm', 'a', 'x', 'r' ), + GF_ISOM_BOX_TYPE_DMED = GF_4CC( 'd', 'm', 'e', 'd' ), + GF_ISOM_BOX_TYPE_DIMM = GF_4CC( 'd', 'i', 'm', 'm' ), + GF_ISOM_BOX_TYPE_DREP = GF_4CC( 'd', 'r', 'e', 'p' ), + GF_ISOM_BOX_TYPE_TMIN = GF_4CC( 't', 'm', 'i', 'n' ), + GF_ISOM_BOX_TYPE_TMAX = GF_4CC( 't', 'm', 'a', 'x' ), + GF_ISOM_BOX_TYPE_PMAX = GF_4CC( 'p', 'm', 'a', 'x' ), + GF_ISOM_BOX_TYPE_DMAX = GF_4CC( 'd', 'm', 'a', 'x' ), + GF_ISOM_BOX_TYPE_PAYT = GF_4CC( 'p', 'a', 'y', 't' ), + GF_ISOM_BOX_TYPE_RELY = GF_4CC( 'r', 'e', 'l', 'y' ), + GF_ISOM_BOX_TYPE_TIMS = GF_4CC( 't', 'i', 'm', 's' ), + GF_ISOM_BOX_TYPE_TSRO = GF_4CC( 't', 's', 'r', 'o' ), + GF_ISOM_BOX_TYPE_SNRO = GF_4CC( 's', 'n', 'r', 'o' ), + GF_ISOM_BOX_TYPE_RTPO = GF_4CC( 'r', 't', 'p', 'o' ), + + /*internal type for track references*/ + GF_ISOM_BOX_TYPE_REFT = GF_4CC( 'R', 'E', 'F', 'T' ), + + /* Apple extensions */ + + GF_ISOM_BOX_TYPE_ILST = GF_4CC( 'i', 'l', 's', 't' ), + GF_ISOM_BOX_TYPE_0xA9NAM = GF_4CC( 0xA9, 'n', 'a', 'm' ), + GF_ISOM_BOX_TYPE_0xA9CMT = GF_4CC( 0xA9, 'c', 'm', 't' ), + GF_ISOM_BOX_TYPE_0xA9DAY = GF_4CC( 0xA9, 'd', 'a', 'y' ), + GF_ISOM_BOX_TYPE_0xA9ART = GF_4CC( 0xA9, 'A', 'R', 'T' ), + GF_ISOM_BOX_TYPE_0xA9TRK = GF_4CC( 0xA9, 't', 'r', 'k' ), + GF_ISOM_BOX_TYPE_0xA9ALB = GF_4CC( 0xA9, 'a', 'l', 'b' ), + GF_ISOM_BOX_TYPE_0xA9COM = GF_4CC( 0xA9, 'c', 'o', 'm' ), + GF_ISOM_BOX_TYPE_0xA9WRT = GF_4CC( 0xA9, 'w', 'r', 't' ), + GF_ISOM_BOX_TYPE_0xA9TOO = GF_4CC( 0xA9, 't', 'o', 'o' ), + GF_ISOM_BOX_TYPE_0xA9CPY = GF_4CC( 0xA9, 'c', 'p', 'y' ), + GF_ISOM_BOX_TYPE_0xA9DES = GF_4CC( 0xA9, 'd', 'e', 's' ), + GF_ISOM_BOX_TYPE_0xA9GEN = GF_4CC( 0xA9, 'g', 'e', 'n' ), + GF_ISOM_BOX_TYPE_0xA9GRP = GF_4CC( 0xA9, 'g', 'r', 'p' ), + GF_ISOM_BOX_TYPE_0xA9ENC = GF_4CC( 0xA9, 'e', 'n', 'c' ), + GF_ISOM_BOX_TYPE_aART = GF_4CC( 'a', 'A', 'R', 'T' ), + GF_ISOM_BOX_TYPE_PGAP = GF_4CC( 'p', 'g', 'a', 'p' ), + GF_ISOM_BOX_TYPE_GNRE = GF_4CC( 'g', 'n', 'r', 'e' ), + GF_ISOM_BOX_TYPE_DISK = GF_4CC( 'd', 'i', 's', 'k' ), + GF_ISOM_BOX_TYPE_TRKN = GF_4CC( 't', 'r', 'k', 'n' ), + GF_ISOM_BOX_TYPE_TMPO = GF_4CC( 't', 'm', 'p', 'o' ), + GF_ISOM_BOX_TYPE_CPIL = GF_4CC( 'c', 'p', 'i', 'l' ), + GF_ISOM_BOX_TYPE_COVR = GF_4CC( 'c', 'o', 'v', 'r' ), + GF_ISOM_BOX_TYPE_iTunesSpecificInfo = GF_4CC( '-', '-', '-', '-' ), + GF_ISOM_BOX_TYPE_DATA = GF_4CC( 'd', 'a', 't', 'a' ), + + GF_ISOM_HANDLER_TYPE_MDIR = GF_4CC( 'm', 'd', 'i', 'r' ), + GF_ISOM_BOX_TYPE_CHAP = GF_4CC( 'c', 'h', 'a', 'p' ), + GF_ISOM_BOX_TYPE_TEXT = GF_4CC( 't', 'e', 'x', 't' ), + + /*OMA (P)DCF boxes*/ + GF_ISOM_BOX_TYPE_OHDR = GF_4CC( 'o', 'h', 'd', 'r' ), + GF_ISOM_BOX_TYPE_GRPI = GF_4CC( 'g', 'r', 'p', 'i' ), + GF_ISOM_BOX_TYPE_MDRI = GF_4CC( 'm', 'd', 'r', 'i' ), + GF_ISOM_BOX_TYPE_ODTT = GF_4CC( 'o', 'd', 't', 't' ), + GF_ISOM_BOX_TYPE_ODRB = GF_4CC( 'o', 'd', 'r', 'b' ), + GF_ISOM_BOX_TYPE_ODKM = GF_4CC( 'o', 'd', 'k', 'm' ), + GF_ISOM_BOX_TYPE_ODAF = GF_4CC( 'o', 'd', 'a', 'f' ), + + /*3GPP DIMS */ + GF_ISOM_BOX_TYPE_DIMS = GF_4CC( 'd', 'i', 'm', 's' ), + GF_ISOM_BOX_TYPE_DIMC = GF_4CC( 'd', 'i', 'm', 'C' ), + GF_ISOM_BOX_TYPE_DIST = GF_4CC( 'd', 'i', 'S', 'T' ), + + + GF_ISOM_BOX_TYPE_AC3 = GF_4CC( 'a', 'c', '-', '3' ), + GF_ISOM_BOX_TYPE_DAC3 = GF_4CC( 'd', 'a', 'c', '3' ), + + GF_ISOM_BOX_TYPE_SUBS = GF_4CC( 's', 'u', 'b', 's' ), + + GF_ISOM_BOX_TYPE_RVCC = GF_4CC( 'r', 'v', 'c', 'c' ), + + /*ALL INTERNAL BOXES - NEVER WRITTEN TO FILE!!*/ + + /*generic handlers*/ + GF_ISOM_BOX_TYPE_GNRM = GF_4CC( 'g', 'n', 'r', 'm' ), + GF_ISOM_BOX_TYPE_GNRV = GF_4CC( 'g', 'n', 'r', 'v' ), + GF_ISOM_BOX_TYPE_GNRA = GF_4CC( 'g', 'n', 'r', 'a' ), + /*storage of AU fragments (for MPEG-4 visual resync marker (video packets), located in stbl.*/ + GF_ISOM_BOX_TYPE_STSF = GF_4CC( 'S', 'T', 'S', 'F' ), + /*base constructor of all hint formats (currently only RTP uses it)*/ + GF_ISOM_BOX_TYPE_GHNT = GF_4CC( 'g', 'h', 'n', 't' ), + /*for compatibility with old files hinted for DSS - needs special parsing*/ + GF_ISOM_BOX_TYPE_VOID = GF_4CC( 'V', 'O', 'I', 'D' ), +}; + + +typedef struct +{ + GF_ISOM_BOX + /*note: the data is NEVER loaded to the mdat in this lib*/ + u64 dataSize; + char *data; +} GF_MediaDataBox; + +typedef struct +{ + GF_ISOM_BOX + char *data; + u32 dataSize; +} GF_UnknownBox; + +typedef struct +{ + GF_ISOM_UUID_BOX + char *data; + u32 dataSize; +} GF_UnknownUUIDBox; + +typedef struct +{ + GF_ISOM_FULL_BOX + u64 creationTime; + u64 modificationTime; + u32 timeScale; + u64 duration; + u32 nextTrackID; + u32 preferredRate; + u16 preferredVolume; + char reserved[10]; + u32 matrixA; + u32 matrixB; + u32 matrixU; + u32 matrixC; + u32 matrixD; + u32 matrixV; + u32 matrixW; + u32 matrixX; + u32 matrixY; + u32 previewTime; + u32 previewDuration; + u32 posterTime; + u32 selectionTime; + u32 selectionDuration; + u32 currentTime; +} GF_MovieHeaderBox; + +typedef struct +{ + GF_ISOM_FULL_BOX + GF_Descriptor *descriptor; +} GF_ObjectDescriptorBox; + +/*used for entry list*/ +typedef struct +{ + u64 segmentDuration; + s64 mediaTime; + u32 mediaRate; +} GF_EdtsEntry; + +typedef struct +{ + GF_ISOM_FULL_BOX + GF_List *entryList; +} GF_EditListBox; + +typedef struct +{ + GF_ISOM_BOX + GF_EditListBox *editList; +} GF_EditBox; + + +/*used to classify boxes in the UserData GF_Box*/ +typedef struct +{ + u32 boxType; + u8 uuid[16]; + GF_List *boxList; +} GF_UserDataMap; + +typedef struct +{ + GF_ISOM_BOX + GF_List *recordList; +} GF_UserDataBox; + +typedef struct +{ + GF_ISOM_BOX + GF_MovieHeaderBox *mvhd; + GF_ObjectDescriptorBox *iods; + GF_UserDataBox *udta; +#ifndef GPAC_DISABLE_ISOM_FRAGMENTS + struct __tag_mvex_box *mvex; +#endif + /*meta box if any*/ + struct __tag_meta_box *meta; + /*track boxes*/ + GF_List *trackList; + /*other boxes*/ + GF_List *boxes; + + GF_ISOFile *mov; + +} GF_MovieBox; + +typedef struct +{ + GF_ISOM_FULL_BOX + u64 creationTime; + u64 modificationTime; + u32 trackID; + u32 reserved1; + u64 duration; + u32 reserved2[2]; + u16 layer; + u16 alternate_group; + u16 volume; + u16 reserved3; + u32 matrix[9]; + u32 width, height; +} GF_TrackHeaderBox; + +typedef struct +{ + GF_ISOM_BOX + GF_List *boxList; +} GF_TrackReferenceBox; + + + +typedef struct +{ + GF_ISOM_BOX + GF_UserDataBox *udta; + GF_TrackHeaderBox *Header; + struct __tag_media_box *Media; + GF_EditBox *editBox; + GF_TrackReferenceBox *References; + /*meta box if any*/ + struct __tag_meta_box *meta; + /*other*/ + GF_List *boxes; + + GF_MovieBox *moov; + /*private for media padding*/ + u32 padding_bytes; + /*private for editing*/ + char *name; + /*private for editing*/ + Bool is_unpacked; + +#ifndef GPAC_DISABLE_ISOM_FRAGMENTS + u64 dts_at_seg_start; + u32 sample_count_at_seg_start; +#endif +} GF_TrackBox; + +typedef struct +{ + GF_ISOM_FULL_BOX + u64 creationTime; + u64 modificationTime; + u32 timeScale; + u64 duration; + char packedLanguage[4]; + u16 reserved; +} GF_MediaHeaderBox; + + +typedef struct +{ + GF_ISOM_FULL_BOX + u32 reserved1; + u32 handlerType; + u8 reserved2[12]; + char *nameUTF8; +} GF_HandlerBox; + +typedef struct __tag_media_box +{ + GF_ISOM_BOX + GF_TrackBox *mediaTrack; + GF_MediaHeaderBox *mediaHeader; + GF_HandlerBox *handler; + struct __tag_media_info_box *information; + u64 BytesMissing; +} GF_MediaBox; + +typedef struct +{ + GF_ISOM_FULL_BOX + u64 reserved; +} GF_VideoMediaHeaderBox; + +typedef struct +{ + GF_ISOM_FULL_BOX + u32 reserved; +} GF_SoundMediaHeaderBox; + +typedef struct +{ + GF_ISOM_FULL_BOX + /*this is used for us INTERNALLY*/ + u32 subType; + u32 maxPDUSize; + u32 avgPDUSize; + u32 maxBitrate; + u32 avgBitrate; + u32 slidingAverageBitrate; +} GF_HintMediaHeaderBox; + +typedef struct +{ + GF_ISOM_FULL_BOX +} GF_MPEGMediaHeaderBox; + +typedef struct +{ + GF_ISOM_FULL_BOX +} GF_ODMediaHeaderBox; + +typedef struct +{ + GF_ISOM_FULL_BOX +} GF_OCRMediaHeaderBox; + +typedef struct +{ + GF_ISOM_FULL_BOX +} GF_SceneMediaHeaderBox; + +typedef struct +{ + GF_ISOM_FULL_BOX + GF_List *boxList; +} GF_DataReferenceBox; + +typedef struct +{ + GF_ISOM_BOX + GF_DataReferenceBox *dref; +} GF_DataInformationBox; + +#define GF_ISOM_DATAENTRY_FIELDS \ + char *location; + +typedef struct +{ + GF_ISOM_FULL_BOX + GF_ISOM_DATAENTRY_FIELDS +} GF_DataEntryBox; + +typedef struct +{ + GF_ISOM_FULL_BOX + GF_ISOM_DATAENTRY_FIELDS +} GF_DataEntryURLBox; + +typedef struct +{ + GF_ISOM_FULL_BOX + GF_ISOM_DATAENTRY_FIELDS + char *nameURN; +} GF_DataEntryURNBox; + +typedef struct +{ + u32 sampleCount; + u32 sampleDelta; +} GF_SttsEntry; + +typedef struct +{ + GF_ISOM_FULL_BOX + GF_SttsEntry *entries; + u32 nb_entries, alloc_size; + +#ifndef GPAC_DISABLE_ISOM_WRITE + /*cache for WRITE*/ + u32 w_currentSampleNum; + u64 w_LastDTS; +#endif + /*cache for READ*/ + u32 r_FirstSampleInEntry; + u32 r_currentEntryIndex; + u64 r_CurrentDTS; +} GF_TimeToSampleBox; + + +/*TO CHECK - it could be reasonnable to only use 16bits for both count and offset*/ +typedef struct +{ + u32 sampleCount; + u32 decodingOffset; +} GF_DttsEntry; + +typedef struct +{ + GF_ISOM_FULL_BOX + GF_DttsEntry *entries; + u32 nb_entries, alloc_size; + +#ifndef GPAC_DISABLE_ISOM_WRITE + u32 w_LastSampleNumber; + /*force one sample per entry*/ + Bool unpack_mode; +#endif + /*Cache for read*/ + u32 r_currentEntryIndex; + u32 r_FirstSampleInEntry; +} GF_CompositionOffsetBox; + + +typedef struct +{ + u32 SampleNumber; + u32 fragmentCount; + u16 *fragmentSizes; +} GF_StsfEntry; + +typedef struct +{ + GF_ISOM_FULL_BOX + GF_List *entryList; +#ifndef GPAC_DISABLE_ISOM_WRITE + /*Cache for write*/ + GF_StsfEntry *w_currentEntry; + u32 w_currentEntryIndex; +#endif + /*Cache for read*/ + u32 r_currentEntryIndex; + GF_StsfEntry *r_currentEntry; +} GF_SampleFragmentBox; + + +#define GF_ISOM_SAMPLE_ENTRY_FIELDS \ + GF_ISOM_UUID_BOX \ + u16 dataReferenceIndex; \ + char reserved[ 6 ]; \ + struct __tag_protect_box *protection_info; + +/*base sample entry box (never used but for typecasting)*/ +typedef struct +{ + GF_ISOM_SAMPLE_ENTRY_FIELDS +} GF_SampleEntryBox; + +typedef struct +{ + GF_ISOM_SAMPLE_ENTRY_FIELDS + /*box type as specified in the file (not this box's type!!)*/ + u32 EntryType; + + char *data; + u32 data_size; +} GF_GenericSampleEntryBox; + +typedef struct +{ + GF_ISOM_FULL_BOX + GF_ESD *desc; +} GF_ESDBox; + +typedef struct +{ + GF_ISOM_BOX + u32 bufferSizeDB; + u32 maxBitrate; + u32 avgBitrate; +} GF_MPEG4BitRateBox; + +typedef struct +{ + GF_ISOM_BOX + GF_List *descriptors; +} GF_MPEG4ExtensionDescriptorsBox; + +/*for most MPEG4 media */ +typedef struct +{ + GF_ISOM_SAMPLE_ENTRY_FIELDS + GF_ESDBox *esd; + /*used for hinting when extracting the OD stream...*/ + GF_SLConfig *slc; +} GF_MPEGSampleEntryBox; + +typedef struct +{ + GF_ISOM_BOX + char *hdr; + u32 hdr_size; +} GF_LASERConfigurationBox; + + +typedef struct +{ + GF_ISOM_SAMPLE_ENTRY_FIELDS + + GF_LASERConfigurationBox *lsr_config; + GF_MPEG4BitRateBox *bitrate; + GF_MPEG4ExtensionDescriptorsBox *descr; + + /*used for hinting when extracting the OD stream...*/ + GF_SLConfig *slc; +} GF_LASeRSampleEntryBox; + +GF_Err LSR_UpdateESD(GF_LASeRSampleEntryBox *lsr, GF_ESD *esd); + +typedef struct +{ + GF_ISOM_BOX + u32 hSpacing; + u32 vSpacing; +} GF_PixelAspectRatioBox; + +typedef struct +{ + GF_ISOM_BOX + u16 predefined_rvc_config; + u32 rvc_meta_idx; +} GF_RVCConfigurationBox; + +#define GF_ISOM_VISUAL_SAMPLE_ENTRY \ + GF_ISOM_SAMPLE_ENTRY_FIELDS \ + u16 version; \ + u16 revision; \ + u32 vendor; \ + u32 temporal_quality; \ + u32 spacial_quality; \ + u16 Width, Height; \ + u32 horiz_res, vert_res; \ + u32 entry_data_size; \ + u16 frames_per_sample; \ + char compressor_name[33]; \ + u16 bit_depth; \ + s16 color_table_index; \ + GF_PixelAspectRatioBox *pasp; \ + GF_RVCConfigurationBox *rvcc; \ + +typedef struct +{ + GF_ISOM_VISUAL_SAMPLE_ENTRY +} GF_VisualSampleEntryBox; + +void gf_isom_video_sample_entry_init(GF_VisualSampleEntryBox *ent); +GF_Err gf_isom_video_sample_entry_read(GF_VisualSampleEntryBox *ptr, GF_BitStream *bs); +#ifndef GPAC_DISABLE_ISOM_WRITE +void gf_isom_video_sample_entry_write(GF_VisualSampleEntryBox *ent, GF_BitStream *bs); +void gf_isom_video_sample_entry_size(GF_VisualSampleEntryBox *ent); +#endif + +typedef struct +{ + GF_ISOM_BOX + GF_AVCConfig *config; +} GF_AVCConfigurationBox; + +typedef struct +{ + GF_ISOM_VISUAL_SAMPLE_ENTRY + GF_ESDBox *esd; + /*used for Publishing*/ + GF_SLConfig *slc; + + /*avc extensions - we merged with regular 'mp4v' box to handle isma E&A signaling of AVC*/ + GF_AVCConfigurationBox *avc_config; + GF_AVCConfigurationBox *svc_config; + GF_MPEG4BitRateBox *bitrate; + /*ext descriptors*/ + GF_MPEG4ExtensionDescriptorsBox *descr; + /*internally emulated esd*/ + GF_ESD *emul_esd; + /*iPod's hack*/ + GF_UnknownUUIDBox *ipod_ext; + +} GF_MPEGVisualSampleEntryBox; + + +/*this is the default visual sdst (to handle unknown media)*/ +typedef struct +{ + GF_ISOM_VISUAL_SAMPLE_ENTRY + /*box type as specified in the file (not this box's type!!)*/ + u32 EntryType; + /*opaque description data (ESDS in MP4, SMI in SVQ3, ...)*/ + char *data; + u32 data_size; +} GF_GenericVisualSampleEntryBox; + + +#define GF_ISOM_AUDIO_SAMPLE_ENTRY \ + GF_ISOM_SAMPLE_ENTRY_FIELDS \ + u16 version; \ + u16 revision; \ + u32 vendor; \ + u16 channel_count; \ + u16 bitspersample; \ + u16 compression_id; \ + u16 packet_size; \ + u16 samplerate_hi; \ + u16 samplerate_lo; + + +typedef struct +{ + GF_ISOM_AUDIO_SAMPLE_ENTRY +} GF_AudioSampleEntryBox; + +void gf_isom_audio_sample_entry_init(GF_AudioSampleEntryBox *ptr); +GF_Err gf_isom_audio_sample_entry_read(GF_AudioSampleEntryBox *ptr, GF_BitStream *bs); +#ifndef GPAC_DISABLE_ISOM_WRITE +void gf_isom_audio_sample_entry_write(GF_AudioSampleEntryBox *ptr, GF_BitStream *bs); +void gf_isom_audio_sample_entry_size(GF_AudioSampleEntryBox *ptr); +#endif + + +typedef struct +{ + GF_ISOM_AUDIO_SAMPLE_ENTRY + GF_ESDBox *esd; + GF_SLConfig *slc; +} GF_MPEGAudioSampleEntryBox; + +typedef struct +{ + GF_ISOM_BOX + GF_3GPConfig cfg; +} GF_3GPPConfigBox; + +typedef struct +{ + GF_ISOM_AUDIO_SAMPLE_ENTRY + GF_3GPPConfigBox *info; +} GF_3GPPAudioSampleEntryBox; + +typedef struct +{ + GF_ISOM_VISUAL_SAMPLE_ENTRY + GF_3GPPConfigBox *info; +} GF_3GPPVisualSampleEntryBox; + +/*this is the default visual sdst (to handle unknown media)*/ +typedef struct +{ + GF_ISOM_AUDIO_SAMPLE_ENTRY + /*box type as specified in the file (not this box's type!!)*/ + u32 EntryType; + /*opaque description data (ESDS in MP4, ...)*/ + char *data; + u32 data_size; +} GF_GenericAudioSampleEntryBox; + +typedef struct +{ + GF_ISOM_BOX + GF_AC3Config cfg; +} GF_AC3ConfigBox; + +typedef struct +{ + GF_ISOM_AUDIO_SAMPLE_ENTRY + GF_AC3ConfigBox *info; +} GF_AC3SampleEntryBox; + + + + +typedef struct +{ + GF_ISOM_FULL_BOX + u8 profile; + u8 level; + u8 pathComponents; + Bool fullRequestHost; + Bool streamType; + u8 containsRedundant; + char *textEncoding; + char *contentEncoding; +} GF_DIMSSceneConfigBox; + +typedef struct +{ + GF_ISOM_BOX + char *content_script_types; +} GF_DIMSScriptTypesBox; + +typedef struct +{ + GF_ISOM_SAMPLE_ENTRY_FIELDS + GF_DIMSSceneConfigBox *config; + GF_MPEG4BitRateBox *bitrate; + GF_DIMSScriptTypesBox *scripts; +} GF_DIMSSampleEntryBox; + + +/*base sample entry box (never used but for typecasting)*/ +typedef struct +{ + GF_ISOM_SAMPLE_ENTRY_FIELDS + char *content_encoding; //optional + char *mime_type_or_namespace; //not optional + char *xml_schema_loc; // optional + GF_MPEG4BitRateBox *bitrate; // optional +} GF_MetaDataSampleEntryBox; + +typedef struct +{ + GF_ISOM_FULL_BOX + GF_List *boxList; +} GF_SampleDescriptionBox; + + +typedef struct +{ + GF_ISOM_FULL_BOX + /*if this is the compact version, sample size is actually fieldSize*/ + u32 sampleSize; + u32 sampleCount; + u32 alloc_size; + u32 *sizes; +} GF_SampleSizeBox; + +typedef struct +{ + GF_ISOM_FULL_BOX + u32 nb_entries; + u32 alloc_size; + u32 *offsets; +} GF_ChunkOffsetBox; + +typedef struct +{ + GF_ISOM_FULL_BOX + u32 nb_entries; + u32 alloc_size; + u64 *offsets; +} GF_ChunkLargeOffsetBox; + +typedef struct +{ + u32 firstChunk; + u32 nextChunk; + u32 samplesPerChunk; + u32 sampleDescriptionIndex; + u8 isEdited; +} GF_StscEntry; + +typedef struct +{ + GF_ISOM_FULL_BOX + GF_StscEntry *entries; + u32 alloc_size, nb_entries; + + /*0-based cache for READ. In WRITE mode, we always have 1 sample per chunk so no need for a cache*/ + u32 currentIndex; + /*first sample number in this chunk*/ + u32 firstSampleInCurrentChunk; + u32 currentChunk; + u32 ghostNumber; +} GF_SampleToChunkBox; + +typedef struct +{ + GF_ISOM_FULL_BOX + u32 alloc_size, nb_entries; + u32 *sampleNumbers; + /*cache for READ mode (in write we realloc no matter what)*/ + u32 r_LastSyncSample; + /*0-based index in the array*/ + u32 r_LastSampleIndex; +} GF_SyncSampleBox; + +typedef struct +{ + u32 shadowedSampleNumber; + s32 syncSampleNumber; +} GF_StshEntry; + +typedef struct +{ + GF_ISOM_FULL_BOX + GF_List *entries; + /*Cache for read mode*/ + u32 r_LastEntryIndex; + u32 r_LastFoundSample; +} GF_ShadowSyncBox; + +typedef struct +{ + GF_ISOM_FULL_BOX + u32 nb_entries; + u16 *priorities; +} GF_DegradationPriorityBox; + +typedef struct +{ + GF_ISOM_FULL_BOX + u32 SampleCount; + u8 *padbits; +} GF_PaddingBitsBox; + +typedef struct +{ + GF_ISOM_FULL_BOX + u32 sampleCount; + /*each dep type is packed on 1 byte*/ + u8 *sample_info; +} GF_SampleDependencyTypeBox; + + +typedef struct +{ + u32 sample_delta; + GF_List *SubSamples; +} GF_SampleEntry; + +typedef struct +{ + u32 subsample_size; + u8 subsample_priority; + u8 discardable; + u32 reserved; +} GF_SubSampleEntry; + +typedef struct +{ + GF_ISOM_FULL_BOX + GF_List *Samples; +} GF_SubSampleInformationBox; + +u32 gf_isom_sample_get_subsample_entry(GF_ISOFile *movie, u32 track, u32 sampleNumber, GF_SampleEntry **sub_sample); +#ifndef GPAC_DISABLE_ISOM_WRITE +GF_Err gf_isom_add_subsample_info(GF_SubSampleInformationBox *sub_samples, u32 sampleNumber, u32 subSampleSize, u8 priority, u32 reserved, Bool discardable); +#endif + +typedef struct +{ + GF_ISOM_BOX + GF_TimeToSampleBox *TimeToSample; + GF_CompositionOffsetBox *CompositionOffset; + GF_SyncSampleBox *SyncSample; + GF_SampleDescriptionBox *SampleDescription; + GF_SampleSizeBox *SampleSize; + GF_SampleToChunkBox *SampleToChunk; + /*untyped, to handle 32 bits and 64 bits chunkOffsets*/ + GF_Box *ChunkOffset; + GF_ShadowSyncBox *ShadowSync; + GF_DegradationPriorityBox *DegradationPriority; + GF_PaddingBitsBox *PaddingBits; + GF_SampleDependencyTypeBox *SampleDep; + GF_SampleFragmentBox *Fragments; + + GF_SubSampleInformationBox *SubSamples; + + u32 MaxSamplePerChunk; + u16 groupID; + u16 trackPriority; + u32 currentEntryIndex; +} GF_SampleTableBox; + +typedef struct __tag_media_info_box +{ + GF_ISOM_BOX + GF_DataInformationBox *dataInformation; + GF_SampleTableBox *sampleTable; + GF_Box *InfoHeader; + struct __tag_data_map *dataHandler; + u32 dataEntryIndex; + GF_List *boxes; +} GF_MediaInformationBox; + + +typedef struct +{ + GF_ISOM_BOX + char *data; + u32 dataSize; +} GF_FreeSpaceBox; + +typedef struct +{ + GF_ISOM_FULL_BOX + char packedLanguageCode[4]; + char *notice; +} GF_CopyrightBox; + + +typedef struct +{ + char *name; + u64 start_time; +} GF_ChapterEntry; + +typedef struct +{ + GF_ISOM_FULL_BOX + GF_List *list; +} GF_ChapterListBox; + +typedef struct +{ + GF_ISOM_BOX + u32 reference_type; + u32 trackIDCount; + u32 *trackIDs; +} GF_TrackReferenceTypeBox; + +typedef struct +{ + GF_ISOM_BOX + u32 majorBrand; + u32 minorVersion; + u32 altCount; + u32 *altBrand; +} GF_FileTypeBox; + +typedef struct +{ + GF_ISOM_FULL_BOX + u32 *rates; + u32 *times; + u32 count; +} GF_ProgressiveDownloadBox; + + +/* + 3GPP streaming text boxes +*/ + +typedef struct +{ + GF_ISOM_BOX + u32 entry_count; + GF_FontRecord *fonts; +} GF_FontTableBox; + +typedef struct +{ + GF_ISOM_SAMPLE_ENTRY_FIELDS \ + u32 displayFlags; + s8 horizontal_justification; + s8 vertical_justification; + /*ARGB*/ + u32 back_color; + GF_BoxRecord default_box; + GF_StyleRecord default_style; + GF_FontTableBox *font_table; +} GF_Tx3gSampleEntryBox; + +/*Apple specific*/ +typedef struct +{ + GF_ISOM_SAMPLE_ENTRY_FIELDS \ + u32 displayFlags; + u32 textJustification; + char background_color[6], foreground_color[6]; + GF_BoxRecord default_box; + u16 fontNumber; + u16 fontFace; + char reserved1[8]; + u8 reserved2; + u16 reserved3; + char *textName; /*font name*/ +} GF_TextSampleEntryBox; + +typedef struct +{ + GF_ISOM_BOX + u32 entry_count; + GF_StyleRecord *styles; +} GF_TextStyleBox; + +typedef struct +{ + GF_ISOM_BOX + u16 startcharoffset; + u16 endcharoffset; +} GF_TextHighlightBox; + +typedef struct +{ + GF_ISOM_BOX + /*ARGB*/ + u32 hil_color; +} GF_TextHighlightColorBox; + +typedef struct +{ + u32 highlight_endtime; + u16 start_charoffset; + u16 end_charoffset; +} KaraokeRecord; + +typedef struct +{ + GF_ISOM_BOX + u32 highlight_starttime; + u16 nb_entries; + KaraokeRecord *records; +} GF_TextKaraokeBox; + +typedef struct +{ + GF_ISOM_BOX + u32 scroll_delay; +} GF_TextScrollDelayBox; + +typedef struct +{ + GF_ISOM_BOX + u16 startcharoffset; + u16 endcharoffset; + char *URL; + char *URL_hint; +} GF_TextHyperTextBox; + +typedef struct +{ + GF_ISOM_BOX + GF_BoxRecord box; +} GF_TextBoxBox; + +typedef struct +{ + GF_ISOM_BOX + u16 startcharoffset; + u16 endcharoffset; +} GF_TextBlinkBox; + +typedef struct +{ + GF_ISOM_BOX + u8 wrap_flag; +} GF_TextWrapBox; + +typedef struct +{ + GF_ISOM_FULL_BOX + u32 switchGroup; + u32 *attributeList; + u32 attributeListCount; +} GF_TrackSelectionBox; + +/* + MPEG-21 extensions +*/ +typedef struct +{ + GF_ISOM_FULL_BOX + u32 xml_length; + char *xml; +} GF_XMLBox; + +typedef struct +{ + GF_ISOM_FULL_BOX + u32 data_length; + char *data; +} GF_BinaryXMLBox; + +typedef struct +{ + u64 extent_offset; + u64 extent_length; +#ifndef GPAC_DISABLE_ISOM_WRITE + /*for storage only*/ + u64 original_extent_offset; +#endif +} GF_ItemExtentEntry; + +typedef struct +{ + u16 item_ID; + u16 data_reference_index; + u64 base_offset; +#ifndef GPAC_DISABLE_ISOM_WRITE + /*for storage only*/ + u64 original_base_offset; +#endif + GF_List *extent_entries; +} GF_ItemLocationEntry; + +typedef struct +{ + GF_ISOM_FULL_BOX + u8 offset_size; + u8 length_size; + u8 base_offset_size; + GF_List *location_entries; +} GF_ItemLocationBox; + +typedef struct +{ + GF_ISOM_FULL_BOX + u16 item_ID; +} GF_PrimaryItemBox; + +typedef struct +{ + GF_ISOM_FULL_BOX + GF_List *protection_information; +} GF_ItemProtectionBox; + +typedef struct +{ + GF_ISOM_FULL_BOX + u16 item_ID; + u16 item_protection_index; + /*zero-terminated strings*/ + char *item_name; + char *content_type; + char *content_encoding; + // needed to actually read the resource file, but not written in the MP21 file. + char *full_path; + // if not 0, full_path is actually the data to write. + u32 data_len; +} GF_ItemInfoEntryBox; + +typedef struct +{ + GF_ISOM_FULL_BOX + GF_List *item_infos; +} GF_ItemInfoBox; + +typedef struct +{ + GF_ISOM_BOX + u32 data_format; +} GF_OriginalFormatBox; + +typedef struct +{ + GF_ISOM_FULL_BOX + u32 scheme_type; + u32 scheme_version; + char *URI; +} GF_SchemeTypeBox; + +/*ISMACryp specific*/ +typedef struct +{ + GF_ISOM_FULL_BOX + /*zero-terminated string*/ + char *URI; +} GF_ISMAKMSBox; + +/*ISMACryp specific*/ +typedef struct __isma_format_box +{ + GF_ISOM_FULL_BOX + u8 selective_encryption; + u8 key_indicator_length; + u8 IV_length; +} GF_ISMASampleFormatBox; + +typedef struct +{ + GF_ISOM_BOX + GF_ISMAKMSBox *ikms; + GF_ISMASampleFormatBox *isfm; + struct __oma_kms_box *okms; +} GF_SchemeInformationBox; + +typedef struct __tag_protect_box +{ + GF_ISOM_BOX + GF_OriginalFormatBox *original_format; + GF_SchemeTypeBox *scheme_type; + GF_SchemeInformationBox *info; +} GF_ProtectionInfoBox; + +typedef struct +{ + GF_ISOM_FULL_BOX + GF_List *descriptors; +} GF_IPMPInfoBox; + +typedef struct +{ + GF_ISOM_FULL_BOX + GF_IPMP_ToolList *ipmp_tools; + GF_List *descriptors; +} GF_IPMPControlBox; + + +typedef struct __tag_meta_box +{ + GF_ISOM_FULL_BOX + GF_HandlerBox *handler; + GF_PrimaryItemBox *primary_resource; + GF_DataInformationBox *file_locations; + GF_ItemLocationBox *item_locations; + GF_ItemProtectionBox *protections; + GF_ItemInfoBox *item_infos; + GF_IPMPControlBox *IPMP_control; + GF_List *other_boxes; +} GF_MetaBox; + + + + +#ifndef GPAC_DISABLE_ISOM_FRAGMENTS + +/*V2 boxes - Movie Fragments*/ + +typedef struct +{ + GF_ISOM_FULL_BOX + u64 fragment_duration; +} GF_MovieExtendsHeaderBox; + + +typedef struct __tag_mvex_box +{ + GF_ISOM_BOX + GF_List *TrackExList; + GF_MovieExtendsHeaderBox *mehd; + GF_ISOFile *mov; +} GF_MovieExtendsBox; + +/*the TrackExtends contains default values for the track fragments*/ +typedef struct +{ + GF_ISOM_FULL_BOX + u32 trackID; + u32 def_sample_desc_index; + u32 def_sample_duration; + u32 def_sample_size; + u32 def_sample_flags; + GF_TrackBox *track; +} GF_TrackExtendsBox; + +/*indicates the seq num of this fragment*/ +typedef struct +{ + GF_ISOM_FULL_BOX + u32 sequence_number; +} GF_MovieFragmentHeaderBox; + +/*MovieFragment is a container IN THE FILE, contains 1 fragment*/ +typedef struct +{ + GF_ISOM_BOX + GF_MovieFragmentHeaderBox *mfhd; + GF_List *TrackList; + GF_ISOFile *mov; + /*offset in the file of moof or mdat (whichever comes first) for this fragment*/ + u64 fragment_offset; + u32 mdat_size; + char *mdat; +} GF_MovieFragmentBox; + + +/*FLAGS for TRAF*/ +enum +{ + GF_ISOM_TRAF_BASE_OFFSET = 0x01, + GF_ISOM_TRAF_SAMPLE_DESC = 0x02, + GF_ISOM_TRAF_SAMPLE_DUR = 0x08, + GF_ISOM_TRAF_SAMPLE_SIZE = 0x10, + GF_ISOM_TRAF_SAMPLE_FLAGS = 0x20, + GF_ISOM_TRAF_DUR_EMPTY = 0x10000 +}; + +typedef struct +{ + GF_ISOM_FULL_BOX + u32 trackID; + /* all the following are optional fields */ + u64 base_data_offset; + u32 sample_desc_index; + u32 def_sample_duration; + u32 def_sample_size; + u32 def_sample_flags; + u32 EmptyDuration; + u8 IFrameSwitching; +} GF_TrackFragmentHeaderBox; + + +typedef struct +{ + GF_ISOM_FULL_BOX + u64 baseMediaDecodeTime; +} GF_TFBaseMediaDecodeTimeBox; + +typedef struct +{ + GF_ISOM_BOX + GF_TrackFragmentHeaderBox *tfhd; + GF_List *TrackRuns; + /*keep a pointer to default flags*/ + GF_TrackExtendsBox *trex; + GF_SampleDependencyTypeBox *sdtp; + GF_SubSampleInformationBox *subs; + /*when data caching is on*/ + u32 DataCache; + GF_TFBaseMediaDecodeTimeBox *tfdt; +} GF_TrackFragmentBox; + +/*FLAGS for TRUN : specify what is written in the SampleTable of TRUN*/ +enum +{ + GF_ISOM_TRUN_DATA_OFFSET = 0x01, + GF_ISOM_TRUN_FIRST_FLAG = 0x04, + GF_ISOM_TRUN_DURATION = 0x100, + GF_ISOM_TRUN_SIZE = 0x200, + GF_ISOM_TRUN_FLAGS = 0x400, + GF_ISOM_TRUN_CTS_OFFSET = 0x800 +}; + +typedef struct +{ + GF_ISOM_FULL_BOX + u32 sample_count; + /*the following are optional fields */ + s32 data_offset; + u32 first_sample_flags; + /*can be empty*/ + GF_List *entries; + + /*in write mode with data caching*/ + GF_BitStream *cache; +} GF_TrackFragmentRunBox; + +typedef struct +{ + u32 Duration; + u32 size; + u32 flags; + u32 CTS_Offset; +} GF_TrunEntry; + +typedef struct +{ + GF_ISOM_BOX + u32 majorBrand; + u32 minorVersion; + u32 altCount; + u32 *altBrand; +} GF_SegmentTypeBox; + +#endif /*GPAC_DISABLE_ISOM_FRAGMENTS*/ + + +/*RTP Hint Track Sample Entry*/ +typedef struct +{ + GF_ISOM_SAMPLE_ENTRY_FIELDS + u16 HintTrackVersion; + u16 LastCompatibleVersion; + u32 MaxPacketSize; + GF_List *HintDataTable; + /*this is where we store the current RTP sample in read/write mode*/ + struct __tag_hint_sample *hint_sample; + /*current hint sample in read mode, 1-based (0 is reset)*/ + u32 cur_sample; + u32 pck_sn, ts_offset, ssrc; + GF_TrackReferenceTypeBox *hint_ref; +} GF_HintSampleEntryBox; + + +typedef struct +{ + GF_ISOM_BOX + u32 subType; + char *sdpText; +} GF_RTPBox; + +typedef struct +{ + GF_ISOM_BOX + char *sdpText; +} GF_SDPBox; + +typedef struct +{ + GF_ISOM_BOX + s32 timeOffset; +} GF_RTPOBox; + +typedef struct +{ + GF_ISOM_BOX + /*contains GF_SDPBox if in track, GF_RTPBox if in movie*/ + GF_Box *SDP; + GF_List *boxList; +} GF_HintTrackInfoBox; + +typedef struct +{ + GF_ISOM_BOX + u8 reserved; + u8 prefered; + u8 required; +} GF_RelyHintBox; + +/*********************************************************** + data entry tables for RTP +***********************************************************/ +typedef struct +{ + GF_ISOM_BOX + u32 timeScale; +} GF_TSHintEntryBox; + +typedef struct +{ + GF_ISOM_BOX + u32 TimeOffset; +} GF_TimeOffHintEntryBox; + +typedef struct +{ + GF_ISOM_BOX + u32 SeqOffset; +} GF_SeqOffHintEntryBox; + + + +/*********************************************************** + hint track information boxes for RTP +***********************************************************/ + +/*Total number of bytes that will be sent, including 12-byte RTP headers, but not including any network headers*/ +typedef struct +{ + GF_ISOM_BOX + u64 nbBytes; +} GF_TRPYBox; + +/*32-bits version of trpy used in Darwin*/ +typedef struct +{ + GF_ISOM_BOX + u32 nbBytes; +} GF_TOTLBox; + +/*Total number of network packets that will be sent*/ +typedef struct +{ + GF_ISOM_BOX + u64 nbPackets; +} GF_NUMPBox; + +/*32-bits version of nump used in Darwin*/ +typedef struct +{ + GF_ISOM_BOX + u32 nbPackets; +} GF_NPCKBox; + + +/*Total number of bytes that will be sent, not including 12-byte RTP headers*/ +typedef struct +{ + GF_ISOM_BOX + u64 nbBytes; +} GF_NTYLBox; + +/*32-bits version of tpyl used in Darwin*/ +typedef struct +{ + GF_ISOM_BOX + u32 nbBytes; +} GF_TPAYBox; + +/*Maximum data rate in bits per second.*/ +typedef struct +{ + GF_ISOM_BOX + u32 granularity; + u32 maxDataRate; +} GF_MAXRBox; + + +/*Total number of bytes from the media track to be sent*/ +typedef struct +{ + GF_ISOM_BOX + u64 nbBytes; +} GF_DMEDBox; + +/*Number of bytes of immediate data to be sent*/ +typedef struct +{ + GF_ISOM_BOX + u64 nbBytes; +} GF_DIMMBox; + + +/*Number of bytes of repeated data to be sent*/ +typedef struct +{ + GF_ISOM_BOX + u64 nbBytes; +} GF_DREPBox; + +/*Smallest relative transmission time, in milliseconds. signed integer for smoothing*/ +typedef struct +{ + GF_ISOM_BOX + s32 minTime; +} GF_TMINBox; + +/*Largest relative transmission time, in milliseconds.*/ +typedef struct +{ + GF_ISOM_BOX + s32 maxTime; +} GF_TMAXBox; + +/*Largest packet, in bytes, including 12-byte RTP header*/ +typedef struct +{ + GF_ISOM_BOX + u32 maxSize; +} GF_PMAXBox; + +/*Longest packet duration, in milliseconds*/ +typedef struct +{ + GF_ISOM_BOX + u32 maxDur; +} GF_DMAXBox; + +/*32-bit payload type number, followed by rtpmap payload string */ +typedef struct +{ + GF_ISOM_BOX + u32 payloadCode; + char *payloadString; +} GF_PAYTBox; + + +typedef struct +{ + GF_ISOM_BOX + char *string; +} GF_NameBox; + +typedef struct +{ + GF_ISOM_BOX + GF_List *dataRates; + GF_List *boxList; +} GF_HintInfoBox; + +/*Apple extension*/ + +typedef struct +{ + GF_ISOM_FULL_BOX + u32 reserved; + char *data; + u32 dataSize; +} GF_DataBox; + +typedef struct +{ + GF_ISOM_BOX + GF_DataBox *data; +} GF_ListItemBox; + +typedef struct +{ + GF_ISOM_BOX + GF_List *tags; +} GF_ItemListBox; + +/*OMA (P)DCF extensions*/ +typedef struct +{ + GF_ISOM_FULL_BOX + u8 EncryptionMethod; + u8 PaddingScheme; + u64 PlaintextLength; + char *ContentID; + char *RightsIssuerURL; + char *TextualHeaders; + u32 TextualHeadersLen; + GF_List *ExtendedHeaders; +} GF_OMADRMCommonHeaderBox; + +typedef struct +{ + GF_ISOM_FULL_BOX + u8 GKEncryptionMethod; + char *GroupID; + u16 GKLength; + char *GroupKey; +} GF_OMADRMGroupIDBox; + +typedef struct +{ + GF_ISOM_BOX + GF_List *boxes; +} GF_OMADRMMutableInformationBox; + +typedef struct +{ + GF_ISOM_FULL_BOX + char TransactionID[16]; +} GF_OMADRMTransactionTrackingBox; + +typedef struct +{ + GF_ISOM_FULL_BOX + char *oma_ro; + u32 oma_ro_size; +} GF_OMADRMRightsObjectBox; + +/*identical*/ +typedef struct __isma_format_box GF_OMADRMAUFormatBox; + +typedef struct __oma_kms_box +{ + GF_ISOM_FULL_BOX + GF_OMADRMCommonHeaderBox *hdr; + GF_OMADRMAUFormatBox *fmt; +} GF_OMADRMKMSBox; + +#ifndef GPAC_DISABLE_ISOM_FRAGMENTS + +typedef struct +{ + Bool reference_type; + u32 reference_size; + u32 subsegment_duration; + Bool contains_RAP; + u32 RAP_delta_time; +} GF_SIDXReference; + +typedef struct __sidx_box +{ + GF_ISOM_FULL_BOX + + u32 reference_ID; + u32 timescale; + u64 earliest_presentation_time; + u64 first_offset; + u32 nb_refs; + GF_SIDXReference *refs; +} GF_SegmentIndexBox; +#endif + +/* + Data Map (media storage) stuff +*/ + +/*regular file IO*/ +#define GF_ISOM_DATA_FILE 0x01 +/*File Mapaing object, read-only mode on complete files (no download)*/ +#define GF_ISOM_DATA_FILE_MAPPING 0x02 +/*External file object. Needs implementation*/ +#define GF_ISOM_DATA_FILE_EXTERN 0x03 + +/*Data Map modes*/ +enum +{ + /*read mode*/ + GF_ISOM_DATA_MAP_READ = 1, + /*write mode*/ + GF_ISOM_DATA_MAP_WRITE = 2, + /*the following modes are just ways of signaling extended functionalities + edit mode, to make sure the file is here, set to GF_ISOM_DATA_MAP_READ afterwards*/ + GF_ISOM_DATA_MAP_EDIT = 3, + /*read-only access to the movie file: we create a file mapping object + mode is set to GF_ISOM_DATA_MAP_READ afterwards*/ + GF_ISOM_DATA_MAP_READ_ONLY = 4, + /*write-only access at the end of the movie - only used for movie fragments concatenation*/ + GF_ISOM_DATA_MAP_CAT = 5, +}; + +/*this is the DataHandler structure each data handler has its own bitstream*/ +#define GF_ISOM_BASE_DATA_HANDLER \ + u8 type; \ + u64 curPos; \ + u8 mode; \ + GF_BitStream *bs; + +typedef struct __tag_data_map +{ + GF_ISOM_BASE_DATA_HANDLER +} GF_DataMap; + +typedef struct +{ + GF_ISOM_BASE_DATA_HANDLER + FILE *stream; + Bool last_acces_was_read; +#ifndef GPAC_DISABLE_ISOM_WRITE + char *temp_file; +#endif +} GF_FileDataMap; + +/*file mapping handler. used if supported, only on read mode for complete files (not in file download)*/ +typedef struct +{ + GF_ISOM_BASE_DATA_HANDLER + char *name; + u64 file_size; + char *byte_map; + u64 byte_pos; +} GF_FileMappingDataMap; + +GF_Err gf_isom_datamap_new(const char *location, const char *parentPath, u8 mode, GF_DataMap **outDataMap); +void gf_isom_datamap_del(GF_DataMap *ptr); +GF_Err gf_isom_datamap_open(GF_MediaBox *minf, u32 dataRefIndex, u8 Edit); +void gf_isom_datamap_close(GF_MediaInformationBox *minf); +u32 gf_isom_datamap_get_data(GF_DataMap *map, char *buffer, u32 bufferLength, u64 Offset); + +/*File-based data map*/ +GF_DataMap *gf_isom_fdm_new(const char *sPath, u8 mode); +void gf_isom_fdm_del(GF_FileDataMap *ptr); +u32 gf_isom_fdm_get_data(GF_FileDataMap *ptr, char *buffer, u32 bufferLength, u64 fileOffset); + +#ifndef GPAC_DISABLE_ISOM_WRITE +GF_DataMap *gf_isom_fdm_new_temp(const char *sTempPath); +#endif + +/*file-mapping, read only*/ +GF_DataMap *gf_isom_fmo_new(const char *sPath, u8 mode); +void gf_isom_fmo_del(GF_FileMappingDataMap *ptr); +u32 gf_isom_fmo_get_data(GF_FileMappingDataMap *ptr, char *buffer, u32 bufferLength, u64 fileOffset); + +#ifndef GPAC_DISABLE_ISOM_WRITE +u64 gf_isom_datamap_get_offset(GF_DataMap *map); +GF_Err gf_isom_datamap_add_data(GF_DataMap *ptr, char *data, u32 dataSize); +#endif + +/* + Movie stuff +*/ + + +/*time def for MP4/QT/MJ2K files*/ +#define GF_ISOM_MAC_TIME_OFFSET 2082844800 + +#ifndef GPAC_DISABLE_ISOM_FRAGMENTS +#define GF_ISOM_FORMAT_FRAG_FLAGS(pad, sync, deg) ( ( (pad) << 17) | ( ( !(sync) ) << 16) | (deg) ); +#define GF_ISOM_GET_FRAG_PAD(flag) ( (flag) >> 17) & 0x7 +#define GF_ISOM_GET_FRAG_SYNC(flag) ( ! ( ( (flag) >> 16) & 0x1)) +#define GF_ISOM_GET_FRAG_DEG(flag) (flag) & 0x7FFF + +GF_TrackExtendsBox *GetTrex(GF_MovieBox *moov, u32 TrackID); +#endif + +enum +{ + GF_ISOM_FRAG_WRITE_READY = 0x01, + GF_ISOM_FRAG_READ_DEBUG = 0x02, +}; + +/*this is our movie object*/ +struct __tag_isom { + /*the last fatal error*/ + GF_Err LastError; + /*the original filename*/ + char *fileName; + /*the original file in read/edit, and also used in fragments mode + once the first moov has been written + Nota: this API doesn't allow fragments BEFORE the MOOV in order + to make easily parsable files (note there could be some data (mdat) before + the moov*/ + GF_DataMap *movieFileMap; + +#ifndef GPAC_DISABLE_ISOM_WRITE + /*the final file name*/ + char *finalName; + /*the file where we store edited samples (for READ_WRITE and WRITE mode only)*/ + GF_DataMap *editFileMap; + /*the interleaving time for dummy mode (in movie TimeScale)*/ + u32 interleavingTime; +#endif + + u8 openMode; + u8 storageMode; + /*if true 3GPP text streams are read as MPEG-4 StreamingText*/ + u8 convert_streaming_text; + u8 is_jp2; + + /*main boxes for fast access*/ + /*moov*/ + GF_MovieBox *moov; + /*our MDAT box (one and only one when we store the file)*/ + GF_MediaDataBox *mdat; + /*file brand (since v2, NULL means mp4 v1)*/ + GF_FileTypeBox *brand; + /*progressive download info*/ + GF_ProgressiveDownloadBox *pdin; + /*meta box if any*/ + GF_MetaBox *meta; + +#ifndef GPAC_DISABLE_ISOM_FRAGMENTS + u32 FragmentsFlags, NextMoofNumber; + Bool first_moof_merged; + /*active fragment*/ + GF_MovieFragmentBox *moof; + /*in WRITE mode, this is the current MDAT where data is written*/ + /*in READ mode this is the last valid file position before a gf_isom_box_read failed*/ + u64 current_top_box_start; + u64 segment_start; + + GF_List *moof_list; + Bool use_segments, moof_first, append_segment; + + Bool is_index_segment; +#endif + + /*this contains ALL the root boxes excepts fragments*/ + GF_List *TopBoxes; + + /*default track for sync of MPEG4 streams - this is the first accessed stream without OCR info - only set in READ mode*/ + s32 es_id_default_sync; +}; + +/*time function*/ +u64 gf_isom_get_mp4time(); +/*set the last error of the file. if file is NULL, set the static error (used for IO errors*/ +void gf_isom_set_last_error(GF_ISOFile *the_file, GF_Err error); +GF_Err gf_isom_parse_movie_boxes(GF_ISOFile *mov, u64 *bytesMissing, Bool progressive_mode); +GF_ISOFile *gf_isom_new_movie(); +/*Movie and Track access functions*/ +GF_TrackBox *gf_isom_get_track_from_file(GF_ISOFile *the_file, u32 trackNumber); +GF_TrackBox *gf_isom_get_track(GF_MovieBox *moov, u32 trackNumber); +GF_TrackBox *gf_isom_get_track_from_id(GF_MovieBox *moov, u32 trackID); +u32 gf_isom_get_tracknum_from_id(GF_MovieBox *moov, u32 trackID); +/*open a movie*/ +GF_ISOFile *gf_isom_open_file(const char *fileName, u32 OpenMode, const char *tmp_dir); +/*close and delete a movie*/ +void gf_isom_delete_movie(GF_ISOFile *mov); +/*StreamDescription reconstruction Functions*/ +GF_Err GetESD(GF_MovieBox *moov, u32 trackID, u32 StreamDescIndex, GF_ESD **outESD); +GF_Err GetESDForTime(GF_MovieBox *moov, u32 trackID, u64 CTS, GF_ESD **outESD); +GF_Err Media_GetSampleDesc(GF_MediaBox *mdia, u32 SampleDescIndex, GF_SampleEntryBox **out_entry, u32 *dataRefIndex); +GF_Err Media_GetSampleDescIndex(GF_MediaBox *mdia, u64 DTS, u32 *sampleDescIndex); +/*get esd for given sample desc - + @true_desc_only: if true doesn't emulate desc and returns native ESD, + otherwise emulates if needed/possible (TimedText) and return a hard copy of the desc +*/ +GF_Err Media_GetESD(GF_MediaBox *mdia, u32 sampleDescIndex, GF_ESD **esd, Bool true_desc_only); +Bool Track_IsMPEG4Stream(u32 HandlerType); +Bool IsMP4Description(u32 entryType); +/*Find a reference of a given type*/ +GF_Err Track_FindRef(GF_TrackBox *trak, u32 ReferenceType, GF_TrackReferenceTypeBox **dpnd); +/*Time and sample*/ +GF_Err GetMediaTime(GF_TrackBox *trak, u64 movieTime, u64 *MediaTime, s64 *SegmentStartTime, s64 *MediaOffset, u8 *useEdit); +GF_Err Media_GetSample(GF_MediaBox *mdia, u32 sampleNumber, GF_ISOSample **samp, u32 *sampleDescriptionIndex, Bool no_data, u64 *out_offset); +GF_Err Media_CheckDataEntry(GF_MediaBox *mdia, u32 dataEntryIndex); +GF_Err Media_FindSyncSample(GF_SampleTableBox *stbl, u32 searchFromTime, u32 *sampleNumber, u8 mode); +GF_Err Media_RewriteODFrame(GF_MediaBox *mdia, GF_ISOSample *sample); +GF_Err Media_FindDataRef(GF_DataReferenceBox *dref, char *URLname, char *URNname, u32 *dataRefIndex); +Bool Media_IsSelfContained(GF_MediaBox *mdia, u32 StreamDescIndex); + +/*check the TimeToSample for the given time and return the Sample number +if the entry is not found, return the closest sampleNumber in prevSampleNumber and 0 in sampleNumber +if the DTS required is after all DTSs in the list, set prevSampleNumber and SampleNumber to 0 +useCTS specifies that we're looking for a composition time +*/ +GF_Err findEntryForTime(GF_SampleTableBox *stbl, u64 DTS, u8 useCTS, u32 *sampleNumber, u32 *prevSampleNumber); +/*Reading of the sample tables*/ +GF_Err stbl_GetSampleSize(GF_SampleSizeBox *stsz, u32 SampleNumber, u32 *Size); +GF_Err stbl_GetSampleCTS(GF_CompositionOffsetBox *ctts, u32 SampleNumber, u32 *CTSoffset); +GF_Err stbl_GetSampleDTS(GF_TimeToSampleBox *stts, u32 SampleNumber, u64 *DTS); +GF_Err stbl_GetSampleDTS_and_Duration(GF_TimeToSampleBox *stts, u32 SampleNumber, u64 *DTS, u32 *duration); + +/*find a RAP or set the prev / next RAPs if vars are passed*/ +GF_Err stbl_GetSampleRAP(GF_SyncSampleBox *stss, u32 SampleNumber, u8 *IsRAP, u32 *prevRAP, u32 *nextRAP); +GF_Err stbl_GetSampleInfos(GF_SampleTableBox *stbl, u32 sampleNumber, u64 *offset, u32 *chunkNumber, u32 *descIndex, u8 *isEdited); +GF_Err stbl_GetSampleShadow(GF_ShadowSyncBox *stsh, u32 *sampleNumber, u32 *syncNum); +GF_Err stbl_GetPaddingBits(GF_PaddingBitsBox *padb, u32 SampleNumber, u8 *PadBits); +u32 stbl_GetSampleFragmentCount(GF_SampleFragmentBox *stsf, u32 sampleNumber); +u32 stbl_GetSampleFragmentSize(GF_SampleFragmentBox *stsf, u32 sampleNumber, u32 FragmentIndex); +GF_Err stbl_GetSampleDepType(GF_SampleDependencyTypeBox *stbl, u32 SampleNumber, u32 *dependsOn, u32 *dependedOn, u32 *redundant); + +/*unpack sample2chunk and chunk offset so that we have 1 sample per chunk (edition mode only)*/ +GF_Err stbl_UnpackOffsets(GF_SampleTableBox *stbl); +GF_Err SetTrackDuration(GF_TrackBox *trak); +GF_Err Media_SetDuration(GF_TrackBox *trak); + +/*rewrites 3GP samples desc as MPEG-4 ESD*/ +GF_Err gf_isom_get_ttxt_esd(GF_MediaBox *mdia, GF_ESD **out_esd); +/*inserts TTU header - only used when conversion to StreamingText is on*/ +GF_Err gf_isom_rewrite_text_sample(GF_ISOSample *samp, u32 sampleDescriptionIndex, u32 sample_dur); + +GF_UserDataMap *udta_getEntry(GF_UserDataBox *ptr, u32 box_type, bin128 *uuid); + +#ifndef GPAC_DISABLE_ISOM_WRITE + +GF_Err FlushCaptureMode(GF_ISOFile *movie); +GF_Err CanAccessMovie(GF_ISOFile *movie, u32 Mode); +GF_ISOFile *gf_isom_create_movie(const char *fileName, u32 OpenMode, const char *tmp_dir); +void gf_isom_insert_moov(GF_ISOFile *file); + +GF_Err WriteToFile(GF_ISOFile *movie); +GF_Err Track_SetStreamDescriptor(GF_TrackBox *trak, u32 StreamDescriptionIndex, u32 DataReferenceIndex, GF_ESD *esd, u32 *outStreamIndex); +u8 RequestTrack(GF_MovieBox *moov, u32 TrackID); +/*Track-Media setup*/ +GF_Err NewMedia(GF_MediaBox **mdia, u32 MediaType, u32 TimeScale); +GF_Err Media_ParseODFrame(GF_MediaBox *mdia, GF_ISOSample *sample, GF_ISOSample **od_samp); +GF_Err Media_AddSample(GF_MediaBox *mdia, u64 data_offset, GF_ISOSample *sample, u32 StreamDescIndex, u32 syncShadowNumber); +GF_Err Media_CreateDataRef(GF_DataReferenceBox *dref, char *URLname, char *URNname, u32 *dataRefIndex); +/*update a media sample. ONLY in edit mode*/ +GF_Err Media_UpdateSample(GF_MediaBox *mdia, u32 sampleNumber, GF_ISOSample *sample, Bool data_only); +GF_Err Media_UpdateSampleReference(GF_MediaBox *mdia, u32 sampleNumber, GF_ISOSample *sample, u64 data_offset); +/*addition in the sample tables*/ +GF_Err stbl_AddDTS(GF_SampleTableBox *stbl, u64 DTS, u32 *sampleNumber, u32 LastAUDefDuration); +GF_Err stbl_AddCTS(GF_SampleTableBox *stbl, u32 sampleNumber, u32 CTSoffset); +GF_Err stbl_AddSize(GF_SampleSizeBox *stsz, u32 sampleNumber, u32 size); +GF_Err stbl_AddRAP(GF_SyncSampleBox *stss, u32 sampleNumber); +GF_Err stbl_AddShadow(GF_ShadowSyncBox *stsh, u32 sampleNumber, u32 shadowNumber); +GF_Err stbl_AddChunkOffset(GF_MediaBox *mdia, u32 sampleNumber, u32 StreamDescIndex, u64 offset); +/*NB - no add for padding, this is done only through SetPaddingBits*/ + +GF_Err stbl_AddSampleFragment(GF_SampleTableBox *stbl, u32 sampleNumber, u16 size); + +/*update of the sample table +all these functions are called in edit and we always have 1 sample per chunk*/ +GF_Err stbl_SetChunkOffset(GF_MediaBox *mdia, u32 sampleNumber, u64 offset); +GF_Err stbl_SetSampleCTS(GF_SampleTableBox *stbl, u32 sampleNumber, u32 offset); +GF_Err stbl_SetSampleSize(GF_SampleSizeBox *stsz, u32 SampleNumber, u32 size); +GF_Err stbl_SetSampleRAP(GF_SyncSampleBox *stss, u32 SampleNumber, u8 isRAP); +GF_Err stbl_SetSyncShadow(GF_ShadowSyncBox *stsh, u32 sampleNumber, u32 syncSample); +GF_Err stbl_SetPaddingBits(GF_SampleTableBox *stbl, u32 SampleNumber, u8 bits); +/*for adding fragmented samples*/ +GF_Err stbl_SampleSizeAppend(GF_SampleSizeBox *stsz, u32 data_size); +/*writing of the final chunk info in edit mode*/ +GF_Err stbl_SetChunkAndOffset(GF_SampleTableBox *stbl, u32 sampleNumber, u32 StreamDescIndex, GF_SampleToChunkBox *the_stsc, GF_Box **the_stco, u64 data_offset, u8 forceNewChunk); +/*EDIT LIST functions*/ +GF_EdtsEntry *CreateEditEntry(u64 EditDuration, u64 MediaTime, u8 EditMode); + +GF_Err stbl_SetRedundant(GF_SampleTableBox *stbl, u32 sampleNumber); +GF_Err stbl_AddRedundant(GF_SampleTableBox *stbl, u32 sampleNumber); + +/*REMOVE functions*/ +GF_Err stbl_RemoveDTS(GF_SampleTableBox *stbl, u32 sampleNumber, u32 LastAUDefDuration); +GF_Err stbl_RemoveCTS(GF_SampleTableBox *stbl, u32 sampleNumber); +GF_Err stbl_RemoveSize(GF_SampleSizeBox *stsz, u32 sampleNumber); +GF_Err stbl_RemoveChunk(GF_SampleTableBox *stbl, u32 sampleNumber); +GF_Err stbl_RemoveRAP(GF_SampleTableBox *stbl, u32 sampleNumber); +GF_Err stbl_RemoveShadow(GF_ShadowSyncBox *stsh, u32 sampleNumber); +GF_Err stbl_RemovePaddingBits(GF_SampleTableBox *stbl, u32 SampleNumber); +GF_Err stbl_RemoveSampleFragments(GF_SampleTableBox *stbl, u32 sampleNumber); +GF_Err stbl_RemoveRedundant(GF_SampleTableBox *stbl, u32 SampleNumber); + +#ifndef GPAC_DISABLE_ISOM_FRAGMENTS +GF_Err gf_isom_close_fragments(GF_ISOFile *movie); +#endif + +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + + +GF_Err GetNextMediaTime(GF_TrackBox *trak, u64 movieTime, u64 *OutMovieTime); +GF_Err GetPrevMediaTime(GF_TrackBox *trak, u64 movieTime, u64 *OutMovieTime); + +Bool IsHintTrack(GF_TrackBox *trak); +Bool CheckHintFormat(GF_TrackBox *trak, u32 HintType); +u32 GetHintFormat(GF_TrackBox *trak); + + +/*locate a box by its type or UUID*/ +GF_ItemListBox *gf_ismo_locate_box(GF_List *list, u32 boxType, bin128 UUID); + +GF_Err moov_AddBox(GF_Box *ptr, GF_Box *a); +GF_Err tref_AddBox(GF_Box *ptr, GF_Box *a); +GF_Err trak_AddBox(GF_Box *ptr, GF_Box *a); +GF_Err mvex_AddBox(GF_Box *ptr, GF_Box *a); +GF_Err stsd_AddBox(GF_SampleDescriptionBox *ptr, GF_Box *a); +GF_Err hnti_AddBox(GF_HintTrackInfoBox *hnti, GF_Box *a); +GF_Err udta_AddBox(GF_UserDataBox *ptr, GF_Box *a); +GF_Err edts_AddBox(GF_Box *s, GF_Box *a); +GF_Err stdp_Read(GF_Box *s, GF_BitStream *bs); +GF_Err sdtp_Read(GF_Box *s, GF_BitStream *bs); +GF_Err dinf_AddBox(GF_Box *s, GF_Box *a); +GF_Err minf_AddBox(GF_Box *s, GF_Box *a); +GF_Err mdia_AddBox(GF_Box *s, GF_Box *a); +GF_Err stbl_AddBox(GF_SampleTableBox *ptr, GF_Box *a); + +GF_Err AVC_UpdateESD(GF_MPEGVisualSampleEntryBox *avc, GF_ESD *esd); +void AVC_RewriteESDescriptor(GF_MPEGVisualSampleEntryBox *avc); +GF_Err reftype_AddRefTrack(GF_TrackReferenceTypeBox *ref, u32 trackID, u16 *outRefIndex); + +GF_XMLBox *gf_isom_get_meta_xml(GF_ISOFile *file, Bool root_meta, u32 track_num, Bool *is_binary); + +#ifndef GPAC_DISABLE_ISOM_HINTING + +/* + Hinting stuff +*/ + +/*the HintType for each protocol*/ +enum +{ + GF_ISMO_HINT_RTP = 1, + /*not supported yet*/ + GF_ISMO_MPEG2_TS = 2 +}; + +/***************************************************** + RTP Data Entries +*****************************************************/ + +#define GF_ISMO_BASE_DTE_ENTRY \ + u8 source; + +typedef struct +{ + GF_ISMO_BASE_DTE_ENTRY +} GF_GenericDTE; + +typedef struct +{ + GF_ISMO_BASE_DTE_ENTRY +} GF_EmptyDTE; + +typedef struct +{ + GF_ISMO_BASE_DTE_ENTRY + u8 dataLength; + char data[14]; +} GF_ImmediateDTE; + +typedef struct +{ + GF_ISMO_BASE_DTE_ENTRY + s8 trackRefIndex; + u32 sampleNumber; + u16 dataLength; + u32 byteOffset; + u16 bytesPerComp; + u16 samplesPerComp; +} GF_SampleDTE; + +typedef struct +{ + GF_ISMO_BASE_DTE_ENTRY + s8 trackRefIndex; + u32 streamDescIndex; + u16 dataLength; + u32 byteOffset; + u32 reserved; +} GF_StreamDescDTE; + +GF_GenericDTE *NewDTE(u8 type); +void DelDTE(GF_GenericDTE *dte); +GF_Err ReadDTE(GF_GenericDTE *dte, GF_BitStream *bs); +GF_Err WriteDTE(GF_GenericDTE *dte, GF_BitStream *bs); +GF_Err OffsetDTE(GF_GenericDTE *dte, u32 offset, u32 HintSampleNumber); + +/***************************************************** + RTP Sample +*****************************************************/ + +/*data cache when reading*/ +typedef struct __tag_hint_data_cache +{ + GF_ISOSample *samp; + GF_TrackBox *trak; + u32 sample_num; +} GF_HintDataCache; + + +typedef struct __tag_hint_sample +{ + /*used internally for future protocol support (write only)*/ + u8 HintType; + /*QT packets*/ + u16 reserved; + GF_List *packetTable; + char *AdditionalData; + u32 dataLength; + /*used internally for hinting*/ + u64 TransmissionTime; + /*for read only, used to store samples fetched while building packets*/ + GF_List *sample_cache; +} GF_HintSample; + +GF_HintSample *gf_isom_hint_sample_new(u32 ProtocolType); +void gf_isom_hint_sample_del(GF_HintSample *ptr); +GF_Err gf_isom_hint_sample_read(GF_HintSample *ptr, GF_BitStream *bs, u32 sampleSize); +GF_Err gf_isom_hint_sample_write(GF_HintSample *ptr, GF_BitStream *bs); +u32 gf_isom_hint_sample_size(GF_HintSample *ptr); + + +/***************************************************** + Hint Packets (generic packet for future protocol support) +*****************************************************/ +#define GF_ISOM_BASE_PACKET \ + s32 relativeTransTime; + + +typedef struct +{ + GF_ISOM_BASE_PACKET +} GF_HintPacket; + +GF_HintPacket *gf_isom_hint_pck_new(u8 HintType); +void gf_isom_hint_pck_del(u8 HintType, GF_HintPacket *ptr); +GF_Err gf_isom_hint_pck_read(u8 HintType, GF_HintPacket *ptr, GF_BitStream *bs); +GF_Err gf_isom_hint_pck_write(u8 HintType, GF_HintPacket *ptr, GF_BitStream *bs); +u32 gf_isom_hint_pck_size(u8 HintType, GF_HintPacket *ptr); +GF_Err gf_isom_hint_pck_offset(u8 HintType, GF_HintPacket *ptr, u32 offset, u32 HintSampleNumber); +GF_Err gf_isom_hint_pck_add_dte(u8 HintType, GF_HintPacket *ptr, GF_GenericDTE *dte, u8 AtBegin); +/*get the size of the packet AS RECONSTRUCTED BY THE SERVER (without CSRC)*/ +u32 gf_isom_hint_pck_length(u8 HintType, GF_HintPacket *ptr); + +/*the RTP packet*/ +typedef struct +{ + GF_ISOM_BASE_PACKET + + /*RTP Header*/ + u8 P_bit; + u8 X_bit; + u8 M_bit; + /*on 7 bits */ + u8 payloadType; + u16 SequenceNumber; + /*Hinting flags*/ + u8 B_bit; + u8 R_bit; + /*ExtraInfos TLVs - not really used */ + GF_List *TLV; + /*DataTable - contains the DTEs...*/ + GF_List *DataTable; +} GF_RTPPacket; + +GF_RTPPacket *gf_isom_hint_rtp_new(); +void gf_isom_hint_rtp_del(GF_RTPPacket *ptr); +GF_Err gf_isom_hint_rtp_read(GF_RTPPacket *ptr, GF_BitStream *bs); +GF_Err gf_isom_hint_rtp_write(GF_RTPPacket *ptr, GF_BitStream *bs); +u32 gf_isom_hint_rtp_size(GF_RTPPacket *ptr); +GF_Err gf_isom_hint_rtp_offset(GF_RTPPacket *ptr, u32 offset, u32 HintSampleNumber); +u32 gf_isom_hint_rtp_length(GF_RTPPacket *ptr); + + +#endif + + +struct _3gpp_text_sample +{ + char *text; + u32 len; + + GF_TextStyleBox *styles; + /*at most one of these*/ + GF_TextHighlightColorBox *highlight_color; + GF_TextScrollDelayBox *scroll_delay; + GF_TextBoxBox *box; + GF_TextWrapBox *wrap; + + GF_List *others; + GF_TextKaraokeBox *cur_karaoke; +}; + +GF_TextSample *gf_isom_parse_texte_sample(GF_BitStream *bs); +GF_TextSample *gf_isom_parse_texte_sample_from_data(char *data, u32 dataLength); + +/*do not throw fatal errors if boxes are duplicated, just warn and remove extra ones*/ +#define ERROR_ON_DUPLICATED_BOX(__abox, __parent) { \ + char __ptype[5];\ + strcpy(__ptype, gf_4cc_to_str(__parent->type) );\ + GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[iso file] extra box %s found in %s, deleting\n", gf_4cc_to_str(__abox->type), __ptype)); \ + gf_isom_box_del(a);\ + return GF_OK;\ + } + + +/* + these are exported just in case, there should never be needed outside the lib +*/ + +GF_Box *reftype_New(); +GF_Box *free_New(); +GF_Box *mdat_New(); +GF_Box *moov_New(); +GF_Box *mvhd_New(); +GF_Box *mdhd_New(); +GF_Box *vmhd_New(); +GF_Box *smhd_New(); +GF_Box *hmhd_New(); +GF_Box *nmhd_New(); +GF_Box *stbl_New(); +GF_Box *dinf_New(); +GF_Box *url_New(); +GF_Box *urn_New(); +GF_Box *cprt_New(); +GF_Box *chpl_New(); +GF_Box *hdlr_New(); +GF_Box *iods_New(); +GF_Box *trak_New(); +GF_Box *mp4s_New(); +GF_Box *mp4v_New(); +GF_Box *mp4a_New(); +GF_Box *edts_New(); +GF_Box *udta_New(); +GF_Box *dref_New(); +GF_Box *stsd_New(); +GF_Box *stts_New(); +GF_Box *ctts_New(); +GF_Box *stsh_New(); +GF_Box *elst_New(); +GF_Box *stsc_New(); +GF_Box *stsz_New(); +GF_Box *stco_New(); +GF_Box *stss_New(); +GF_Box *stdp_New(); +GF_Box *sdtp_New(); +GF_Box *co64_New(); +GF_Box *esds_New(); +GF_Box *minf_New(); +GF_Box *tkhd_New(); +GF_Box *tref_New(); +GF_Box *mdia_New(); +GF_Box *defa_New(); +GF_Box *uuid_New(); +GF_Box *void_New(); +GF_Box *stsf_New(); +GF_Box *gnrm_New(); +GF_Box *gnrv_New(); +GF_Box *gnra_New(); +GF_Box *pdin_New(); + +void reftype_del(GF_Box *); +void free_del(GF_Box *); +void mdat_del(GF_Box *); +void moov_del(GF_Box *); +void mvhd_del(GF_Box *); +void mdhd_del(GF_Box *); +void vmhd_del(GF_Box *); +void smhd_del(GF_Box *); +void hmhd_del(GF_Box *); +void nmhd_del(GF_Box *); +void stbl_del(GF_Box *); +void dinf_del(GF_Box *); +void url_del(GF_Box *); +void urn_del(GF_Box *); +void chpl_del(GF_Box *); +void cprt_del(GF_Box *); +void hdlr_del(GF_Box *); +void iods_del(GF_Box *); +void trak_del(GF_Box *); +void mp4s_del(GF_Box *); +void mp4v_del(GF_Box *); +void mp4a_del(GF_Box *); +void edts_del(GF_Box *); +void udta_del(GF_Box *); +void dref_del(GF_Box *); +void stsd_del(GF_Box *); +void stts_del(GF_Box *); +void ctts_del(GF_Box *); +void stsh_del(GF_Box *); +void elst_del(GF_Box *); +void stsc_del(GF_Box *); +void stsz_del(GF_Box *); +void stco_del(GF_Box *); +void stss_del(GF_Box *); +void stdp_del(GF_Box *); +void sdtp_del(GF_Box *); +void co64_del(GF_Box *); +void esds_del(GF_Box *); +void minf_del(GF_Box *); +void tkhd_del(GF_Box *); +void tref_del(GF_Box *); +void mdia_del(GF_Box *); +void defa_del(GF_Box *); +void uuid_del(GF_Box *); +void void_del(GF_Box *); +void stsf_del(GF_Box *); +void gnrm_del(GF_Box *); +void gnrv_del(GF_Box *); +void gnra_del(GF_Box *); +void pdin_del(GF_Box *); + +GF_Err reftype_Write(GF_Box *s, GF_BitStream *bs); +GF_Err free_Write(GF_Box *s, GF_BitStream *bs); +GF_Err mdat_Write(GF_Box *s, GF_BitStream *bs); +GF_Err moov_Write(GF_Box *s, GF_BitStream *bs); +GF_Err mvhd_Write(GF_Box *s, GF_BitStream *bs); +GF_Err mdhd_Write(GF_Box *s, GF_BitStream *bs); +GF_Err vmhd_Write(GF_Box *s, GF_BitStream *bs); +GF_Err smhd_Write(GF_Box *s, GF_BitStream *bs); +GF_Err hmhd_Write(GF_Box *s, GF_BitStream *bs); +GF_Err nmhd_Write(GF_Box *s, GF_BitStream *bs); +GF_Err stbl_Write(GF_Box *s, GF_BitStream *bs); +GF_Err dinf_Write(GF_Box *s, GF_BitStream *bs); +GF_Err url_Write(GF_Box *s, GF_BitStream *bs); +GF_Err urn_Write(GF_Box *s, GF_BitStream *bs); +GF_Err chpl_Write(GF_Box *s, GF_BitStream *bs); +GF_Err cprt_Write(GF_Box *s, GF_BitStream *bs); +GF_Err hdlr_Write(GF_Box *s, GF_BitStream *bs); +GF_Err iods_Write(GF_Box *s, GF_BitStream *bs); +GF_Err trak_Write(GF_Box *s, GF_BitStream *bs); +GF_Err mp4s_Write(GF_Box *s, GF_BitStream *bs); +GF_Err mp4v_Write(GF_Box *s, GF_BitStream *bs); +GF_Err mp4a_Write(GF_Box *s, GF_BitStream *bs); +GF_Err edts_Write(GF_Box *s, GF_BitStream *bs); +GF_Err udta_Write(GF_Box *s, GF_BitStream *bs); +GF_Err dref_Write(GF_Box *s, GF_BitStream *bs); +GF_Err stsd_Write(GF_Box *s, GF_BitStream *bs); +GF_Err stts_Write(GF_Box *s, GF_BitStream *bs); +GF_Err ctts_Write(GF_Box *s, GF_BitStream *bs); +GF_Err stsh_Write(GF_Box *s, GF_BitStream *bs); +GF_Err elst_Write(GF_Box *s, GF_BitStream *bs); +GF_Err stsc_Write(GF_Box *s, GF_BitStream *bs); +GF_Err stsz_Write(GF_Box *s, GF_BitStream *bs); +GF_Err stco_Write(GF_Box *s, GF_BitStream *bs); +GF_Err stss_Write(GF_Box *s, GF_BitStream *bs); +GF_Err stdp_Write(GF_Box *s, GF_BitStream *bs); +GF_Err sdtp_Write(GF_Box *s, GF_BitStream *bs); +GF_Err co64_Write(GF_Box *s, GF_BitStream *bs); +GF_Err esds_Write(GF_Box *s, GF_BitStream *bs); +GF_Err minf_Write(GF_Box *s, GF_BitStream *bs); +GF_Err tkhd_Write(GF_Box *s, GF_BitStream *bs); +GF_Err tref_Write(GF_Box *s, GF_BitStream *bs); +GF_Err mdia_Write(GF_Box *s, GF_BitStream *bs); +GF_Err defa_Write(GF_Box *s, GF_BitStream *bs); +GF_Err uuid_Write(GF_Box *s, GF_BitStream *bs); +GF_Err void_Write(GF_Box *s, GF_BitStream *bs); +GF_Err stsf_Write(GF_Box *s, GF_BitStream *bs); +GF_Err gnrm_Write(GF_Box *s, GF_BitStream *bs); +GF_Err gnrv_Write(GF_Box *s, GF_BitStream *bs); +GF_Err gnra_Write(GF_Box *s, GF_BitStream *bs); +GF_Err pdin_Write(GF_Box *s, GF_BitStream *bs); + +GF_Err reftype_Size(GF_Box *); +GF_Err free_Size(GF_Box *); +GF_Err mdat_Size(GF_Box *); +GF_Err moov_Size(GF_Box *); +GF_Err mvhd_Size(GF_Box *); +GF_Err mdhd_Size(GF_Box *); +GF_Err vmhd_Size(GF_Box *); +GF_Err smhd_Size(GF_Box *); +GF_Err hmhd_Size(GF_Box *); +GF_Err nmhd_Size(GF_Box *); +GF_Err stbl_Size(GF_Box *); +GF_Err dinf_Size(GF_Box *); +GF_Err url_Size(GF_Box *); +GF_Err urn_Size(GF_Box *); +GF_Err chpl_Size(GF_Box *); +GF_Err cprt_Size(GF_Box *); +GF_Err hdlr_Size(GF_Box *); +GF_Err iods_Size(GF_Box *); +GF_Err trak_Size(GF_Box *); +GF_Err mp4s_Size(GF_Box *); +GF_Err mp4v_Size(GF_Box *); +GF_Err mp4a_Size(GF_Box *); +GF_Err edts_Size(GF_Box *); +GF_Err udta_Size(GF_Box *); +GF_Err dref_Size(GF_Box *); +GF_Err stsd_Size(GF_Box *); +GF_Err stts_Size(GF_Box *); +GF_Err ctts_Size(GF_Box *); +GF_Err stsh_Size(GF_Box *); +GF_Err elst_Size(GF_Box *); +GF_Err stsc_Size(GF_Box *); +GF_Err stsz_Size(GF_Box *); +GF_Err stco_Size(GF_Box *); +GF_Err stss_Size(GF_Box *); +GF_Err stdp_Size(GF_Box *); +GF_Err sdtp_Size(GF_Box *); +GF_Err co64_Size(GF_Box *); +GF_Err esds_Size(GF_Box *); +GF_Err minf_Size(GF_Box *); +GF_Err tkhd_Size(GF_Box *); +GF_Err tref_Size(GF_Box *); +GF_Err mdia_Size(GF_Box *); +GF_Err defa_Size(GF_Box *); +GF_Err uuid_Size(GF_Box *); +GF_Err void_Size(GF_Box *); +GF_Err stsf_Size(GF_Box *); +GF_Err gnrm_Size(GF_Box *); +GF_Err gnrv_Size(GF_Box *); +GF_Err gnra_Size(GF_Box *); +GF_Err pdin_Size(GF_Box *); + +GF_Err reftype_Read(GF_Box *s, GF_BitStream *bs); +GF_Err free_Read(GF_Box *s, GF_BitStream *bs); +GF_Err mdat_Read(GF_Box *s, GF_BitStream *bs); +GF_Err moov_Read(GF_Box *s, GF_BitStream *bs); +GF_Err mvhd_Read(GF_Box *s, GF_BitStream *bs); +GF_Err mdhd_Read(GF_Box *s, GF_BitStream *bs); +GF_Err vmhd_Read(GF_Box *s, GF_BitStream *bs); +GF_Err smhd_Read(GF_Box *s, GF_BitStream *bs); +GF_Err hmhd_Read(GF_Box *s, GF_BitStream *bs); +GF_Err nmhd_Read(GF_Box *s, GF_BitStream *bs); +GF_Err stbl_Read(GF_Box *s, GF_BitStream *bs); +GF_Err dinf_Read(GF_Box *s, GF_BitStream *bs); +GF_Err url_Read(GF_Box *s, GF_BitStream *bs); +GF_Err urn_Read(GF_Box *s, GF_BitStream *bs); +GF_Err chpl_Read(GF_Box *s, GF_BitStream *bs); +GF_Err cprt_Read(GF_Box *s, GF_BitStream *bs); +GF_Err hdlr_Read(GF_Box *s, GF_BitStream *bs); +GF_Err iods_Read(GF_Box *s, GF_BitStream *bs); +GF_Err trak_Read(GF_Box *s, GF_BitStream *bs); +GF_Err mp4s_Read(GF_Box *s, GF_BitStream *bs); +GF_Err mp4v_Read(GF_Box *s, GF_BitStream *bs); +GF_Err mp4a_Read(GF_Box *s, GF_BitStream *bs); +GF_Err edts_Read(GF_Box *s, GF_BitStream *bs); +GF_Err udta_Read(GF_Box *s, GF_BitStream *bs); +GF_Err dref_Read(GF_Box *s, GF_BitStream *bs); +GF_Err stsd_Read(GF_Box *s, GF_BitStream *bs); +GF_Err stts_Read(GF_Box *s, GF_BitStream *bs); +GF_Err ctts_Read(GF_Box *s, GF_BitStream *bs); +GF_Err stsh_Read(GF_Box *s, GF_BitStream *bs); +GF_Err elst_Read(GF_Box *s, GF_BitStream *bs); +GF_Err stsc_Read(GF_Box *s, GF_BitStream *bs); +GF_Err stsz_Read(GF_Box *s, GF_BitStream *bs); +GF_Err stco_Read(GF_Box *s, GF_BitStream *bs); +GF_Err stss_Read(GF_Box *s, GF_BitStream *bs); +GF_Err stdp_Read(GF_Box *s, GF_BitStream *bs); +GF_Err sdtp_Read(GF_Box *s, GF_BitStream *bs); +GF_Err co64_Read(GF_Box *s, GF_BitStream *bs); +GF_Err esds_Read(GF_Box *s, GF_BitStream *bs); +GF_Err minf_Read(GF_Box *s, GF_BitStream *bs); +GF_Err tkhd_Read(GF_Box *s, GF_BitStream *bs); +GF_Err tref_Read(GF_Box *s, GF_BitStream *bs); +GF_Err mdia_Read(GF_Box *s, GF_BitStream *bs); +GF_Err defa_Read(GF_Box *s, GF_BitStream *bs); +GF_Err uuid_Read(GF_Box *s, GF_BitStream *bs); +GF_Err void_Read(GF_Box *s, GF_BitStream *bs); +GF_Err stsf_Read(GF_Box *s, GF_BitStream *bs); +GF_Err pdin_Read(GF_Box *s, GF_BitStream *bs); + +#ifndef GPAC_DISABLE_ISOM_HINTING + +GF_Box *hinf_New(); +GF_Box *trpy_New(); +GF_Box *totl_New(); +GF_Box *nump_New(); +GF_Box *npck_New(); +GF_Box *tpyl_New(); +GF_Box *tpay_New(); +GF_Box *maxr_New(); +GF_Box *dmed_New(); +GF_Box *dimm_New(); +GF_Box *drep_New(); +GF_Box *tmin_New(); +GF_Box *tmax_New(); +GF_Box *pmax_New(); +GF_Box *dmax_New(); +GF_Box *payt_New(); +GF_Box *name_New(); +GF_Box *rely_New(); +GF_Box *snro_New(); +GF_Box *tims_New(); +GF_Box *tsro_New(); +GF_Box *ghnt_New(); +GF_Box *hnti_New(); +GF_Box *sdp_New(); +GF_Box *rtpo_New(); + +void hinf_del(GF_Box *s); +void trpy_del(GF_Box *s); +void totl_del(GF_Box *s); +void nump_del(GF_Box *s); +void npck_del(GF_Box *s); +void tpyl_del(GF_Box *s); +void tpay_del(GF_Box *s); +void maxr_del(GF_Box *s); +void dmed_del(GF_Box *s); +void dimm_del(GF_Box *s); +void drep_del(GF_Box *s); +void tmin_del(GF_Box *s); +void tmax_del(GF_Box *s); +void pmax_del(GF_Box *s); +void dmax_del(GF_Box *s); +void payt_del(GF_Box *s); +void name_del(GF_Box *s); +void rely_del(GF_Box *s); +void snro_del(GF_Box *s); +void tims_del(GF_Box *s); +void tsro_del(GF_Box *s); +void ghnt_del(GF_Box *s); +void hnti_del(GF_Box *a); +void sdp_del(GF_Box *a); +void rtpo_del(GF_Box *s); + +GF_Err hinf_Read(GF_Box *s, GF_BitStream *bs); +GF_Err trpy_Read(GF_Box *s, GF_BitStream *bs); +GF_Err totl_Read(GF_Box *s, GF_BitStream *bs); +GF_Err nump_Read(GF_Box *s, GF_BitStream *bs); +GF_Err npck_Read(GF_Box *s, GF_BitStream *bs); +GF_Err tpyl_Read(GF_Box *s, GF_BitStream *bs); +GF_Err tpay_Read(GF_Box *s, GF_BitStream *bs); +GF_Err maxr_Read(GF_Box *s, GF_BitStream *bs); +GF_Err dmed_Read(GF_Box *s, GF_BitStream *bs); +GF_Err dimm_Read(GF_Box *s, GF_BitStream *bs); +GF_Err drep_Read(GF_Box *s, GF_BitStream *bs); +GF_Err tmin_Read(GF_Box *s, GF_BitStream *bs); +GF_Err tmax_Read(GF_Box *s, GF_BitStream *bs); +GF_Err pmax_Read(GF_Box *s, GF_BitStream *bs); +GF_Err dmax_Read(GF_Box *s, GF_BitStream *bs); +GF_Err payt_Read(GF_Box *s, GF_BitStream *bs); +GF_Err name_Read(GF_Box *s, GF_BitStream *bs); +GF_Err rely_Read(GF_Box *s, GF_BitStream *bs); +GF_Err snro_Read(GF_Box *s, GF_BitStream *bs); +GF_Err tims_Read(GF_Box *s, GF_BitStream *bs); +GF_Err tsro_Read(GF_Box *s, GF_BitStream *bs); +GF_Err ghnt_Read(GF_Box *s, GF_BitStream *bs); +GF_Err hnti_Read(GF_Box *s, GF_BitStream *bs); +GF_Err sdp_Read(GF_Box *s, GF_BitStream *bs); +GF_Err rtpo_Read(GF_Box *s, GF_BitStream *bs); + +GF_Err hinf_Write(GF_Box *s, GF_BitStream *bs); +GF_Err trpy_Write(GF_Box *s, GF_BitStream *bs); +GF_Err totl_Write(GF_Box *s, GF_BitStream *bs); +GF_Err nump_Write(GF_Box *s, GF_BitStream *bs); +GF_Err npck_Write(GF_Box *s, GF_BitStream *bs); +GF_Err tpyl_Write(GF_Box *s, GF_BitStream *bs); +GF_Err tpay_Write(GF_Box *s, GF_BitStream *bs); +GF_Err maxr_Write(GF_Box *s, GF_BitStream *bs); +GF_Err dmed_Write(GF_Box *s, GF_BitStream *bs); +GF_Err dimm_Write(GF_Box *s, GF_BitStream *bs); +GF_Err drep_Write(GF_Box *s, GF_BitStream *bs); +GF_Err tmin_Write(GF_Box *s, GF_BitStream *bs); +GF_Err tmax_Write(GF_Box *s, GF_BitStream *bs); +GF_Err pmax_Write(GF_Box *s, GF_BitStream *bs); +GF_Err dmax_Write(GF_Box *s, GF_BitStream *bs); +GF_Err payt_Write(GF_Box *s, GF_BitStream *bs); +GF_Err name_Write(GF_Box *s, GF_BitStream *bs); +GF_Err rely_Write(GF_Box *s, GF_BitStream *bs); +GF_Err snro_Write(GF_Box *s, GF_BitStream *bs); +GF_Err tims_Write(GF_Box *s, GF_BitStream *bs); +GF_Err tsro_Write(GF_Box *s, GF_BitStream *bs); +GF_Err ghnt_Write(GF_Box *s, GF_BitStream *bs); +GF_Err hnti_Write(GF_Box *s, GF_BitStream *bs); +GF_Err sdp_Write(GF_Box *s, GF_BitStream *bs); +GF_Err rtpo_Write(GF_Box *s, GF_BitStream *bs); + +GF_Err hinf_Size(GF_Box *s); +GF_Err trpy_Size(GF_Box *s); +GF_Err totl_Size(GF_Box *s); +GF_Err nump_Size(GF_Box *s); +GF_Err npck_Size(GF_Box *s); +GF_Err tpyl_Size(GF_Box *s); +GF_Err tpay_Size(GF_Box *s); +GF_Err maxr_Size(GF_Box *s); +GF_Err dmed_Size(GF_Box *s); +GF_Err dimm_Size(GF_Box *s); +GF_Err drep_Size(GF_Box *s); +GF_Err tmin_Size(GF_Box *s); +GF_Err tmax_Size(GF_Box *s); +GF_Err pmax_Size(GF_Box *s); +GF_Err dmax_Size(GF_Box *s); +GF_Err payt_Size(GF_Box *s); +GF_Err name_Size(GF_Box *s); +GF_Err rely_Size(GF_Box *s); +GF_Err snro_Size(GF_Box *s); +GF_Err tims_Size(GF_Box *s); +GF_Err tsro_Size(GF_Box *s); +GF_Err ghnt_Size(GF_Box *s); +GF_Err hnti_Size(GF_Box *s); +GF_Err sdp_Size(GF_Box *s); +GF_Err rtpo_Size(GF_Box *s); + +#endif + + +GF_Box *ftyp_New(); +void ftyp_del(GF_Box *s); +GF_Err ftyp_Read(GF_Box *s,GF_BitStream *bs); +GF_Err ftyp_Write(GF_Box *s, GF_BitStream *bs); +GF_Err ftyp_Size(GF_Box *s); + +GF_Box *padb_New(); +void padb_del(GF_Box *s); +GF_Err padb_Read(GF_Box *s, GF_BitStream *bs); +GF_Err padb_Write(GF_Box *s, GF_BitStream *bs); +GF_Err padb_Size(GF_Box *s); + +GF_Box *gppa_New(u32 type); +GF_Box *gppv_New(u32 type); +GF_Box *gppc_New(u32 type); +void gppa_del(GF_Box *s); +void gppv_del(GF_Box *s); +void gppc_del(GF_Box *s); +GF_Err gppa_Read(GF_Box *s, GF_BitStream *bs); +GF_Err gppv_Read(GF_Box *s, GF_BitStream *bs); +GF_Err gppc_Read(GF_Box *s, GF_BitStream *bs); +#ifndef GPAC_DISABLE_ISOM_WRITE +GF_Err gppa_Write(GF_Box *s, GF_BitStream *bs); +GF_Err gppv_Write(GF_Box *s, GF_BitStream *bs); +GF_Err gppc_Write(GF_Box *s, GF_BitStream *bs); +GF_Err gppa_Size(GF_Box *s); +GF_Err gppv_Size(GF_Box *s); +GF_Err gppc_Size(GF_Box *s); +#endif + + +#ifndef GPAC_DISABLE_ISOM_FRAGMENTS +GF_Box *mvex_New(); +GF_Box *trex_New(); +GF_Box *moof_New(); +GF_Box *mfhd_New(); +GF_Box *traf_New(); +GF_Box *tfhd_New(); +GF_Box *trun_New(); +GF_Box *styp_New(); + +void mvex_del(GF_Box *s); +void trex_del(GF_Box *s); +void moof_del(GF_Box *s); +void mfhd_del(GF_Box *s); +void traf_del(GF_Box *s); +void tfhd_del(GF_Box *s); +void trun_del(GF_Box *s); +void styp_del(GF_Box *s); + +GF_Err mvex_Read(GF_Box *s, GF_BitStream *bs); +GF_Err trex_Read(GF_Box *s, GF_BitStream *bs); +GF_Err moof_Read(GF_Box *s, GF_BitStream *bs); +GF_Err mfhd_Read(GF_Box *s, GF_BitStream *bs); +GF_Err traf_Read(GF_Box *s, GF_BitStream *bs); +GF_Err tfhd_Read(GF_Box *s, GF_BitStream *bs); +GF_Err trun_Read(GF_Box *s, GF_BitStream *bs); +GF_Err styp_Read(GF_Box *s, GF_BitStream *bs); + +GF_Err mvex_Write(GF_Box *s, GF_BitStream *bs); +GF_Err trex_Write(GF_Box *s, GF_BitStream *bs); +GF_Err moof_Write(GF_Box *s, GF_BitStream *bs); +GF_Err mfhd_Write(GF_Box *s, GF_BitStream *bs); +GF_Err traf_Write(GF_Box *s, GF_BitStream *bs); +GF_Err tfhd_Write(GF_Box *s, GF_BitStream *bs); +GF_Err trun_Write(GF_Box *s, GF_BitStream *bs); +GF_Err styp_Write(GF_Box *s, GF_BitStream *bs); + +GF_Err mvex_Size(GF_Box *s); +GF_Err trex_Size(GF_Box *s); +GF_Err moof_Size(GF_Box *s); +GF_Err mfhd_Size(GF_Box *s); +GF_Err traf_Size(GF_Box *s); +GF_Err tfhd_Size(GF_Box *s); +GF_Err trun_Size(GF_Box *s); +GF_Err styp_Size(GF_Box *s); + + +GF_Box *mehd_New(); +void mehd_del(GF_Box *s); +GF_Err mehd_Read(GF_Box *s, GF_BitStream *bs); +GF_Err mehd_Write(GF_Box *s, GF_BitStream *bs); +GF_Err mehd_Size(GF_Box *s); + +#endif + +/*avc ext*/ +GF_Box *avcc_New(); +void avcc_del(GF_Box *s); +GF_Err avcc_Read(GF_Box *s, GF_BitStream *bs); +GF_Err avcc_Write(GF_Box *s, GF_BitStream *bs); +GF_Err avcc_Size(GF_Box *s); + +GF_Box *avc1_New(); +GF_Box *avc2_New(); +GF_Box *svc1_New(); + +GF_Box *m4ds_New(); +void m4ds_del(GF_Box *s); +GF_Err m4ds_Read(GF_Box *s, GF_BitStream *bs); +GF_Err m4ds_Write(GF_Box *s, GF_BitStream *bs); +GF_Err m4ds_Size(GF_Box *s); + +GF_Box *btrt_New(); +void btrt_del(GF_Box *s); +GF_Err btrt_Read(GF_Box *s, GF_BitStream *bs); +GF_Err btrt_Write(GF_Box *s, GF_BitStream *bs); +GF_Err btrt_Size(GF_Box *s); + + +/*3GPP streaming text*/ +GF_Box *ftab_New(); +GF_Box *tx3g_New(); +GF_Box *text_New(); +GF_Box *styl_New(); +GF_Box *hlit_New(); +GF_Box *hclr_New(); +GF_Box *krok_New(); +GF_Box *dlay_New(); +GF_Box *href_New(); +GF_Box *tbox_New(); +GF_Box *blnk_New(); +GF_Box *twrp_New(); + +void ftab_del(GF_Box *s); +void tx3g_del(GF_Box *s); +void text_del(GF_Box *s); +void styl_del(GF_Box *s); +void hlit_del(GF_Box *s); +void hclr_del(GF_Box *s); +void krok_del(GF_Box *s); +void dlay_del(GF_Box *s); +void href_del(GF_Box *s); +void tbox_del(GF_Box *s); +void blnk_del(GF_Box *s); +void twrp_del(GF_Box *s); + +GF_Err ftab_Read(GF_Box *s, GF_BitStream *bs); +GF_Err tx3g_Read(GF_Box *s, GF_BitStream *bs); +GF_Err text_Read(GF_Box *s, GF_BitStream *bs); +GF_Err styl_Read(GF_Box *s, GF_BitStream *bs); +GF_Err hlit_Read(GF_Box *s, GF_BitStream *bs); +GF_Err hclr_Read(GF_Box *s, GF_BitStream *bs); +GF_Err krok_Read(GF_Box *s, GF_BitStream *bs); +GF_Err dlay_Read(GF_Box *s, GF_BitStream *bs); +GF_Err href_Read(GF_Box *s, GF_BitStream *bs); +GF_Err tbox_Read(GF_Box *s, GF_BitStream *bs); +GF_Err blnk_Read(GF_Box *s, GF_BitStream *bs); +GF_Err twrp_Read(GF_Box *s, GF_BitStream *bs); + +#ifndef GPAC_DISABLE_ISOM_WRITE +GF_Err ftab_Write(GF_Box *s, GF_BitStream *bs); +GF_Err tx3g_Write(GF_Box *s, GF_BitStream *bs); +GF_Err text_Write(GF_Box *s, GF_BitStream *bs); +GF_Err styl_Write(GF_Box *s, GF_BitStream *bs); +GF_Err hlit_Write(GF_Box *s, GF_BitStream *bs); +GF_Err hclr_Write(GF_Box *s, GF_BitStream *bs); +GF_Err krok_Write(GF_Box *s, GF_BitStream *bs); +GF_Err dlay_Write(GF_Box *s, GF_BitStream *bs); +GF_Err href_Write(GF_Box *s, GF_BitStream *bs); +GF_Err tbox_Write(GF_Box *s, GF_BitStream *bs); +GF_Err blnk_Write(GF_Box *s, GF_BitStream *bs); +GF_Err twrp_Write(GF_Box *s, GF_BitStream *bs); + +GF_Err ftab_Size(GF_Box *s); +GF_Err tx3g_Size(GF_Box *s); +GF_Err text_Size(GF_Box *s); +GF_Err styl_Size(GF_Box *s); +GF_Err hlit_Size(GF_Box *s); +GF_Err hclr_Size(GF_Box *s); +GF_Err krok_Size(GF_Box *s); +GF_Err dlay_Size(GF_Box *s); +GF_Err href_Size(GF_Box *s); +GF_Err tbox_Size(GF_Box *s); +GF_Err blnk_Size(GF_Box *s); +GF_Err twrp_Size(GF_Box *s); +#endif + + +/* MPEG-21 functions */ +GF_Box *meta_New(); +GF_Box *xml_New(); +GF_Box *bxml_New(); +GF_Box *iloc_New(); +GF_Box *pitm_New(); +GF_Box *ipro_New(); +GF_Box *infe_New(); +GF_Box *iinf_New(); +GF_Box *sinf_New(); +GF_Box *frma_New(); +GF_Box *schm_New(); +GF_Box *schi_New(); +GF_Box *enca_New(); +GF_Box *encv_New(); +GF_Box *encs_New(); + +void meta_del(GF_Box *s); +void xml_del(GF_Box *s); +void bxml_del(GF_Box *s); +void iloc_del(GF_Box *s); +void pitm_del(GF_Box *s); +void ipro_del(GF_Box *s); +void infe_del(GF_Box *s); +void iinf_del(GF_Box *s); +void sinf_del(GF_Box *s); +void frma_del(GF_Box *s); +void schm_del(GF_Box *s); +void schi_del(GF_Box *s); + +GF_Err meta_Read(GF_Box *s, GF_BitStream *bs); +GF_Err xml_Read(GF_Box *s, GF_BitStream *bs); +GF_Err bxml_Read(GF_Box *s, GF_BitStream *bs); +GF_Err iloc_Read(GF_Box *s, GF_BitStream *bs); +GF_Err pitm_Read(GF_Box *s, GF_BitStream *bs); +GF_Err ipro_Read(GF_Box *s, GF_BitStream *bs); +GF_Err infe_Read(GF_Box *s, GF_BitStream *bs); +GF_Err iinf_Read(GF_Box *s, GF_BitStream *bs); +GF_Err sinf_Read(GF_Box *s, GF_BitStream *bs); +GF_Err frma_Read(GF_Box *s, GF_BitStream *bs); +GF_Err schm_Read(GF_Box *s, GF_BitStream *bs); +GF_Err schi_Read(GF_Box *s, GF_BitStream *bs); + +#ifndef GPAC_DISABLE_ISOM_WRITE +GF_Err meta_Write(GF_Box *s, GF_BitStream *bs); +GF_Err xml_Write(GF_Box *s, GF_BitStream *bs); +GF_Err bxml_Write(GF_Box *s, GF_BitStream *bs); +GF_Err iloc_Write(GF_Box *s, GF_BitStream *bs); +GF_Err pitm_Write(GF_Box *s, GF_BitStream *bs); +GF_Err ipro_Write(GF_Box *s, GF_BitStream *bs); +GF_Err infe_Write(GF_Box *s, GF_BitStream *bs); +GF_Err iinf_Write(GF_Box *s, GF_BitStream *bs); +GF_Err sinf_Write(GF_Box *s, GF_BitStream *bs); +GF_Err frma_Write(GF_Box *s, GF_BitStream *bs); +GF_Err schm_Write(GF_Box *s, GF_BitStream *bs); +GF_Err schi_Write(GF_Box *s, GF_BitStream *bs); + +GF_Err meta_Size(GF_Box *s); +GF_Err xml_Size(GF_Box *s); +GF_Err bxml_Size(GF_Box *s); +GF_Err iloc_Size(GF_Box *s); +GF_Err pitm_Size(GF_Box *s); +GF_Err ipro_Size(GF_Box *s); +GF_Err infe_Size(GF_Box *s); +GF_Err iinf_Size(GF_Box *s); +GF_Err sinf_Size(GF_Box *s); +GF_Err frma_Size(GF_Box *s); +GF_Err schm_Size(GF_Box *s); +GF_Err schi_Size(GF_Box *s); +#endif + +/* end of MPEG-21 functions */ + + +/** ISMACryp functions **/ +GF_Box *iKMS_New(); +GF_Box *iSFM_New(); +void iKMS_del(GF_Box *s); +void iSFM_del(GF_Box *s); +GF_Err iKMS_Read(GF_Box *s, GF_BitStream *bs); +GF_Err iSFM_Read(GF_Box *s, GF_BitStream *bs); +#ifndef GPAC_DISABLE_ISOM_WRITE +GF_Err iKMS_Write(GF_Box *s, GF_BitStream *bs); +GF_Err iSFM_Write(GF_Box *s, GF_BitStream *bs); +GF_Err iKMS_Size(GF_Box *s); +GF_Err iSFM_Size(GF_Box *s); +#endif + +/* Apple extensions */ +void ilst_del(GF_Box *s); +void ListItem_del(GF_Box *s); +void data_del(GF_Box *s); +GF_Err ilst_Read(GF_Box *s, GF_BitStream *bs); +GF_Err ListItem_Read(GF_Box *s, GF_BitStream *bs); +GF_Err data_Read(GF_Box *s, GF_BitStream *bs); +GF_Box *ilst_New(); +GF_Box *ListItem_New(u32 type); +GF_Box *data_New(); +#ifndef GPAC_DISABLE_ISOM_WRITE +GF_Err ilst_Write(GF_Box *s, GF_BitStream *bs); +GF_Err ListItem_Write(GF_Box *s, GF_BitStream *bs); +GF_Err data_Write(GF_Box *s, GF_BitStream *bs); +GF_Err ilst_Size(GF_Box *s); +GF_Err ListItem_Size(GF_Box *s); +GF_Err data_Size(GF_Box *s); +#endif + + +GF_Err gb_box_array_dump(GF_List *list, FILE * trace); +GF_Err reftype_dump(GF_Box *a, FILE * trace); +GF_Err free_dump(GF_Box *a, FILE * trace); +GF_Err mdat_dump(GF_Box *a, FILE * trace); +GF_Err moov_dump(GF_Box *a, FILE * trace); +GF_Err mvhd_dump(GF_Box *a, FILE * trace); +GF_Err mdhd_dump(GF_Box *a, FILE * trace); +GF_Err vmhd_dump(GF_Box *a, FILE * trace); +GF_Err smhd_dump(GF_Box *a, FILE * trace); +GF_Err hmhd_dump(GF_Box *a, FILE * trace); +GF_Err nmhd_dump(GF_Box *a, FILE * trace); +GF_Err stbl_dump(GF_Box *a, FILE * trace); +GF_Err dinf_dump(GF_Box *a, FILE * trace); +GF_Err url_dump(GF_Box *a, FILE * trace); +GF_Err urn_dump(GF_Box *a, FILE * trace); +GF_Err cprt_dump(GF_Box *a, FILE * trace); +GF_Err hdlr_dump(GF_Box *a, FILE * trace); +GF_Err iods_dump(GF_Box *a, FILE * trace); +GF_Err trak_dump(GF_Box *a, FILE * trace); +GF_Err mp4s_dump(GF_Box *a, FILE * trace); +GF_Err mp4v_dump(GF_Box *a, FILE * trace); +GF_Err mp4a_dump(GF_Box *a, FILE * trace); +GF_Err edts_dump(GF_Box *a, FILE * trace); +GF_Err udta_dump(GF_Box *a, FILE * trace); +GF_Err dref_dump(GF_Box *a, FILE * trace); +GF_Err stsd_dump(GF_Box *a, FILE * trace); +GF_Err stts_dump(GF_Box *a, FILE * trace); +GF_Err ctts_dump(GF_Box *a, FILE * trace); +GF_Err stsh_dump(GF_Box *a, FILE * trace); +GF_Err elst_dump(GF_Box *a, FILE * trace); +GF_Err stsc_dump(GF_Box *a, FILE * trace); +GF_Err stsz_dump(GF_Box *a, FILE * trace); +GF_Err stco_dump(GF_Box *a, FILE * trace); +GF_Err stss_dump(GF_Box *a, FILE * trace); +GF_Err stdp_dump(GF_Box *a, FILE * trace); +GF_Err sdtp_dump(GF_Box *a, FILE * trace); +GF_Err co64_dump(GF_Box *a, FILE * trace); +GF_Err esds_dump(GF_Box *a, FILE * trace); +GF_Err minf_dump(GF_Box *a, FILE * trace); +GF_Err tkhd_dump(GF_Box *a, FILE * trace); +GF_Err tref_dump(GF_Box *a, FILE * trace); +GF_Err mdia_dump(GF_Box *a, FILE * trace); +GF_Err defa_dump(GF_Box *a, FILE * trace); +GF_Err void_dump(GF_Box *a, FILE * trace); +GF_Err ftyp_dump(GF_Box *a, FILE * trace); +GF_Err padb_dump(GF_Box *a, FILE * trace); +GF_Err stsf_dump(GF_Box *a, FILE * trace); +GF_Err gnrm_dump(GF_Box *a, FILE * trace); +GF_Err gnrv_dump(GF_Box *a, FILE * trace); +GF_Err gnra_dump(GF_Box *a, FILE * trace); +GF_Err gppa_dump(GF_Box *a, FILE * trace); +GF_Err gppv_dump(GF_Box *a, FILE * trace); +GF_Err gppc_dump(GF_Box *a, FILE * trace); +GF_Err chpl_dump(GF_Box *a, FILE * trace); +GF_Err dpin_dump(GF_Box *a, FILE * trace); + +GF_Err hinf_dump(GF_Box *a, FILE * trace); +GF_Err trpy_dump(GF_Box *a, FILE * trace); +GF_Err totl_dump(GF_Box *a, FILE * trace); +GF_Err nump_dump(GF_Box *a, FILE * trace); +GF_Err npck_dump(GF_Box *a, FILE * trace); +GF_Err tpyl_dump(GF_Box *a, FILE * trace); +GF_Err tpay_dump(GF_Box *a, FILE * trace); +GF_Err maxr_dump(GF_Box *a, FILE * trace); +GF_Err dmed_dump(GF_Box *a, FILE * trace); +GF_Err dimm_dump(GF_Box *a, FILE * trace); +GF_Err drep_dump(GF_Box *a, FILE * trace); +GF_Err tmin_dump(GF_Box *a, FILE * trace); +GF_Err tmax_dump(GF_Box *a, FILE * trace); +GF_Err pmax_dump(GF_Box *a, FILE * trace); +GF_Err dmax_dump(GF_Box *a, FILE * trace); +GF_Err payt_dump(GF_Box *a, FILE * trace); +GF_Err name_dump(GF_Box *a, FILE * trace); +GF_Err rely_dump(GF_Box *a, FILE * trace); +GF_Err snro_dump(GF_Box *a, FILE * trace); +GF_Err tims_dump(GF_Box *a, FILE * trace); +GF_Err tsro_dump(GF_Box *a, FILE * trace); +GF_Err ghnt_dump(GF_Box *a, FILE * trace); +GF_Err hnti_dump(GF_Box *a, FILE * trace); +GF_Err sdp_dump(GF_Box *a, FILE * trace); +GF_Err rtpo_dump(GF_Box *a, FILE * trace); + + + +#ifndef GPAC_DISABLE_ISOM_FRAGMENTS +GF_Err mvex_dump(GF_Box *a, FILE * trace); +GF_Err mehd_dump(GF_Box *a, FILE * trace); +GF_Err trex_dump(GF_Box *a, FILE * trace); +GF_Err moof_dump(GF_Box *a, FILE * trace); +GF_Err mfhd_dump(GF_Box *a, FILE * trace); +GF_Err traf_dump(GF_Box *a, FILE * trace); +GF_Err tfhd_dump(GF_Box *a, FILE * trace); +GF_Err trun_dump(GF_Box *a, FILE * trace); +GF_Err styp_dump(GF_Box *a, FILE * trace); +#endif + +GF_Err avcc_dump(GF_Box *a, FILE * trace); +GF_Err avc1_dump(GF_Box *a, FILE * trace); +GF_Err m4ds_dump(GF_Box *a, FILE * trace); +GF_Err btrt_dump(GF_Box *a, FILE * trace); + +GF_Err ftab_dump(GF_Box *a, FILE * trace); +GF_Err tx3g_dump(GF_Box *a, FILE * trace); +GF_Err text_dump(GF_Box *a, FILE * trace); +GF_Err styl_dump(GF_Box *a, FILE * trace); +GF_Err hlit_dump(GF_Box *a, FILE * trace); +GF_Err hclr_dump(GF_Box *a, FILE * trace); +GF_Err krok_dump(GF_Box *a, FILE * trace); +GF_Err dlay_dump(GF_Box *a, FILE * trace); +GF_Err href_dump(GF_Box *a, FILE * trace); +GF_Err tbox_dump(GF_Box *a, FILE * trace); +GF_Err blnk_dump(GF_Box *a, FILE * trace); +GF_Err twrp_dump(GF_Box *a, FILE * trace); + +/* ISMACryp dump */ +GF_Err iKMS_dump(GF_Box *a, FILE * trace); +GF_Err iSFM_dump(GF_Box *a, FILE * trace); + +/*MPEG-21 extensions dump*/ +GF_Err meta_dump(GF_Box *a, FILE * trace); +GF_Err xml_dump(GF_Box *a, FILE * trace); +GF_Err bxml_dump(GF_Box *a, FILE * trace); +GF_Err iloc_dump(GF_Box *a, FILE * trace); +GF_Err pitm_dump(GF_Box *a, FILE * trace); +GF_Err ipro_dump(GF_Box *a, FILE * trace); +GF_Err infe_dump(GF_Box *a, FILE * trace); +GF_Err iinf_dump(GF_Box *a, FILE * trace); +GF_Err sinf_dump(GF_Box *a, FILE * trace); +GF_Err frma_dump(GF_Box *a, FILE * trace); +GF_Err schm_dump(GF_Box *a, FILE * trace); +GF_Err schi_dump(GF_Box *a, FILE * trace); + +/*Apple extensions*/ +GF_Err ilst_dump(GF_Box *a, FILE * trace); +GF_Err ListItem_dump(GF_Box *a, FILE * trace); +GF_Err data_dump(GF_Box *a, FILE * trace); + +GF_Err lsrc_dump(GF_Box *a, FILE * trace); +GF_Err lsr1_dump(GF_Box *a, FILE * trace); + +/*Apple extensions*/ +GF_MetaBox *gf_isom_apple_get_meta_extensions(GF_ISOFile *mov); + +#ifndef GPAC_DISABLE_ISOM_WRITE +GF_MetaBox *gf_isom_apple_create_meta_extensions(GF_ISOFile *mov); +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + +/*OMA extensions*/ +GF_Box *ohdr_New(); +void ohdr_del(GF_Box *s); +GF_Err ohdr_Read(GF_Box *s, GF_BitStream *bs); +GF_Err ohdr_Write(GF_Box *s, GF_BitStream *bs); +GF_Err ohdr_Size(GF_Box *s); +GF_Err ohdr_dump(GF_Box *a, FILE * trace); +GF_Box *grpi_New(); +void grpi_del(GF_Box *s); +GF_Err grpi_Read(GF_Box *s, GF_BitStream *bs); +GF_Err grpi_Write(GF_Box *s, GF_BitStream *bs); +GF_Err grpi_Size(GF_Box *s); +GF_Err grpi_dump(GF_Box *a, FILE * trace); +GF_Box *mdri_New(); +void mdri_del(GF_Box *s); +GF_Err mdri_Read(GF_Box *s, GF_BitStream *bs); +GF_Err mdri_Write(GF_Box *s, GF_BitStream *bs); +GF_Err mdri_Size(GF_Box *s); +GF_Err mdri_dump(GF_Box *a, FILE * trace); +GF_Box *odtt_New(); +void odtt_del(GF_Box *s); +GF_Err odtt_Read(GF_Box *s, GF_BitStream *bs); +GF_Err odtt_Write(GF_Box *s, GF_BitStream *bs); +GF_Err odtt_Size(GF_Box *s); +GF_Err odtt_dump(GF_Box *a, FILE * trace); +GF_Box *odrb_New(); +void odrb_del(GF_Box *s); +GF_Err odrb_Read(GF_Box *s, GF_BitStream *bs); +GF_Err odrb_Write(GF_Box *s, GF_BitStream *bs); +GF_Err odrb_Size(GF_Box *s); +GF_Err odrb_dump(GF_Box *a, FILE * trace); +GF_Box *odkm_New(); +void odkm_del(GF_Box *s); +GF_Err odkm_Read(GF_Box *s, GF_BitStream *bs); +GF_Err odkm_Write(GF_Box *s, GF_BitStream *bs); +GF_Err odkm_Size(GF_Box *s); +GF_Err odkm_dump(GF_Box *a, FILE * trace); + + +GF_Box *pasp_New(); +void pasp_del(GF_Box *s); +GF_Err pasp_Read(GF_Box *s, GF_BitStream *bs); +GF_Err pasp_Write(GF_Box *s, GF_BitStream *bs); +GF_Err pasp_Size(GF_Box *s); +GF_Err pasp_dump(GF_Box *a, FILE * trace); + +GF_Box *metx_New(u32 type); +void metx_del(GF_Box *s); +GF_Err metx_Read(GF_Box *s, GF_BitStream *bs); +GF_Err metx_Write(GF_Box *s, GF_BitStream *bs); +GF_Err metx_Size(GF_Box *s); +GF_Err metx_dump(GF_Box *a, FILE * trace); + + + +GF_Box *tsel_New(); +void tsel_del(GF_Box *s); +GF_Err tsel_Read(GF_Box *s, GF_BitStream *bs); +GF_Err tsel_Write(GF_Box *s, GF_BitStream *bs); +GF_Err tsel_Size(GF_Box *s); +GF_Err tsel_dump(GF_Box *a, FILE * trace); + + +GF_Box *dimC_New(); +void dimC_del(GF_Box *s); +GF_Err dimC_Read(GF_Box *s, GF_BitStream *bs); +GF_Err dimC_Write(GF_Box *s, GF_BitStream *bs); +GF_Err dimC_Size(GF_Box *s); +GF_Err dimC_dump(GF_Box *a, FILE * trace); + +GF_Box *dims_New(); +void dims_del(GF_Box *s); +GF_Err dims_Read(GF_Box *s, GF_BitStream *bs); +GF_Err dims_Write(GF_Box *s, GF_BitStream *bs); +GF_Err dims_Size(GF_Box *s); +GF_Err dims_dump(GF_Box *a, FILE * trace); + +GF_Box *diST_New(); +void diST_del(GF_Box *s); +GF_Err diST_Read(GF_Box *s, GF_BitStream *bs); +GF_Err diST_Write(GF_Box *s, GF_BitStream *bs); +GF_Err diST_Size(GF_Box *s); +GF_Err diST_dump(GF_Box *a, FILE * trace); + + +GF_Box *ac3_New(); +void ac3_del(GF_Box *s); +GF_Err ac3_Read(GF_Box *s, GF_BitStream *bs); +GF_Err ac3_Write(GF_Box *s, GF_BitStream *bs); +GF_Err ac3_Size(GF_Box *s); +GF_Err ac3_dump(GF_Box *a, FILE * trace); + +GF_Box *dac3_New(); +void dac3_del(GF_Box *s); +GF_Err dac3_Read(GF_Box *s, GF_BitStream *bs); +GF_Err dac3_Write(GF_Box *s, GF_BitStream *bs); +GF_Err dac3_Size(GF_Box *s); +GF_Err dac3_dump(GF_Box *a, FILE * trace); + +GF_Box *lsrc_New(); +void lsrc_del(GF_Box *s); +GF_Err lsrc_Read(GF_Box *s, GF_BitStream *bs); +GF_Err lsrc_Write(GF_Box *s, GF_BitStream *bs); +GF_Err lsrc_Size(GF_Box *s); +GF_Err lsrc_dump(GF_Box *a, FILE * trace); + +GF_Box *lsr1_New(); +void lsr1_del(GF_Box *s); +GF_Err lsr1_Read(GF_Box *s, GF_BitStream *bs); +GF_Err lsr1_Write(GF_Box *s, GF_BitStream *bs); +GF_Err lsr1_Size(GF_Box *s); +GF_Err lsr1_dump(GF_Box *a, FILE * trace); + +GF_Box *sidx_New(); +void sidx_del(GF_Box *s); +GF_Err sidx_Read(GF_Box *s, GF_BitStream *bs); +GF_Err sidx_Write(GF_Box *s, GF_BitStream *bs); +GF_Err sidx_Size(GF_Box *s); +GF_Err sidx_dump(GF_Box *a, FILE * trace); + +GF_Box *subs_New(); +void subs_del(GF_Box *); +GF_Err subs_Write(GF_Box *s, GF_BitStream *bs); +GF_Err subs_Size(GF_Box *s); +GF_Err subs_Read(GF_Box *s, GF_BitStream *bs); +GF_Err subs_dump(GF_Box *a, FILE * trace); + +GF_Box *tfdt_New(); +void tfdt_del(GF_Box *); +GF_Err tfdt_Write(GF_Box *s, GF_BitStream *bs); +GF_Err tfdt_Size(GF_Box *s); +GF_Err tfdt_Read(GF_Box *s, GF_BitStream *bs); +GF_Err tfdt_dump(GF_Box *a, FILE * trace); + +GF_Box *rvcc_New(); +void rvcc_del(GF_Box *); +GF_Err rvcc_Write(GF_Box *s, GF_BitStream *bs); +GF_Err rvcc_Size(GF_Box *s); +GF_Err rvcc_Read(GF_Box *s, GF_BitStream *bs); +GF_Err rvcc_dump(GF_Box *a, FILE * trace); + +#endif /*GPAC_DISABLE_ISOM*/ + +#ifdef __cplusplus +} +#endif + +#endif //_GF_ISOMEDIA_DEV_H_ + diff --git a/src/gpacmp4/gpac/internal/media_dev.h b/src/gpacmp4/gpac/internal/media_dev.h new file mode 100644 index 00000000..c6c4b267 --- /dev/null +++ b/src/gpacmp4/gpac/internal/media_dev.h @@ -0,0 +1,240 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / Media Tools sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +#ifndef _GF_MEDIA_DEV_H_ +#define _GF_MEDIA_DEV_H_ + +#include +#include + +#ifndef GPAC_DISABLE_ISOM +void gf_media_get_sample_average_infos(GF_ISOFile *file, u32 Track, u32 *avgSize, u32 *MaxSize, u32 *TimeDelta, u32 *maxCTSDelta, u32 *const_duration, u32 *bandwidth); +#endif + + +#ifndef GPAC_DISABLE_MEDIA_IMPORT +GF_Err gf_import_message(GF_MediaImporter *import, GF_Err e, char *format, ...); +#endif /*GPAC_DISABLE_MEDIA_IMPORT*/ + +#ifndef GPAC_DISABLE_AV_PARSERS + + +#define GF_SVC_SSPS_ID_SHIFT 16 + +/*returns 0 if not a start code, or size of start code (3 or 4 bytes). If start code, bitstream +is positionned AFTER start code*/ +u32 AVC_IsStartCode(GF_BitStream *bs); +/*returns size of chunk between current and next startcode (excluding startcode sizes), 0 if no more startcodes (eos)*/ +u32 AVC_NextStartCode(GF_BitStream *bs); +/*returns NAL unit type - bitstream must be sync'ed!!*/ +u8 AVC_NALUType(GF_BitStream *bs); +Bool SVC_NALUIsSlice(u8 type); + + +enum +{ + /*SPS has been parsed*/ + AVC_SPS_PARSED = 1, + /*SPS has been declared to the upper layer*/ + AVC_SPS_DECLARED = 1<<1, + /*SUB-SPS has been parsed*/ + AVC_SUBSPS_PARSED = 1<<2, + /*SUB-SPS has been declared to the upper layer*/ + AVC_SUBSPS_DECLARED = 1<<3, +}; + +typedef struct +{ + u8 cpb_removal_delay_length_minus1; + u8 dpb_output_delay_length_minus1; + u8 time_offset_length; + /*to be eventually completed by other hrd members*/ +} AVC_HRD; + +typedef struct +{ + s32 timing_info_present_flag; + u32 num_units_in_tick; + u32 time_scale; + s32 fixed_frame_rate_flag; + + u32 par_num, par_den; + + Bool nal_hrd_parameters_present_flag; + Bool vcl_hrd_parameters_present_flag; + AVC_HRD hrd; + + Bool pic_struct_present_flag; + /*to be eventually completed by other vui members*/ +} AVC_VUI; + +typedef struct +{ + s32 profile_idc; + s32 level_idc; + s32 prof_compat; + s32 log2_max_frame_num; + u32 poc_type, poc_cycle_length; + s32 log2_max_poc_lsb; + s32 delta_pic_order_always_zero_flag; + s32 offset_for_non_ref_pic, offset_for_top_to_bottom_field; + Bool frame_mbs_only_flag; + + s16 offset_for_ref_frame[256]; + + u32 width, height; + + AVC_VUI vui; + + /*used to discard repeated SPSs - 0: not parsed, 1 parsed, 2 sent*/ + u32 state; + + /*for SVC stats during import*/ + u32 nb_ei, nb_ep, nb_eb; +} AVC_SPS; + +typedef struct +{ + s32 id; /* used to compare pps when storing SVC PSS */ + s32 sps_id; + s32 pic_order_present; /* pic_order_present_flag*/ + s32 redundant_pic_cnt_present; /* redundant_pic_cnt_present_flag */ + u32 slice_group_count; /* num_slice_groups_minus1 + 1*/ + /*used to discard repeated SPSs - 0: not parsed, 1 parsed, 2 sent*/ + u32 status; +} AVC_PPS; + +typedef struct +{ + s32 idr_pic_flag; + u8 temporal_id, priority_id, dependency_id, quality_id; +} SVC_NALUHeader; + +typedef struct +{ + u8 nal_ref_idc, nal_unit_type, field_pic_flag, bottom_field_flag; + u32 frame_num, idr_pic_id, poc_lsb, slice_type; + s32 delta_poc_bottom; + s32 delta_poc[2]; + s32 redundant_pic_cnt; + + s32 poc; + u32 poc_msb, poc_msb_prev, poc_lsb_prev, frame_num_prev; + s32 frame_num_offset, frame_num_offset_prev; + + AVC_SPS *sps; + AVC_PPS *pps; + SVC_NALUHeader NalHeader; +} AVCSliceInfo; + + +typedef struct +{ + u32 frame_cnt; + u8 exact_match_flag; + u8 broken_link_flag; + u8 changing_slice_group_idc; + u8 valid; +} AVCSeiRecoveryPoint; + +typedef struct +{ + u8 pic_struct; + /*to be eventually completed by other pic_timing members*/ +} AVCSeiPicTiming; + +typedef struct +{ + AVCSeiRecoveryPoint recovery_point; + AVCSeiPicTiming pic_timing; + /*to be eventually completed by other sei*/ +} AVCSei; + +typedef struct +{ + AVC_SPS sps[32]; /* range allowed in the spec is 0..31 */ + s8 sps_active_idx; /*currently active sps; must be initalized to -1 in order to discard not yet decodable SEIs*/ + + AVC_PPS pps[255]; + + AVCSliceInfo s_info; + AVCSei sei; + + Bool is_svc; +} AVCState; + +/*return sps ID or -1 if error*/ +s32 AVC_ReadSeqInfo(char *sps_data, u32 sps_size, AVCState *avc, u32 subseq_sps, u32 *vui_flag_pos); +/*return pps ID or -1 if error*/ +s32 AVC_ReadPictParamSet(char *pps_data, u32 pps_size, AVCState *avc); +/*is slice a RAP*/ +Bool AVC_SliceIsIDR(AVCState *avc); +/*parses NALU, updates avc state and returns: + 1 if NALU part of new frame + 0 if NALU part of prev frame + -1 if bitstream error +*/ +s32 AVC_ParseNALU(GF_BitStream *bs, u32 nal_hdr, AVCState *avc); +/*remove SEI messages not allowed in MP4*/ +/*nota: 'buffer' remains unmodified but cannot be set const*/ +u32 AVC_ReformatSEI_NALU(char *buffer, u32 nal_size, AVCState *avc); + +#ifndef GPAC_DISABLE_ISOM +GF_Err AVC_ChangePAR(GF_AVCConfig *avcc, s32 ar_n, s32 ar_d); +#endif + +#endif /*GPAC_DISABLE_AV_PARSERS*/ + +typedef struct +{ + u8 rate_idx; + u8 pck_size; +} QCPRateTable; + + +#if !defined(GPAC_DISABLE_ISOM) && !defined(GPAC_DISABLE_STREAMING) + +GP_RTPPacketizer *gf_rtp_packetizer_create_and_init_from_file(GF_ISOFile *file, + u32 TrackNum, + void *cbk_obj, + void (*OnNewPacket)(void *cbk, GF_RTPHeader *header), + void (*OnPacketDone)(void *cbk, GF_RTPHeader *header), + void (*OnDataReference)(void *cbk, u32 payload_size, u32 offset_from_orig), + void (*OnData)(void *cbk, char *data, u32 data_size, Bool is_head), + u32 Path_MTU, + u32 max_ptime, + u32 default_rtp_rate, + u32 flags, + u8 PayloadID, + Bool copy_media, + u32 InterleaveGroupID, + u8 InterleaveGroupPriority); + +void gf_media_format_ttxt_sdp(GP_RTPPacketizer *builder, char *payload_name, char *sdpLine, GF_ISOFile *file, u32 track); + +#endif + +#endif /*_GF_MEDIA_DEV_H_*/ + diff --git a/src/gpacmp4/gpac/internal/odf_dev.h b/src/gpacmp4/gpac/internal/odf_dev.h new file mode 100644 index 00000000..8ff4046f --- /dev/null +++ b/src/gpacmp4/gpac/internal/odf_dev.h @@ -0,0 +1,364 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / MPEG-4 ObjectDescriptor sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +#ifndef _GF_OD_DEV_H_ +#define _GF_OD_DEV_H_ + +#include + +/*read-write OD formatted strings*/ +GF_Err gf_odf_read_url_string(GF_BitStream *bs, char **string, u32 *readBytes); +GF_Err gf_odf_write_url_string(GF_BitStream *bs, char *string); +u32 gf_odf_size_url_string(char *string); + +/*descriptors base functions*/ +GF_Descriptor *gf_odf_create_descriptor(u8 tag); +GF_Err gf_odf_delete_descriptor(GF_Descriptor *desc); +GF_Err gf_odf_parse_descriptor(GF_BitStream *bs, GF_Descriptor **desc, u32 *size); +GF_Err gf_odf_read_descriptor(GF_BitStream *bs, GF_Descriptor *desc, u32 DescSize); +GF_Err gf_odf_write_base_descriptor(GF_BitStream *bs, u8 tag, u32 size); +GF_Err gf_odf_write_descriptor(GF_BitStream *bs, GF_Descriptor *desc); +GF_Err gf_odf_size_descriptor(GF_Descriptor *desc, u32 *outSize); +GF_Err gf_odf_delete_descriptor_list(GF_List *descList); +GF_Err gf_odf_write_descriptor_list(GF_BitStream *bs, GF_List *descList); +GF_Err gf_odf_write_descriptor_list_filter(GF_BitStream *bs, GF_List *descList, u8 tag_only); +GF_Err gf_odf_size_descriptor_list(GF_List *descList, u32 *outSize); + +/*handle lazy bitstreams where SizeOfInstance is always encoded on 4 bytes*/ +s32 gf_odf_size_field_size(u32 size_desc); + +GF_Err DumpDescList(GF_List *list, FILE *trace, u32 indent, const char *ListName, Bool XMTDump, Bool no_skip_empty); + +/*IPMPX tools*/ +u32 gf_ipmpx_array_size(GF_BitStream *bs, u32 *array_size); +void gf_ipmpx_write_array(GF_BitStream *bs, char *data, u32 data_len); + +/*QoS qualifiers base functions*/ +GF_Err gf_odf_parse_qos_qual(GF_BitStream *bs, GF_QoS_Default **qos_qual, u32 *qos_size); +void gf_odf_delete_qos_qual(GF_QoS_Default *qos); +GF_Err gf_odf_size_qos_qual(GF_QoS_Default *qos); +GF_Err gf_odf_write_qos_qual(GF_BitStream *bs, GF_QoS_Default *qos); + +GF_Descriptor *gf_odf_new_iod(); +GF_Descriptor *gf_odf_new_esd(); +GF_Descriptor *gf_odf_new_dcd(); +GF_Descriptor *gf_odf_new_slc(u8 predef); +GF_Descriptor *gf_odf_new_cc(); +GF_Descriptor *gf_odf_new_cc_date(); +GF_Descriptor *gf_odf_new_cc_name(); +GF_Descriptor *gf_odf_new_ci(); +GF_Descriptor *gf_odf_new_default(); +GF_Descriptor *gf_odf_new_esd_inc(); +GF_Descriptor *gf_odf_new_esd_ref(); +GF_Descriptor *gf_odf_new_exp_text(); +GF_Descriptor *gf_odf_new_pl_ext(); +GF_Descriptor *gf_odf_new_ipi_ptr(); +GF_Descriptor *gf_odf_new_ipmp(); +GF_Descriptor *gf_odf_new_ipmp_ptr(); +GF_Descriptor *gf_odf_new_kw(); +GF_Descriptor *gf_odf_new_lang(); +GF_Descriptor *gf_odf_new_isom_iod(); +GF_Descriptor *gf_odf_new_isom_od(); +GF_Descriptor *gf_odf_new_od(); +GF_Descriptor *gf_odf_new_oci_date(); +GF_Descriptor *gf_odf_new_oci_name(); +GF_Descriptor *gf_odf_new_pl_idx(); +GF_Descriptor *gf_odf_new_qos(); +GF_Descriptor *gf_odf_new_rating(); +GF_Descriptor *gf_odf_new_reg(); +GF_Descriptor *gf_odf_new_short_text(); +GF_Descriptor *gf_odf_new_smpte_camera(); +GF_Descriptor *gf_odf_new_sup_cid(); +GF_Descriptor *gf_odf_new_segment(); +GF_Descriptor *gf_odf_new_mediatime(); +GF_Descriptor *gf_odf_new_ipmp_tool_list(); +GF_Descriptor *gf_odf_new_ipmp_tool(); +GF_Descriptor *gf_odf_new_muxinfo(); +GF_Descriptor *gf_odf_New_ElemMask(); +GF_Descriptor *gf_odf_new_bifs_cfg(); +GF_Descriptor *gf_odf_new_ui_cfg(); +GF_Descriptor *gf_odf_new_laser_cfg(); +GF_Descriptor *gf_odf_new_auxvid(); + + +GF_Err gf_odf_del_iod(GF_InitialObjectDescriptor *iod); +GF_Err gf_odf_del_esd(GF_ESD *esd); +GF_Err gf_odf_del_dcd(GF_DecoderConfig *dcd); +GF_Err gf_odf_del_slc(GF_SLConfig *sl); +GF_Err gf_odf_del_cc(GF_CCDescriptor *ccd); +GF_Err gf_odf_del_cc_date(GF_CC_Date *cdd); +GF_Err gf_odf_del_cc_name(GF_CC_Name *cnd); +GF_Err gf_odf_del_ci(GF_CIDesc *cid); +GF_Err gf_odf_del_default(GF_DefaultDescriptor *dd); +GF_Err gf_odf_del_esd_inc(GF_ES_ID_Inc *esd_inc); +GF_Err gf_odf_del_esd_ref(GF_ES_ID_Ref *esd_ref); +GF_Err gf_odf_del_exp_text(GF_ExpandedTextual *etd); +GF_Err gf_odf_del_pl_ext(GF_PLExt *pld); +GF_Err gf_odf_del_ipi_ptr(GF_IPIPtr *ipid); +GF_Err gf_odf_del_ipmp(GF_IPMP_Descriptor *ipmp); +GF_Err gf_odf_del_ipmp_ptr(GF_IPMPPtr *ipmpd); +GF_Err gf_odf_del_kw(GF_KeyWord *kwd); +GF_Err gf_odf_del_lang(GF_Language *ld); +GF_Err gf_odf_del_isom_iod(GF_IsomInitialObjectDescriptor *iod); +GF_Err gf_odf_del_isom_od(GF_IsomObjectDescriptor *od); +GF_Err gf_odf_del_od(GF_ObjectDescriptor *od); +GF_Err gf_odf_del_oci_date(GF_OCI_Data *ocd); +GF_Err gf_odf_del_oci_name(GF_OCICreators *ocn); +GF_Err gf_odf_del_pl_idx(GF_PL_IDX *plid); +GF_Err gf_odf_del_qos(GF_QoS_Descriptor *qos); +GF_Err gf_odf_del_rating(GF_Rating *rd); +GF_Err gf_odf_del_reg(GF_Registration *reg); +GF_Err gf_odf_del_short_text(GF_ShortTextual *std); +GF_Err gf_odf_del_smpte_camera(GF_SMPTECamera *cpd); +GF_Err gf_odf_del_sup_cid(GF_SCIDesc *scid); +GF_Err gf_odf_del_segment(GF_Segment *sd); +GF_Err gf_odf_del_mediatime(GF_MediaTime *mt); +GF_Err gf_odf_del_ipmp_tool_list(GF_IPMP_ToolList *ipmptl); +GF_Err gf_odf_del_ipmp_tool(GF_IPMP_Tool *ipmp); +GF_Err gf_odf_del_muxinfo(GF_MuxInfo *mi); +GF_Err gf_odf_del_bifs_cfg(GF_BIFSConfig *desc); +GF_Err gf_odf_del_ui_cfg(GF_UIConfig *desc); +GF_Err gf_odf_del_laser_cfg(GF_LASERConfig *desc); +GF_Err gf_odf_del_auxvid(GF_AuxVideoDescriptor *ld); + +GF_Err gf_odf_read_iod(GF_BitStream *bs, GF_InitialObjectDescriptor *iod, u32 DescSize); +GF_Err gf_odf_read_esd(GF_BitStream *bs, GF_ESD *esd, u32 DescSize); +GF_Err gf_odf_read_dcd(GF_BitStream *bs, GF_DecoderConfig *dcd, u32 DescSize); +GF_Err gf_odf_read_slc(GF_BitStream *bs, GF_SLConfig *sl, u32 DescSize); +GF_Err gf_odf_read_cc(GF_BitStream *bs, GF_CCDescriptor *ccd, u32 DescSize); +GF_Err gf_odf_read_cc_date(GF_BitStream *bs, GF_CC_Date *cdd, u32 DescSize); +GF_Err gf_odf_read_cc_name(GF_BitStream *bs, GF_CC_Name *cnd, u32 DescSize); +GF_Err gf_odf_read_ci(GF_BitStream *bs, GF_CIDesc *cid, u32 DescSize); +GF_Err gf_odf_read_default(GF_BitStream *bs, GF_DefaultDescriptor *dd, u32 DescSize); +GF_Err gf_odf_read_esd_inc(GF_BitStream *bs, GF_ES_ID_Inc *esd_inc, u32 DescSize); +GF_Err gf_odf_read_esd_ref(GF_BitStream *bs, GF_ES_ID_Ref *esd_ref, u32 DescSize); +GF_Err gf_odf_read_exp_text(GF_BitStream *bs, GF_ExpandedTextual *etd, u32 DescSize); +GF_Err gf_odf_read_pl_ext(GF_BitStream *bs, GF_PLExt *pld, u32 DescSize); +GF_Err gf_odf_read_ipi_ptr(GF_BitStream *bs, GF_IPIPtr *ipid, u32 DescSize); +GF_Err gf_odf_read_ipmp(GF_BitStream *bs, GF_IPMP_Descriptor *ipmp, u32 DescSize); +GF_Err gf_odf_read_ipmp_ptr(GF_BitStream *bs, GF_IPMPPtr *ipmpd, u32 DescSize); +GF_Err gf_odf_read_kw(GF_BitStream *bs, GF_KeyWord *kwd, u32 DescSize); +GF_Err gf_odf_read_lang(GF_BitStream *bs, GF_Language *ld, u32 DescSize); +GF_Err gf_odf_read_isom_iod(GF_BitStream *bs, GF_IsomInitialObjectDescriptor *iod, u32 DescSize); +GF_Err gf_odf_read_isom_od(GF_BitStream *bs, GF_IsomObjectDescriptor *od, u32 DescSize); +GF_Err gf_odf_read_od(GF_BitStream *bs, GF_ObjectDescriptor *od, u32 DescSize); +GF_Err gf_odf_read_oci_date(GF_BitStream *bs, GF_OCI_Data *ocd, u32 DescSize); +GF_Err gf_odf_read_oci_name(GF_BitStream *bs, GF_OCICreators *ocn, u32 DescSize); +GF_Err gf_odf_read_pl_idx(GF_BitStream *bs, GF_PL_IDX *plid, u32 DescSize); +GF_Err gf_odf_read_qos(GF_BitStream *bs, GF_QoS_Descriptor *qos, u32 DescSize); +GF_Err gf_odf_read_rating(GF_BitStream *bs, GF_Rating *rd, u32 DescSize); +GF_Err gf_odf_read_reg(GF_BitStream *bs, GF_Registration *reg, u32 DescSize); +GF_Err gf_odf_read_short_text(GF_BitStream *bs, GF_ShortTextual *std, u32 DescSize); +GF_Err gf_odf_read_smpte_camera(GF_BitStream *bs, GF_SMPTECamera *cpd, u32 DescSize); +GF_Err gf_odf_read_sup_cid(GF_BitStream *bs, GF_SCIDesc *scid, u32 DescSize); +GF_Err gf_odf_read_segment(GF_BitStream *bs, GF_Segment *sd, u32 DescSize); +GF_Err gf_odf_read_mediatime(GF_BitStream *bs, GF_MediaTime *mt, u32 DescSize); +GF_Err gf_odf_read_muxinfo(GF_BitStream *bs, GF_MuxInfo *mi, u32 DescSize); +GF_Err gf_odf_read_ipmp_tool_list(GF_BitStream *bs, GF_IPMP_ToolList *ipmptl, u32 DescSize); +GF_Err gf_odf_read_ipmp_tool(GF_BitStream *bs, GF_IPMP_Tool *ipmp, u32 DescSize); +GF_Err gf_odf_read_auxvid(GF_BitStream *bs, GF_AuxVideoDescriptor *ld, u32 DescSize); + +GF_Err gf_odf_size_iod(GF_InitialObjectDescriptor *iod, u32 *outSize); +GF_Err gf_odf_size_esd(GF_ESD *esd, u32 *outSize); +GF_Err gf_odf_size_dcd(GF_DecoderConfig *dcd, u32 *outSize); +GF_Err gf_odf_size_slc(GF_SLConfig *sl, u32 *outSize); +GF_Err gf_odf_size_cc(GF_CCDescriptor *ccd, u32 *outSize); +GF_Err gf_odf_size_cc_date(GF_CC_Date *cdd, u32 *outSize); +GF_Err gf_odf_size_cc_name(GF_CC_Name *cnd, u32 *outSize); +GF_Err gf_odf_size_ci(GF_CIDesc *cid, u32 *outSize); +GF_Err gf_odf_size_default(GF_DefaultDescriptor *dd, u32 *outSize); +GF_Err gf_odf_size_esd_inc(GF_ES_ID_Inc *esd_inc, u32 *outSize); +GF_Err gf_odf_size_esd_ref(GF_ES_ID_Ref *esd_ref, u32 *outSize); +GF_Err gf_odf_size_exp_text(GF_ExpandedTextual *etd, u32 *outSize); +GF_Err gf_odf_size_pl_ext(GF_PLExt *pld, u32 *outSize); +GF_Err gf_odf_size_ipi_ptr(GF_IPIPtr *ipid, u32 *outSize); +GF_Err gf_odf_size_ipmp(GF_IPMP_Descriptor *ipmp, u32 *outSize); +GF_Err gf_odf_size_ipmp_ptr(GF_IPMPPtr *ipmpd, u32 *outSize); +GF_Err gf_odf_size_kw(GF_KeyWord *kwd, u32 *outSize); +GF_Err gf_odf_size_lang(GF_Language *ld, u32 *outSize); +GF_Err gf_odf_size_isom_iod(GF_IsomInitialObjectDescriptor *iod, u32 *outSize); +GF_Err gf_odf_size_isom_od(GF_IsomObjectDescriptor *od, u32 *outSize); +GF_Err gf_odf_size_od(GF_ObjectDescriptor *od, u32 *outSize); +GF_Err gf_odf_size_oci_date(GF_OCI_Data *ocd, u32 *outSize); +GF_Err gf_odf_size_oci_name(GF_OCICreators *ocn, u32 *outSize); +GF_Err gf_odf_size_pl_idx(GF_PL_IDX *plid, u32 *outSize); +GF_Err gf_odf_size_qos(GF_QoS_Descriptor *qos, u32 *outSize); +GF_Err gf_odf_size_rating(GF_Rating *rd, u32 *outSize); +GF_Err gf_odf_size_reg(GF_Registration *reg, u32 *outSize); +GF_Err gf_odf_size_short_text(GF_ShortTextual *std, u32 *outSize); +GF_Err gf_odf_size_smpte_camera(GF_SMPTECamera *cpd, u32 *outSize); +GF_Err gf_odf_size_sup_cid(GF_SCIDesc *scid, u32 *outSize); +GF_Err gf_odf_size_segment(GF_Segment *sd, u32 *outSize); +GF_Err gf_odf_size_mediatime(GF_MediaTime *mt, u32 *outSize); +GF_Err gf_odf_size_muxinfo(GF_MuxInfo *mi, u32 *outSize); +GF_Err gf_odf_size_ipmp_tool_list(GF_IPMP_ToolList *ipmptl, u32 *outSize); +GF_Err gf_odf_size_ipmp_tool(GF_IPMP_Tool *ipmp, u32 *outSize); +GF_Err gf_odf_size_auxvid(GF_AuxVideoDescriptor *ld, u32 *outSize); + +GF_Err gf_odf_write_iod(GF_BitStream *bs, GF_InitialObjectDescriptor *iod); +GF_Err gf_odf_write_esd(GF_BitStream *bs, GF_ESD *esd); +GF_Err gf_odf_write_dcd(GF_BitStream *bs, GF_DecoderConfig *dcd); +GF_Err gf_odf_write_slc(GF_BitStream *bs, GF_SLConfig *sl); +GF_Err gf_odf_write_cc(GF_BitStream *bs, GF_CCDescriptor *ccd); +GF_Err gf_odf_write_cc_date(GF_BitStream *bs, GF_CC_Date *cdd); +GF_Err gf_odf_write_cc_name(GF_BitStream *bs, GF_CC_Name *cnd); +GF_Err gf_odf_write_ci(GF_BitStream *bs, GF_CIDesc *cid); +GF_Err gf_odf_write_default(GF_BitStream *bs, GF_DefaultDescriptor *dd); +GF_Err gf_odf_write_esd_inc(GF_BitStream *bs, GF_ES_ID_Inc *esd_inc); +GF_Err gf_odf_write_esd_ref(GF_BitStream *bs, GF_ES_ID_Ref *esd_ref); +GF_Err gf_odf_write_exp_text(GF_BitStream *bs, GF_ExpandedTextual *etd); +GF_Err gf_odf_write_pl_ext(GF_BitStream *bs, GF_PLExt *pld); +GF_Err gf_odf_write_ipi_ptr(GF_BitStream *bs, GF_IPIPtr *ipid); +GF_Err gf_odf_write_ipmp(GF_BitStream *bs, GF_IPMP_Descriptor *ipmp); +GF_Err gf_odf_write_ipmp_ptr(GF_BitStream *bs, GF_IPMPPtr *ipmpd); +GF_Err gf_odf_write_kw(GF_BitStream *bs, GF_KeyWord *kwd); +GF_Err gf_odf_write_lang(GF_BitStream *bs, GF_Language *ld); +GF_Err gf_odf_write_isom_iod(GF_BitStream *bs, GF_IsomInitialObjectDescriptor *iod); +GF_Err gf_odf_write_isom_od(GF_BitStream *bs, GF_IsomObjectDescriptor *od); +GF_Err gf_odf_write_od(GF_BitStream *bs, GF_ObjectDescriptor *od); +GF_Err gf_odf_write_oci_date(GF_BitStream *bs, GF_OCI_Data *ocd); +GF_Err gf_odf_write_oci_name(GF_BitStream *bs, GF_OCICreators *ocn); +GF_Err gf_odf_write_pl_idx(GF_BitStream *bs, GF_PL_IDX *plid); +GF_Err gf_odf_write_qos(GF_BitStream *bs, GF_QoS_Descriptor *qos); +GF_Err gf_odf_write_rating(GF_BitStream *bs, GF_Rating *rd); +GF_Err gf_odf_write_reg(GF_BitStream *bs, GF_Registration *reg); +GF_Err gf_odf_write_short_text(GF_BitStream *bs, GF_ShortTextual *std); +GF_Err gf_odf_write_smpte_camera(GF_BitStream *bs, GF_SMPTECamera *cpd); +GF_Err gf_odf_write_sup_cid(GF_BitStream *bs, GF_SCIDesc *scid); +GF_Err gf_odf_write_segment(GF_BitStream *bs, GF_Segment *sd); +GF_Err gf_odf_write_mediatime(GF_BitStream *bs, GF_MediaTime *mt); +GF_Err gf_odf_write_muxinfo(GF_BitStream *bs, GF_MuxInfo *mi); +GF_Err gf_odf_write_ipmp_tool_list(GF_BitStream *bs, GF_IPMP_ToolList *ipmptl); +GF_Err gf_odf_write_ipmp_tool(GF_BitStream *bs, GF_IPMP_Tool *ipmp); +GF_Err gf_odf_write_auxvid(GF_BitStream *bs, GF_AuxVideoDescriptor *ld); + +GF_Descriptor *gf_odf_new_text_cfg(); +GF_Err gf_odf_del_text_cfg(GF_TextConfig *desc); +GF_Descriptor *gf_odf_new_tx3g(); +GF_Err gf_odf_del_tx3g(GF_TextSampleDescriptor *sd); + +/*our commands base functions*/ +GF_ODCom *gf_odf_create_command(u8 tag); +GF_Err gf_odf_delete_command(GF_ODCom *com); +GF_Err gf_odf_parse_command(GF_BitStream *bs, GF_ODCom **com, u32 *com_size); +GF_Err gf_odf_read_command(GF_BitStream *bs, GF_ODCom *com, u32 gf_odf_size_command); +GF_Err gf_odf_size_command(GF_ODCom *com, u32 *outSize); +GF_Err gf_odf_write_command(GF_BitStream *bs, GF_ODCom *com); + +GF_ODCom *gf_odf_new_od_remove(); +GF_ODCom *gf_odf_new_od_update(); +GF_ODCom *gf_odf_new_esd_update(); +GF_ODCom *gf_odf_new_esd_remove(); +GF_ODCom *gf_odf_new_ipmp_update(); +GF_ODCom *gf_odf_new_ipmp_remove(); +GF_ODCom *gf_odf_new_base_command(); + +GF_Err gf_odf_del_od_remove(GF_ODRemove *ODRemove); +GF_Err gf_odf_del_od_update(GF_ODUpdate *ODUpdate); +GF_Err gf_odf_del_esd_update(GF_ESDUpdate *ESDUpdate); +GF_Err gf_odf_del_esd_remove(GF_ESDRemove *ESDRemove); +GF_Err gf_odf_del_ipmp_update(GF_IPMPUpdate *IPMPDUpdate); +GF_Err gf_odf_del_ipmp_remove(GF_IPMPRemove *IPMPDRemove); +GF_Err gf_odf_del_base_command(GF_BaseODCom *bcRemove); + +GF_Err gf_odf_read_od_remove(GF_BitStream *bs, GF_ODRemove *odRem, u32 gf_odf_size_command); +GF_Err gf_odf_read_od_update(GF_BitStream *bs, GF_ODUpdate *odUp, u32 gf_odf_size_command); +GF_Err gf_odf_read_esd_update(GF_BitStream *bs, GF_ESDUpdate *esdUp, u32 gf_odf_size_command); +GF_Err gf_odf_read_esd_remove(GF_BitStream *bs, GF_ESDRemove *esdRem, u32 gf_odf_size_command); +GF_Err gf_odf_read_ipmp_update(GF_BitStream *bs, GF_IPMPUpdate *ipmpUp, u32 gf_odf_size_command); +GF_Err gf_odf_read_ipmp_remove(GF_BitStream *bs, GF_IPMPRemove *ipmpRem, u32 gf_odf_size_command); +GF_Err gf_odf_read_base_command(GF_BitStream *bs, GF_BaseODCom *bcRem, u32 gf_odf_size_command); + +GF_Err gf_odf_size_od_remove(GF_ODRemove *odRem, u32 *outSize); +GF_Err gf_odf_size_od_update(GF_ODUpdate *odUp, u32 *outSize); +GF_Err gf_odf_size_esd_update(GF_ESDUpdate *esdUp, u32 *outSize); +GF_Err gf_odf_size_esd_remove(GF_ESDRemove *esdRem, u32 *outSize); +GF_Err gf_odf_size_ipmp_update(GF_IPMPUpdate *ipmpUp, u32 *outSize); +GF_Err gf_odf_size_ipmp_remove(GF_IPMPRemove *ipmpRem, u32 *outSize); +GF_Err gf_odf_size_base_command(GF_BaseODCom *bcRem, u32 *outSize); + +GF_Err gf_odf_write_od_remove(GF_BitStream *bs, GF_ODRemove *odRem); +GF_Err gf_odf_write_od_update(GF_BitStream *bs, GF_ODUpdate *odUp); +GF_Err gf_odf_write_esd_update(GF_BitStream *bs, GF_ESDUpdate *esdUp); +GF_Err gf_odf_write_esd_remove(GF_BitStream *bs, GF_ESDRemove *esdRem); +GF_Err gf_odf_write_ipmp_update(GF_BitStream *bs, GF_IPMPUpdate *ipmpUp); +GF_Err gf_odf_write_ipmp_remove(GF_BitStream *bs, GF_IPMPRemove *ipmpRem); +GF_Err gf_odf_write_base_command(GF_BitStream *bs, GF_BaseODCom *bcRem); + + + +/*dumping*/ +GF_Err gf_odf_dump_iod(GF_InitialObjectDescriptor *iod, FILE *trace, u32 indent, Bool XMTDump); +GF_Err gf_odf_dump_esd(GF_ESD *esd, FILE *trace, u32 indent, Bool XMTDump); +GF_Err gf_odf_dump_dcd(GF_DecoderConfig *dcd, FILE *trace, u32 indent, Bool XMTDump); +GF_Err gf_odf_dump_slc(GF_SLConfig *sl, FILE *trace, u32 indent, Bool XMTDump); +GF_Err gf_odf_dump_cc(GF_CCDescriptor *ccd, FILE *trace, u32 indent, Bool XMTDump); +GF_Err gf_odf_dump_cc_date(GF_CC_Date *cdd, FILE *trace, u32 indent, Bool XMTDump); +GF_Err gf_odf_dump_cc_name(GF_CC_Name *cnd, FILE *trace, u32 indent, Bool XMTDump); +GF_Err gf_odf_dump_ci(GF_CIDesc *cid, FILE *trace, u32 indent, Bool XMTDump); +GF_Err gf_odf_dump_default(GF_DefaultDescriptor *dd, FILE *trace, u32 indent, Bool XMTDump); +GF_Err gf_odf_dump_esd_inc(GF_ES_ID_Inc *esd_inc, FILE *trace, u32 indent, Bool XMTDump); +GF_Err gf_odf_dump_esd_ref(GF_ES_ID_Ref *esd_ref, FILE *trace, u32 indent, Bool XMTDump); +GF_Err gf_odf_dump_exp_text(GF_ExpandedTextual *etd, FILE *trace, u32 indent, Bool XMTDump); +GF_Err gf_odf_dump_pl_ext(GF_PLExt *pld, FILE *trace, u32 indent, Bool XMTDump); +GF_Err gf_odf_dump_ipi_ptr(GF_IPIPtr *ipid, FILE *trace, u32 indent, Bool XMTDump); +GF_Err gf_odf_dump_ipmp(GF_IPMP_Descriptor *ipmp, FILE *trace, u32 indent, Bool XMTDump); +GF_Err gf_odf_dump_ipmp_ptr(GF_IPMPPtr *ipmpd, FILE *trace, u32 indent, Bool XMTDump); +GF_Err gf_odf_dump_kw(GF_KeyWord *kwd, FILE *trace, u32 indent, Bool XMTDump); +GF_Err gf_odf_dump_lang(GF_Language *ld, FILE *trace, u32 indent, Bool XMTDump); +GF_Err gf_odf_dump_isom_iod(GF_IsomInitialObjectDescriptor *iod, FILE *trace, u32 indent, Bool XMTDump); +GF_Err gf_odf_dump_isom_od(GF_IsomObjectDescriptor *od, FILE *trace, u32 indent, Bool XMTDump); +GF_Err gf_odf_dump_od(GF_ObjectDescriptor *od, FILE *trace, u32 indent, Bool XMTDump); +GF_Err gf_odf_dump_oci_date(GF_OCI_Data *ocd, FILE *trace, u32 indent, Bool XMTDump); +GF_Err gf_odf_dump_oci_name(GF_OCICreators *ocn, FILE *trace, u32 indent, Bool XMTDump); +GF_Err gf_odf_dump_pl_idx(GF_PL_IDX *plid, FILE *trace, u32 indent, Bool XMTDump); +GF_Err gf_odf_dump_qos(GF_QoS_Descriptor *qos, FILE *trace, u32 indent, Bool XMTDump); +GF_Err gf_odf_dump_rating(GF_Rating *rd, FILE *trace, u32 indent, Bool XMTDump); +GF_Err gf_odf_dump_reg(GF_Registration *reg, FILE *trace, u32 indent, Bool XMTDump); +GF_Err gf_odf_dump_short_text(GF_ShortTextual *std, FILE *trace, u32 indent, Bool XMTDump); +GF_Err gf_odf_dump_smpte_camera(GF_SMPTECamera *cpd, FILE *trace, u32 indent, Bool XMTDump); +GF_Err gf_odf_dump_sup_cid(GF_SCIDesc *scid, FILE *trace, u32 indent, Bool XMTDump); +GF_Err gf_odf_dump_segment(GF_Segment *sd, FILE *trace, u32 indent, Bool XMTDump); +GF_Err gf_odf_dump_mediatime(GF_MediaTime *mt, FILE *trace, u32 indent, Bool XMTDump); +GF_Err gf_odf_dump_muxinfo(GF_MuxInfo *mi, FILE *trace, u32 indent, Bool XMTDump); +GF_Err gf_odf_dump_bifs_cfg(GF_BIFSConfig *dsi, FILE *trace, u32 indent, Bool XMTDump); +GF_Err gf_odf_dump_laser_cfg(GF_LASERConfig *dsi, FILE *trace, u32 indent, Bool XMTDump); +GF_Err gf_odf_dump_ui_cfg(GF_UIConfig *dsi, FILE *trace, u32 indent, Bool XMTDump); +GF_Err gf_odf_dump_txtcfg(GF_TextConfig *desc, FILE *trace, u32 indent, Bool XMTDump); +GF_Err gf_odf_dump_ipmp_tool_list(GF_IPMP_ToolList *tl, FILE *trace, u32 indent, Bool XMTDump); +GF_Err gf_odf_dump_ipmp_tool(GF_IPMP_Tool*t, FILE *trace, u32 indent, Bool XMTDump); +GF_Err gf_odf_dump_aux_vid(GF_AuxVideoDescriptor *ld, FILE *trace, u32 indent, Bool XMTDump); + + +GF_Err gf_odf_dump_od_update(GF_ODUpdate *com, FILE *trace, u32 indent, Bool XMTDump); +GF_Err gf_odf_dump_od_remove(GF_ODRemove *com, FILE *trace, u32 indent, Bool XMTDump); +GF_Err gf_odf_dump_esd_update(GF_ESDUpdate *com, FILE *trace, u32 indent, Bool XMTDump); +GF_Err gf_odf_dump_esd_remove(GF_ESDRemove *com, FILE *trace, u32 indent, Bool XMTDump); +GF_Err gf_odf_dump_ipmp_update(GF_IPMPUpdate *com, FILE *trace, u32 indent, Bool XMTDump); +GF_Err gf_odf_dump_ipmp_remove(GF_IPMPRemove *com, FILE *trace, u32 indent, Bool XMTDump); +GF_Err gf_odf_dump_base_command(GF_BaseODCom *com, FILE *trace, u32 indent, Bool XMTDump); + +#endif /*_GF_OD_DEV_H_*/ + diff --git a/src/gpacmp4/gpac/internal/odf_parse_common.h b/src/gpacmp4/gpac/internal/odf_parse_common.h new file mode 100644 index 00000000..43b31c97 --- /dev/null +++ b/src/gpacmp4/gpac/internal/odf_parse_common.h @@ -0,0 +1,43 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / MPEG-4 ObjectDescriptor sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +#ifndef _GF_OD_PARSE_COMMON_H_ +#define _GF_OD_PARSE_COMMON_H_ +#include + +#define GET_U8(field) { u32 d; if (strstr(val, "0x")) { ret += sscanf(val, "%x", &d); if (ret) field = (u8) d; } else { ret += sscanf(val, "%u", &d); if (ret) field = (u8) d; } } +#define GET_U16(field) { u16 d; if (strstr(val, "0x")) { ret += sscanf(val, "%hx", &d); if (ret) field = d; } else { ret += sscanf(val, "%hu", &d); if (ret) field = d; } } +#define GET_S16(field) { s16 d; if (strstr(val, "0x")) { ret += sscanf(val, "%hx", (u16*)&d); if (ret) field = d; } else { ret += sscanf(val, "%hd", &d); if (ret) field = d; } } +#define GET_U32(field) { u32 d; if (strstr(val, "0x")) { ret += sscanf(val, "%x", &d); if (ret) field = d; } else { ret += sscanf(val, "%ud", &d); if (ret) field = d; } } +#define GET_S32(field) { s32 d; if (strstr(val, "0x")) { ret += sscanf(val, "%x", (u32*)&d); if (ret) field = d; } else { ret += sscanf(val, "%d", &d); if (ret) field = d; } } +#define GET_BOOL(field) { ret = 1; field = (!stricmp(val, "true") || !stricmp(val, "1")) ? 1 : 0; } +#define GET_U64(field) { u64 d; if (strstr(val, "0x")) { ret += sscanf(val, LLX, &d); if (ret) field = d; } else { ret += sscanf(val, LLU, &d); if (ret) field = d; } } + +#define GET_DOUBLE(field) { Float v; ret = 1; sscanf(val, "%f", &v); field = (Double) v;} +#define GET_STRING(field) { ret = 1; field = gf_strdup(val); if (val[0] == '"') strcpy(field, val+1); if (field[strlen(field)-1] == '"') field[strlen(field)-1] = 0; } + + +#endif /* _GF_OD_PARSE_COMMON_H_ */ + diff --git a/src/gpacmp4/gpac/internal/ogg.h b/src/gpacmp4/gpac/internal/ogg.h new file mode 100644 index 00000000..36aa3b80 --- /dev/null +++ b/src/gpacmp4/gpac/internal/ogg.h @@ -0,0 +1,213 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2002 * + * by the Xiph.Org Foundation http://www.xiph.org/ * + * * + ******************************************************************** + + function: code raw [Vorbis] packets into framed OggSquish stream and + decode Ogg streams back into raw packets + + note: The CRC code is directly derived from public domain code by + Ross Williams (ross@guest.adelaide.edu.au). See docs/framing.html + for details. + + ********************************************************************/ + + +#ifndef _GF_OGG_H_ +#define _GF_OGG_H_ + +#include + +#ifndef GPAC_DISABLE_OGG + +/*DON'T CLASH WITH OFFICIAL OGG IF ALREADY INCLUDED*/ +#ifndef _OGG_H + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + s32 endbyte; + s32 endbit; + + unsigned char *buffer; + unsigned char *ptr; + s32 storage; +} oggpack_buffer; + +/* ogg_page is used to encapsulate the data in one Ogg bitstream page *****/ + +typedef struct { + unsigned char *header; + s32 header_len; + unsigned char *body; + s32 body_len; +} ogg_page; + +/* ogg_stream_state contains the current encode/decode state of a logical + Ogg bitstream **********************************************************/ + +typedef struct { + unsigned char *body_data; /* bytes from packet bodies */ + s32 body_storage; /* storage elements allocated */ + s32 body_fill; /* elements stored; fill mark */ + s32 body_returned; /* elements of fill returned */ + + + s32 *lacing_vals; /* The values that will go to the segment table */ + s64 *granule_vals; /* granulepos values for headers. Not compact + this way, but it is simple coupled to the + lacing fifo */ + s32 lacing_storage; + s32 lacing_fill; + s32 lacing_packet; + s32 lacing_returned; + + unsigned char header[282]; /* working space for header encode */ + s32 header_fill; + + s32 e_o_s; /* set when we have buffered the last packet in the + logical bitstream */ + s32 b_o_s; /* set after we've written the initial page + of a logical bitstream */ + s32 serialno; + s32 pageno; + s64 packetno; /* sequence number for decode; the framing + knows where there's a hole in the data, + but we need coupling so that the codec + (which is in a seperate abstraction + layer) also knows about the gap */ + s64 granulepos; + +} ogg_stream_state; + +/* ogg_packet is used to encapsulate the data and metadata belonging + to a single raw Ogg/Vorbis packet *************************************/ + +typedef struct { + unsigned char *packet; + s32 bytes; + s32 b_o_s; + s32 e_o_s; + + s64 granulepos; + + s64 packetno; /* sequence number for decode; the framing + knows where there's a hole in the data, + but we need coupling so that the codec + (which is in a seperate abstraction + layer) also knows about the gap */ +} ogg_packet; + +typedef struct { + unsigned char *data; + s32 storage; + s32 fill; + s32 returned; + + s32 unsynced; + s32 headerbytes; + s32 bodybytes; +} ogg_sync_state; + + + +/* Ogg BITSTREAM PRIMITIVES: bitstream ************************/ + +void oggpack_writeinit(oggpack_buffer *b); +void oggpack_writetrunc(oggpack_buffer *b,s32 bits); +void oggpack_writealign(oggpack_buffer *b); +void oggpack_writecopy(oggpack_buffer *b,void *source,s32 bits); +void oggpack_reset(oggpack_buffer *b); +void oggpack_writeclear(oggpack_buffer *b); +void oggpack_readinit(oggpack_buffer *b,unsigned char *buf,s32 bytes); +void oggpack_write(oggpack_buffer *b,u32 value,s32 bits); +s32 oggpack_look(oggpack_buffer *b,s32 bits); +s32 oggpack_look1(oggpack_buffer *b); +void oggpack_adv(oggpack_buffer *b,s32 bits); +void oggpack_adv1(oggpack_buffer *b); +s32 oggpack_read(oggpack_buffer *b,s32 bits); +s32 oggpack_read1(oggpack_buffer *b); +s32 oggpack_bytes(oggpack_buffer *b); +s32 oggpack_bits(oggpack_buffer *b); +unsigned char *oggpack_get_buffer(oggpack_buffer *b); + +void oggpackB_writeinit(oggpack_buffer *b); +void oggpackB_writetrunc(oggpack_buffer *b,s32 bits); +void oggpackB_writealign(oggpack_buffer *b); +void oggpackB_writecopy(oggpack_buffer *b,void *source,s32 bits); +void oggpackB_reset(oggpack_buffer *b); +void oggpackB_writeclear(oggpack_buffer *b); +void oggpackB_readinit(oggpack_buffer *b,unsigned char *buf,s32 bytes); +void oggpackB_write(oggpack_buffer *b,u32 value,s32 bits); +s32 oggpackB_look(oggpack_buffer *b,s32 bits); +s32 oggpackB_look1(oggpack_buffer *b); +void oggpackB_adv(oggpack_buffer *b,s32 bits); +void oggpackB_adv1(oggpack_buffer *b); +s32 oggpackB_read(oggpack_buffer *b,s32 bits); +s32 oggpackB_read1(oggpack_buffer *b); +s32 oggpackB_bytes(oggpack_buffer *b); +s32 oggpackB_bits(oggpack_buffer *b); +unsigned char *oggpackB_get_buffer(oggpack_buffer *b); + +/* Ogg BITSTREAM PRIMITIVES: encoding **************************/ + +s32 ogg_stream_packetin(ogg_stream_state *os, ogg_packet *op); +s32 ogg_stream_pageout(ogg_stream_state *os, ogg_page *og); +s32 ogg_stream_flush(ogg_stream_state *os, ogg_page *og); + +/* Ogg BITSTREAM PRIMITIVES: decoding **************************/ + +s32 ogg_sync_init(ogg_sync_state *oy); +s32 ogg_sync_clear(ogg_sync_state *oy); +s32 ogg_sync_reset(ogg_sync_state *oy); +s32 ogg_sync_destroy(ogg_sync_state *oy); + +char *ogg_sync_buffer(ogg_sync_state *oy, s32 size); +s32 ogg_sync_wrote(ogg_sync_state *oy, s32 bytes); +s32 ogg_sync_pageseek(ogg_sync_state *oy,ogg_page *og); +s32 ogg_sync_pageout(ogg_sync_state *oy, ogg_page *og); +s32 ogg_stream_pagein(ogg_stream_state *os, ogg_page *og); +s32 ogg_stream_packetout(ogg_stream_state *os,ogg_packet *op); +s32 ogg_stream_packetpeek(ogg_stream_state *os,ogg_packet *op); + +/* Ogg BITSTREAM PRIMITIVES: general ***************************/ + +s32 ogg_stream_init(ogg_stream_state *os,s32 serialno); +s32 ogg_stream_clear(ogg_stream_state *os); +s32 ogg_stream_reset(ogg_stream_state *os); +s32 ogg_stream_reset_serialno(ogg_stream_state *os,s32 serialno); +s32 ogg_stream_destroy(ogg_stream_state *os); +s32 ogg_stream_eos(ogg_stream_state *os); +void ogg_page_checksum_set(ogg_page *og); +s32 ogg_page_version(ogg_page *og); +s32 ogg_page_continued(ogg_page *og); +s32 ogg_page_bos(ogg_page *og); +s32 ogg_page_eos(ogg_page *og); +s64 ogg_page_granulepos(ogg_page *og); +s32 ogg_page_serialno(ogg_page *og); +s32 ogg_page_pageno(ogg_page *og); +s32 ogg_page_packets(ogg_page *og); + +void ogg_packet_clear(ogg_packet *op); + + +#ifdef __cplusplus +} +#endif + + +#endif /*_OGG_H*/ + +#endif /*GPAC_DISABLE_OGG*/ + +#endif /*_GF_OGG_H_*/ + diff --git a/src/gpacmp4/gpac/isomedia.h b/src/gpacmp4/gpac/isomedia.h new file mode 100644 index 00000000..b7de0540 --- /dev/null +++ b/src/gpacmp4/gpac/isomedia.h @@ -0,0 +1,1995 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / ISO Media File Format sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + + +#ifndef _GF_ISOMEDIA_H_ +#define _GF_ISOMEDIA_H_ + + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#ifndef GPAC_DISABLE_ISOM + +#include + +/*the isomedia file*/ +typedef struct __tag_isom GF_ISOFile; + +/*media sample object*/ +typedef struct +{ + /*data size*/ + u32 dataLength; + /*data with padding if requested*/ + char *data; + /*decoding time*/ + u64 DTS; + /*relative offset for composition if needed*/ + u32 CTS_Offset; + /*Random Access Point flag: + 0: not random access + 1: regular RAP, + 2: sample is a redundant RAP. If set when adding the sample, this will create a sample dependency entry + */ + u8 IsRAP; +} GF_ISOSample; + + +/*creates a new empty sample*/ +GF_ISOSample *gf_isom_sample_new(); + +/*delete a sample. NOTE:the buffer content will be destroyed by default. +if you wish to keep the buffer, set dataLength to 0 in the sample +before deleting it +the pointer is set to NULL after deletion*/ +void gf_isom_sample_del(GF_ISOSample **samp); + + + +/******************************************************************** + FILE FORMAT CONSTANTS +********************************************************************/ + +/*Modes for file opening + NOTE 1: All the READ function in this API can be used in EDIT/WRITE mode. +However, some unexpected errors or values may happen in that case, depending +on how much modifications you made (timing, track with 0 samples, ...) + On the other hand, none of the EDIT/WRITE functions will work in +READ mode. + NOTE 2: The output structure of a edited file will sometimes be different +from the original file, but the media-data and meta-data will be identical. +The only change happens in the file media-data container(s) during edition + NOTE 3: when editing the file, you MUST set the final name of the modified file +to something different. This API doesn't allow file overwriting. +*/ +enum +{ + /*Opens file for dumping: same as read-only but keeps all movie fragments info untouched*/ + GF_ISOM_OPEN_READ_DUMP = 0, + /*Opens a file in READ ONLY mode*/ + GF_ISOM_OPEN_READ, + /*Opens a file in WRITE ONLY mode. Media Data is captured on the fly. In this mode, + the editing functions are disabled.*/ + GF_ISOM_OPEN_WRITE, + /*Opens an existing file in EDIT mode*/ + GF_ISOM_OPEN_EDIT, + /*Creates a new file in EDIT mode*/ + GF_ISOM_WRITE_EDIT, + /*Opens an existing file for fragment concatenation*/ + GF_ISOM_OPEN_CAT_FRAGMENTS, +}; + +/*Movie Options for file writing*/ +enum +{ + /*FLAT: the MediaData (MPEG4 ESs) is stored at the begining of the file*/ + GF_ISOM_STORE_FLAT = 1, + /*STREAMABLE: the MetaData (File Info) is stored at the begining of the file + for fast access during download*/ + GF_ISOM_STORE_STREAMABLE, + /*INTERLEAVED: Same as STREAMABLE, plus the media data is mixed by chunk of fixed duration*/ + GF_ISOM_STORE_INTERLEAVED, + /*INTERLEAVED +DRIFT: Same as INTERLEAVED, and adds time drift control to avoid creating too long chunks*/ + GF_ISOM_STORE_DRIFT_INTERLEAVED, + /*tightly interleaves samples based on their DTS, therefore allowing better placement of samples in the file. + This is used for both http interleaving and Hinting optimizations*/ + GF_ISOM_STORE_TIGHT + +}; + +/*Some track may depend on other tracks for several reasons. They reference these tracks +through the following Reference Types*/ +enum +{ + /*ref type for the OD track dependencies*/ + GF_ISOM_REF_OD = GF_4CC( 'm', 'p', 'o', 'd' ), + /*ref type for stream dependencies*/ + GF_ISOM_REF_DECODE = GF_4CC( 'd', 'p', 'n', 'd' ), + /*ref type for OCR (Object Clock Reference) dependencies*/ + GF_ISOM_REF_OCR = GF_4CC( 's', 'y', 'n', 'c' ), + /*ref type for IPI (Intellectual Property Information) dependencies*/ + GF_ISOM_REF_IPI = GF_4CC( 'i', 'p', 'i', 'r' ), + /*ref type for timed Meta Data tracks*/ + GF_ISOM_REF_META = GF_4CC( 'c', 'd', 's', 'c' ), + /*ref type for Hint tracks*/ + GF_ISOM_REF_HINT = GF_4CC( 'h', 'i', 'n', 't' ), + /*ref type for QT Chapter tracks*/ + GF_ISOM_REF_CHAP = GF_4CC( 'c', 'h', 'a', 'p' ) +}; + +/*Track Edition flag*/ +enum { + /*empty segment in the track (no media for this segment)*/ + GF_ISOM_EDIT_EMPTY = 0x00, + /*dwelled segment in the track (one media sample for this segment)*/ + GF_ISOM_EDIT_DWELL = 0x01, + /*normal segment in the track*/ + GF_ISOM_EDIT_NORMAL = 0x02 +}; + +/*Generic Media Types (YOU HAVE TO USE ONE OF THESE TYPES FOR COMPLIANT ISO MEDIA FILES)*/ +enum +{ + /*base media types*/ + GF_ISOM_MEDIA_VISUAL = GF_4CC( 'v', 'i', 'd', 'e' ), + GF_ISOM_MEDIA_AUDIO = GF_4CC( 's', 'o', 'u', 'n' ), + GF_ISOM_MEDIA_HINT = GF_4CC( 'h', 'i', 'n', 't' ), + GF_ISOM_MEDIA_META = GF_4CC( 'm', 'e', 't', 'a' ), + GF_ISOM_MEDIA_TEXT = GF_4CC( 't', 'e', 'x', 't' ), + /*subtitle code point used on ipod - same as text*/ + GF_ISOM_MEDIA_SUBT = GF_4CC( 's', 'b', 't', 'l' ), + GF_ISOM_MEDIA_SUBPIC = GF_4CC( 's', 'u', 'b', 'p' ), + /* CFS: Seen this in an itunes sample */ + GF_ISOM_MEDIA_CAPTIONS = GF_4CC ('c', 'l', 'c', 'p' ), + + /*MPEG-4 media types*/ + GF_ISOM_MEDIA_OD = GF_4CC( 'o', 'd', 's', 'm' ), + GF_ISOM_MEDIA_OCR = GF_4CC( 'c', 'r', 's', 'm' ), + GF_ISOM_MEDIA_SCENE = GF_4CC( 's', 'd', 's', 'm' ), + GF_ISOM_MEDIA_MPEG7 = GF_4CC( 'm', '7', 's', 'm' ), + GF_ISOM_MEDIA_OCI = GF_4CC( 'o', 'c', 's', 'm' ), + GF_ISOM_MEDIA_IPMP = GF_4CC( 'i', 'p', 's', 'm' ), + GF_ISOM_MEDIA_MPEGJ = GF_4CC( 'm', 'j', 's', 'm' ), + /*GPAC-defined, for any track using MPEG-4 systems signaling but with undefined streaml types*/ + GF_ISOM_MEDIA_ESM = GF_4CC( 'g', 'e', 's', 'm' ), + + /*DIMS media type (same as scene but with a different mediaInfo)*/ + GF_ISOM_MEDIA_DIMS = GF_4CC( 'd', 'i', 'm', 's' ), + + GF_ISOM_MEDIA_FLASH = GF_4CC( 'f', 'l', 's', 'h' ) +}; + +/* Encryption Scheme Type in the SchemeTypeInfoBox */ +enum +{ + GF_ISOM_ISMACRYP_SCHEME = GF_4CC( 'i', 'A', 'E', 'C' ) +}; + +/*specific media sub-types - you shall make sure the media sub type is what you expect*/ +enum +{ + /*reserved, internal use in the lib. Indicates the track complies to MPEG-4 system + specification, and the usual OD framework tools may be used*/ + GF_ISOM_SUBTYPE_MPEG4 = GF_4CC( 'M', 'P', 'E', 'G' ), + + /*reserved, internal use in the lib. Indicates the track is of GF_ISOM_SUBTYPE_MPEG4 + but it is encrypted.*/ + GF_ISOM_SUBTYPE_MPEG4_CRYP = GF_4CC( 'E', 'N', 'C', 'M' ), + + /* xdvb, seems MPEG-2, needed by some US TV stations so I'm doing my best */ + GF_ISOM_SUBTYPE_XDVB = GF_4CC( 'x', 'd', 'v', 'b' ), + + /*AVC/H264 media type - not listed as an MPEG-4 type, ALTHOUGH this library automatically remaps + GF_AVCConfig to MPEG-4 ESD*/ + GF_ISOM_SUBTYPE_AVC_H264 = GF_4CC( 'a', 'v', 'c', '1' ), + GF_ISOM_SUBTYPE_AVC2_H264 = GF_4CC( 'a', 'v', 'c', '2' ), + GF_ISOM_SUBTYPE_SVC_H264 = GF_4CC( 's', 'v', 'c', '1' ), + + /*3GPP(2) extension subtypes*/ + GF_ISOM_SUBTYPE_3GP_H263 = GF_4CC( 's', '2', '6', '3' ), + GF_ISOM_SUBTYPE_3GP_AMR = GF_4CC( 's', 'a', 'm', 'r' ), + GF_ISOM_SUBTYPE_3GP_AMR_WB = GF_4CC( 's', 'a', 'w', 'b' ), + GF_ISOM_SUBTYPE_3GP_EVRC = GF_4CC( 's', 'e', 'v', 'c' ), + GF_ISOM_SUBTYPE_3GP_QCELP = GF_4CC( 's', 'q', 'c', 'p' ), + GF_ISOM_SUBTYPE_3GP_SMV = GF_4CC( 's', 's', 'm', 'v' ), + + /*3GPP DIMS*/ + GF_ISOM_SUBTYPE_3GP_DIMS = GF_4CC( 'd', 'i', 'm', 's' ), + + GF_ISOM_SUBTYPE_AC3 = GF_4CC( 'a', 'c', '-', '3' ), + + GF_ISOM_SUBTYPE_LSR1 = GF_4CC( 'l', 's', 'r', '1' ), + + /* CAPTIONS */ + GF_ISOM_SUBTYPE_C608 = GF_4CC ('c', '6', '0', '8' ) +}; + + + + +/*direction for sample search (including SyncSamples search) +Function using search allways specify the desired time in composition (presentation) time + + (Sample N-1) DesiredTime (Sample N) + +FORWARD: will give the next sample given the desired time (eg, N) +BACKWARD: will give the previous sample given the desired time (eg, N-1) +SYNCFORWARD: will search from the desired point in time for a sync sample if any + If no sync info, behaves as FORWARD +SYNCBACKWARD: will search till the desired point in time for a sync sample if any + If no sync info, behaves as BACKWARD +SYNCSHADOW: use the sync shadow information to retrieve the sample. + If no SyncShadow info, behave as SYNCBACKWARD +*/ +enum +{ + GF_ISOM_SEARCH_FORWARD = 1, + GF_ISOM_SEARCH_BACKWARD = 2, + GF_ISOM_SEARCH_SYNC_FORWARD = 3, + GF_ISOM_SEARCH_SYNC_BACKWARD = 4, + GF_ISOM_SEARCH_SYNC_SHADOW = 5 +}; + +/*Predefined File Brand codes (MPEG-4 and JPEG2000)*/ +enum +{ + /*file complying to the generic ISO Media File (base specification ISO/IEC 14496-12) + this is the default brand when creating a new movie*/ + GF_ISOM_BRAND_ISOM = GF_4CC( 'i', 's', 'o', 'm' ), + /*file complying to the generic ISO Media File (base specification ISO/IEC 14496-12) + Meta extensions*/ + GF_ISOM_BRAND_ISO2 = GF_4CC( 'i', 's', 'o', '2' ), + /*file complying to ISO/IEC 14496-1 2001 edition. A .mp4 file without a brand + is equivalent to a file compatible with this brand*/ + GF_ISOM_BRAND_MP41 = GF_4CC( 'm', 'p', '4', '1' ), + /*file complying to ISO/IEC 14496-14 (MP4 spec)*/ + GF_ISOM_BRAND_MP42 = GF_4CC( 'm', 'p', '4', '2' ), + /*file complying to ISO/IEC 15444-3 (JPEG2000) without profile restriction*/ + GF_ISOM_BRAND_MJP2 = GF_4CC( 'm', 'j', 'p', '2' ), + /*file complying to ISO/IEC 15444-3 (JPEG2000) with simple profile restriction*/ + GF_ISOM_BRAND_MJ2S = GF_4CC( 'm', 'j', '2', 's' ), + /*old versions of 3GPP spec (without timed text)*/ + GF_ISOM_BRAND_3GP4 = GF_4CC('3', 'g', 'p', '4'), + GF_ISOM_BRAND_3GP5 = GF_4CC('3', 'g', 'p', '5'), + /*final version of 3GPP file spec*/ + GF_ISOM_BRAND_3GP6 = GF_4CC('3', 'g', 'p', '6'), + /*generci 3GPP file (several audio tracks, etc..)*/ + GF_ISOM_BRAND_3GG6 = GF_4CC('3', 'g', 'g', '6'), + /*3GPP2 file spec*/ + GF_ISOM_BRAND_3G2A = GF_4CC('3', 'g', '2', 'a'), + /*AVC file spec*/ + GF_ISOM_BRAND_AVC1 = GF_4CC('a', 'v', 'c', '1'), + /* file complying to ISO/IEC 21000-9:2005 (MPEG-21 spec)*/ + GF_ISOM_BRAND_MP21 = GF_4CC('m', 'p', '2', '1'), +}; + + +/*MPEG-4 ProfileAndLevel codes*/ +enum +{ + GF_ISOM_PL_AUDIO, + GF_ISOM_PL_VISUAL, + GF_ISOM_PL_GRAPHICS, + GF_ISOM_PL_SCENE, + GF_ISOM_PL_OD, + GF_ISOM_PL_MPEGJ, + /*not a profile, just set/unset inlineFlag*/ + GF_ISOM_PL_INLINE, +}; + + +/******************************************************************** + GENERAL API FUNCTIONS +********************************************************************/ + +/*get the last fatal error that occured in the file +ANY FUNCTION OF THIS API WON'T BE PROCESSED IF THE FILE HAS AN ERROR +Note: some function may return an error while the movie has no error +the last error is a FatalError, and is not always set if a bad +param is specified...*/ +GF_Err gf_isom_last_error(GF_ISOFile *the_file); + +/*returns 1 if target file is an IsoMedia file, 0 otherwise*/ +Bool gf_isom_probe_file(const char *fileName); + +/*Opens an isoMedia File. +tmp_dir: for the 2 edit modes only, specifies a location for temp file. If NULL, the librairy will use the default +OS temporary file management schemes.*/ +GF_ISOFile *gf_isom_open(const char *fileName, u32 OpenMode, const char *tmp_dir); + +/*close the file, write it if new/edited*/ +GF_Err gf_isom_close(GF_ISOFile *the_file); + +/*delete the movie without saving it.*/ +void gf_isom_delete(GF_ISOFile *the_file); + +/*Get the mode of an open file*/ +u8 gf_isom_get_mode(GF_ISOFile *the_file); + +Bool gf_isom_is_JPEG2000(GF_ISOFile *mov); + +u64 gf_isom_get_file_size(GF_ISOFile *the_file); + +Bool gf_isom_moov_first(GF_ISOFile *movie); + +/******************************************************************** + STREAMING API FUNCTIONS +********************************************************************/ +/*open a movie that can be uncomplete in READ_ONLY mode +to use for http streaming & co + +NOTE: you must buffer the data to a local file, this mode DOES NOT handle +http/ftp/... streaming + +BytesMissing is the predicted number of bytes missing for the file to be loaded +Note that if the file is not optimized for streaming, this number is not accurate +If the movie is successfully loaded (the_file non-NULL), BytesMissing is zero +*/ +GF_Err gf_isom_open_progressive(const char *fileName, GF_ISOFile **the_file, u64 *BytesMissing); + +/*If requesting a sample fails with error GF_ISOM_INCOMPLETE_FILE, use this function +to get the number of bytes missing to retrieve the sample*/ +u64 gf_isom_get_missing_bytes(GF_ISOFile *the_file, u32 trackNumber); + + +/*Fragmented movie extensions*/ + +/*return 0 if movie isn't fragmented, 1 otherwise*/ +u32 gf_isom_is_fragmented(GF_ISOFile *the_file); +/*return 0 if track isn't fragmented, 1 otherwise*/ +u32 gf_isom_is_track_fragmented(GF_ISOFile *the_file, u32 TrackID); + +/*a file being downloaded may be a fragmented file. In this case only partial info +is available once the file is successfully open (gf_isom_open_progressive), and since there is +no information wrt number fragments (which could actually be generated on the fly +at the sender side), you must call this function on regular bases in order to +load newly downloaded fragments. Note this may result in Track/Movie duration changes +and SampleCount change too ...*/ +GF_Err gf_isom_refresh_fragmented(GF_ISOFile *the_file, u64 *MissingBytes); + +/*check if file has movie info, eg has tracks & dynamic media. Some files may just use +the base IsoMedia structure without "moov" container*/ +Bool gf_isom_has_movie(GF_ISOFile *file); + +/* check if the file has a top styp box and returns the brand and version of the first styp found */ +Bool gf_isom_has_segment(GF_ISOFile *file, u32 *brand, u32 *version); + +/******************************************************************** + READING API FUNCTIONS +********************************************************************/ + +/*return the number of tracks in the movie, or -1 if error*/ +u32 gf_isom_get_track_count(GF_ISOFile *the_file); + +/*return the timescale of the movie, 0 if error*/ +u32 gf_isom_get_timescale(GF_ISOFile *the_file); + +/*return the duration of the movie, 0 if error*/ +u64 gf_isom_get_duration(GF_ISOFile *the_file); + +/*return the creation info of the movie*/ +GF_Err gf_isom_get_creation_time(GF_ISOFile *the_file, u64 *creationTime, u64 *modificationTime); + +/*return the trackID of track number n, or 0 if error*/ +u32 gf_isom_get_track_id(GF_ISOFile *the_file, u32 trackNumber); + +/*return the track number of the track of specified ID, or 0 if error*/ +u32 gf_isom_get_track_by_id(GF_ISOFile *the_file, u32 trackID); + +/*gets the enable flag of a track 0: NO, 1: yes, 2: error*/ +u8 gf_isom_is_track_enabled(GF_ISOFile *the_file, u32 trackNumber); + +/* determines if the track is encrypted 0: NO, 1: yes, 2: error*/ +u8 gf_isom_is_track_encrypted(GF_ISOFile *the_file, u32 trackNumber); + +/*get the track duration return 0 if bad param*/ +u64 gf_isom_get_track_duration(GF_ISOFile *the_file, u32 trackNumber); + +/*return the media type FOUR CHAR code type of the media*/ +u32 gf_isom_get_media_type(GF_ISOFile *the_file, u32 trackNumber); + +/*return the media type FOUR CHAR code type of the media*/ +u32 gf_isom_get_media_subtype(GF_ISOFile *the_file, u32 trackNumber, u32 DescriptionIndex); + +/*return the media type FOUR CHAR code type of an MPEG4 media (eg, mp4a, mp4v, enca, encv, etc...) +returns 0 if not MPEG-4 subtype*/ +u32 gf_isom_get_mpeg4_subtype(GF_ISOFile *the_file, u32 trackNumber, u32 DescriptionIndex); + +/*Get the media (composition) time given the absolute time in the Movie +mediaTime is set to 0 if the media is not playing at that time (empty time segment)*/ +GF_Err gf_isom_get_media_time(GF_ISOFile *the_file, u32 trackNumber, u32 movieTime, u64 *MediaTime); + +/*Get the number of "streams" stored in the media - a media can have several stream descriptions...*/ +u32 gf_isom_get_sample_description_count(GF_ISOFile *the_file, u32 trackNumber); + +/*Get the stream description index (eg, the ESD) for a given time IN MEDIA TIMESCALE +return 0 if error or if empty*/ +u32 gf_isom_get_sample_description_index(GF_ISOFile *the_file, u32 trackNumber, u64 for_time); + +/*returns 1 if samples refering to the given stream description are present in the file +0 otherwise*/ +Bool gf_isom_is_self_contained(GF_ISOFile *the_file, u32 trackNumber, u32 sampleDescriptionIndex); + +/*get the media duration (without edit) return 0 if no samples (URL streams)*/ +u64 gf_isom_get_media_duration(GF_ISOFile *the_file, u32 trackNumber); + +/*Get the timeScale of the media. */ +u32 gf_isom_get_media_timescale(GF_ISOFile *the_file, u32 trackNumber); + +/*gets min, average and max maximum chunk durations (each of them s optional) of the track in media timescale*/ +GF_Err gf_isom_get_chunks_infos(GF_ISOFile *movie, u32 trackNumber, u32 *dur_min, u32 *dur_avg, u32 *dur_max, u32 *size_min, u32 *size_avg, u32 *size_max); + +/*Get the HandlerDescription name. The outName must be: + (outName != NULL && *outName == NULL) +the handler name is the string version of the MediaTypes*/ +GF_Err gf_isom_get_handler_name(GF_ISOFile *the_file, u32 trackNumber, const char **outName); + +/*Check a DataReference of this track (index >= 1) +A Data Reference allows to construct a file without integrating the media data*/ +GF_Err gf_isom_check_data_reference(GF_ISOFile *the_file, u32 trackNumber, u32 StreamDescriptionIndex); + +/*get the location of the data. If URL && URN are NULL, the data is in this file +both strings are const: don't free them.*/ +GF_Err gf_isom_get_data_reference(GF_ISOFile *the_file, u32 trackNumber, u32 StreamDescriptionIndex, const char **outURL, const char **outURN); + +/*Get the number of samples - return 0 if error*/ +u32 gf_isom_get_sample_count(GF_ISOFile *the_file, u32 trackNumber); + +/*Get constant sample size, or 0 if size not constant*/ +u32 gf_isom_get_constant_sample_size(GF_ISOFile *the_file, u32 trackNumber); +/*returns total amount of media bytes in track*/ +u64 gf_isom_get_media_data_size(GF_ISOFile *the_file, u32 trackNumber); + +/*It may be desired to fetch samples with a bigger allocated buffer than their real size, in case the decoder +reads more data than available. This sets the amount of extra bytes to allocate when reading samples from this track +NOTE: the dataLength of the sample does NOT include padding*/ +GF_Err gf_isom_set_sample_padding(GF_ISOFile *the_file, u32 trackNumber, u32 padding_bytes); + +/*return a sample given its number, and set the StreamDescIndex of this sample +this index allows to retrieve the stream description if needed (2 media in 1 track) +return NULL if error*/ +GF_ISOSample *gf_isom_get_sample(GF_ISOFile *the_file, u32 trackNumber, u32 sampleNumber, u32 *StreamDescriptionIndex); + +/*same as gf_isom_get_sample but doesn't fetch media data +@StreamDescriptionIndex (optional): set to stream description index +@data_offset (optional): set to sample start offset in file. + + NOTE: when both StreamDescriptionIndex and data_offset are NULL, only DTS, CTS_Offset and RAP indications are +retrieved (faster) +*/ +GF_ISOSample *gf_isom_get_sample_info(GF_ISOFile *the_file, u32 trackNumber, u32 sampleNumber, u32 *StreamDescriptionIndex, u64 *data_offset); + +/*retrieves given sample DTS*/ +u64 gf_isom_get_sample_dts(GF_ISOFile *the_file, u32 trackNumber, u32 sampleNumber); + +/*returns sample duration in media timeScale*/ +u32 gf_isom_get_sample_duration(GF_ISOFile *the_file, u32 trackNumber, u32 sampleNumber); + +/*returns sample size in bytes*/ +u32 gf_isom_get_sample_size(GF_ISOFile *the_file, u32 trackNumber, u32 sampleNumber); + +/*returns sync flag of sample*/ +u8 gf_isom_get_sample_sync(GF_ISOFile *the_file, u32 trackNumber, u32 sampleNumber); + +/*gets a sample given a desired decoding time IN MEDIA TIME SCALE +and set the StreamDescIndex of this sample +this index allows to retrieve the stream description if needed (2 media in 1 track) +return GF_EOS if the desired time exceeds the media duration +WARNING: the sample may not be sync even though the sync was requested (depends on the media and the editList) +the SampleNum is optional. If non-NULL, will contain the sampleNumber*/ +GF_Err gf_isom_get_sample_for_media_time(GF_ISOFile *the_file, u32 trackNumber, u64 desiredTime, u32 *StreamDescriptionIndex, u8 SearchMode, GF_ISOSample **sample, u32 *SampleNum); + +/*retrieves given sample DTS*/ +u32 gf_isom_get_sample_from_dts(GF_ISOFile *the_file, u32 trackNumber, u64 dts); + +/*Track Edition functions*/ + +/*return a sample given a desired time in the movie. MovieTime is IN MEDIA TIME SCALE , handles edit list. +and set the StreamDescIndex of this sample +this index allows to retrieve the stream description if needed (2 media in 1 track) +sample must be set to NULL before calling. + +result Sample is NULL if an error occured +if no sample is playing, an empty sample is returned with no data and a DTS set to MovieTime when serching in sync modes +if no sample is playing, the closest sample in the edit time-line is returned when serching in regular modes + +WARNING: the sample may not be sync even though the sync was requested (depends on the media and the editList) + +Note: this function will handle re-timestamping the sample according to the mapping of the media time-line +on the track time-line. The sample TSs (DTS / CTS offset) are expressed in MEDIA TIME SCALE +(to match the media stream TS resolution as indicated in media header / SLConfig) + +sampleNumber is optional and gives the number of the sample in the media +*/ +GF_Err gf_isom_get_sample_for_movie_time(GF_ISOFile *the_file, u32 trackNumber, u64 movieTime, u32 *StreamDescriptionIndex, u8 SearchMode, GF_ISOSample **sample, u32 *sampleNumber); + +/*get the number of edited segment*/ +u32 gf_isom_get_edit_segment_count(GF_ISOFile *the_file, u32 trackNumber); + +/*Get the desired segment information*/ +GF_Err gf_isom_get_edit_segment(GF_ISOFile *the_file, u32 trackNumber, u32 SegmentIndex, u64 *EditTime, u64 *SegmentDuration, u64 *MediaTime, u8 *EditMode); + +/*get the number of languages for the copyright*/ +u32 gf_isom_get_copyright_count(GF_ISOFile *the_file); +/*get the copyright and its language code given the index*/ +GF_Err gf_isom_get_copyright(GF_ISOFile *the_file, u32 Index, const char **threeCharCodes, const char **notice); +/*get the opaque watermark info if any - returns GF_NOT_SUPPORTED if not present*/ +GF_Err gf_isom_get_watermark(GF_ISOFile *the_file, bin128 UUID, u8** data, u32* length); + +/*get the number of chapter for movie or track if trackNumber !=0*/ +u32 gf_isom_get_chapter_count(GF_ISOFile *the_file, u32 trackNumber); +/*get the given movie or track (trackNumber!=0) chapter time and name - index is 1-based +@chapter_time: retrives start time in milliseconds - may be NULL. +@name: retrieves chapter name - may be NULL - SHALL NOT be destroyed by user +*/ +GF_Err gf_isom_get_chapter(GF_ISOFile *the_file, u32 trackNumber, u32 Index, u64 *chapter_time, const char **name); + +/* +return 0 if the media has no sync point info (eg, all samples are RAPs) +return 1 if the media has sync points (eg some samples are RAPs) +return 2 if the media has empty sync point info (eg no samples are RAPs). This will likely only happen + in scalable context +*/ +u8 gf_isom_has_sync_points(GF_ISOFile *the_file, u32 trackNumber); + +/*returns number of sync points*/ +u32 gf_isom_get_sync_point_count(GF_ISOFile *the_file, u32 trackNumber); + +/*returns 1 if one sample of the track is found to have a composition time offset (DTSdata MUST be NULL and sample->dataLength must be NON NULL;*/ +GF_Err gf_isom_update_sample_reference(GF_ISOFile *the_file, u32 trackNumber, u32 sampleNumber, GF_ISOSample *sample, u64 data_offset); + +/*Remove a given sample*/ +GF_Err gf_isom_remove_sample(GF_ISOFile *the_file, u32 trackNumber, u32 sampleNumber); + +/*changes media time scale*/ +GF_Err gf_isom_set_media_timescale(GF_ISOFile *the_file, u32 trackNumber, u32 new_timescale); + +/*set the save file name of the (edited) movie. +If the movie is edited, the default fileName is avp_#openName) +NOTE: you cannot save an edited file under the same name (overwrite not allowed) +If the movie is created (WRITE mode), the default filename is #openName*/ +GF_Err gf_isom_set_final_name(GF_ISOFile *the_file, char *filename); + + +/*set the storage mode of a file (FLAT, STREAMABLE, INTERLEAVED)*/ +GF_Err gf_isom_set_storage_mode(GF_ISOFile *the_file, u8 storageMode); +u8 gf_isom_get_storage_mode(GF_ISOFile *the_file); + +/*set the interleaving time of media data (INTERLEAVED mode only) +InterleaveTime is in MovieTimeScale*/ +GF_Err gf_isom_set_interleave_time(GF_ISOFile *the_file, u32 InterleaveTime); +u32 gf_isom_get_interleave_time(GF_ISOFile *the_file); + +/*set the copyright in one language.*/ +GF_Err gf_isom_set_copyright(GF_ISOFile *the_file, const char *threeCharCode, char *notice); + +/*deletes copyright (1-based indexes)*/ +GF_Err gf_isom_remove_copyright(GF_ISOFile *the_file, u32 index); + +/*changes the handler type of the media*/ +GF_Err gf_isom_set_media_type(GF_ISOFile *movie, u32 trackNumber, u32 new_type); + +/*changes the type of the sampleDescriptionBox - USE AT YOUR OWN RISK, the file may not be understood afterwards*/ +GF_Err gf_isom_set_media_subtype(GF_ISOFile *movie, u32 trackNumber, u32 sampleDescriptionIndex, u32 new_type); + +GF_Err gf_isom_set_alternate_group_id(GF_ISOFile *movie, u32 trackNumber, u32 groupId); + +/*add chapter info: +if trackNumber is 0, the chapter info is added to the movie, otherwise to the track +@timestamp: chapter start time in milliseconds. Chapters are added in order to the file. If a chapter with same timestamp + is found, its name is updated but no entry is created. +@name: chapter name. If NULL, defaults to 'Chapter N' +*/ +GF_Err gf_isom_add_chapter(GF_ISOFile *the_file, u32 trackNumber, u64 timestamp, char *name); + +/*deletes copyright (1-based index, index 0 for all)*/ +GF_Err gf_isom_remove_chapter(GF_ISOFile *the_file, u32 trackNumber, u32 index); + +/*set watermark info for movie*/ +GF_Err gf_isom_set_watermark(GF_ISOFile *the_file, bin128 UUID, u8* data, u32 length); + +/*Track Edition functions - used to change the normal playback of the media if desired +NOTE: IT IS THE USER RESPONSABILITY TO CREATE A CONSISTENT TIMELINE FOR THE TRACK +This API provides the basic hooks and some basic consistency checking +but can not check the desired functionality of the track edits +*/ + +/*update or insert a new edit segment in the track time line. Edits are used to modify +the media normal timing. EditTime and EditDuration are expressed in Movie TimeScale +If a segment with EditTime already exists, IT IS ERASED +if there is a segment before this new one, its duration is adjust to match EditTime of +the new segment +WARNING: The first segment always have an EditTime of 0. You should insert an empty or dwelled segment first.*/ +GF_Err gf_isom_set_edit_segment(GF_ISOFile *the_file, u32 trackNumber, u64 EditTime, u64 EditDuration, u64 MediaTime, u8 EditMode); + +/*same as above except only modifies duartion type and mediaType*/ +GF_Err gf_isom_modify_edit_segment(GF_ISOFile *the_file, u32 trackNumber, u32 seg_index, u64 EditDuration, u64 MediaTime, u8 EditMode); +/*same as above except only appends new segment*/ +GF_Err gf_isom_append_edit_segment(GF_ISOFile *the_file, u32 trackNumber, u64 EditDuration, u64 MediaTime, u8 EditMode); + +/*remove the edit segments for the whole track*/ +GF_Err gf_isom_remove_edit_segments(GF_ISOFile *the_file, u32 trackNumber); + +/*remove the given edit segment (1-based index). If this is not the last segment, the next segment duration +is updated to maintain a continous timeline*/ +GF_Err gf_isom_remove_edit_segment(GF_ISOFile *the_file, u32 trackNumber, u32 seg_index); + +/* + User Data Manipulation + + You can add specific typed data to either a track or the movie: the UserData + The type must be formated as a FourCC if you have a registered 4CC type + but the usual is to set a UUID (128 bit ID for box type) which never conflict + with existing structures in the format + To manipulate a UUID user data set the UserDataType to 0 and specify a valid UUID. +Otherwise the UUID parameter is ignored + Several items with the same ID or UUID can be added (this allows you to store any + kind/number of private information under a unique ID / UUID) +*/ +/*Add a user data item in the desired track or in the movie if TrackNumber is 0*/ +GF_Err gf_isom_add_user_data(GF_ISOFile *the_file, u32 trackNumber, u32 UserDataType, bin128 UUID, char *data, u32 DataLength); + +/*remove all user data items from the desired track or from the movie if TrackNumber is 0*/ +GF_Err gf_isom_remove_user_data(GF_ISOFile *the_file, u32 trackNumber, u32 UserDataType, bin128 UUID); + +/*remove a user data item from the desired track or from the movie if TrackNumber is 0 +use the UDAT read functions to get the item index*/ +GF_Err gf_isom_remove_user_data_item(GF_ISOFile *the_file, u32 trackNumber, u32 UserDataType, bin128 UUID, u32 UserDataIndex); + +/*remove track, moov (trackNumber=0) or file-level (trackNumber=0xFFFFFFFF) UUID box of matching type*/ +GF_Err gf_isom_remove_uuid(GF_ISOFile *movie, u32 trackNumber, bin128 UUID); +/*adds track, moov (trackNumber=0) or file-level (trackNumber=0xFFFFFFFF) UUID box of given type*/ +GF_Err gf_isom_add_uuid(GF_ISOFile *movie, u32 trackNumber, bin128 UUID, char *data, u32 data_size); + +/* + Update of the Writing API for IsoMedia Version 2 +*/ + +/*use a compact track version for sample size. This is not usually recommended +except for speech codecs where the track has a lot of small samples +compaction is done automatically while writing based on the track's sample sizes*/ +GF_Err gf_isom_use_compact_size(GF_ISOFile *the_file, u32 trackNumber, u8 CompactionOn); + +/*sets the brand of the movie*/ +GF_Err gf_isom_set_brand_info(GF_ISOFile *the_file, u32 MajorBrand, u32 MinorVersion); + +/*adds or remove an alternate brand for the movie*/ +GF_Err gf_isom_modify_alternate_brand(GF_ISOFile *the_file, u32 Brand, u8 AddIt); + +/*removes all alternate brands except major brand*/ +GF_Err gf_isom_reset_alt_brands(GF_ISOFile *movie); + +/*set the number of padding bits at the end of a given sample if needed +if the function is never called the padding bit info is ignored +this MUST be called on an existin sample*/ +GF_Err gf_isom_set_sample_padding_bits(GF_ISOFile *the_file, u32 trackNumber, u32 sampleNumber, u8 NbBits); + + +/*since v2 you must specify w/h of video tracks for authoring tools (no decode the video cfg / first sample)*/ +GF_Err gf_isom_set_visual_info(GF_ISOFile *the_file, u32 trackNumber, u32 StreamDescriptionIndex, u32 Width, u32 Height); + +/*mainly used for 3GPP text since most ISO-based formats ignore these (except MJ2K) +all coord values are expressed as 16.16 fixed point floats*/ +GF_Err gf_isom_set_track_layout_info(GF_ISOFile *the_file, u32 trackNumber, u32 width, u32 height, s32 translation_x, s32 translation_y, s16 layer); + +/*sets track matrix - all coordinates are expressed as 16.16 floating points*/ +GF_Err gf_isom_set_track_matrix(GF_ISOFile *the_file, u32 trackNumber, u32 matrix[9]); + +GF_Err gf_isom_set_pixel_aspect_ratio(GF_ISOFile *the_file, u32 trackNumber, u32 StreamDescriptionIndex, u32 hSpacing, u32 vSpacing); + +/*set SR & nbChans for audio description*/ +GF_Err gf_isom_set_audio_info(GF_ISOFile *the_file, u32 trackNumber, u32 StreamDescriptionIndex, u32 sampleRate, u32 nbChannels, u8 bitsPerSample); + +/*non standard extensions: set/remove a fragment of a sample - this is used for video packets +in order to keep AU structure in the file format (no normative tables for that). Info is NOT written to disk*/ +GF_Err gf_isom_add_sample_fragment(GF_ISOFile *the_file, u32 trackNumber, u32 sampleNumber, u16 FragmentSize); +GF_Err gf_isom_remove_sample_fragment(GF_ISOFile *the_file, u32 trackNumber, u32 sampleNumber); +/*remove all sample fragment info for this track*/ +GF_Err gf_isom_remove_sample_fragments(GF_ISOFile *the_file, u32 trackNumber); + +/*set CTS unpack mode (used for B-frames & like): in unpack mode, each sample uses one entry in CTTS tables +unpack=0: set unpack on - !!creates a CTTS table if none found!! +unpack=1: set unpack off and repacks all table info +*/ +GF_Err gf_isom_set_cts_packing(GF_ISOFile *the_file, u32 trackNumber, Bool unpack); +/*modify CTS offset of a given sample (used for B-frames) - MUST be called in unpack mode only*/ +GF_Err gf_isom_modify_cts_offset(GF_ISOFile *the_file, u32 trackNumber, u32 sample_number, u32 offset); +/*remove CTS offset table (used for B-frames)*/ +GF_Err gf_isom_remove_cts_info(GF_ISOFile *the_file, u32 trackNumber); + +/*set 3char code media language*/ +GF_Err gf_isom_set_media_language(GF_ISOFile *the_file, u32 trackNumber, char *three_char_code); + +/*removes given stream description*/ +GF_Err gf_isom_remove_sample_description(GF_ISOFile *the_file, u32 trackNumber, u32 streamDescIndex); + +/*sets the RVC config for the given track sample description*/ +GF_Err gf_isom_set_rvc_config(GF_ISOFile *movie, u32 track, u32 sampleDescriptionIndex, u16 rvc_predefined, char *mime, char *data, u32 size); + +/* + some authoring extensions +*/ +/*sets name for authoring - if name is NULL reset authoring name*/ +GF_Err gf_isom_set_track_name(GF_ISOFile *the_file, u32 trackNumber, char *name); +/*gets authoring name*/ +const char *gf_isom_get_track_name(GF_ISOFile *the_file, u32 trackNumber); + +/* + MPEG-4 Extensions +*/ + +/*set a profile and level indication for the movie iod (created if needed) +if the flag is ProfileLevel is 0 this means the movie doesn't require +the specific codec (equivalent to 0xFF value in MPEG profiles)*/ +GF_Err gf_isom_set_pl_indication(GF_ISOFile *the_file, u8 PL_Code, u8 ProfileLevel); + +/*set the rootOD ID of the movie if you need it. By default, movies are created without root ODs*/ +GF_Err gf_isom_set_root_od_id(GF_ISOFile *the_file, u32 OD_ID); + +/*set the rootOD URL of the movie if you need it (only needed to create empty file pointing +to external ressource)*/ +GF_Err gf_isom_set_root_od_url(GF_ISOFile *the_file, char *url_string); + +/*remove the root OD*/ +GF_Err gf_isom_remove_root_od(GF_ISOFile *the_file); + +/*Add a system descriptor to the OD of the movie*/ +GF_Err gf_isom_add_desc_to_root_od(GF_ISOFile *the_file, GF_Descriptor *theDesc); + +/*add a track to the root OD*/ +GF_Err gf_isom_add_track_to_root_od(GF_ISOFile *the_file, u32 trackNumber); + +/*remove a track to the root OD*/ +GF_Err gf_isom_remove_track_from_root_od(GF_ISOFile *the_file, u32 trackNumber); + +/*Create a new StreamDescription (GF_ESD) in the file. The URL and URN are used to +describe external media, this will creat a data reference for the media*/ +GF_Err gf_isom_new_mpeg4_description(GF_ISOFile *the_file, u32 trackNumber, GF_ESD *esd, char *URLname, char *URNname, u32 *outDescriptionIndex); + +/*use carefully. Very usefull when you made a lot of changes (IPMP, IPI, OCI, ...) +THIS WILL REPLACE THE WHOLE DESCRIPTOR ...*/ +GF_Err gf_isom_change_mpeg4_description(GF_ISOFile *the_file, u32 trackNumber, u32 StreamDescriptionIndex, GF_ESD *newESD); + +/*Add a system descriptor to the ESD of a stream - you have to delete the descriptor*/ +GF_Err gf_isom_add_desc_to_description(GF_ISOFile *the_file, u32 trackNumber, u32 StreamDescriptionIndex, GF_Descriptor *theDesc); + + +/*Default extensions*/ + +/*Create a new unknown StreamDescription in the file. The URL and URN are used to +describe external media, this will creat a data reference for the media +use this to store media not currently supported by the ISO media format +*/ +GF_Err gf_isom_new_generic_sample_description(GF_ISOFile *the_file, u32 trackNumber, char *URLname, char *URNname, GF_GenericSampleDescription *udesc, u32 *outDescriptionIndex); + +/*change the data field of an unknown sample description*/ +GF_Err gf_isom_change_generic_sample_description(GF_ISOFile *the_file, u32 trackNumber, u32 StreamDescriptionIndex, GF_GenericSampleDescription *udesc); + +/* +special shortcut for stream description cloning from a given input file (this avoids inspecting for media type) +@the_file, @trackNumber: destination file and track +@orig_file, @orig_track, @orig_desc_index: orginal file, track and sample description +@URLname, @URNname, @outDescriptionIndex: same usage as with gf_isom_new_mpeg4_description +*/ +GF_Err gf_isom_clone_sample_description(GF_ISOFile *the_file, u32 trackNumber, GF_ISOFile *orig_file, u32 orig_track, u32 orig_desc_index, char *URLname, char *URNname, u32 *outDescriptionIndex); + +/*special shortcut: clones a track (everything except media data and sample info (DTS? CTS, RAPs, etc...) +also clones sampleDescriptions +@keep_data_ref: if set, external data references are kept, otherwise they are removed (track media data will be self-contained) +@dest_track: track number of cloned track*/ +GF_Err gf_isom_clone_track(GF_ISOFile *orig_file, u32 orig_track, GF_ISOFile *dest_file, Bool keep_data_ref, u32 *dest_track); +/*special shortcut: clones IOD PLs from orig to dest if any*/ +GF_Err gf_isom_clone_pl_indications(GF_ISOFile *orig, GF_ISOFile *dest); +/*clones root OD from input to output file, without copying root OD track references*/ +GF_Err gf_isom_clone_root_od(GF_ISOFile *input, GF_ISOFile *output); + +/*clones the entire movie file to destination. Tracks can be cloned if clone_tracks is set, in which case hint tracks can be +kept if keep_hint_tracks is set*/ +GF_Err gf_isom_clone_movie(GF_ISOFile *orig_file, GF_ISOFile *dest_file, Bool clone_tracks, Bool keep_hint_tracks); + +/*returns true if same set of sample description in both tracks - this does include self-contained checking +and reserved flags. The specific media cfg (DSI & co) is not analysed, only +a brutal memory comparaison is done*/ +Bool gf_isom_is_same_sample_description(GF_ISOFile *f1, u32 tk1, GF_ISOFile *f2, u32 tk2); + +GF_Err gf_isom_set_JPEG2000(GF_ISOFile *mov, Bool set_on); + +/*releases current movie segment - this closes the associated file IO object. +If reset_tables is set, sample information for all tracks setup as segment are destroyed. This allows keeping the memory +footprint low when playing segments. Note however that seeking in the file is then no longer possible*/ +GF_Err gf_isom_release_segment(GF_ISOFile *movie, Bool reset_tables); +/*opens a new segment file. Access to samples in previous segments is no longer possible*/ +GF_Err gf_isom_open_segment(GF_ISOFile *movie, const char *fileName); + +#ifndef GPAC_DISBALE_ISOM_FRAGMENTS + +/* + Movie Fragments Writing API + Movie Fragments is a feature of ISO media files for fragmentation + of a presentation meta-data and interleaving with its media data. + This enables faster http fast start for big movies, and also reduces the risk + of data loss in case of a recording crash, because meta data and media data + can be written to disk at regular times + This API provides simple function calls to setup such a movie and write it + The process implies: + 1- creating a movie in the usual way (track, stream descriptions, (IOD setup + copyright, ...) + 2- possibly add some samples in the regular fashion + 3- setup track fragments for all track that will be written in a fragmented way + (note that you can create/write a track that has no fragmentation at all) + 4- finalize the movie for fragmentation (this will flush all meta-data and + any media-data added to disk, ensuring all vital information for the presentation + is stored on file and not lost in case of crash/poweroff) + + then 5-6 as often as desired + 5- start a new movie fragment + 6- add samples to each setup track + + + IMPORTANT NOTES: + * Movie Fragments can only be used in GF_ISOM_OPEN_WRITE mode (capturing) + and no editing functionalities can be used + * the fragmented movie API uses TrackID and not TrackNumber +*/ + +/* +setup a track for fragmentation by specifying some default values for +storage efficiency +*TrackID: track identifier +*DefaultStreamDescriptionIndex: the default description used by samples in this track +*DefaultSampleDuration: default duration of samples in this track +*DefaultSampleSize: default size of samples in this track (0 if unknown) +*DefaultSampleIsSync: default key-flag (RAP) of samples in this track +*DefaultSamplePadding: default padding bits for samples in this track +*DefaultDegradationPriority: default degradation priority for samples in this track + +*/ +GF_Err gf_isom_setup_track_fragment(GF_ISOFile *the_file, u32 TrackID, + u32 DefaultStreamDescriptionIndex, + u32 DefaultSampleDuration, + u32 DefaultSampleSize, + u8 DefaultSampleIsSync, + u8 DefaultSamplePadding, + u16 DefaultDegradationPriority); + +/*flushes data to disk and prepare movie fragmentation*/ +GF_Err gf_isom_finalize_for_fragment(GF_ISOFile *the_file, Bool use_segments); + +/*starts a new movie fragment - if force_cache is set, fragment metadata will be written before +fragment media data for all tracks*/ +GF_Err gf_isom_start_fragment(GF_ISOFile *movie, Bool moof_first); + +/*starts a new segment in the file. If SegName is given, the output will be written in the SegName file*/ +GF_Err gf_isom_start_segment(GF_ISOFile *movie, char *SegName); + +/*sets the baseMediaDecodeTime of the first sample of the given track*/ +GF_Err gf_isom_set_traf_base_media_decode_time(GF_ISOFile *movie, u32 TrackID, u64 decode_time); + +/*closes current segment - if fragments_per_sidx is <0, no sidx is used - if fragments_per_sidx is ==0, a single sidx is used*/ +GF_Err gf_isom_close_segment(GF_ISOFile *movie, s32 fragments_per_sidx, u32 referenceTrackID, u64 ref_track_decode_time, Bool daisy_chain_sidx, Bool last_segment); + +enum +{ + /*indicates that the track fragment has no samples but still has a duration + (silence-detection in audio codecs, ...). + param: indicates duration*/ + GF_ISOM_TRAF_EMPTY, + /*I-Frame detection: this can reduce file size by detecting I-frames and + optimizing sample flags (padding, priority, ..) + param: on/off (0/1)*/ + GF_ISOM_TRAF_RANDOM_ACCESS, + /*activate data cache on track fragment. This is usefull when writing interleaved + media from a live source (typically audio-video), and greatly reduces file size + param: Number of samples (> 1) to cache before disk flushing. You shouldn't try + to cache too many samples since this will load your memory. base that on FPS/SR*/ + GF_ISOM_TRAF_DATA_CACHE +}; + +/*set options. Options can be set at the begining of each new fragment only, and for the +lifetime of the fragment*/ +GF_Err gf_isom_set_fragment_option(GF_ISOFile *the_file, u32 TrackID, u32 Code, u32 param); + + +/*adds a sample to a fragmented track + +*TrackID: destination track +*sample: sample to add +*StreamDescriptionIndex: stream description for this sample. If 0, the default one +is used +*Duration: sample duration. +Note: because of the interleaved nature of the meta/media data, the sample duration +MUST be provided (in case of regular tracks, this was computed internally by the lib) +*PaddingBits: padding bits for the sample, or 0 +*DegradationPriority for the sample, or 0 + +*/ + +GF_Err gf_isom_fragment_add_sample(GF_ISOFile *the_file, u32 TrackID, GF_ISOSample *sample, + u32 StreamDescriptionIndex, + u32 Duration, + u8 PaddingBits, u16 DegradationPriority); + +/*appends data into last sample of track for video fragments/other media +CANNOT be used with OD tracks*/ +GF_Err gf_isom_fragment_append_data(GF_ISOFile *the_file, u32 TrackID, char *data, u32 data_size, u8 PaddingBits); + +#endif /*GPAC_DISBALE_ISOM_FRAGMENTS*/ + + +/****************************************************************** + GENERIC Publishing API +******************************************************************/ + +/*Removes all sync shadow entries for a given track. The shadow samples are NOT removed; they must be removed +by the user app*/ +GF_Err gf_isom_remove_sync_shadows(GF_ISOFile *the_file, u32 trackNumber); + +/*Use this function to do the shadowing if you use shadowing. +the sample to be shadowed MUST be a non-sync sample (ignored if not) +the sample shadowing must be a Sync sample (error if not)*/ +GF_Err gf_isom_set_sync_shadow(GF_ISOFile *the_file, u32 trackNumber, u32 sampleNumber, u32 syncSample); + +/*set the GroupID of a track (only used for optimized interleaving). By setting GroupIDs +you can specify the storage order for media data of a group of streams. This is usefull +for BIFS presentation so that static resources of the scene can be downloaded before BIFS*/ +GF_Err gf_isom_set_track_group(GF_ISOFile *the_file, u32 trackNumber, u32 GroupID); + +/*set the priority of a track within a Group (used for optimized interleaving and hinting). +This allows tracks to be stored before other within a same group, for instance the +hint track data can be stored just before the media data, reducing disk seeking +for a same time, within a group of tracks, the track with the lowest inversePriority will +be written first*/ +GF_Err gf_isom_set_track_priority_in_group(GF_ISOFile *the_file, u32 trackNumber, u32 InversePriority); + +/*set the max SamplesPerChunk (for file optimization, mainly in FLAT and STREAMABLE modes)*/ +GF_Err gf_isom_set_max_samples_per_chunk(GF_ISOFile *the_file, u32 trackNumber, u32 maxSamplesPerChunk); + +/*associate a given SL config with a given ESD while extracting the OD information +all the SL params must be fixed by the calling app! +The SLConfig is stored by the API for further use. A NULL pointer will result +in using the default SLConfig (predefined = 2) remapped to predefined = 0 +This is usefull while reading the IOD / OD stream of an MP4 file. Note however that +only full AUs are extracted, therefore the calling application must SL-packetize the streams*/ +GF_Err gf_isom_set_extraction_slc(GF_ISOFile *the_file, u32 trackNumber, u32 StreamDescriptionIndex, GF_SLConfig *slConfig); + +GF_Err gf_isom_get_extraction_slc(GF_ISOFile *the_file, u32 trackNumber, u32 StreamDescriptionIndex, GF_SLConfig **slConfig); + +u32 gf_isom_get_track_group(GF_ISOFile *the_file, u32 trackNumber); +u32 gf_isom_get_track_priority_in_group(GF_ISOFile *the_file, u32 trackNumber); + +/*stores movie config (storage mode, interleave time, track groupIDs, priorities and names) in UDTA(kept on disk) +if @remove_all is set, removes all stored info, otherwise recompute all stored info*/ +GF_Err gf_isom_store_movie_config(GF_ISOFile *the_file, Bool remove_all); +/*restores movie config (storage mode, interleave time, track groupIDs, priorities and names) if found*/ +GF_Err gf_isom_load_movie_config(GF_ISOFile *the_file); + +/*setup interleaving for storage (shortcut for storeage mode + interleave_time)*/ +GF_Err gf_isom_make_interleave(GF_ISOFile *mp4file, Double TimeInSec); + + +/****************************************************************** + GENERIC HINTING WRITING API +******************************************************************/ + +/*supported hint formats - ONLY RTP now*/ +enum +{ + GF_ISOM_HINT_RTP = GF_4CC('r', 't', 'p', ' '), +}; + +#ifndef GPAC_DISABLE_ISOM_HINTING + + +/*Setup the resources based on the hint format +This function MUST be called after creating a new hint track and before +any other calls on this track*/ +GF_Err gf_isom_setup_hint_track(GF_ISOFile *the_file, u32 trackNumber, u32 HintType); + +/*Create a HintDescription for the HintTrack +the rely flag indicates whether a reliable transport protocol is desired/required +for data transport + 0: not desired (UDP/IP). NB: most RTP streaming servers only support UDP/IP for data + 1: preferable (TCP/IP if possible or UDP/IP) + 2: required (TCP/IP only) +The HintDescriptionIndex is set, to be used when creating a HINT sample +*/ +GF_Err gf_isom_new_hint_description(GF_ISOFile *the_file, u32 trackNumber, s32 HintTrackVersion, s32 LastCompatibleVersion, u8 Rely, u32 *HintDescriptionIndex); + +/*Starts a new sample for the hint track. A sample is just a collection of packets +the transmissionTime is indicated in the media timeScale of the hint track*/ +GF_Err gf_isom_begin_hint_sample(GF_ISOFile *the_file, u32 trackNumber, u32 HintDescriptionIndex, u32 TransmissionTime); + +/*stores the hint sample in the file once all your packets for this sample are done +set IsRandomAccessPoint if you want to indicate that this is a random access point +in the stream*/ +GF_Err gf_isom_end_hint_sample(GF_ISOFile *the_file, u32 trackNumber, u8 IsRandomAccessPoint); + + +/****************************************************************** + PacketHandling functions + Data can be added at the end or at the beginning of the current packet + by setting AtBegin to 1 the data will be added at the begining + This allows constructing the packet payload before any meta-data +******************************************************************/ + +/*adds a blank chunk of data in the sample that is skipped while streaming*/ +GF_Err gf_isom_hint_blank_data(GF_ISOFile *the_file, u32 trackNumber, u8 AtBegin); + +/*adds a chunk of data in the packet that is directly copied while streaming +NOTE: dataLength MUST BE <= 14 bytes, and you should only use this function +to add small blocks of data (encrypted parts, specific headers, ...)*/ +GF_Err gf_isom_hint_direct_data(GF_ISOFile *the_file, u32 trackNumber, char *data, u32 dataLength, u8 AtBegin); + +/*adds a reference to some sample data in the packet +SourceTrackID: the ID of the track where the referenced sample is +SampleNumber: the sample number containing the data to be added +DataLength: the length of bytes to copy in the packet +offsetInSample: the offset in bytes in the sample at which to begin copying data + +extra_data: only used when the sample is actually the sample that will contain this packet +(usefull to store en encrypted version of a packet only available while streaming) + In this case, set SourceTrackID to the HintTrack ID and SampleNumber to 0 + In this case, the DataOffset MUST BE NULL and length will indicate the extra_data size + +Note that if you want to reference a previous HintSample in the hintTrack, you will +have to parse the sample yourself ... +*/ +GF_Err gf_isom_hint_sample_data(GF_ISOFile *the_file, u32 trackNumber, u32 SourceTrackID, u32 SampleNumber, u16 DataLength, u32 offsetInSample, char *extra_data, u8 AtBegin); + + +/*adds a reference to some stream description data in the packet (headers, ...) +SourceTrackID: the ID of the track where the referenced sample is +StreamDescriptionIndex: the index of the stream description in the desired track +DataLength: the length of bytes to copy in the packet +offsetInDescription: the offset in bytes in the description at which to begin copying data + +Since it is far from being obvious what this offset is, we recommend not using this +function. The ISO Media Format specification is currently being updated to solve +this issue*/ +GF_Err gf_isom_hint_sample_description_data(GF_ISOFile *the_file, u32 trackNumber, u32 SourceTrackID, u32 StreamDescriptionIndex, u16 DataLength, u32 offsetInDescription, u8 AtBegin); + + +/****************************************************************** + RTP SPECIFIC WRITING API +******************************************************************/ + +/*Creates a new RTP packet in the HintSample. If a previous packet was created, +it is stored in the hint sample and a new packet is created. +- relativeTime: RTP time offset of this packet in the HintSample if any - in hint track +time scale. Used for data smoothing by servers. +- PackingBit: the 'P' bit of the RTP packet header +- eXtensionBit: the'X' bit of the RTP packet header +- MarkerBit: the 'M' bit of the RTP packet header +- PayloadType: the payload type, on 7 bits, format 0x0XXXXXXX +- B_frame: indicates if this is a B-frame packet. Can be skipped by a server +- IsRepeatedPacket: indicates if this is a duplicate packet of a previous one. +Can be skipped by a server +- SequenceNumber: the RTP base sequence number of the packet. Because of support for repeated +packets, you have to set the sequence number yourself.*/ +GF_Err gf_isom_rtp_packet_begin(GF_ISOFile *the_file, u32 trackNumber, s32 relativeTime, u8 PackingBit, u8 eXtensionBit, u8 MarkerBit, u8 PayloadType, u8 B_frame, u8 IsRepeatedPacket, u16 SequenceNumber); + +/*set the flags of the RTP packet*/ +GF_Err gf_isom_rtp_packet_set_flags(GF_ISOFile *the_file, u32 trackNumber, u8 PackingBit, u8 eXtensionBit, u8 MarkerBit, u8 disposable_packet, u8 IsRepeatedPacket); + +/*set the time offset of this packet. This enables packets to be placed in the hint track +in decoding order, but have their presentation time-stamp in the transmitted +packet in a different order. Typically used for MPEG video with B-frames +*/ +GF_Err gf_isom_rtp_packet_set_offset(GF_ISOFile *the_file, u32 trackNumber, s32 timeOffset); + + +/*set some specific info in the HintDescription for RTP*/ + +/*sets the RTP TimeScale that the server use to send packets +some RTP payloads may need a specific timeScale that is not the timeScale in the file format +the default timeScale choosen by the API is the MediaTimeScale of the hint track*/ +GF_Err gf_isom_rtp_set_timescale(GF_ISOFile *the_file, u32 trackNumber, u32 HintDescriptionIndex, u32 TimeScale); +/*sets the RTP TimeOffset that the server will add to the packets +if not set, the server adds a random offset*/ +GF_Err gf_isom_rtp_set_time_offset(GF_ISOFile *the_file, u32 trackNumber, u32 HintDescriptionIndex, u32 TimeOffset); +/*sets the RTP SequenceNumber Offset that the server will add to the packets +if not set, the server adds a random offset*/ +GF_Err gf_isom_rtp_set_time_sequence_offset(GF_ISOFile *the_file, u32 trackNumber, u32 HintDescriptionIndex, u32 SequenceNumberOffset); + + + +/****************************************************************** + SDP SPECIFIC WRITING API +******************************************************************/ +/*add an SDP line to the SDP container at the track level (media-specific SDP info) +NOTE: the \r\n end of line for SDP is automatically inserted*/ +GF_Err gf_isom_sdp_add_track_line(GF_ISOFile *the_file, u32 trackNumber, const char *text); +/*remove all SDP info at the track level*/ +GF_Err gf_isom_sdp_clean_track(GF_ISOFile *the_file, u32 trackNumber); + +/*add an SDP line to the SDP container at the movie level (presentation SDP info) +NOTE: the \r\n end of line for SDP is automatically inserted*/ +GF_Err gf_isom_sdp_add_line(GF_ISOFile *the_file, const char *text); +/*remove all SDP info at the movie level*/ +GF_Err gf_isom_sdp_clean(GF_ISOFile *the_file); + +#endif /*GPAC_DISABLE_ISOM_HINTING*/ + + +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + +#ifndef GPAC_DISABLE_ISOM_DUMP + +/*dumps file structures into XML trace file */ +GF_Err gf_isom_dump(GF_ISOFile *file, FILE *trace); + +#endif /*GPAC_DISABLE_ISOM_DUMP*/ + + +#ifndef GPAC_DISABLE_ISOM_HINTING + +#ifndef GPAC_DISABLE_ISOM_DUMP +/*dumps RTP hint samples structure into XML trace file + @trackNumber, @SampleNum: hint track and hint sample number + @trace: output +*/ +GF_Err gf_isom_dump_hint_sample(GF_ISOFile *the_file, u32 trackNumber, u32 SampleNum, FILE * trace); +#endif + +/*Get SDP info at the movie level*/ +GF_Err gf_isom_sdp_get(GF_ISOFile *the_file, const char **sdp, u32 *length); +/*Get SDP info at the track level*/ +GF_Err gf_isom_sdp_track_get(GF_ISOFile *the_file, u32 trackNumber, const char **sdp, u32 *length); + +u32 gf_isom_get_payt_count(GF_ISOFile *the_file, u32 trackNumber); +const char *gf_isom_get_payt_info(GF_ISOFile *the_file, u32 trackNumber, u32 index, u32 *payID); + + + + +/*small hint reader - performs data caching*/ + +/*resets hint reading parameters, returns an error if the hint type is not supported for reading +packet sequence number is always reseted to 0 +@sample_start: indicates from where the packets should be read (regular 1-based sample number) +@ts_offset: constant offset for timestamps, must be expressed in media timescale (which is the hint timescale). + usually 0 (no offset) +@sn_offset: offset for packet sequence number (first packet will have a SN of 1 + sn_offset) + usually 0 +@ssrc: sync source identifier for RTP +*/ +GF_Err gf_isom_reset_hint_reader(GF_ISOFile *the_file, u32 trackNumber, u32 sample_start, u32 ts_offset, u32 sn_offset, u32 ssrc); + +/*reads next hint packet. ALl packets are read in transmission (decoding) order +returns an error if not supported, or GF_EOS when no more packets are available +currently only RTP reader is supported +@pck_data, @pck_size: output packet data (must be freed by caller) - contains all info to be sent + on the wire, eg for RTP contains the RTP header and the data +@disposable (optional): indicates that the packet can be droped when late (B-frames & co) +@repeated (optional): indicates this is a repeated packet (same one has already been sent) +@trans_ts (optional): indicates the transmission time of the packet, expressed in hint timescale, taking into account +the ts_offset specified in gf_isom_reset_hint_reader. Depending on packets this may not be the same +as the hint sample timestamp + ts_offset, some packets may need to be sent earlier (B-frames) +@sample_num (optional): indicates hint sample number the packet belongs to +*/ +GF_Err gf_isom_next_hint_packet(GF_ISOFile *the_file, u32 trackNumber, char **pck_data, u32 *pck_size, Bool *disposable, Bool *repeated, u32 *trans_ts, u32 *sample_num); + +#endif /*GPAC_DISABLE_ISOM_HINTING*/ + + + +/* + 3GPP specific extensions + NOTE: MPEG-4 OD Framework cannot be used with 3GPP files. + Stream Descriptions are not GF_ESD, just generic config options as specified in this file +*/ + +/*Generic 3GP/3GP2 config record*/ +typedef struct +{ + /*GF_4CC record type, one fo the above GF_ISOM_SUBTYPE_3GP_ * subtypes*/ + u32 type; + /*4CC vendor name*/ + u32 vendor; + /*codec version*/ + u8 decoder_version; + /*number of sound frames per IsoMedia sample, >0 and <=15. The very last sample may contain less frames. */ + u8 frames_per_sample; + + /*H263 ONLY - Level and profile*/ + u8 H263_level, H263_profile; + + /*AMR(WB) ONLY - num of mode for the codec*/ + u16 AMR_mode_set; + /*AMR(WB) ONLY - changes in codec mode per sample*/ + u8 AMR_mode_change_period; +} GF_3GPConfig; + + +/*return the 3GP config for this tream description, NULL if not a 3GPP track*/ +GF_3GPConfig *gf_isom_3gp_config_get(GF_ISOFile *the_file, u32 trackNumber, u32 StreamDescriptionIndex); +#ifndef GPAC_DISABLE_ISOM_WRITE +/*create the track config*/ +GF_Err gf_isom_3gp_config_new(GF_ISOFile *the_file, u32 trackNumber, GF_3GPConfig *config, char *URLname, char *URNname, u32 *outDescriptionIndex); +/*update the track config - subtypes shall NOT differ*/ +GF_Err gf_isom_3gp_config_update(GF_ISOFile *the_file, u32 trackNumber, GF_3GPConfig *config, u32 DescriptionIndex); +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + +/*AVC/H264 extensions - GF_AVCConfig is defined in mpeg4_odf.h*/ + +/*gets uncompressed AVC config - user is responsible for deleting it*/ +GF_AVCConfig *gf_isom_avc_config_get(GF_ISOFile *the_file, u32 trackNumber, u32 DescriptionIndex); +/*gets uncompressed AVC config - user is responsible for deleting it*/ +GF_AVCConfig *gf_isom_svc_config_get(GF_ISOFile *the_file, u32 trackNumber, u32 DescriptionIndex); + +#ifndef GPAC_DISABLE_ISOM_WRITE +/*creates new AVC config*/ +GF_Err gf_isom_avc_config_new(GF_ISOFile *the_file, u32 trackNumber, GF_AVCConfig *cfg, char *URLname, char *URNname, u32 *outDescriptionIndex); +/*updates AVC config*/ +GF_Err gf_isom_avc_config_update(GF_ISOFile *the_file, u32 trackNumber, u32 DescriptionIndex, GF_AVCConfig *cfg); +/*updates SVC config. If is_additional is set, the SVCConfig will be added to the AVC sample description, otherwise the sample description will be SVC-only*/ +GF_Err gf_isom_svc_config_update(GF_ISOFile *the_file, u32 trackNumber, u32 DescriptionIndex, GF_AVCConfig *cfg, Bool is_additional); +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + + +/* + 3GP timed text handling + + NOTE: currently only writing API is developped, the reading one is not used in MPEG-4 since + MPEG-4 maps 3GP timed text to MPEG-4 Streaming Text (part 17) +*/ + +/*set streamihng text reading mode: if do_convert is set, all text samples will be retrieved as TTUs +and ESD will be emulated for text tracks.*/ +GF_Err gf_isom_text_set_streaming_mode(GF_ISOFile *the_file, Bool do_convert); + + +#ifndef GPAC_DISABLE_ISOM_DUMP +/*exports text track to given format +@dump_type: 0 for TTXT, 1 for srt, 2 for SVG +*/ +GF_Err gf_isom_text_dump(GF_ISOFile *the_file, u32 track, FILE *dump, u32 dump_type); +#endif + +/*returns encoded TX3G box (text sample description for 3GPP text streams) as needed by RTP or other standards: + @sidx: 1-based stream description index + @sidx_offset: + if 0, the sidx will NOT be written before the encoded TX3G + if not 0, the sidx will be written before the encoded TX3G, with the given offset. Offset sshould be at + least 128 for most commmon usage of TX3G (RTP, MPEG-4 timed text, etc) + +*/ +GF_Err gf_isom_text_get_encoded_tx3g(GF_ISOFile *file, u32 track, u32 sidx, u32 sidx_offset, char **tx3g, u32 *tx3g_size); + +/*checks if this text description is already inserted +@outDescIdx: set to 0 if not found, or descIndex +@same_style, @same_box: indicates if default styles and box are used +*/ +GF_Err gf_isom_text_has_similar_description(GF_ISOFile *the_file, u32 trackNumber, GF_TextSampleDescriptor *desc, u32 *outDescIdx, Bool *same_box, Bool *same_styles); + +/*text sample formatting*/ +typedef struct _3gpp_text_sample GF_TextSample; +/*creates text sample handle*/ +GF_TextSample *gf_isom_new_text_sample(); +/*destroy text sample handle*/ +void gf_isom_delete_text_sample(GF_TextSample *tx_samp); + +#ifndef GPAC_DISABLE_ISOM_WRITE + +/*Create a new TextSampleDescription in the file. +The URL and URN are used to describe external media, this will create a data reference for the media +GF_TextSampleDescriptor is defined in mpeg4_odf.h +*/ +GF_Err gf_isom_new_text_description(GF_ISOFile *the_file, u32 trackNumber, GF_TextSampleDescriptor *desc, char *URLname, char *URNname, u32 *outDescriptionIndex); +/*change the text sample description*/ +GF_Err gf_isom_update_text_description(GF_ISOFile *movie, u32 trackNumber, u32 descriptionIndex, GF_TextSampleDescriptor *desc); + +/*reset text sample content*/ +GF_Err gf_isom_text_reset(GF_TextSample * tx_samp); +/*reset text sample styles but keep text*/ +GF_Err gf_isom_text_reset_styles(GF_TextSample * samp); + +/*sets UTF16 marker for text data. This MUST be called on an empty sample. If text data added later +on (cf below) is not formatted as UTF16 data(2 bytes char) the resulting text sample won't be compliant, +but this library WON'T WARN*/ +GF_Err gf_isom_text_set_utf16_marker(GF_TextSample * samp); +/*append text to sample - text_len is the number of bytes to be written from text_data. This allows +handling UTF8 and UTF16 strings in a transparent manner*/ +GF_Err gf_isom_text_add_text(GF_TextSample * tx_samp, char *text_data, u32 text_len); +/*append style modifyer to sample*/ +GF_Err gf_isom_text_add_style(GF_TextSample * tx_samp, GF_StyleRecord *rec); +/*appends highlight modifier for the sample + @start_char: first char highlighted, + @end_char: first char not highlighted*/ +GF_Err gf_isom_text_add_highlight(GF_TextSample * samp, u16 start_char, u16 end_char); +/*sets highlight color for the whole sample*/ +GF_Err gf_isom_text_set_highlight_color(GF_TextSample * samp, u8 r, u8 g, u8 b, u8 a); +GF_Err gf_isom_text_set_highlight_color_argb(GF_TextSample * samp, u32 argb); +/*appends a new karaoke sequence in the sample + @start_time: karaoke start time expressed in text stream timescale, but relative to the sample media time +*/ +GF_Err gf_isom_text_add_karaoke(GF_TextSample * samp, u32 start_time); +/*appends a new segment in the current karaoke sequence - you must build sequences in order to be compliant + @end_time: segment end time expressed in text stream timescale, but relative to the sample media time + @start_char: first char highlighted, + @end_char: first char not highlighted +*/ +GF_Err gf_isom_text_set_karaoke_segment(GF_TextSample * samp, u32 end_time, u16 start_char, u16 end_char); +/*sets scroll delay for the whole sample (scrolling is enabled through GF_TextSampleDescriptor.DisplayFlags) + @scroll_delay: delay for scrolling expressed in text stream timescale +*/ +GF_Err gf_isom_text_set_scroll_delay(GF_TextSample * samp, u32 scroll_delay); +/*appends hyperlinking for the sample + @URL: ASCII url + @altString: ASCII hint (tooltip, ...) for end user + @start_char: first char hyperlinked, + @end_char: first char not hyperlinked +*/ +GF_Err gf_isom_text_add_hyperlink(GF_TextSample * samp, char *URL, char *altString, u16 start_char, u16 end_char); +/*sets current text box (display pos&size within the text track window) for the sample*/ +GF_Err gf_isom_text_set_box(GF_TextSample * samp, s16 top, s16 left, s16 bottom, s16 right); +/*appends blinking for the sample + @start_char: first char blinking, + @end_char: first char not blinking +*/ +GF_Err gf_isom_text_add_blink(GF_TextSample * samp, u16 start_char, u16 end_char); +/*sets wrap flag for the sample - currently only 0 (no wrap) and 1 ("soft wrap") are allowed in 3GP*/ +GF_Err gf_isom_text_set_wrap(GF_TextSample * samp, u8 wrap_flags); + +/*formats sample as a regular GF_ISOSample. The resulting sample will always be marked as random access +text sample content is kept untouched*/ +GF_ISOSample *gf_isom_text_to_sample(GF_TextSample * tx_samp); + +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + +/***************************************************** + ISMACryp Samples +*****************************************************/ +/*flags for GF_ISMASample*/ +enum +{ + /*signals the stream the sample belongs to uses selective encryption*/ + GF_ISOM_ISMA_USE_SEL_ENC = 1, + /*signals the sample is encrypted*/ + GF_ISOM_ISMA_IS_ENCRYPTED = 2, +}; + +typedef struct +{ + /*IV in ISMACryp is Byte Stream Offset*/ + u64 IV; + u8 IV_length;/*repeated from sampleDesc for convenience*/ + u8 *key_indicator; + u8 KI_length;/*repeated from sampleDesc for convenience*/ + u32 dataLength; + char *data; + u32 flags; +} GF_ISMASample; +/** + * creates a new empty ISMA sample + */ +GF_ISMASample *gf_isom_ismacryp_new_sample(); + +/*delete an ISMA sample. NOTE:the buffers content will be destroyed by default. +if you wish to keep the buffer, set dataLength to 0 in the sample before deleting it*/ +void gf_isom_ismacryp_delete_sample(GF_ISMASample *samp); + +/*decodes ISMACryp sample based on all info in ISMACryp sample description*/ +GF_ISMASample *gf_isom_ismacryp_sample_from_data(char *data, u32 dataLength, Bool use_selective_encryption, u8 KI_length, u8 IV_length); +/*rewrites samp content from s content*/ +GF_Err gf_isom_ismacryp_sample_to_sample(GF_ISMASample *s, GF_ISOSample *dest); + +/*decodes ISMACryp sample based on sample and its descrition index - returns NULL if not an ISMA sample +Note: input sample is NOT destroyed*/ +GF_ISMASample *gf_isom_get_ismacryp_sample(GF_ISOFile *the_file, u32 trackNumber, GF_ISOSample *samp, u32 sampleDescriptionIndex); + +/*returns whether the given media is a protected one or not - return scheme protection 4CC*/ +u32 gf_isom_is_media_encrypted(GF_ISOFile *the_file, u32 trackNumber, u32 sampleDescriptionIndex); + +/*returns whether the given media is a protected ISMACryp one or not*/ +Bool gf_isom_is_ismacryp_media(GF_ISOFile *the_file, u32 trackNumber, u32 sampleDescriptionIndex); + +/*returns whether the given media is a protected ISMACryp one or not*/ +Bool gf_isom_is_omadrm_media(GF_ISOFile *the_file, u32 trackNumber, u32 sampleDescriptionIndex); + +GF_Err gf_isom_get_omadrm_info(GF_ISOFile *the_file, u32 trackNumber, u32 sampleDescriptionIndex, u32 *outOriginalFormat, + u32 *outSchemeType, u32 *outSchemeVersion, + const char **outContentID, const char **outRightsIssuerURL, const char **outTextualHeaders, u32 *outTextualHeadersLen, u64 *outPlaintextLength, u32 *outEncryptionType, Bool *outSelectiveEncryption, u32 *outIVLength, u32 *outKeyIndicationLength); +/*retrieves ISMACryp info for the given track & SDI - all output parameters are optional - URIs SHALL NOT BE MODIFIED BY USER + @outOriginalFormat: retrieves orginal protected media format - usually GF_ISOM_SUBTYPE_MPEG4 + @outSchemeType: retrieves 4CC of protection scheme (GF_ISOM_ISMACRYP_SCHEME = iAEC in ISMACryp 1.0) + outSchemeVersion: retrieves version of protection scheme (1 in ISMACryp 1.0) + outSchemeURI: retrieves URI location of scheme + outKMS_URI: retrieves URI location of key management system - only valid with ISMACryp 1.0 + outSelectiveEncryption: specifies whether sample-based encryption is used in media - only valid with ISMACryp 1.0 + outIVLength: specifies length of Initial Vector - only valid with ISMACryp 1.0 + outKeyIndicationLength: specifies length of key indicator - only valid with ISMACryp 1.0 + + outSelectiveEncryption, outIVLength and outKeyIndicationLength are usually not needed to decode an + ISMA sample when using gf_isom_get_ismacryp_sample fct above +*/ +GF_Err gf_isom_get_ismacryp_info(GF_ISOFile *the_file, u32 trackNumber, u32 sampleDescriptionIndex, u32 *outOriginalFormat, u32 *outSchemeType, u32 *outSchemeVersion, const char **outSchemeURI, const char **outKMS_URI, Bool *outSelectiveEncryption, u32 *outIVLength, u32 *outKeyIndicationLength); + + +#ifndef GPAC_DISABLE_ISOM_WRITE +/*removes ISMACryp protection info (does not perform decryption :)*/ +GF_Err gf_isom_remove_ismacryp_protection(GF_ISOFile *the_file, u32 trackNumber, u32 StreamDescriptionIndex); + +/*creates ISMACryp protection info (does not perform encryption :)*/ +GF_Err gf_isom_set_ismacryp_protection(GF_ISOFile *the_file, u32 trackNumber, u32 desc_index, u32 scheme_type, + u32 scheme_version, char *scheme_uri, char *kms_URI, + Bool selective_encryption, u32 KI_length, u32 IV_length); + +/*change scheme URI and/or KMS URI for crypted files. Other params cannot be changed once the media is crypted + @scheme_uri: new scheme URI, or NULL to keep previous + @kms_uri: new KMS URI, or NULL to keep previous +*/ +GF_Err gf_isom_change_ismacryp_protection(GF_ISOFile *the_file, u32 trackNumber, u32 StreamDescriptionIndex, char *scheme_uri, char *kms_uri); + + +GF_Err gf_isom_set_oma_protection(GF_ISOFile *the_file, u32 trackNumber, u32 desc_index, + char *contentID, char *kms_URI, u32 encryption_type, u64 plainTextLength, char *textual_headers, u32 textual_headers_len, + Bool selective_encryption, u32 KI_length, u32 IV_length); + +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + +#ifndef GPAC_DISABLE_ISOM_DUMP +/*xml dumpers*/ +GF_Err gf_isom_dump_ismacryp_protection(GF_ISOFile *the_file, u32 trackNumber, FILE * trace); +GF_Err gf_isom_dump_ismacryp_sample(GF_ISOFile *the_file, u32 trackNumber, u32 SampleNum, FILE *trace); +#endif + + +/******************************************************************** + GENERAL META API FUNCTIONS + + Meta can be stored at several places in the file layout: + * root level (like moov, ftyp and co) + * moov level + * track level + Meta API uses the following parameters for all functions: + + gf_isom_*_meta_*(GF_ISOFile *file, Bool root_meta, u32 track_num, ....) with: + @root_meta: if set, accesses file root meta + @track_num: if root_meta not set, specifies whether the target meta is at the + moov level (track_num=0) or at the track level. + +********************************************************************/ + +/*gets meta type. Returned value: 0 if no meta found, or four char code of meta (eg, "mp21", "smil", ...)*/ +u32 gf_isom_get_meta_type(GF_ISOFile *file, Bool root_meta, u32 track_num); + +/*indicates if the meta has an XML container (note that XML data can also be included as items). +return value: 0 (no XML or error), 1 (XML text), 2 (BinaryXML, eg BiM) */ +u32 gf_isom_has_meta_xml(GF_ISOFile *file, Bool root_meta, u32 track_num); + +/*extracts XML (if any) from given meta + @outName: output file path and location for writing + @is_binary: indicates if XML is Bim or regular XML +*/ +GF_Err gf_isom_extract_meta_xml(GF_ISOFile *file, Bool root_meta, u32 track_num, char *outName, Bool *is_binary); + +/*returns number of items described in this meta*/ +u32 gf_isom_get_meta_item_count(GF_ISOFile *file, Bool root_meta, u32 track_num); + +/*gets item info for the given item + @item_num: 1-based index of item to query + @itemID (optional): item ID in file + @is_self_reference: item is the file itself + @item_name (optional): item name + @item_mime_type (optional): item mime type + @item_encoding (optional): item content encoding type + @item_url, @item_urn (optional): url/urn of external resource containing this item data if any. + When item is fully contained in file, these are set to NULL + +*/ +GF_Err gf_isom_get_meta_item_info(GF_ISOFile *file, Bool root_meta, u32 track_num, u32 item_num, + u32 *itemID, u32 *protection_idx, Bool *is_self_reference, + const char **item_name, const char **item_mime_type, const char **item_encoding, + const char **item_url, const char **item_urn); + + +/*gets item idx from item ID*/ +u32 gf_isom_get_meta_item_by_id(GF_ISOFile *file, Bool root_meta, u32 track_num, u32 item_ID); + +/*extracts item from given meta + @item_num: 1-based index of item to query + @dump_file_name: if NULL, use item name for dumping +*/ +GF_Err gf_isom_extract_meta_item(GF_ISOFile *file, Bool root_meta, u32 track_num, u32 item_num, const char *dump_file_name); + +/*extracts item from given meta in memory + @item_num: 1-based index of item to query +*/ +GF_Err gf_isom_extract_meta_item_mem(GF_ISOFile *file, Bool root_meta, u32 track_num, u32 item_id, char **out_data, u32 *out_size, const char **mime_type); + +/*retirves primary item ID, 0 if none found (primary can also be stored through meta XML)*/ +u32 gf_isom_get_meta_primary_item_id(GF_ISOFile *file, Bool root_meta, u32 track_num); + +#ifndef GPAC_DISABLE_ISOM_WRITE + +/*sets meta type (four char int, eg "mp21", ... + Creates a meta box if none found + if metaType is 0, REMOVES META +*/ +GF_Err gf_isom_set_meta_type(GF_ISOFile *file, Bool root_meta, u32 track_num, u32 metaType); + +/*removes meta XML info if any*/ +GF_Err gf_isom_remove_meta_xml(GF_ISOFile *file, Bool root_meta, u32 track_num); + +/*set meta XML data from file - erase any previously (Binary)XML info*/ +GF_Err gf_isom_set_meta_xml(GF_ISOFile *file, Bool root_meta, u32 track_num, char *XMLFileName, Bool IsBinaryXML); +/*set meta XML data from memory - erase any previously (Binary)XML info*/ +GF_Err gf_isom_set_meta_xml_memory(GF_ISOFile *file, Bool root_meta, u32 track_num, unsigned char *data, u32 data_size, Bool IsBinaryXML); + +/*adds item to meta: + @self_reference: indicates this item is the file itself + @resource_path: file to add - can be NULL when URL/URN is used + @item_name: item name - if NULL, use file name. CANNOT BE NULL if resource_path is not set + @mime_type: item mime type - if NULL, use "application/octet-stream" + @content_encoding: content encoding type - if NULL, none specified + @URL, @URN: if set, resource will be remote (same as stream descriptions) +*/ +GF_Err gf_isom_add_meta_item(GF_ISOFile *file, Bool root_meta, u32 track_num, Bool self_reference, char *resource_path, const char *item_name, const char *mime_type, const char *content_encoding, const char *URL, const char *URN); +/*same as above excepts take the item directly in memory*/ +GF_Err gf_isom_add_meta_item_memory(GF_ISOFile *file, Bool root_meta, u32 track_num, const char *item_name, const char *mime_type, const char *content_encoding, char *data, u32 data_len); + +/*removes item from meta*/ +GF_Err gf_isom_remove_meta_item(GF_ISOFile *file, Bool root_meta, u32 track_num, u32 item_num); + +/*sets the given item as the primary one. You SHALL NOT use this if the meta has a valid XML data*/ +GF_Err gf_isom_set_meta_primary_item(GF_ISOFile *file, Bool root_meta, u32 track_num, u32 item_num); + +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + + +/******************************************************************** + Timed Meta-Data extensions +********************************************************************/ + +GF_Err gf_isom_get_timed_meta_data_info(GF_ISOFile *file, u32 track, u32 sampleDescription, Bool *is_xml, const char **mime_or_namespace, const char **content_encoding, const char **schema_loc); + +#ifndef GPAC_DISABLE_ISOM_WRITE +/*create a new timed metat data sample description for this track*/ +GF_Err gf_isom_timed_meta_data_config_new(GF_ISOFile *movie, u32 trackNumber, Bool is_xml, char *mime_or_namespace, char *content_encoding, char *schema_loc, char *URLname, char *URNname, u32 *outDescriptionIndex); +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + + +/******************************************************************** + iTunes info tags +********************************************************************/ +enum +{ + /*probe is only used ti check if iTunes info are present*/ + GF_ISOM_ITUNE_PROBE = 0, + GF_ISOM_ITUNE_ALBUM = GF_4CC( 0xA9, 'a', 'l', 'b' ), + GF_ISOM_ITUNE_ARTIST = GF_4CC( 0xA9, 'A', 'R', 'T' ), + GF_ISOM_ITUNE_COMMENT = GF_4CC( 0xA9, 'c', 'm', 't' ), + GF_ISOM_ITUNE_COMPILATION = GF_4CC( 'c', 'p', 'i', 'l' ), + GF_ISOM_ITUNE_COMPOSER = GF_4CC( 0xA9, 'c', 'o', 'm' ), + GF_ISOM_ITUNE_COVER_ART = GF_4CC( 'c', 'o', 'v', 'r' ), + GF_ISOM_ITUNE_CREATED = GF_4CC( 0xA9, 'd', 'a', 'y' ), + GF_ISOM_ITUNE_DISK = GF_4CC( 'd', 'i', 's', 'k' ), + GF_ISOM_ITUNE_TOOL = GF_4CC( 0xA9, 't', 'o', 'o' ), + GF_ISOM_ITUNE_GENRE = GF_4CC( 'g', 'n', 'r', 'e' ), + GF_ISOM_ITUNE_GROUP = GF_4CC( 0xA9, 'g', 'r', 'p' ), + GF_ISOM_ITUNE_ITUNES_DATA = GF_4CC( '-', '-', '-', '-' ), + GF_ISOM_ITUNE_NAME = GF_4CC( 0xA9, 'n', 'a', 'm' ), + GF_ISOM_ITUNE_TEMPO = GF_4CC( 't', 'm', 'p', 'o' ), + GF_ISOM_ITUNE_TRACK = GF_4CC( 0xA9, 't', 'r', 'k' ), + GF_ISOM_ITUNE_TRACKNUMBER = GF_4CC( 't', 'r', 'k', 'n' ), + GF_ISOM_ITUNE_WRITER = GF_4CC( 0xA9, 'w', 'r', 't' ), + GF_ISOM_ITUNE_ENCODER = GF_4CC( 0xA9, 'e', 'n', 'c' ), + GF_ISOM_ITUNE_ALBUM_ARTIST = GF_4CC( 'a', 'A', 'R', 'T' ), + GF_ISOM_ITUNE_GAPLESS = GF_4CC( 'p', 'g', 'a', 'p' ), +}; +/*get the given tag info. +!! 'genre' may be coded by ID, the libisomedia doesn't translate the ID. In such a case, the result data is set to NULL +and the data_len to the genre ID +returns GF_URL_ERROR if no tag is present in the file +*/ +GF_Err gf_isom_apple_get_tag(GF_ISOFile *mov, u32 tag, const char **data, u32 *data_len); +#ifndef GPAC_DISABLE_ISOM_WRITE +/*set the given tag info. If data and data_len are 0, removes the given tag +For 'genre', data may be NULL in which case the genre ID taken from the data_len parameter +*/ +GF_Err gf_isom_apple_set_tag(GF_ISOFile *mov, u32 tag, const char *data, u32 data_len); + +/*sets compatibility tag on AVC tracks (needed by iPod to play files... hurray for standards)*/ +GF_Err gf_isom_set_ipod_compatible(GF_ISOFile *the_file, u32 trackNumber); +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + + +/*3GPP Alternate Group API - (c) 2007 ENST & ResonateMP4*/ + +/*gets the number of switching groups declared in this track if any: +trackNumber: track number +alternateGroupID: alternate group id of track if speciifed, 0 otherwise +nb_groups: number of switching groups defined for this track +*/ +GF_Err gf_isom_get_track_switch_group_count(GF_ISOFile *movie, u32 trackNumber, u32 *alternateGroupID, u32 *nb_groups); + +/*returns the list of criteria (expressed as 4CC IDs, cf 3GPP TS 26.244) +trackNumber: track number +group_index: 1-based index of the group to inspect +switchGroupID: ID of the switch group if any, 0 otherwise (alternate-only group) +criteriaListSize: number of criteria items in returned list +*/ +const u32 *gf_isom_get_track_switch_parameter(GF_ISOFile *movie, u32 trackNumber, u32 group_index, u32 *switchGroupID, u32 *criteriaListSize); + +#ifndef GPAC_DISABLE_ISOM_WRITE +/*sets a new (switch) group for this track +trackNumber: track +trackRefGroup: number of a track belonging to the same alternate group. If 0, a new alternate group will be created for this track +is_switch_group: if set, indicates that a switch group identifier shall be assigned to the created group. Otherwise, the criteria list is associated with the entire alternate group +switchGroupID: SHALL NOT BE NULL + input: specifies the desired switchGroupID to use. If value is 0, next available switchGroupID in file is used. + output: indicates the switchGroupID used. +criteriaList, criteriaListCount: criteria list and size. Criterias are expressed as 4CC IDs, cf 3GPP TS 26.244 +*/ +GF_Err gf_isom_set_track_switch_parameter(GF_ISOFile *movie, u32 trackNumber, u32 trackRefGroup, Bool is_switch_group, u32 *switchGroupID, u32 *criteriaList, u32 criteriaListCount); + +/*resets track switch group information for the track or for the entire alternate group this track belongs to if reset_all_group is set*/ +GF_Err gf_isom_reset_track_switch_parameter(GF_ISOFile *movie, u32 trackNumber, Bool reset_all_group); + +/*resets ALL track switch group information in the entire movie*/ +GF_Err gf_isom_reset_switch_parameters(GF_ISOFile *movie); + +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + + +typedef struct +{ + u8 profile; + u8 level; + u8 pathComponents; + Bool fullRequestHost; + Bool streamType; + u8 containsRedundant; + const char *textEncoding; + const char *contentEncoding; + const char *content_script_types; +} GF_DIMSDescription; + +GF_Err gf_isom_get_dims_description(GF_ISOFile *movie, u32 trackNumber, u32 descriptionIndex, GF_DIMSDescription *desc); +#ifndef GPAC_DISABLE_ISOM_WRITE +GF_Err gf_isom_new_dims_description(GF_ISOFile *movie, u32 trackNumber, GF_DIMSDescription *desc, char *URLname, char *URNname, u32 *outDescriptionIndex); +GF_Err gf_isom_update_dims_description(GF_ISOFile *movie, u32 trackNumber, GF_DIMSDescription *desc, char *URLname, char *URNname, u32 DescriptionIndex); +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + + + + +/*AC3 config record*/ +typedef struct +{ + u8 fscod; + u8 bsid; + u8 bsmod; + u8 acmod; + u8 lfon; + u8 brcode; +} GF_AC3Config; + +GF_AC3Config *gf_isom_ac3_config_get(GF_ISOFile *the_file, u32 trackNumber, u32 StreamDescriptionIndex); + +#ifndef GPAC_DISABLE_ISOM_WRITE +GF_Err gf_isom_ac3_config_new(GF_ISOFile *the_file, u32 trackNumber, GF_AC3Config *cfg, char *URLname, char *URNname, u32 *outDescriptionIndex); +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + + +/*returns the number of subsamples in the given sample */ +u32 gf_isom_sample_has_subsamples(GF_ISOFile *movie, u32 track, u32 sampleNumber); +GF_Err gf_isom_sample_get_subsample(GF_ISOFile *movie, u32 track, u32 sampleNumber, u32 subSampleNumber, u32 *size, u8 *priority, u32 *reserved, Bool *discardable); +#ifndef GPAC_DISABLE_ISOM_WRITE +/*adds subsample information to a given sample. Subsample information shall be added in increasing order of sampleNumbers, insertion of information is not supported. +Note that you may add subsample information for samples not yet added to the file +specifying 0 as subSampleSize will remove the last subsample information if any*/ +GF_Err gf_isom_add_subsample(GF_ISOFile *movie, u32 track, u32 sampleNumber, u32 subSampleSize, u8 priority, u32 reserved, Bool discardable); +#endif +/*add subsample information for the latest sample added to the current track fragment*/ +GF_Err gf_isom_fragment_add_subsample(GF_ISOFile *movie, u32 TrackID, u32 subSampleSize, u8 priority, u32 reserved, Bool discardable); +/*copy over the subsample information of the given sample from the source track/file to the last sample added to the current track fragment of the destination file*/ +GF_Err gf_isom_fragment_copy_subsample(GF_ISOFile *dest, u32 TrackID, GF_ISOFile *orig, u32 track, u32 sampleNumber); + +/*gets the number of the next moof to be produced*/ +u32 gf_isom_get_next_moof_number(GF_ISOFile *movie); +/*Sets the number of the next moof to be produced*/ +void gf_isom_set_next_moof_number(GF_ISOFile *movie, u32 value); + +#endif /*GPAC_DISABLE_ISOM*/ + +#ifdef __cplusplus +} +#endif + + +#endif /*_GF_ISOMEDIA_H_*/ + + diff --git a/src/gpacmp4/gpac/list.h b/src/gpacmp4/gpac/list.h new file mode 100644 index 00000000..1b9a8e4a --- /dev/null +++ b/src/gpacmp4/gpac/list.h @@ -0,0 +1,168 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / common tools sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef _GF_LIST_H_ +#define _GF_LIST_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/*! + * \file + * \brief list functions. + */ + +/*! + * \addtogroup list_grp list + * \ingroup utils_grp + * \brief List object + * + * This section documents the list object of the GPAC framework. + * @{ + */ + +#include + +typedef struct _tag_array GF_List; + +/*! + * \brief list constructor + * + * Constructs a new list object + * \return new list object + */ +GF_List *gf_list_new(); +/*! + * \brief list destructor + * + * Destructs a list object + * \param ptr list object to destruct + * \note It is the caller responsability to destroy the content of the list if needed + */ +void gf_list_del(GF_List *ptr); +/*! + * \brief get count + * + * Returns number of items in the list + * \param ptr target list object + * \return number of items in the list + */ +u32 gf_list_count(const GF_List *ptr); +/*! + * \brief add item + * + * Adds an item at the end of the list + * \param ptr target list object + * \param item item to add + */ +GF_Err gf_list_add(GF_List *ptr, void* item); +/*! + * \brief inserts item + * + * Insert an item in the list + * \param ptr target list object + * \param item item to add + * \param position insertion position. It is expressed between 0 and gf_list_count-1, and any bigger value is equivalent to gf_list_add + */ +GF_Err gf_list_insert(GF_List *ptr, void *item, u32 position); +/*! + * \brief removes item + * + * Removes an item from the list given its position + * \param ptr target list object + * \param position position of the item to remove. It is expressed between 0 and gf_list_count-1. + * \note It is the caller responsability to destroy the content of the list if needed + */ +GF_Err gf_list_rem(GF_List *ptr, u32 position); +/*! + * \brief gets item + * + * Gets an item from the list given its position + * \param ptr target list object + * \param position position of the item to get. It is expressed between 0 and gf_list_count-1. + */ +void *gf_list_get(GF_List *ptr, u32 position); +/*! + * \brief finds item + * + * Finds an item in the list + * \param ptr target list object. + * \param item the item to find. + * \return 0-based item position in the list, or -1 if the item could not be found. + */ +s32 gf_list_find(GF_List *ptr, void *item); +/*! + * \brief deletes item + * + * Deletes an item from the list + * \param ptr target list object. + * \param item the item to find. + * \return 0-based item position in the list before removal, or -1 if the item could not be found. + */ +s32 gf_list_del_item(GF_List *ptr, void *item); +/*! + * \brief resets list + * + * Resets the content of the list + * \param ptr target list object. + * \note It is the caller responsability to destroy the content of the list if needed + */ +void gf_list_reset(GF_List *ptr); +/*! + * \brief gets last item + * + * Gets last item o fthe list + * \param ptr target list object + */ +void *gf_list_last(GF_List *ptr); +/*! + * \brief removes last item + * + * Removes the last item of the list + * \param ptr target list object + * \note It is the caller responsability to destroy the content of the list if needed + */ +GF_Err gf_list_rem_last(GF_List *ptr); + + +/*! + * \brief list enumerator + * + * Retrieves given list item and increment current position + * \param ptr target list object + * \param pos target item position. The position is automatically incremented regardless of the return value + * \note A typical enumeration will start with a value of 0 until NULL is returned. + */ +void *gf_list_enum(GF_List *ptr, u32 *pos); + +/*! @} */ + +#ifdef __cplusplus +} +#endif + + +#endif /*_GF_LIST_H_*/ + diff --git a/src/gpacmp4/gpac/math.h b/src/gpacmp4/gpac/math.h new file mode 100644 index 00000000..3c610467 --- /dev/null +++ b/src/gpacmp4/gpac/math.h @@ -0,0 +1,1038 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / common tools sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef _GF_MATH_H_ +#define _GF_MATH_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/*! + * \file + * \brief math and trigo functions. + */ + +#include + +/*NOTE: there is a conflict on Win32 VC6 with C++ and gpac headers when including */ +#if !defined(__cplusplus) || defined(__SYMBIAN32__) +#include +#endif + + +/*! + *\addtogroup math_grp math + *\ingroup utils_grp + *\brief Mathematics and Trigonometric functions + * + *This section documents the math and trigo functions used in the GPAC framework. GPAC can be compiled with + *fixed-point support, representing float values on a 16.16 signed integer, which implies a developer + *must take care of float computations when using GPAC.\n + *A developper should not need to know in which mode the framework has been compiled as long as he uses + *the math functions of GPAC which work in both float and fixed-point mode.\n + *Using fixed-point version is decided at compilation time and cannot be changed. The feature is signaled + *through the following macros: + *- GPAC_FIXED_POINT: when defined, GPAC has been compiled in fixed-point mode + *- GPAC_NO_FIXED_POINT: when defined, GPAC has been compiled in regular (float) mode + * @{ + */ + + +/***************************************************************************************** + FIXED-POINT SUPPORT - HARDCODED FOR 16.16 representation + the software rasterizer also use a 16.16 representation even in non-fixed version +******************************************************************************************/ + +#ifdef GPAC_FIXED_POINT + +/*! + *Fixed 16.16 number + *\hideinitializer + \note This documentation has been generated for a fixed-point version of the GPAC framework. + */ +typedef s32 Fixed; +#define FIX_ONE 0x10000L +#define INT2FIX(v) ((Fixed)( ((s32) (v) ) << 16)) +#define FLT2FIX(v) ((Fixed) ((v) * FIX_ONE)) +#define FIX2INT(v) ((s32)(((v)+((FIX_ONE>>1)))>>16)) +#define FIX2FLT(v) ((Float)( ((Float)(v)) / ((Float) FIX_ONE))) +#define FIX_EPSILON 2 +#define FIX_MAX 0x7FFFFFFF +#define FIX_MIN -FIX_MAX +#define GF_PI2 102944 +#define GF_PI 205887 +#define GF_2PI 411774 + +/*!\return 1/a, expressed as fixed number*/ +Fixed gf_invfix(Fixed a); +/*!\return a*b, expressed as fixed number*/ +Fixed gf_mulfix(Fixed a, Fixed b); +/*!\return a*b/c, expressed as fixed number*/ +Fixed gf_muldiv(Fixed a, Fixed b, Fixed c); +/*!\return a/b, expressed as fixed number*/ +Fixed gf_divfix(Fixed a, Fixed b); +/*!\return sqrt(a), expressed as fixed number*/ +Fixed gf_sqrt(Fixed x); +/*!\return ceil(a), expressed as fixed number*/ +Fixed gf_ceil(Fixed a); +/*!\return floor(a), expressed as fixed number*/ +Fixed gf_floor(Fixed a); +/*!\return cos(a), expressed as fixed number*/ +Fixed gf_cos(Fixed angle); +/*!\return sin(a), expressed as fixed number*/ +Fixed gf_sin(Fixed angle); +/*!\return tan(a), expressed as fixed number*/ +Fixed gf_tan(Fixed angle); +/*!\return acos(a), expressed as fixed number*/ +Fixed gf_acos(Fixed angle); +/*!\return asin(a), expressed as fixed number*/ +Fixed gf_asin(Fixed angle); +/*!\return atan(y, x), expressed as fixed number*/ +Fixed gf_atan2(Fixed y, Fixed x); + +#else + + +/*!Fixed is 32bit float number + \note This documentation has been generated for a float version of the GPAC framework. +*/ +typedef Float Fixed; +#define FIX_ONE 1.0f +#define INT2FIX(v) ((Float) (v)) +#define FLT2FIX(v) ((Float) (v)) +#define FIX2INT(v) ((s32)(v)) +#define FIX2FLT(v) ((Float) (v)) +#define FIX_EPSILON GF_EPSILON_FLOAT +#define FIX_MAX GF_MAX_FLOAT +#define FIX_MIN -GF_MAX_FLOAT +#define GF_PI2 1.5707963267949f +#define GF_PI 3.1415926535898f +#define GF_2PI 6.2831853071796f + +/*!\hideinitializer 1/_a, expressed as fixed number*/ +#define gf_invfix(_a) (FIX_ONE/(_a)) +/*!\hideinitializer _a*_b, expressed as fixed number*/ +#define gf_mulfix(_a, _b) ((_a)*(_b)) +/*!\hideinitializer _a*_b/_c, expressed as fixed number*/ +#define gf_muldiv(_a, _b, _c) ((_c) ? (_a)*(_b)/(_c) : GF_MAX_FLOAT) +/*!\hideinitializer _a/_b, expressed as fixed number*/ +#define gf_divfix(_a, _b) ((_b) ? (_a)/(_b) : GF_MAX_FLOAT) +/*!\hideinitializer sqrt(_a), expressed as fixed number*/ +#define gf_sqrt(_a) ((Float) sqrt(_a)) +/*!\hideinitializer ceil(_a), expressed as fixed number*/ +#define gf_ceil(_a) ((Float) ceil(_a)) +/*!\hideinitializer floor(_a), expressed as fixed number*/ +#define gf_floor(_a) ((Float) floor(_a)) +/*!\hideinitializer cos(_a), expressed as fixed number*/ +#define gf_cos(_a) ((Float) cos(_a)) +/*!\hideinitializer sin(_a), expressed as fixed number*/ +#define gf_sin(_a) ((Float) sin(_a)) +/*!\hideinitializer tan(_a), expressed as fixed number*/ +#define gf_tan(_a) ((Float) tan(_a)) +/*!\hideinitializer atan2(_y,_x), expressed as fixed number*/ +#define gf_atan2(_y, _x) ((Float) atan2(_y, _x)) +/*!\hideinitializer acos(_a), expressed as fixed number*/ +#define gf_acos(_a) ((Float) acos(_a)) +/*!\hideinitializer asin(_a), expressed as fixed number*/ +#define gf_asin(_a) ((Float) asin(_a)) + +#endif + +/*!\def FIX_ONE + \hideinitializer + Fixed unit value +*/ +/*!\def INT2FIX(v) + \hideinitializer + Conversion from integer to fixed +*/ +/*!\def FLT2FIX(v) + \hideinitializer + Conversion from float to fixed +*/ +/*!\def FIX2INT(v) + \hideinitializer + Conversion from fixed to integer +*/ +/*!\def FIX2FLT(v) + \hideinitializer + Conversion from fixed to float +*/ +/*!\def FIX_EPSILON + \hideinitializer + Epsilon Fixed (positive value closest to 0) +*/ +/*!\def FIX_MAX + \hideinitializer + Maximum Fixed (maximum representable fixed value) +*/ +/*!\def FIX_MIN + \hideinitializer + Minimum Fixed (minimum representable fixed value) +*/ +/*!\def GF_PI2 + \hideinitializer + PI/2 expressed as Fixed +*/ +/*!\def GF_PI + \hideinitializer + PI expressed as Fixed +*/ +/*!\def GF_2PI + \hideinitializer + 2*PI expressed as Fixed +*/ + +Fixed gf_angle_diff(Fixed a, Fixed b); + +/*! + * \brief Field bit-size + * + * Gets the number of bits needed to represent the value. + * \param MaxVal Maximum value to be represented. + * \return number of bits required to represent the value. + */ +u32 gf_get_bit_size(u32 MaxVal); + +/*! + * \brief Get power of 2 + * + * Gets the closest power of 2 greater or equal to the value. + * \param val value to be used. + * \return requested power of 2. + */ +u32 gf_get_next_pow2(u32 val); + +/*! + *\addtogroup math2d_grp math2d + *\ingroup math_grp + *\brief 2D Mathematics functions + * + *This section documents mathematic tools for 2D geometry and color matrices operations + * @{ + */ + +/*!\brief 2D point + * + *The 2D point object is used in all the GPAC framework for both point and vector representation. +*/ +typedef struct __vec2f +{ + Fixed x; + Fixed y; +} GF_Point2D; +/*! + *\brief get 2D vector length + * + *Gets the length of a 2D vector + *\return length of the vector + */ +Fixed gf_v2d_len(GF_Point2D *vec); +/*! + *\brief get distance between 2 points + * + *Gets the distance between the 2 points + *\return distance + */ +Fixed gf_v2d_distance(GF_Point2D *a, GF_Point2D *b); +/*! + *\brief 2D vector from polar coordinates + * + *Constructs a 2D vector from its polar coordinates + *\param length the length of the vector + *\param angle the angle of the vector in radians + *\return the 2D vector + */ +GF_Point2D gf_v2d_from_polar(Fixed length, Fixed angle); + +/*!\brief rectangle 2D + * + *The 2D rectangle used in the GPAC project. + */ +typedef struct +{ + /*!the left coordinate of the rectangle*/ + Fixed x; + /*!the top coordinate of the rectangle, regardless of the canvas orientation. In other words, y is always the + greatest coordinate value, even if the rectangle is presented bottom-up. This insures proper rectangles testing*/ + Fixed y; + /*!the width of the rectangle. Width must be greater than or equal to 0*/ + Fixed width; + /*!the height of the rectangle. Height must be greater than or equal to 0*/ + Fixed height; +} GF_Rect; + +/*! + \brief rectangle union + * + *Gets the union of two rectangles. + *\param rc1 first rectangle of the union. Upon return, this rectangle will contain the result of the union + *\param rc2 second rectangle of the union +*/ +void gf_rect_union(GF_Rect *rc1, GF_Rect *rc2); +/*! + \brief centers a rectangle + * + *Builds a rectangle centered on the origin + *\param w width of the rectangle + *\param h height of the rectangle + *\return centered rectangle object +*/ +GF_Rect gf_rect_center(Fixed w, Fixed h); +/*! + \brief rectangle overlap test + * + *Tests if two rectangles overlap. + *\param rc1 first rectangle to test + *\param rc2 second rectangle to test + *\return 1 if rectangles overlap, 0 otherwise +*/ +Bool gf_rect_overlaps(GF_Rect rc1, GF_Rect rc2); +/*! + \brief rectangle identity test + * + *Tests if two rectangles are identical. + *\param rc1 first rectangle to test + *\param rc2 second rectangle to test + *\return 1 if rectangles are identical, 0 otherwise +*/ +Bool gf_rect_equal(GF_Rect rc1, GF_Rect rc2); + +/*! + *\brief pixel-aligned rectangle + * + *Pixel-aligned rectangle used in the GPAC framework. This is usually needed for 2D drawing algorithms. + */ +typedef struct +{ + /*!the left coordinate of the rectangle*/ + s32 x; + /*!the top coordinate of the rectangle, regardless of the canvas orientation. In other words, y is always the + greatest coordinate value, even if the rectangle is presented bottom-up. This insures proper rectangles operations*/ + s32 y; + /*!the width of the rectangle. Width must be greater than or equal to 0*/ + s32 width; + /*!the height of the rectangle. Height must be greater than or equal to 0*/ + s32 height; +} GF_IRect; +/*! + *\brief gets the pixelized version of a rectangle + * + *Returns the smallest pixel-aligned rectangle completely containing a rectangle + *\param r the rectangle to transform + *\return the pixel-aligned transformed rectangle +*/ +GF_IRect gf_rect_pixelize(GF_Rect *r); + + +/*! + *\brief 2D matrix + * + *The 2D affine matrix object usied in GPAC. The transformation of P(x,y) in P'(X, Y) is: + \code + X = m[0]*x + m[1]*y + m[2]; + Y = m[3]*x + m[4]*y + m[5]; + \endcode +*/ +typedef struct +{ + Fixed m[6]; +} GF_Matrix2D; + +/*!\brief matrix initialization + *\hideinitializer + * + *Inits the matrix to the identity matrix +*/ +#define gf_mx2d_init(_obj) { memset((_obj).m, 0, sizeof(Fixed)*6); (_obj).m[0] = (_obj).m[4] = FIX_ONE; } +/*!\brief matrix copy + *\hideinitializer + * + *Copies the matrix _from to the matrix _obj +*/ +#define gf_mx2d_copy(_obj, from) memcpy((_obj).m, (from).m, sizeof(Fixed)*6) +/*!\brief matrix identity testing + *\hideinitializer + * + *This macro evaluates to 1 if the matrix _obj is the identity matrix, 0 otherwise +*/ +#define gf_mx2d_is_identity(_obj) ((!(_obj).m[1] && !(_obj).m[2] && !(_obj).m[3] && !(_obj).m[5] && ((_obj).m[0]==FIX_ONE) && ((_obj).m[4]==FIX_ONE)) ? 1 : 0) + +/*!\brief 2D matrix multiplication + * + *Multiplies two 2D matrices from*_this + *\param _this matrix being transformed. Once the function is called, _this contains the result matrix + *\param from transformation matrix to add +*/ +void gf_mx2d_add_matrix(GF_Matrix2D *_this, GF_Matrix2D *from); + +/*!\brief 2D matrix pre-multiplication + * + *Multiplies two 2D matrices _this*from + *\param _this matrix being transformed. Once the function is called, _this contains the result matrix + *\param from transformation matrix to add +*/ +void gf_mx2d_pre_multiply(GF_Matrix2D *_this, GF_Matrix2D *from); + +/*!\brief matrix translating + * + *Translates a 2D matrix + *\param _this matrix being transformed. Once the function is called, _this contains the result matrix + *\param cx horizontal translation + *\param cy vertical translation +*/ +void gf_mx2d_add_translation(GF_Matrix2D *_this, Fixed cx, Fixed cy); +/*!\brief matrix rotating + * + *Rotates a 2D matrix + *\param _this matrix being transformed. Once the function is called, _this contains the result matrix + *\param cx horizontal rotation center coordinate + *\param cy vertical rotation center coordinate + *\param angle rotation angle in radians +*/ +void gf_mx2d_add_rotation(GF_Matrix2D *_this, Fixed cx, Fixed cy, Fixed angle); +/*!\brief matrix scaling + * + *Scales a 2D matrix + *\param _this matrix being transformed. Once the function is called, _this contains the result matrix + *\param scale_x horizontal scaling factor + *\param scale_y vertical scaling factor +*/ +void gf_mx2d_add_scale(GF_Matrix2D *_this, Fixed scale_x, Fixed scale_y); +/*!\brief matrix uncentered scaling + * + *Scales a 2D matrix with a non-centered scale + *\param _this matrix being transformed. Once the function is called, _this contains the result matrix + *\param scale_x horizontal scaling factor + *\param scale_y vertical scaling factor + *\param cx horizontal scaling center coordinate + *\param cy vertical scaling center coordinate + *\param angle scale orienttion angle in radians +*/ +void gf_mx2d_add_scale_at(GF_Matrix2D *_this, Fixed scale_x, Fixed scale_y, Fixed cx, Fixed cy, Fixed angle); +/*!\brief matrix skewing + * + *Skews a 2D matrix + *\param _this matrix being transformed. Once the function is called, _this contains the result matrix + *\param skew_x horizontal skew factor + *\param skew_y vertical skew factor +*/ +void gf_mx2d_add_skew(GF_Matrix2D *_this, Fixed skew_x, Fixed skew_y); +/*!\brief matrix horizontal skewing + * + *Skews a 2D matrix horizontally by a given angle + *\param _this matrix being transformed. Once the function is called, _this contains the result matrix + *\param angle horizontal skew angle in radians +*/ +void gf_mx2d_add_skew_x(GF_Matrix2D *_this, Fixed angle); +/*!\brief matrix vertical skewing + * + *Skews a 2D matrix vertically by a given angle + *\param _this matrix being transformed. Once the function is called, _this contains the result matrix + *\param angle vertical skew angle in radians +*/ +void gf_mx2d_add_skew_y(GF_Matrix2D *_this, Fixed angle); +/*!\brief matrix inversing + * + *Inverses a 2D matrix + *\param _this matrix being transformed. Once the function is called, _this contains the result matrix +*/ +void gf_mx2d_inverse(GF_Matrix2D *_this); +/*!\brief matrix coordinate transformation + * + *Applies a 2D matrix transformation to coordinates + *\param _this transformation matrix + *\param x pointer to horizontal coordinate. Once the function is called, x contains the transformed horizontal coordinate + *\param y pointer to vertical coordinate. Once the function is called, y contains the transformed vertical coordinate +*/ +void gf_mx2d_apply_coords(GF_Matrix2D *_this, Fixed *x, Fixed *y); +/*!\brief matrix point transformation + * + *Applies a 2D matrix transformation to a 2D point + *\param _this transformation matrix + *\param pt pointer to 2D point. Once the function is called, pt contains the transformed point +*/ +void gf_mx2d_apply_point(GF_Matrix2D *_this, GF_Point2D *pt); +/*!\brief matrix rectangle transformation + * + *Applies a 2D matrix transformation to a rectangle, giving the enclosing rectangle of the transformed one + *\param _this transformation matrix + *\param rc pointer to rectangle. Once the function is called, rc contains the transformed rectangle +*/ +void gf_mx2d_apply_rect(GF_Matrix2D *_this, GF_Rect *rc); + +/*!\brief matrix decomposition + * + *Decomposes a 2D matrix M as M=Scale x Rotation x Translation if possible + *\param _this transformation matrix + *\param scale resulting scale part + *\param rotate resulting rotation part + *\param translate resulting translation part + *\return 0 if matrix cannot be decomposed, 1 otherwise +*/ +Bool gf_mx2d_decompose(GF_Matrix2D *_this, GF_Point2D *scale, Fixed *rotate, GF_Point2D *translate); + +/*! @} */ + + +/*! + *\addtogroup math3d_grp math3d + *\ingroup math_grp + *\brief 3D Mathematics functions + * + *This section documents mathematic tools for 3D geometry operations + * @{ + */ + +/*!\brief 3D point or vector + * + *The 3D point object is used in all the GPAC framework for both point and vector representation. +*/ +typedef struct __vec3f +{ + Fixed x; + Fixed y; + Fixed z; +} GF_Vec; + +/*base vector operations are MACROs for faster access*/ +/*!\hideinitializer macro evaluating to 1 if vectors are equal, 0 otherwise*/ +#define gf_vec_equal(v1, v2) (((v1).x == (v2).x) && ((v1).y == (v2).y) && ((v1).z == (v2).z)) +/*!\hideinitializer macro reversing a vector v = v*/ +#define gf_vec_rev(v) { (v).x = -(v).x; (v).y = -(v).y; (v).z = -(v).z; } +/*!\hideinitializer macro performing the minus operation res = v1 - v2*/ +#define gf_vec_diff(res, v1, v2) { (res).x = (v1).x - (v2).x; (res).y = (v1).y - (v2).y; (res).z = (v1).z - (v2).z; } +/*!\hideinitializer macro performing the add operation res = v1 + v2*/ +#define gf_vec_add(res, v1, v2) { (res).x = (v1).x + (v2).x; (res).y = (v1).y + (v2).y; (res).z = (v1).z + (v2).z; } + +/*! + *\brief get 3D vector length + * + *Gets the length of a 3D vector + *\return length of the vector + */ +Fixed gf_vec_len(GF_Vec v); +/*! + *\brief get 3D vector square length + * + *Gets the square length of a 3D vector + *\return square length of the vector + */ +Fixed gf_vec_lensq(GF_Vec v); +/*! + *\brief get 3D vector dot product + * + *Gets the dot product of two vectors + *\return dot product of the vectors + */ +Fixed gf_vec_dot(GF_Vec v1, GF_Vec v2); +/*! + *\brief vector normalization + * + *Norms the vector, eg make its length equal to \ref FIX_ONE + *\param v vector to normalize + */ +void gf_vec_norm(GF_Vec *v); +/*! + *\brief vector scaling + * + *Scales a vector by a given amount + *\param v vector to scale + *\param f scale factor + *\return scaled vector + */ +GF_Vec gf_vec_scale(GF_Vec v, Fixed f); +/*! + *\brief vector cross product + * + *Gets the cross product of two vectors + *\param v1 first vector + *\param v2 second vector + *\return cross-product vector + */ +GF_Vec gf_vec_cross(GF_Vec v1, GF_Vec v2); + +/*!\brief 4D vector + * + *The 4D vector object is used in all the GPAC framework for 4 dimension vectors, VRML Rotations and quaternions representation. +*/ +typedef struct __vec4f +{ + Fixed x; + Fixed y; + Fixed z; + Fixed q; +} GF_Vec4; + + +/*!\brief 3D matrix + * + *The 3D matrix object used in GPAC. The matrix is oriented like OpenGL matrices (column-major ordering), with + the translation part at the end of the coefficients list. + \note Unless specified otherwise, the matrix object is always expected to represent an affine transformation. + */ +typedef struct +{ + Fixed m[16]; +} GF_Matrix; + + +/*!\hideinitializer gets the len of a quaternion*/ +#define gf_quat_len(v) gf_sqrt(gf_mulfix((v).q,(v).q) + gf_mulfix((v).x,(v).x) + gf_mulfix((v).y,(v).y) + gf_mulfix((v).z,(v).z)) +/*!\hideinitializer normalizes a quaternion*/ +#define gf_quat_norm(v) { \ + Fixed __mag = gf_quat_len(v); \ + (v).x = gf_divfix((v).x, __mag); (v).y = gf_divfix((v).y, __mag); (v).z = gf_divfix((v).z, __mag); (v).q = gf_divfix((v).q, __mag); \ + } \ + +/*!\brief quaternion to rotation + * + *Transforms a quaternion to a Rotation, expressed as a 4 dimension vector with x,y,z for axis and q for rotation angle + *\param quat the quaternion to transform + *\return the rotation value + */ +GF_Vec4 gf_quat_to_rotation(GF_Vec4 *quat); +/*!\brief quaternion from rotation + * + *Transforms a Rotation to a quaternion + *\param rot the rotation to transform + *\return the quaternion value + */ +GF_Vec4 gf_quat_from_rotation(GF_Vec4 rot); +/*!inverses a quaternion*/ +GF_Vec4 gf_quat_get_inv(GF_Vec4 *quat); +/*!\brief quaternion multiplication + * + *Multiplies two quaternions + *\param q1 the first quaternion + *\param q2 the second quaternion + *\return the resulting quaternion + */ +GF_Vec4 gf_quat_multiply(GF_Vec4 *q1, GF_Vec4 *q2); +/*!\brief quaternion vector rotating + * + *Rotates a vector with a quaternion + *\param quat the quaternion modelizing the rotation + *\param vec the vector to rotate + *\return the resulting vector + */ +GF_Vec gf_quat_rotate(GF_Vec4 *quat, GF_Vec *vec); +/*!\brief quaternion from axis and cos + * + *Constructs a quaternion from an axis and a cosinus value (shortcut to \ref gf_quat_from_rotation) + *\param axis the rotation axis + *\param cos_a the rotation cosinus value + *\return the resulting quaternion + */ +GF_Vec4 gf_quat_from_axis_cos(GF_Vec axis, Fixed cos_a); +/*!\brief quaternion interpolation + * + *Interpolates two quaternions using spherical linear interpolation + *\param q1 the first quaternion + *\param q2 the second quaternion + *\param frac the fraction of the interpolation, between 0 and \ref FIX_ONE + *\return the interpolated quaternion + */ +GF_Vec4 gf_quat_slerp(GF_Vec4 q1, GF_Vec4 q2, Fixed frac); + +/*!\brief 3D Bounding Box + * + *The 3D Bounding Box is a 3D Axis-Aligned Bounding Box used to in various tools of the GPAC framework for bounds + estimation of a 3D object. It features an axis-aligned box and a sphere bounding volume for fast intersection tests. + */ +typedef struct +{ + /*!minimum x, y, and z of the object*/ + GF_Vec min_edge; + /*!maximum x, y, and z of the object*/ + GF_Vec max_edge; + + /*!center of the bounding box.\note this is computed from min_edge and max_edge*/ + GF_Vec center; + /*!radius of the bounding sphere for this box.\note this is computed from min_edge and max_edge*/ + Fixed radius; + /*!the bbox center and radius are valid*/ + Bool is_set; +} GF_BBox; +/*!updates information of the bounding box based on the edge information*/ +void gf_bbox_refresh(GF_BBox *b); +/*!builds a bounding box from a 2D rectangle*/ +void gf_bbox_from_rect(GF_BBox *box, GF_Rect *rc); +/*!builds a rectangle from a 3D bounding box.\note The z dimension is lost and no projection is performed*/ +void gf_rect_from_bbox(GF_Rect *rc, GF_BBox *box); +/*!\brief bounding box expansion + * + *Checks if a point is inside a bounding box and updates the bounding box to include it if not the case + *\param box the bounding box object + *\param pt the 3D point to check +*/ +void gf_bbox_grow_point(GF_BBox *box, GF_Vec pt); +/*!performs the union of two bounding boxes*/ +void gf_bbox_union(GF_BBox *b1, GF_BBox *b2); +/*!checks if two bounding boxes are equal or not*/ +Bool gf_bbox_equal(GF_BBox *b1, GF_BBox *b2); +/*!checks if a point is inside a bounding box or not*/ +Bool gf_bbox_point_inside(GF_BBox *box, GF_Vec *p); +/*!\brief get box vertices + * + *Returns the 8 bounding box vertices given the minimum and maximum edge. Vertices are ordered to respect + "p-vertex indexes", (vertex from a box closest to plane) and so that n-vertex (vertex from a box farthest from plane) + is 7-p_vx_idx + *\param bmin minimum edge of the box + *\param bmax maximum edge of the box + *\param vecs list of 8 3D points used to store the vertices. +*/ +void gf_bbox_get_vertices(GF_Vec bmin, GF_Vec bmax, GF_Vec *vecs); + + +/*!\brief matrix initialization + *\hideinitializer + * + *Inits the matrix to the identity matrix +*/ +#define gf_mx_init(_obj) { memset((_obj).m, 0, sizeof(Fixed)*16); (_obj).m[0] = (_obj).m[5] = (_obj).m[10] = (_obj).m[15] = FIX_ONE; } +/*!\brief matrix copy + *\hideinitializer + * + *Copies the matrix _from to the matrix _obj +*/ +#define gf_mx_copy(_obj, from) memcpy(&(_obj), &(from), sizeof(GF_Matrix)); +/*!\brief matrix constructor from 2D + * + *Initializes a 3D matrix from a 2D matrix.\note all z-related coefficients will be set to default. +*/ +void gf_mx_from_mx2d(GF_Matrix *mx, GF_Matrix2D *mat2D); +/*!\brief matrix identity testing + * + *Tests if two matrices are equal or not. + \return 1 if matrices are same, 0 otherwise +*/ +Bool gf_mx_equal(GF_Matrix *mx1, GF_Matrix *mx2); +/*!\brief matrix translation + * + *Translates a matrix + *\param mx the matrix being transformed. Once the function is called, contains the result matrix + *\param tx horizontal translation + *\param ty vertical translation + *\param tz depth translation +*/ +void gf_mx_add_translation(GF_Matrix *mx, Fixed tx, Fixed ty, Fixed tz); +/*!\brief matrix scaling + * + *Scales a matrix + *\param mx the matrix being transformed. Once the function is called, contains the result matrix + *\param sx horizontal translation scaling + *\param sy vertical translation scaling + *\param sz depth translation scaling +*/ +void gf_mx_add_scale(GF_Matrix *mx, Fixed sx, Fixed sy, Fixed sz); +/*!\brief matrix rotating + * + *Rotates a matrix + *\param mx the matrix being transformed. Once the function is called, contains the result matrix + *\param angle rotation angle in radians + *\param x horizontal coordinate of rotation axis + *\param y vertical coordinate of rotation axis + *\param z depth coordinate of rotation axis +*/ +void gf_mx_add_rotation(GF_Matrix *mx, Fixed angle, Fixed x, Fixed y, Fixed z); +/*!\brief matrices multiplication + * + *Multiplies a matrix with another one mx = mx*mul + *\param mx the matrix being transformed. Once the function is called, contains the result matrix + *\param mul the matrix to add +*/ +void gf_mx_add_matrix(GF_Matrix *mx, GF_Matrix *mul); +/*!\brief 2D matrix multiplication + * + *Adds a 2D affine matrix to a matrix + *\param mx the matrix + *\param mat2D the matrix to premultiply + */ +void gf_mx_add_matrix_2d(GF_Matrix *mx, GF_Matrix2D *mat2D); + +/*!\brief affine matrix inversion + * + *Inverses an affine matrix.\warning Results are undefined if the matrix is not an affine one + *\param mx the matrix to inverse + */ +void gf_mx_inverse(GF_Matrix *mx); +/*!\brief matrix point transformation + * + *Applies a 3D matrix transformation to a 3D point + *\param mx transformation matrix + *\param pt pointer to 3D point. Once the function is called, pt contains the transformed point +*/ +void gf_mx_apply_vec(GF_Matrix *mx, GF_Vec *pt); +/*!\brief matrix rectangle transformation + * + *Applies a 3D matrix transformation to a rectangle, giving the enclosing rectangle of the transformed one.\note all depth information are discarded. + *\param _this transformation matrix + *\param rc pointer to rectangle. Once the function is called, rc contains the transformed rectangle +*/ +void gf_mx_apply_rect(GF_Matrix *_this, GF_Rect *rc); +/*!\brief ortho matrix construction + * + *Creates an orthogonal projection matrix + *\param mx matrix to initialize + *\param left min horizontal coordinate of viewport + *\param right max horizontal coordinate of viewport + *\param bottom min vertical coordinate of viewport + *\param top max vertical coordinate of viewport + *\param z_near min depth coordinate of viewport + *\param z_far max depth coordinate of viewport +*/ +void gf_mx_ortho(GF_Matrix *mx, Fixed left, Fixed right, Fixed bottom, Fixed top, Fixed z_near, Fixed z_far); +/*!\brief perspective matrix construction + * + *Creates a perspective projection matrix + *\param mx matrix to initialize + *\param foc camera field of view angle in radian + *\param aspect_ratio viewport aspect ratio + *\param z_near min depth coordinate of viewport + *\param z_far max depth coordinate of viewport +*/ +void gf_mx_perspective(GF_Matrix *mx, Fixed foc, Fixed aspect_ratio, Fixed z_near, Fixed z_far); +/*!\brief creates look matrix + * + *Creates a transformation matrix looking at a given direction from a given point (camera matrix). + *\param mx matrix to initialize + *\param position position + *\param target look direction + *\param up_vector vector describing the up direction +*/ +void gf_mx_lookat(GF_Matrix *mx, GF_Vec position, GF_Vec target, GF_Vec up_vector); +/*!\brief matrix box transformation + * + *Applies a 3D matrix transformation to a bounding box, giving the enclosing box of the transformed one + *\param mx transformation matrix + *\param b pointer to bounding box. Once the function is called, contains the transformed bounding box +*/ +void gf_mx_apply_bbox(GF_Matrix *mx, GF_BBox *b); +/*!\brief matrix box sphere transformation + * + *Applies a 3D matrix transformation to a bounding box, computing only the enclosing sphere of the transformed one. + *\param mx transformation matrix + *\param b pointer to bounding box. Once the function is called, contains the transformed bounding sphere +*/ +void gf_mx_apply_bbox_sphere(GF_Matrix *mx, GF_BBox *box); +/*!\brief non-affine matrix multiplication + * + *Multiplies two non-affine matrices mx = mx*mul +*/ +void gf_mx_add_matrix_4x4(GF_Matrix *mat, GF_Matrix *mul); +/*!\brief non-affine matrix inversion + * + *Inverses a non-affine matrices + *\return 1 if inversion was done, 0 if inversion not possible. +*/ +Bool gf_mx_inverse_4x4(GF_Matrix *mx); +/*!\brief matrix 4D vector transformation + * + *Applies a 3D non-affine matrix transformation to a 4 dimension vector + *\param mx transformation matrix + *\param vec pointer to the vector. Once the function is called, contains the transformed vector +*/ +void gf_mx_apply_vec_4x4(GF_Matrix *mx, GF_Vec4 *vec); +/*!\brief matrix decomposition + * + *Decomposes a matrix into translation, scale, shear and rotate + *\param mx the matrix to decompose + *\param translate the decomposed translation part + *\param scale the decomposed scaling part + *\param rotate the decomposed rotation part, expressed as a Rotataion (axis + angle) + *\param shear the decomposed shear part + */ +void gf_mx_decompose(GF_Matrix *mx, GF_Vec *translate, GF_Vec *scale, GF_Vec4 *rotate, GF_Vec *shear); +/*!\brief matrix vector rotation + * + *Rotates a vector with a given matrix, ignoring any translation. + *\param mx transformation matrix + *\param pt pointer to 3D vector. Once the function is called, pt contains the transformed vector + */ +void gf_mx_rotate_vector(GF_Matrix *mx, GF_Vec *pt); +/*!\brief matrix initialization from vectors + * + *Inits a matrix to rotate the local axis in the given vectors + \param mx matrix to initialize + \param x_axis target normalized X axis + \param y_axis target normalized Y axis + \param z_axis target normalized Z axis +*/ +void gf_mx_rotation_matrix_from_vectors(GF_Matrix *mx, GF_Vec x_axis, GF_Vec y_axis, GF_Vec z_axis); +/*!\brief matrix to 2D matrix + * + *Inits a 2D matrix by removing all depth info from a 3D matrix + *\param mx2d 2D matrix to initialize + *\param mx 3D matrix to use +*/ +void gf_mx2d_from_mx(GF_Matrix2D *mx2d, GF_Matrix *mx); + +/*!\brief Plane object*/ +typedef struct +{ + /*!normal vector to the plane*/ + GF_Vec normal; + /*!distance from origin of the plane*/ + Fixed d; +} GF_Plane; +/*!\brief matrix plane transformation + * + *Transorms a plane by a given matrix + *\param mx the matrix to use + *\param plane pointer to 3D plane. Once the function is called, plane contains the transformed plane + */ +void gf_mx_apply_plane(GF_Matrix *mx, GF_Plane *plane); +/*!\brief point to plane distance + * + *Gets the distance between a point and a plne + *\param plane the plane to use + *\param p pointer to ^point to check + *\return the distance between the place and the point + */ +Fixed gf_plane_get_distance(GF_Plane *plane, GF_Vec *p); +/*!\brief closest point on a line + * + *Gets the closest point on a line from a given point in space + *\param line_pt a point of the line to test + *\param line_vec the normalized direction vector of the line + *\param pt the point to check + *\return the closest point on the line to the desired point + */ +GF_Vec gf_closest_point_to_line(GF_Vec line_pt, GF_Vec line_vec, GF_Vec pt); +/*!\brief box p-vertex index + * + *Gets the p-vertex index for a given plane. The p-vertex index is the index of the closest vertex of a bounding box to the plane. The vertices of a box are always + *ordered in GPAC? cf \ref gf_bbox_get_vertices + *\param p the plane to check + *\return the p-vertex index value, ranging from 0 to 7 +*/ +u32 gf_plane_get_p_vertex_idx(GF_Plane *p); +/*!\brief plane line intersection + * + *Checks for the intersection of a plane and a line + *\param plane plane to test + *\param linepoint a point on the line to test + *\param linevec normalized direction vector of the line to test + *\param outPoint optional pointer to retrieve the intersection point, NULL otherwise + *\return 1 if line and plane intersect, 0 otherwise +*/ +Bool gf_plane_intersect_line(GF_Plane *plane, GF_Vec *linepoint, GF_Vec *linevec, GF_Vec *outPoint); + +/*!Classification types for box/plane position used in \ref gf_bbox_plane_relation*/ +enum +{ + /*!box is in front of the plane*/ + GF_BBOX_FRONT, + /*!box intersects the plane*/ + GF_BBOX_INTER, + /*!box is back of the plane*/ + GF_BBOX_BACK +}; +/*!\brief box-plane relation + * + *Gets the spatial relation between a box and a plane + *\param box the box to check + *\param p the plane to check + *\return the relation type + */ +u32 gf_bbox_plane_relation(GF_BBox *box, GF_Plane *p); + +/*!\brief 3D Ray + * + *The 3D ray object is used in GPAC for all collision and mouse interaction tests +*/ +typedef struct +{ + /*!origin point of the ray*/ + GF_Vec orig; + /*!normalized direction vector of the ray*/ + GF_Vec dir; +} GF_Ray; + +/*!\brief ray constructor + * + *Constructs a ray object + *\param start starting point of the ray + *\param end end point of the ray, or any point on the ray + *\return the ray object +*/ +GF_Ray gf_ray(GF_Vec start, GF_Vec end); +/*!\brief matrix ray transformation + * + *Transforms a ray by a given transformation matrix + *\param mx the matrix to use + *\param r pointer to the ray. Once the function is called, contains the transformed ray +*/ +void gf_mx_apply_ray(GF_Matrix *mx, GF_Ray *r); +/*!\brief ray box intersection test + * + *Checks if a ray intersects a box or not + *\param ray the ray to check + *\param min_edge the minimum edge of the box to check + *\param max_edge the maximum edge of the box to check + *\param out_point optional location of a 3D point to store the intersection, NULL otherwise. + *\return retuns 1 if the ray intersects the box, 0 otherwise +*/ +Bool gf_ray_hit_box(GF_Ray *ray, GF_Vec min_edge, GF_Vec max_edge, GF_Vec *out_point); +/*!\brief ray sphere intersection test + * + *Checks if a ray intersects a box or not + *\param ray the ray to check + *\param center the center of the sphere to check. If NULL, the origin (0,0,0)is used + *\param radius the radius of the sphere to check + *\param out_point optional location of a 3D point to store the intersection, NULL otherwise + *\return retuns 1 if the ray intersects the sphere, 0 otherwise +*/ +Bool gf_ray_hit_sphere(GF_Ray *ray, GF_Vec *center, Fixed radius, GF_Vec *out_point); +/*!\brief ray triangle intersection test + * + *Checks if a ray intersects a triangle or not + *\param ray the ray to check + *\param v0 first vertex of the triangle + *\param v1 second vertex of the triangle + *\param v2 third vertex of the triangle + *\param dist optional location of a fixed number to store the intersection distance from ray origin if any, NULL otherwise + *\return retuns 1 if the ray intersects the triangle, 0 otherwise +*/ +Bool gf_ray_hit_triangle(GF_Ray *ray, GF_Vec *v0, GF_Vec *v1, GF_Vec *v2, Fixed *dist); +/*same as above and performs backface cull (solid meshes)*/ +/*!\brief ray triangle intersection test + * + *Checks if a ray intersects a triangle or not, performing backface culling. For parameters details, look at \ref gf_ray_hit_triangle_backcull + */ +Bool gf_ray_hit_triangle_backcull(GF_Ray *ray, GF_Vec *v0, GF_Vec *v1, GF_Vec *v2, Fixed *dist); + +/*! @} */ + +/*! @} */ + +#ifdef __cplusplus +} +#endif + + +#endif /*_GF_MATH_H_*/ + diff --git a/src/gpacmp4/gpac/media_tools.h b/src/gpacmp4/gpac/media_tools.h new file mode 100644 index 00000000..a1ba49cf --- /dev/null +++ b/src/gpacmp4/gpac/media_tools.h @@ -0,0 +1,395 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / Authoring Tools sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef _GF_MEDIA_H_ +#define _GF_MEDIA_H_ + + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + + +#ifndef GPAC_DISABLE_ISOM +/*creates (if needed) a GF_ESD for the given track - THIS IS RESERVED for local playback +only, since the OTI used when emulated is not standard...*/ +GF_ESD *gf_media_map_esd(GF_ISOFile *mp4, u32 track); +#endif + + +#define GF_IMPORT_DEFAULT_FPS 25.0 + +#ifndef GPAC_DISABLE_MEDIA_IMPORT + +/* + track importers + + All these can import a file into a dedicated track. If esd is NULL the track is blindly added + otherwise it is added with the requested ESID if non-0, otherwise the new trackID is stored in ESID + if use_data_ref is set, data is only referenced in the file + if duration is not 0, only the first duration seconds are imported + NOTE: if an ESD is specified, its decoderSpecificInfo is also updated +*/ +/*track importer flags*/ +enum +{ + /*references data rather than copy, whenever possible*/ + GF_IMPORT_USE_DATAREF = 1, + /*for AVI video: imports at constant FPS (eg imports N-Vops due to encoder drops)*/ + GF_IMPORT_NO_FRAME_DROP = 1<<1, + /*for CMP ASP only: forces treating the input as packed bitsream and discards all n-vops*/ + GF_IMPORT_FORCE_PACKED = 1<<2, + /*for AAC audio: forces SBR mode with implicit signaling (backward compatible)*/ + GF_IMPORT_SBR_IMPLICIT = 1<<3, + /*for AAC audio: forces SBR mode with explicit signaling (non-backward compatible). + Will override GF_IMPORT_SBR_IMPLICIT flag when set*/ + GF_IMPORT_SBR_EXPLICIT = 1<<4, + /*forces MPEG-4 import - some 3GP2 streams have both native IsoMedia sample description and MPEG-4 one possible*/ + GF_IMPORT_FORCE_MPEG4 = 1<<5, + /*special flag for text import at run time (never set on probe), indicates to leave the text box empty + so that we dynamically adapt to display size*/ + GF_IMPORT_SKIP_TXT_BOX = 1<<6, + /*indicates to keep unknown tracks from .MOV/.IsoMedia files*/ + GF_IMPORT_KEEP_ALL_TRACKS = 1<<7, + /*uses compact size in .MOV/.IsoMedia files*/ + GF_IMPORT_USE_COMPACT_SIZE = 1<<8, + /*don't add a final empty sample when importing text tracks from srt*/ + GF_IMPORT_NO_TEXT_FLUSH = 1<<9, + /*for SVC video: forces explicit SVC signaling */ + GF_IMPORT_SVC_EXPLICIT = 1<<10, + /*for SVC video: removes all SVC extensions*/ + GF_IMPORT_SVC_NONE = 1<<11, + + /*for AAC audio: forces PS mode with implicit signaling (backward compatible)*/ + GF_IMPORT_PS_IMPLICIT = 1<<12, + /*for AAC audio: forces PS mode with explicit signaling (non-backward compatible). + Will override GF_IMPORT_PS_IMPLICIT flag when set*/ + GF_IMPORT_PS_EXPLICIT = 1<<13, + + /* oversampled SBR */ + GF_IMPORT_OVSBR = 1<<14, + + /* set subsample information with SVC*/ + GF_IMPORT_SET_SUBSAMPLES = 1<<15, + + + /*when set, only updates tracks info and return*/ + GF_IMPORT_PROBE_ONLY = 1<<20, + /*only set when probing, signals several frames per sample possible*/ + GF_IMPORT_3GPP_AGGREGATION = 1<<21, + /*only set when probing, signals video FPS overridable*/ + GF_IMPORT_OVERRIDE_FPS = 1<<22, + /*only set when probing, signals duration not usable*/ + GF_IMPORT_NO_DURATION = 1<<23, + /*when set IP packets found in MPE sections will be sent to the local network */ + GF_IMPORT_MPE_DEMUX = 1<<24, + /*when set by user during import, will abort*/ + GF_IMPORT_DO_ABORT = 1<<31 +}; + + + +#define GF_IMPORT_AUTO_FPS 10000.0 + +#define GF_IMPORT_MAX_TRACKS 100 +struct __track_video_info +{ + u32 width, height, par; + Double FPS; +}; +struct __track_audio_info +{ + u32 sample_rate, nb_channels; +}; + +struct __track_import_info +{ + u32 track_num; + /*track type (GF_ISOM_MEDIA_****)*/ + u32 type; + /*media type ('MPG1', 'MPG2', ISO 4CC, AVI 4CC)*/ + u32 media_type; + /*possible import flags*/ + u32 flags; + /*media format info*/ + struct __track_video_info video_info; + struct __track_audio_info audio_info; + + u32 lang; + /*for MPEG4 on MPEG-2 TS: mpeg4 ES-ID*/ + u32 mpeg4_es_id; + /*for MPEG2 TS: program number*/ + u16 prog_num; +}; + +struct __program_import_info +{ + u32 number; + char name[40]; +}; + +/*track dumper*/ +typedef struct __track_import +{ + GF_ISOFile *dest; + /*media to import: + MP4/ISO media: trackID + AVI files: + 0: first video and first audio, + 1: video track + 2->any: audio track(s) + MPEG-PS files with N video streams: + 0: first video and first audio + 1->N: video track + N+1->any: audio track + TrackNums can be obtain with probing + */ + u32 trackID; + /*media source - selects importer type based on extension*/ + char *in_name; + /*import duration if any*/ + u32 duration; + /*importer flags*/ + u32 flags; + /*if non 0, force video FPS (CMP, AVI, OGG, H264) - also used by SUB import*/ + Double video_fps; + /*optional ESD*/ + GF_ESD *esd; + /*optional format indication for media source (used in IM1)*/ + char *streamFormat; + /*frame per sample cumulation (3GP media only) - MAX 15, ignored in data ref*/ + u32 frames_per_sample; + /*track ID of imported media in destination file*/ + u32 final_trackID; + /*optional format indication for media source (used in IM1)*/ + char *force_ext; + + /*for MP4 import only*/ + GF_ISOFile *orig; + + /*for text import*/ + u32 fontSize; + char *fontName; + u32 twidth, theight; + + /*Initial offset of the first AU to import*/ + Double initial_time_offset; + + /*number of tracks after probing - may be set to 0, in which case no track + selection can be performed. It may also be inaccurate if probing doesn't + detect all available tracks (cf ogg import)*/ + u32 nb_tracks; + /*track info after probing (GF_IMPORT_PROBE_ONLY set).*/ + struct __track_import_info tk_info[GF_IMPORT_MAX_TRACKS]; + + /*for MPEG-TS and similar: program names*/ + u32 nb_progs; + struct __program_import_info pg_info[GF_IMPORT_MAX_TRACKS]; + + GF_Err last_error; +} GF_MediaImporter; + +GF_Err gf_media_import(GF_MediaImporter *importer); + + +/*adds chapter info contained in file - import_fps is optional (most formats don't use it), defaults to 25*/ +GF_Err gf_media_import_chapters(GF_ISOFile *file, char *chap_file, Double import_fps); + + +/*save file as fragmented movie +@dash_mode: 0 = DASH not used, 1 = DASH used without GOP spliting, 2 = DASH used with GOP spliting, */ +GF_Err gf_media_fragment_file(GF_ISOFile *input, char *output_file, Double max_duration_sec, u32 dash_mode, Double dash_duration_sec, char *seg_rad_name, char *seg_ext, s32 fragments_per_sidx, Bool daisy_chain_sidx, Bool use_url_template, const char *dash_ctx); + +/*make the file ISMA compliant: creates ISMA BIFS / OD tracks if needed, and update audio/video IDs +the file should not contain more than one audio and one video track +@keepImage: if set, generates system info if image is found - only used for image imports +*/ +GF_Err gf_media_make_isma(GF_ISOFile *mp4file, Bool keepESIDs, Bool keepImage, Bool no_ocr); + +/*make the file 3GP compliant && sets profile +*/ +GF_Err gf_media_make_3gpp(GF_ISOFile *mp4file); + +/*make the file playable on a PSP +*/ +GF_Err gf_media_make_psp(GF_ISOFile *mp4file); + +/*changes pixel aspect ratio for visual tracks if supported. Negative values remove any PAR info*/ +GF_Err gf_media_change_par(GF_ISOFile *file, u32 track, s32 ar_num, s32 ar_den); + +/*changes the profile (if not 0) and level (if not 0) indication of the media - only AVC/H264 supported for now*/ +GF_Err gf_media_change_pl(GF_ISOFile *file, u32 track, u32 profile, u32 level); + +/*rewrite AVC samples if nalu size_length has to be changed*/ +GF_Err gf_media_avc_rewrite_samples(GF_ISOFile *file, u32 track, u32 prev_size_in_bits, u32 new_size_in_bits); + +#endif /*GPAC_DISABLE_MEDIA_IMPORT*/ + + + +#ifndef GPAC_DISABLE_MEDIA_EXPORT + +enum +{ + /*track dumper types are formatted as flags for conveniency for + authoring tools, but never used as a OR'ed set*/ + /*native format (JPG, PNG, MP3, etc) if supported*/ + GF_EXPORT_NATIVE = 1, + /*raw samples (including hint tracks for rtp)*/ + GF_EXPORT_RAW_SAMPLES = (1<<1), + /*NHNT format (any MPEG-4 media)*/ + GF_EXPORT_NHNT = (1<<2), + /*AVI (MPEG4 video and AVC tracks only)*/ + GF_EXPORT_AVI = (1<<3), + /*MP4 (all except OD)*/ + GF_EXPORT_MP4 = (1<<4), + /*AVI->RAW to dump video (trackID=1) or audio (trackID>=2)*/ + GF_EXPORT_AVI_NATIVE = (1<<5), + /*NHML format (any media)*/ + GF_EXPORT_NHML = (1<<6), + /*SAF format*/ + GF_EXPORT_SAF = (1<<7), + + /*following ones are real flags*/ + /* + for MP4 extraction, indicates track should be added to dest file if any + for raw extraction, indicates data shall be appended at the end of output file if present + */ + GF_EXPORT_MERGE = (1<<10), + /*indicates QCP file format possible as well as native (EVRC and SMV audio only)*/ + GF_EXPORT_USE_QCP = (1<<11), + /*indicates full NHML dump*/ + GF_EXPORT_NHML_FULL = (1<<11), + /*ony probes extraction format*/ + GF_EXPORT_PROBE_ONLY = (1<<30), + /*when set by user during export, will abort*/ + GF_EXPORT_DO_ABORT = (1<<31), +}; + +/*track dumper*/ +typedef struct __track_exporter +{ + GF_ISOFile *file; + u32 trackID; + /*sample number to export for GF_EXPORT_RAW_SAMPLES only*/ + u32 sample_num; + /*out name WITHOUT extension*/ + char *out_name; + /*dump type*/ + u32 flags; + /*non-IsoMedia file (AVI)*/ + char *in_name; +} GF_MediaExporter; + +/*if error returns same value as error signaled in message*/ +GF_Err gf_media_export(GF_MediaExporter *dump); + +#endif /*GPAC_DISABLE_MEDIA_EXPORT*/ + + +#ifndef GPAC_DISABLE_ISOM_HINTING +/* + RTP IsoMedia file hinting +*/ +typedef struct __tag_isom_hinter GF_RTPHinter; + +GF_RTPHinter *gf_hinter_track_new(GF_ISOFile *file, u32 TrackNum, + u32 Path_MTU, u32 max_ptime, u32 default_rtp_rate, u32 hint_flags, u8 PayloadID, + Bool copy_media, u32 InterleaveGroupID, u8 InterleaveGroupPriority, GF_Err *e); +/*delete the track hinter*/ +void gf_hinter_track_del(GF_RTPHinter *tkHinter); +/*hints all samples in the media track*/ +GF_Err gf_hinter_track_process(GF_RTPHinter *tkHint); +/*returns media bandwidth in kbps*/ +u32 gf_hinter_track_get_bandwidth(GF_RTPHinter *tkHinter); +/*retrieves hinter flags*/ +u32 gf_hinter_track_get_flags(GF_RTPHinter *tkHinter); +/*retrieves rtp payload name + @payloadName: static buffer for retrieval, minimum 30 bytes +*/ +void gf_hinter_track_get_payload_name(GF_RTPHinter *tkHint, char *payloadName); + +/*finalizes hinting process for the track (setup flags, write SDP for RTP, ...) + @AddSystemInfo: if non-0, systems info are duplicated in the SDP (decoder cfg, PL IDs ..) +*/ +GF_Err gf_hinter_track_finalize(GF_RTPHinter *tkHint, Bool AddSystemInfo); + +/*SDP IOD flag*/ +enum +{ + /*no IOD included*/ + GF_SDP_IOD_NONE = 0, + /*base64 encoding of the regular MPEG-4 IOD*/ + GF_SDP_IOD_REGULAR, + /*base64 encoding of IOD containing BIFS and OD tracks (one AU only) - this is used for ISMA 1.0 profiles + note that the "hinted" file will loose all systems info*/ + GF_SDP_IOD_ISMA, + /*same as ISMA but removes all clock references from IOD*/ + GF_SDP_IOD_ISMA_STRICT, +}; + +/*finalizes hinting process for the file (setup flags, write SDP for RTP, ...) + @IOD_Profile: see above + @bandwidth: total bandwidth in kbps of all hinted tracks, 0 means no bandwidth info at session level +*/ +GF_Err gf_hinter_finalize(GF_ISOFile *file, u32 IOD_Profile, u32 bandwidth); + + +/*returns TRUE if the encoded data fits in an ESD url - streamType is the systems stream type needed to +signal data mime-type (OD, BIFS or any) */ +Bool gf_hinter_can_embbed_data(char *data, u32 data_size, u32 streamType); + + +#endif /*GPAC_DISABLE_ISOM_HINTING*/ + + +/*SAF Multiplexer object. The multiplexer supports concurencial (multi-threaded) access*/ +typedef struct __saf_muxer GF_SAFMuxer; +/*SAF Multiplexer constructor*/ +GF_SAFMuxer *gf_saf_mux_new(); +/*SAF Multiplexer destructor*/ +void gf_saf_mux_del(GF_SAFMuxer *mux); +/*adds a new stream in the SAF multiplex*/ +GF_Err gf_saf_mux_stream_add(GF_SAFMuxer *mux, u32 stream_id, u32 ts_res, u32 buffersize_db, u8 stream_type, u8 object_type, char *mime_type, char *dsi, u32 dsi_len, char *remote_url); +/*removes a stream from the SAF multiplex*/ +GF_Err gf_saf_mux_stream_rem(GF_SAFMuxer *mux, u32 stream_id); +/*adds an AU to the given stream. !!AU data will be freed by the multiplexer!! +AUs are not reinterleaved based on their CTS, in order to enable audio interleaving +*/ +GF_Err gf_saf_mux_add_au(GF_SAFMuxer *mux, u32 stream_id, u32 CTS, char *data, u32 data_len, Bool is_rap); +/*gets the content of the multiplexer for the given time. +if force_end_of_session is set, this flushes the SAF Session - no more operations will be allowed on the muxer*/ +GF_Err gf_saf_mux_for_time(GF_SAFMuxer *mux, u32 time_ms, Bool force_end_of_session, char **out_data, u32 *out_size); + + +#ifdef __cplusplus +} +#endif + + +#endif /*_GF_MEDIA_H_*/ + diff --git a/src/gpacmp4/gpac/media_tools.h.orig b/src/gpacmp4/gpac/media_tools.h.orig new file mode 100644 index 00000000..a1ba49cf --- /dev/null +++ b/src/gpacmp4/gpac/media_tools.h.orig @@ -0,0 +1,395 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / Authoring Tools sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef _GF_MEDIA_H_ +#define _GF_MEDIA_H_ + + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + + +#ifndef GPAC_DISABLE_ISOM +/*creates (if needed) a GF_ESD for the given track - THIS IS RESERVED for local playback +only, since the OTI used when emulated is not standard...*/ +GF_ESD *gf_media_map_esd(GF_ISOFile *mp4, u32 track); +#endif + + +#define GF_IMPORT_DEFAULT_FPS 25.0 + +#ifndef GPAC_DISABLE_MEDIA_IMPORT + +/* + track importers + + All these can import a file into a dedicated track. If esd is NULL the track is blindly added + otherwise it is added with the requested ESID if non-0, otherwise the new trackID is stored in ESID + if use_data_ref is set, data is only referenced in the file + if duration is not 0, only the first duration seconds are imported + NOTE: if an ESD is specified, its decoderSpecificInfo is also updated +*/ +/*track importer flags*/ +enum +{ + /*references data rather than copy, whenever possible*/ + GF_IMPORT_USE_DATAREF = 1, + /*for AVI video: imports at constant FPS (eg imports N-Vops due to encoder drops)*/ + GF_IMPORT_NO_FRAME_DROP = 1<<1, + /*for CMP ASP only: forces treating the input as packed bitsream and discards all n-vops*/ + GF_IMPORT_FORCE_PACKED = 1<<2, + /*for AAC audio: forces SBR mode with implicit signaling (backward compatible)*/ + GF_IMPORT_SBR_IMPLICIT = 1<<3, + /*for AAC audio: forces SBR mode with explicit signaling (non-backward compatible). + Will override GF_IMPORT_SBR_IMPLICIT flag when set*/ + GF_IMPORT_SBR_EXPLICIT = 1<<4, + /*forces MPEG-4 import - some 3GP2 streams have both native IsoMedia sample description and MPEG-4 one possible*/ + GF_IMPORT_FORCE_MPEG4 = 1<<5, + /*special flag for text import at run time (never set on probe), indicates to leave the text box empty + so that we dynamically adapt to display size*/ + GF_IMPORT_SKIP_TXT_BOX = 1<<6, + /*indicates to keep unknown tracks from .MOV/.IsoMedia files*/ + GF_IMPORT_KEEP_ALL_TRACKS = 1<<7, + /*uses compact size in .MOV/.IsoMedia files*/ + GF_IMPORT_USE_COMPACT_SIZE = 1<<8, + /*don't add a final empty sample when importing text tracks from srt*/ + GF_IMPORT_NO_TEXT_FLUSH = 1<<9, + /*for SVC video: forces explicit SVC signaling */ + GF_IMPORT_SVC_EXPLICIT = 1<<10, + /*for SVC video: removes all SVC extensions*/ + GF_IMPORT_SVC_NONE = 1<<11, + + /*for AAC audio: forces PS mode with implicit signaling (backward compatible)*/ + GF_IMPORT_PS_IMPLICIT = 1<<12, + /*for AAC audio: forces PS mode with explicit signaling (non-backward compatible). + Will override GF_IMPORT_PS_IMPLICIT flag when set*/ + GF_IMPORT_PS_EXPLICIT = 1<<13, + + /* oversampled SBR */ + GF_IMPORT_OVSBR = 1<<14, + + /* set subsample information with SVC*/ + GF_IMPORT_SET_SUBSAMPLES = 1<<15, + + + /*when set, only updates tracks info and return*/ + GF_IMPORT_PROBE_ONLY = 1<<20, + /*only set when probing, signals several frames per sample possible*/ + GF_IMPORT_3GPP_AGGREGATION = 1<<21, + /*only set when probing, signals video FPS overridable*/ + GF_IMPORT_OVERRIDE_FPS = 1<<22, + /*only set when probing, signals duration not usable*/ + GF_IMPORT_NO_DURATION = 1<<23, + /*when set IP packets found in MPE sections will be sent to the local network */ + GF_IMPORT_MPE_DEMUX = 1<<24, + /*when set by user during import, will abort*/ + GF_IMPORT_DO_ABORT = 1<<31 +}; + + + +#define GF_IMPORT_AUTO_FPS 10000.0 + +#define GF_IMPORT_MAX_TRACKS 100 +struct __track_video_info +{ + u32 width, height, par; + Double FPS; +}; +struct __track_audio_info +{ + u32 sample_rate, nb_channels; +}; + +struct __track_import_info +{ + u32 track_num; + /*track type (GF_ISOM_MEDIA_****)*/ + u32 type; + /*media type ('MPG1', 'MPG2', ISO 4CC, AVI 4CC)*/ + u32 media_type; + /*possible import flags*/ + u32 flags; + /*media format info*/ + struct __track_video_info video_info; + struct __track_audio_info audio_info; + + u32 lang; + /*for MPEG4 on MPEG-2 TS: mpeg4 ES-ID*/ + u32 mpeg4_es_id; + /*for MPEG2 TS: program number*/ + u16 prog_num; +}; + +struct __program_import_info +{ + u32 number; + char name[40]; +}; + +/*track dumper*/ +typedef struct __track_import +{ + GF_ISOFile *dest; + /*media to import: + MP4/ISO media: trackID + AVI files: + 0: first video and first audio, + 1: video track + 2->any: audio track(s) + MPEG-PS files with N video streams: + 0: first video and first audio + 1->N: video track + N+1->any: audio track + TrackNums can be obtain with probing + */ + u32 trackID; + /*media source - selects importer type based on extension*/ + char *in_name; + /*import duration if any*/ + u32 duration; + /*importer flags*/ + u32 flags; + /*if non 0, force video FPS (CMP, AVI, OGG, H264) - also used by SUB import*/ + Double video_fps; + /*optional ESD*/ + GF_ESD *esd; + /*optional format indication for media source (used in IM1)*/ + char *streamFormat; + /*frame per sample cumulation (3GP media only) - MAX 15, ignored in data ref*/ + u32 frames_per_sample; + /*track ID of imported media in destination file*/ + u32 final_trackID; + /*optional format indication for media source (used in IM1)*/ + char *force_ext; + + /*for MP4 import only*/ + GF_ISOFile *orig; + + /*for text import*/ + u32 fontSize; + char *fontName; + u32 twidth, theight; + + /*Initial offset of the first AU to import*/ + Double initial_time_offset; + + /*number of tracks after probing - may be set to 0, in which case no track + selection can be performed. It may also be inaccurate if probing doesn't + detect all available tracks (cf ogg import)*/ + u32 nb_tracks; + /*track info after probing (GF_IMPORT_PROBE_ONLY set).*/ + struct __track_import_info tk_info[GF_IMPORT_MAX_TRACKS]; + + /*for MPEG-TS and similar: program names*/ + u32 nb_progs; + struct __program_import_info pg_info[GF_IMPORT_MAX_TRACKS]; + + GF_Err last_error; +} GF_MediaImporter; + +GF_Err gf_media_import(GF_MediaImporter *importer); + + +/*adds chapter info contained in file - import_fps is optional (most formats don't use it), defaults to 25*/ +GF_Err gf_media_import_chapters(GF_ISOFile *file, char *chap_file, Double import_fps); + + +/*save file as fragmented movie +@dash_mode: 0 = DASH not used, 1 = DASH used without GOP spliting, 2 = DASH used with GOP spliting, */ +GF_Err gf_media_fragment_file(GF_ISOFile *input, char *output_file, Double max_duration_sec, u32 dash_mode, Double dash_duration_sec, char *seg_rad_name, char *seg_ext, s32 fragments_per_sidx, Bool daisy_chain_sidx, Bool use_url_template, const char *dash_ctx); + +/*make the file ISMA compliant: creates ISMA BIFS / OD tracks if needed, and update audio/video IDs +the file should not contain more than one audio and one video track +@keepImage: if set, generates system info if image is found - only used for image imports +*/ +GF_Err gf_media_make_isma(GF_ISOFile *mp4file, Bool keepESIDs, Bool keepImage, Bool no_ocr); + +/*make the file 3GP compliant && sets profile +*/ +GF_Err gf_media_make_3gpp(GF_ISOFile *mp4file); + +/*make the file playable on a PSP +*/ +GF_Err gf_media_make_psp(GF_ISOFile *mp4file); + +/*changes pixel aspect ratio for visual tracks if supported. Negative values remove any PAR info*/ +GF_Err gf_media_change_par(GF_ISOFile *file, u32 track, s32 ar_num, s32 ar_den); + +/*changes the profile (if not 0) and level (if not 0) indication of the media - only AVC/H264 supported for now*/ +GF_Err gf_media_change_pl(GF_ISOFile *file, u32 track, u32 profile, u32 level); + +/*rewrite AVC samples if nalu size_length has to be changed*/ +GF_Err gf_media_avc_rewrite_samples(GF_ISOFile *file, u32 track, u32 prev_size_in_bits, u32 new_size_in_bits); + +#endif /*GPAC_DISABLE_MEDIA_IMPORT*/ + + + +#ifndef GPAC_DISABLE_MEDIA_EXPORT + +enum +{ + /*track dumper types are formatted as flags for conveniency for + authoring tools, but never used as a OR'ed set*/ + /*native format (JPG, PNG, MP3, etc) if supported*/ + GF_EXPORT_NATIVE = 1, + /*raw samples (including hint tracks for rtp)*/ + GF_EXPORT_RAW_SAMPLES = (1<<1), + /*NHNT format (any MPEG-4 media)*/ + GF_EXPORT_NHNT = (1<<2), + /*AVI (MPEG4 video and AVC tracks only)*/ + GF_EXPORT_AVI = (1<<3), + /*MP4 (all except OD)*/ + GF_EXPORT_MP4 = (1<<4), + /*AVI->RAW to dump video (trackID=1) or audio (trackID>=2)*/ + GF_EXPORT_AVI_NATIVE = (1<<5), + /*NHML format (any media)*/ + GF_EXPORT_NHML = (1<<6), + /*SAF format*/ + GF_EXPORT_SAF = (1<<7), + + /*following ones are real flags*/ + /* + for MP4 extraction, indicates track should be added to dest file if any + for raw extraction, indicates data shall be appended at the end of output file if present + */ + GF_EXPORT_MERGE = (1<<10), + /*indicates QCP file format possible as well as native (EVRC and SMV audio only)*/ + GF_EXPORT_USE_QCP = (1<<11), + /*indicates full NHML dump*/ + GF_EXPORT_NHML_FULL = (1<<11), + /*ony probes extraction format*/ + GF_EXPORT_PROBE_ONLY = (1<<30), + /*when set by user during export, will abort*/ + GF_EXPORT_DO_ABORT = (1<<31), +}; + +/*track dumper*/ +typedef struct __track_exporter +{ + GF_ISOFile *file; + u32 trackID; + /*sample number to export for GF_EXPORT_RAW_SAMPLES only*/ + u32 sample_num; + /*out name WITHOUT extension*/ + char *out_name; + /*dump type*/ + u32 flags; + /*non-IsoMedia file (AVI)*/ + char *in_name; +} GF_MediaExporter; + +/*if error returns same value as error signaled in message*/ +GF_Err gf_media_export(GF_MediaExporter *dump); + +#endif /*GPAC_DISABLE_MEDIA_EXPORT*/ + + +#ifndef GPAC_DISABLE_ISOM_HINTING +/* + RTP IsoMedia file hinting +*/ +typedef struct __tag_isom_hinter GF_RTPHinter; + +GF_RTPHinter *gf_hinter_track_new(GF_ISOFile *file, u32 TrackNum, + u32 Path_MTU, u32 max_ptime, u32 default_rtp_rate, u32 hint_flags, u8 PayloadID, + Bool copy_media, u32 InterleaveGroupID, u8 InterleaveGroupPriority, GF_Err *e); +/*delete the track hinter*/ +void gf_hinter_track_del(GF_RTPHinter *tkHinter); +/*hints all samples in the media track*/ +GF_Err gf_hinter_track_process(GF_RTPHinter *tkHint); +/*returns media bandwidth in kbps*/ +u32 gf_hinter_track_get_bandwidth(GF_RTPHinter *tkHinter); +/*retrieves hinter flags*/ +u32 gf_hinter_track_get_flags(GF_RTPHinter *tkHinter); +/*retrieves rtp payload name + @payloadName: static buffer for retrieval, minimum 30 bytes +*/ +void gf_hinter_track_get_payload_name(GF_RTPHinter *tkHint, char *payloadName); + +/*finalizes hinting process for the track (setup flags, write SDP for RTP, ...) + @AddSystemInfo: if non-0, systems info are duplicated in the SDP (decoder cfg, PL IDs ..) +*/ +GF_Err gf_hinter_track_finalize(GF_RTPHinter *tkHint, Bool AddSystemInfo); + +/*SDP IOD flag*/ +enum +{ + /*no IOD included*/ + GF_SDP_IOD_NONE = 0, + /*base64 encoding of the regular MPEG-4 IOD*/ + GF_SDP_IOD_REGULAR, + /*base64 encoding of IOD containing BIFS and OD tracks (one AU only) - this is used for ISMA 1.0 profiles + note that the "hinted" file will loose all systems info*/ + GF_SDP_IOD_ISMA, + /*same as ISMA but removes all clock references from IOD*/ + GF_SDP_IOD_ISMA_STRICT, +}; + +/*finalizes hinting process for the file (setup flags, write SDP for RTP, ...) + @IOD_Profile: see above + @bandwidth: total bandwidth in kbps of all hinted tracks, 0 means no bandwidth info at session level +*/ +GF_Err gf_hinter_finalize(GF_ISOFile *file, u32 IOD_Profile, u32 bandwidth); + + +/*returns TRUE if the encoded data fits in an ESD url - streamType is the systems stream type needed to +signal data mime-type (OD, BIFS or any) */ +Bool gf_hinter_can_embbed_data(char *data, u32 data_size, u32 streamType); + + +#endif /*GPAC_DISABLE_ISOM_HINTING*/ + + +/*SAF Multiplexer object. The multiplexer supports concurencial (multi-threaded) access*/ +typedef struct __saf_muxer GF_SAFMuxer; +/*SAF Multiplexer constructor*/ +GF_SAFMuxer *gf_saf_mux_new(); +/*SAF Multiplexer destructor*/ +void gf_saf_mux_del(GF_SAFMuxer *mux); +/*adds a new stream in the SAF multiplex*/ +GF_Err gf_saf_mux_stream_add(GF_SAFMuxer *mux, u32 stream_id, u32 ts_res, u32 buffersize_db, u8 stream_type, u8 object_type, char *mime_type, char *dsi, u32 dsi_len, char *remote_url); +/*removes a stream from the SAF multiplex*/ +GF_Err gf_saf_mux_stream_rem(GF_SAFMuxer *mux, u32 stream_id); +/*adds an AU to the given stream. !!AU data will be freed by the multiplexer!! +AUs are not reinterleaved based on their CTS, in order to enable audio interleaving +*/ +GF_Err gf_saf_mux_add_au(GF_SAFMuxer *mux, u32 stream_id, u32 CTS, char *data, u32 data_len, Bool is_rap); +/*gets the content of the multiplexer for the given time. +if force_end_of_session is set, this flushes the SAF Session - no more operations will be allowed on the muxer*/ +GF_Err gf_saf_mux_for_time(GF_SAFMuxer *mux, u32 time_ms, Bool force_end_of_session, char **out_data, u32 *out_size); + + +#ifdef __cplusplus +} +#endif + + +#endif /*_GF_MEDIA_H_*/ + diff --git a/src/gpacmp4/gpac/media_tools.h.rej b/src/gpacmp4/gpac/media_tools.h.rej new file mode 100644 index 00000000..acefa6fa --- /dev/null +++ b/src/gpacmp4/gpac/media_tools.h.rej @@ -0,0 +1,11 @@ +--- src/gpacmp4/gpac/media_tools.h ++++ src/gpacmp4/gpac/media_tools.h +@@ -374,7 +374,7 @@ + /*SAF Multiplexer destructor*/ + void gf_saf_mux_del(GF_SAFMuxer *mux); + /*adds a new stream in the SAF multiplex*/ +-GF_Err gf_saf_mux_stream_add(GF_SAFMuxer *mux, u32 stream_id, u32 ts_res, u32 buffersize_db, u8 stream_type, u8 object_type, char *mime_type, char *dsi, u32 dsi_len, char *remote_url); ++GF_Err gf_saf_mux_stream_add(GF_SAFMuxer *mux, u32 stream_id, u32 ts_res, u32 buffersize_db, u8 ccx_stream_type, u8 object_type, char *mime_type, char *dsi, u32 dsi_len, char *remote_url); + /*removes a stream from the SAF multiplex*/ + GF_Err gf_saf_mux_stream_rem(GF_SAFMuxer *mux, u32 stream_id); + /*adds an AU to the given stream. !!AU data will be freed by the multiplexer!! diff --git a/src/gpacmp4/gpac/mpeg4_odf.h b/src/gpacmp4/gpac/mpeg4_odf.h new file mode 100644 index 00000000..787a542f --- /dev/null +++ b/src/gpacmp4/gpac/mpeg4_odf.h @@ -0,0 +1,1744 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / MPEG-4 Object Descriptor sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef _GF_MPEG4_ODF_H_ +#define _GF_MPEG4_ODF_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include + +/*************************************** + Descriptors Tag +***************************************/ +enum +{ + GF_ODF_OD_TAG = 0x01, + GF_ODF_IOD_TAG = 0x02, + GF_ODF_ESD_TAG = 0x03, + GF_ODF_DCD_TAG = 0x04, + GF_ODF_DSI_TAG = 0x05, + GF_ODF_SLC_TAG = 0x06, + GF_ODF_CI_TAG = 0x07, + GF_ODF_SCI_TAG = 0x08, + GF_ODF_IPI_PTR_TAG = 0x09, + GF_ODF_IPMP_PTR_TAG = 0x0A, + GF_ODF_IPMP_TAG = 0x0B, + GF_ODF_QOS_TAG = 0x0C, + GF_ODF_REG_TAG = 0x0D, + + /*FILE FORMAT RESERVED IDs - NEVER CREATE / USE THESE DESCRIPTORS*/ + GF_ODF_ESD_INC_TAG = 0x0E, + GF_ODF_ESD_REF_TAG = 0x0F, + GF_ODF_ISOM_IOD_TAG = 0x10, + GF_ODF_ISOM_OD_TAG = 0x11, + GF_ODF_ISOM_IPI_PTR_TAG = 0x12, + /*END FILE FORMAT RESERVED*/ + + GF_ODF_EXT_PL_TAG = 0x13, + GF_ODF_PL_IDX_TAG = 0x14, + + GF_ODF_ISO_BEGIN_TAG = 0x15, + GF_ODF_ISO_END_TAG = 0x3F, + + GF_ODF_CC_TAG = 0x40, + GF_ODF_KW_TAG = 0x41, + GF_ODF_RATING_TAG = 0x42, + GF_ODF_LANG_TAG = 0x43, + GF_ODF_SHORT_TEXT_TAG = 0x44, + GF_ODF_TEXT_TAG = 0x45, + GF_ODF_CC_NAME_TAG = 0x46, + GF_ODF_CC_DATE_TAG = 0x47, + GF_ODF_OCI_NAME_TAG = 0x48, + GF_ODF_OCI_DATE_TAG = 0x49, + GF_ODF_SMPTE_TAG = 0x4A, + + GF_ODF_SEGMENT_TAG = 0x4B, + GF_ODF_MEDIATIME_TAG = 0x4C, + + GF_ODF_IPMP_TL_TAG = 0x60, + GF_ODF_IPMP_TOOL_TAG = 0x61, + + GF_ODF_ISO_RES_BEGIN_TAG = 0x62, + GF_ODF_ISO_RES_END_TAG = 0xBF, + + GF_ODF_USER_BEGIN_TAG = 0xC0, + + /*internal descriptor for mux input description*/ + GF_ODF_MUXINFO_TAG = GF_ODF_USER_BEGIN_TAG, + /*internal descriptor for bifs config input description*/ + GF_ODF_BIFS_CFG_TAG = GF_ODF_USER_BEGIN_TAG + 1, + /*internal descriptor for UI config input description*/ + GF_ODF_UI_CFG_TAG = GF_ODF_USER_BEGIN_TAG + 2, + /*internal descriptor for TextConfig description*/ + GF_ODF_TEXT_CFG_TAG = GF_ODF_USER_BEGIN_TAG + 3, + GF_ODF_TX3G_TAG = GF_ODF_USER_BEGIN_TAG + 4, + GF_ODF_ELEM_MASK_TAG = GF_ODF_USER_BEGIN_TAG + 5, + /*internal descriptor for LASeR config input description*/ + GF_ODF_LASER_CFG_TAG = GF_ODF_USER_BEGIN_TAG + 6, + + GF_ODF_USER_END_TAG = 0xFE, + + GF_ODF_OCI_BEGIN_TAG = 0x40, + GF_ODF_OCI_END_TAG = (GF_ODF_ISO_RES_BEGIN_TAG - 1), + + GF_ODF_EXT_BEGIN_TAG = 0x80, + GF_ODF_EXT_END_TAG = 0xFE, + + + /*descriptor for aucilary video data*/ + GF_ODF_AUX_VIDEO_DATA = GF_ODF_EXT_BEGIN_TAG + 1 +}; + + +/*************************************** + Descriptors +***************************************/ + +#define BASE_DESCRIPTOR \ + u8 tag; + +typedef struct +{ + BASE_DESCRIPTOR +} GF_Descriptor; + + +/* default descriptor. + NOTE: The decoderSpecificInfo is used as a default desc with tag 0x05 */ +typedef struct +{ + BASE_DESCRIPTOR + u32 dataLength; + char *data; +} GF_DefaultDescriptor; + +/*Object Descriptor*/ +typedef struct +{ + BASE_DESCRIPTOR + GF_List *ipmp_tools; +} GF_IPMP_ToolList; + +/*ObjectDescriptor*/ +typedef struct +{ + BASE_DESCRIPTOR + u16 objectDescriptorID; + char *URLString; + GF_List *ESDescriptors; + GF_List *OCIDescriptors; + /*includes BOTH IPMP_DescriptorPointer (IPMP & IPMPX) and GF_IPMP_Descriptor (IPMPX only)*/ + GF_List *IPMP_Descriptors; + GF_List *extensionDescriptors; + /*MPEG-2 (or other service mux formats) service ID*/ + u16 ServiceID; + /*pointer to the service interface (GF_InputService) of the service having declared the object + only used for DASH*/ + void *service_ifce; +} GF_ObjectDescriptor; + +/*GF_InitialObjectDescriptor - WARNING: even though the bitstream IOD is not +a bit extension of OD, internally it is a real overclass of OD +we usually typecast IOD to OD when flags are not needed !!!*/ +typedef struct +{ + BASE_DESCRIPTOR + u16 objectDescriptorID; + char *URLString; + GF_List *ESDescriptors; + GF_List *OCIDescriptors; + /*includes BOTH IPMP_DescriptorPointer (IPMP & IPMPX) and GF_IPMP_Descriptor (IPMPX only)*/ + GF_List *IPMP_Descriptors; + GF_List *extensionDescriptors; + /*MPEG-2 (or other service mux formats) service ID*/ + u16 ServiceID; + /*pointer to the service interface (GF_InputService) of the service having declared the object + only used for DASH*/ + void *service_ifce; + + /*IOD extensions*/ + u8 inlineProfileFlag; + u8 OD_profileAndLevel; + u8 scene_profileAndLevel; + u8 audio_profileAndLevel; + u8 visual_profileAndLevel; + u8 graphics_profileAndLevel; + + GF_IPMP_ToolList *IPMPToolList; +} GF_InitialObjectDescriptor; + +/*File Format Object Descriptor*/ +typedef struct +{ + BASE_DESCRIPTOR + u16 objectDescriptorID; + char *URLString; + GF_List *ES_ID_RefDescriptors; + GF_List *OCIDescriptors; + GF_List *IPMP_Descriptors; + GF_List *extensionDescriptors; + GF_List *ES_ID_IncDescriptors; +} GF_IsomObjectDescriptor; + +/*File Format Initial Object Descriptor - same remark as IOD*/ +typedef struct +{ + BASE_DESCRIPTOR + u16 objectDescriptorID; + char *URLString; + GF_List *ES_ID_RefDescriptors; + GF_List *OCIDescriptors; + GF_List *IPMP_Descriptors; + GF_List *extensionDescriptors; + GF_List *ES_ID_IncDescriptors; + + u8 inlineProfileFlag; + u8 OD_profileAndLevel; + u8 scene_profileAndLevel; + u8 audio_profileAndLevel; + u8 visual_profileAndLevel; + u8 graphics_profileAndLevel; + + GF_IPMP_ToolList *IPMPToolList; +} GF_IsomInitialObjectDescriptor; + + +/*File Format ES Descriptor for IOD*/ +typedef struct { + BASE_DESCRIPTOR + u32 trackID; +} GF_ES_ID_Inc; + +/*File Format ES Descriptor for OD*/ +typedef struct { + BASE_DESCRIPTOR + u16 trackRef; +} GF_ES_ID_Ref; + +/*Decoder config Descriptor*/ +typedef struct +{ + BASE_DESCRIPTOR + /*coded on 8 bit, but we use 32 bits for internal signaling in GPAC to enable usage of 4CC*/ + u32 objectTypeIndication; + u8 streamType; + u8 upstream; + u32 bufferSizeDB; + u32 maxBitrate; + u32 avgBitrate; + GF_DefaultDescriptor *decoderSpecificInfo; + + /*placeholder for RVC decoder config if any*/ + u16 predefined_rvc_config; + GF_DefaultDescriptor *rvc_config; + + GF_List *profileLevelIndicationIndexDescriptor; + /*pass through data for some modules*/ + void *udta; +} GF_DecoderConfig; + + +/*Content Identification Descriptor*/ +typedef struct { + BASE_DESCRIPTOR + u8 compatibility; + u8 protectedContent; + u8 contentTypeFlag; + u8 contentIdentifierFlag; + u8 contentType; + u8 contentIdentifierType; + /*international code string*/ + char *contentIdentifier; +} GF_CIDesc; + +/*Supplementary Content Identification Descriptor)*/ +typedef struct { + BASE_DESCRIPTOR + u32 languageCode; + char *supplContentIdentifierTitle; + char *supplContentIdentifierValue; +} GF_SCIDesc; + +/*IPI (Intelectual Property Identification) Descriptor Pointer*/ +typedef struct { + BASE_DESCRIPTOR + u16 IPI_ES_Id; +} GF_IPIPtr; + +/*IPMP Descriptor Pointer*/ +typedef struct { + BASE_DESCRIPTOR + u8 IPMP_DescriptorID; + u16 IPMP_DescriptorIDEx; + u16 IPMP_ES_ID; +} GF_IPMPPtr; + +/*IPMPX control points*/ +enum +{ + /*no control point*/ + IPMP_CP_NONE = 0, + /*control point between DB and decoder*/ + IPMP_CP_DB = 1, + /*control point between decoder and CB*/ + IPMP_CP_CB = 2, + /*control point between CB and render*/ + IPMP_CP_CM = 3, + /*control point in BIFS tree (???)*/ + IPMP_CP_BIFS = 4 + /*the rest is reserved or forbidden(0xFF)*/ +}; + +/*IPMPX base classe*/ +#define GF_IPMPX_BASE \ + u8 tag; \ + u8 version; \ + u32 dataID; \ + +typedef struct +{ + GF_IPMPX_BASE +} GF_GF_IPMPX_Base; + +/*IPMP descriptor*/ +typedef struct { + BASE_DESCRIPTOR + u8 IPMP_DescriptorID; + u16 IPMPS_Type; + /*if IPMPS_Type=0, NULL-terminated URL, else if IPMPS_Type is not IPMPX, opaque data*/ + char *opaque_data; + /*if IPMPS_Type=0, irrelevant (strlen(URL)), else if IPMPS_Type is not IPMPX, opaque data size*/ + u32 opaque_data_size; + + /*IPMPX specific*/ + u16 IPMP_DescriptorIDEx; + bin128 IPMP_ToolID; + u8 control_point; + u8 cp_sequence_code; + GF_List *ipmpx_data; +} GF_IPMP_Descriptor; + + +/*IPMPTool*/ +#define MAX_IPMP_ALT_TOOLS 20 +typedef struct +{ + BASE_DESCRIPTOR + bin128 IPMP_ToolID; + /*if set, this is an alternate tool*/ + u32 num_alternate; + bin128 specificToolID[MAX_IPMP_ALT_TOOLS]; + + struct _tagIPMPXParamDesc *toolParamDesc; + char *tool_url; +} GF_IPMP_Tool; + + +/* Elementary Mask of Bifs Config - parsing only */ +typedef struct { + BASE_DESCRIPTOR + u32 node_id; /* referenced nodeID */ + char *node_name; /* referenced node name */ +} GF_ElementaryMask; + +/*BIFSConfig - parsing only, STORED IN ESD:DCD:DSI*/ +typedef struct __tag_bifs_config +{ + BASE_DESCRIPTOR + u32 version; + u16 nodeIDbits; + u16 routeIDbits; + u16 protoIDbits; + Bool pixelMetrics; + u16 pixelWidth, pixelHeight; + /*BIFS-Anim stuff*/ + Bool randomAccess; + GF_List *elementaryMasks; + /*internal extensions for encoding*/ + Bool useNames; +} GF_BIFSConfig; + +/*flags for style*/ +enum +{ + GF_TXT_STYLE_NORMAL = 0, + GF_TXT_STYLE_BOLD = 1, + GF_TXT_STYLE_ITALIC = 2, + GF_TXT_STYLE_UNDERLINED = 4 +}; + +typedef struct +{ + u16 startCharOffset; + u16 endCharOffset; + u16 fontID; + u8 style_flags; + u8 font_size; + /*ARGB*/ + u32 text_color; +} GF_StyleRecord; + +typedef struct +{ + u16 fontID; + char *fontName; +} GF_FontRecord; + +typedef struct +{ + s16 top, left, bottom, right; +} GF_BoxRecord; + +/*scroll flags*/ +enum +{ + GF_TXT_SCROLL_CREDITS = 0, + GF_TXT_SCROLL_MARQUEE = 1, + GF_TXT_SCROLL_DOWN = 2, + GF_TXT_SCROLL_RIGHT = 3 +}; + +/* display flags*/ +enum +{ + GF_TXT_SCROLL_IN = 0x00000020, + GF_TXT_SCROLL_OUT = 0x00000040, + /*use one of the scroll flags, eg GF_TXT_SCROLL_DIRECTION | GF_TXT_SCROLL_CREDITS*/ + GF_TXT_SCROLL_DIRECTION = 0x00000180, + GF_TXT_KARAOKE = 0x00000800, + GF_TXT_VERTICAL = 0x00020000, + GF_TXT_FILL_REGION = 0x00040000 +}; + +typedef struct +{ + /*this is defined as a descriptor for parsing*/ + BASE_DESCRIPTOR + + u32 displayFlags; + /*left, top: 0 - centered: 1 - bottom, right: -1*/ + s8 horiz_justif, vert_justif; + /*ARGB*/ + u32 back_color; + GF_BoxRecord default_pos; + GF_StyleRecord default_style; + + u32 font_count; + GF_FontRecord *fonts; + + /*unused in isomedia but needed for streamingText*/ + u8 sample_index; +} GF_TextSampleDescriptor; + +typedef struct +{ + BASE_DESCRIPTOR + /*only 0x10 shall be used for 3GP text stream*/ + u8 Base3GPPFormat; + /*only 0x10 shall be used for StreamingText*/ + u8 MPEGExtendedFormat; + /*only 0x10 shall be used for StreamingText (base profile, base level)*/ + u8 profileLevel; + u32 timescale; + /*0 forbidden, 1: out-of-band desc only, 2: in-band desc only, 3: both*/ + u8 sampleDescriptionFlags; + /*More negative layer values are towards the viewer*/ + s16 layer; + /*text track width & height*/ + u16 text_width; + u16 text_height; + /*compatible 3GP formats, same coding as 3GPPBaseFormat*/ + u8 nb_compatible_formats; + u8 compatible_formats[20]; + /*defined in isomedia.h*/ + GF_List *sample_descriptions; + + /*if true info below are valid (cf 3GPP for their meaning)*/ + Bool has_vid_info; + u16 video_width; + u16 video_height; + s16 horiz_offset; + s16 vert_offset; +} GF_TextConfig; + + +/*MuxInfo descriptor - parsing only, stored in ESD:extDescr*/ +typedef struct { + BASE_DESCRIPTOR + /*input location*/ + char *file_name; + /*input groupID for interleaving*/ + u32 GroupID; + /*input stream format (not required, guessed from file_name)*/ + char *streamFormat; + /*time offset in ms from first TS (appends an edit list in mp4)*/ + u32 startTime; + + /*media length to import in ms (from 0)*/ + u32 duration; + + /*SRT/SUB import extensions - only support for text and italic style*/ + char *textNode; + char *fontNode; + + /*video and SUB import*/ + Double frame_rate; + + /*same as importer flags, cf media.h*/ + u32 import_flags; + + /*indicates input file shall be destryed - used during SWF import*/ + Bool delete_file; + + /*carousel configuration*/ + u32 carousel_period_plus_one; + u16 aggregate_on_esid; +} GF_MuxInfo; + +typedef struct +{ + BASE_DESCRIPTOR + /*input type*/ + char *deviceName; + /*string sensor terminaison (validation) char*/ + char termChar; + /*string sensor deletion char*/ + char delChar; + /*device-specific data*/ + char *ui_data; + u32 ui_data_length; +} GF_UIConfig; + +/*LASERConfig - parsing only, STORED IN ESD:DCD:DSI*/ +typedef struct __tag_laser_config +{ + BASE_DESCRIPTOR + u8 profile; + u8 level; + u8 pointsCodec; + u8 pathComponents; + u8 fullRequestHost; + u16 time_resolution; + u8 colorComponentBits; + s8 resolution; + u8 coord_bits; + u8 scale_bits_minus_coord_bits; + u8 newSceneIndicator; + u8 extensionIDBits; + + /*the rest of the structure is never coded, only used for the config of GPAC...*/ + Bool force_string_ids;/*forces all nodes to be defined with string IDs*/ +} GF_LASERConfig; + + +/*************************************** + QoS Tags +***************************************/ +enum +{ + QoSMaxDelayTag = 0x01, + QoSPrefMaxDelayTag = 0x02, + QoSLossProbTag = 0x03, + QoSMaxGapLossTag = 0x04, + QoSMaxAUSizeTag = 0x41, + QoSAvgAUSizeTag = 0x42, + QoSMaxAURateTag = 0x43 +}; + +/*************************************** + QoS Qualifiers +***************************************/ +typedef struct { + BASE_DESCRIPTOR + u8 predefined; + GF_List *QoS_Qualifiers; +} GF_QoS_Descriptor; + + +#define QOS_BASE_QUALIFIER \ + u8 tag; \ + u32 size; + +typedef struct { + QOS_BASE_QUALIFIER +} GF_QoS_Default; + +typedef struct { + QOS_BASE_QUALIFIER + u32 MaxDelay; +} GF_QoS_MaxDelay; + +typedef struct { + QOS_BASE_QUALIFIER + u32 PrefMaxDelay; +} GF_QoS_PrefMaxDelay; + +typedef struct { + QOS_BASE_QUALIFIER + Float LossProb; +} GF_QoS_LossProb; + +typedef struct { + QOS_BASE_QUALIFIER + u32 MaxGapLoss; +} GF_QoS_MaxGapLoss; + +typedef struct { + QOS_BASE_QUALIFIER + u32 MaxAUSize; +} GF_QoS_MaxAUSize; + +typedef struct { + QOS_BASE_QUALIFIER + u32 AvgAUSize; +} GF_QoS_AvgAUSize; + +typedef struct { + QOS_BASE_QUALIFIER + u32 MaxAURate; +} GF_QoS_MaxAURate; + +typedef struct { + QOS_BASE_QUALIFIER + u32 DataLength; /*max size class : 2^28 - 1*/ + char *Data; +} GF_QoS_Private; + + +/*Registration Descriptor*/ +typedef struct { + BASE_DESCRIPTOR + u32 formatIdentifier; + u32 dataLength; + char *additionalIdentificationInfo; +} GF_Registration; + +/*Language Descriptor*/ +typedef struct { + BASE_DESCRIPTOR + u32 langCode; +} GF_Language; + +/*Elementary Stream Descriptor*/ +typedef struct +{ + BASE_DESCRIPTOR + u16 ESID; + u16 OCRESID; + u16 dependsOnESID; + u8 streamPriority; + char *URLString; + GF_DecoderConfig *decoderConfig; + GF_SLConfig *slConfig; + GF_IPIPtr *ipiPtr; + GF_QoS_Descriptor *qos; + GF_Registration *RegDescriptor; + /*0 or 1 lang desc*/ + GF_Language *langDesc; + + GF_List *IPIDataSet; + GF_List *IPMPDescriptorPointers; + GF_List *extensionDescriptors; +} GF_ESD; + + +/*Auxiliary Video Data Descriptor*/ +typedef struct { + BASE_DESCRIPTOR + u32 aux_video_type; + u32 position_offset_h; + u32 position_offset_v; + u32 knear; + u32 kfar; + u32 parallax_zero; + u32 parallax_scale; + u32 dref; + u32 wref; +} GF_AuxVideoDescriptor; + +/*Content Classification Descriptor*/ +typedef struct { + BASE_DESCRIPTOR + u32 classificationEntity; + u16 classificationTable; + u32 dataLength; + char *contentClassificationData; +} GF_CCDescriptor; + + +/*this structure is used in GF_KeyWord*/ +typedef struct { + char *keyWord; +} GF_KeyWordItem; + +/*Key Word Descriptor*/ +typedef struct { + BASE_DESCRIPTOR + u32 languageCode; + u8 isUTF8; + GF_List *keyWordsList; +} GF_KeyWord; + +/*Rating Descriptor*/ +typedef struct { + BASE_DESCRIPTOR + u32 ratingEntity; + u16 ratingCriteria; + u32 infoLength; + char *ratingInfo; +} GF_Rating; + + +/*Short Textual Descriptor*/ +typedef struct { + BASE_DESCRIPTOR + u32 langCode; + u8 isUTF8; + char *eventName; + char *eventText; +} GF_ShortTextual; + + +/*this structure is used in GF_ExpandedTextual*/ +typedef struct { + char *text; +} GF_ETD_ItemText; + +/*Expanded Textual Descriptor*/ +typedef struct { + BASE_DESCRIPTOR + u32 langCode; + u8 isUTF8; + GF_List *itemDescriptionList; + GF_List *itemTextList; + char *NonItemText; +} GF_ExpandedTextual; + +/*this structure is used in GF_CC_Name*/ +typedef struct { + u32 langCode; + u8 isUTF8; + char *contentCreatorName; +} GF_ContentCreatorInfo; + +/*Content Creator Name GF_Descriptor +NOTE: the desctructor will delete all the items in the list +(GF_ContentCreatorInfo items) */ +typedef struct { + BASE_DESCRIPTOR + GF_List *ContentCreators; +} GF_CC_Name; + +/*Content Creation Date Descriptor*/ +typedef struct { + BASE_DESCRIPTOR + char contentCreationDate[5]; +} GF_CC_Date; + + +/*this structure is used in GF_OCICreators*/ +typedef struct { + u32 langCode; + u8 isUTF8; + char *OCICreatorName; +} GF_OCICreator_item; + +/*OCI Creator Name Descriptor*/ +typedef struct { + BASE_DESCRIPTOR + GF_List *OCICreators; +} GF_OCICreators; + +/*OCI Creation Date Descriptor*/ +typedef struct { + BASE_DESCRIPTOR + char OCICreationDate[5]; +} GF_OCI_Data; + + +/*this structure is used in GF_SMPTECamera*/ +typedef struct { + u8 paramID; + u32 param; +} GF_SmpteParam; + +/*Smpte Camera Position Descriptor*/ +typedef struct { + BASE_DESCRIPTOR + u8 cameraID; + GF_List *ParamList; +} GF_SMPTECamera; + + +/*Extension Profile Level Descriptor*/ +typedef struct { + BASE_DESCRIPTOR + u8 profileLevelIndicationIndex; + u8 ODProfileLevelIndication; + u8 SceneProfileLevelIndication; + u8 AudioProfileLevelIndication; + u8 VisualProfileLevelIndication; + u8 GraphicsProfileLevelIndication; + u8 MPEGJProfileLevelIndication; +} GF_PLExt; + +/*Profile Level Indication Index Descriptor*/ +typedef struct { + BASE_DESCRIPTOR + u8 profileLevelIndicationIndex; +} GF_PL_IDX; + + +/*AVC config descriptor - not a real MPEG-4 descriptor */ +/*used for sequenceParameterSetNALUnit and pictureParameterSetNALUnit*/ +typedef struct +{ + u16 size; + char *data; + /* used of AVC/SVC detection */ + s32 id; +} GF_AVCConfigSlot; + +typedef struct +{ + u8 configurationVersion; + u8 AVCProfileIndication; + u8 profile_compatibility; + u8 AVCLevelIndication; + u8 nal_unit_size; + + GF_List *sequenceParameterSets; + GF_List *pictureParameterSets; + + /*for SVC*/ + u8 complete_representation; +} GF_AVCConfig; + + +/************************************************************ + Media Control Extensions +************************************************************/ +typedef struct +{ + BASE_DESCRIPTOR + Double startTime; + Double Duration; + char *SegmentName; +} GF_Segment; + +typedef struct +{ + BASE_DESCRIPTOR + Double mediaTimeStamp; +} GF_MediaTime; + + +/**************************************************************************** + + MPEG-4 SYSTEM - OBJECT DESCRIPTORS COMMANDS DECLARATION + +****************************************************************************/ + + +/*************************************** + Commands Tags +***************************************/ +enum +{ + GF_ODF_OD_UPDATE_TAG = 0x01, + GF_ODF_OD_REMOVE_TAG = 0x02, + GF_ODF_ESD_UPDATE_TAG = 0x03, + GF_ODF_ESD_REMOVE_TAG = 0x04, + GF_ODF_IPMP_UPDATE_TAG = 0x05, + GF_ODF_IPMP_REMOVE_TAG = 0x06, + + /*file format reserved*/ + GF_ODF_ESD_REMOVE_REF_TAG = 0x07, + + GF_ODF_COM_ISO_BEGIN_TAG = 0x0D, + GF_ODF_COM_ISO_END_TAG = 0xBF, + + GF_ODF_COM_USER_BEGIN_TAG = 0xC0, + GF_ODF_COM_USER_END_TAG = 0xFE +}; + +/*************************************** + OD commands +***************************************/ +#define BASE_OD_COMMAND \ + u8 tag; + +/*the (abstract) base command. */ +typedef struct { + BASE_OD_COMMAND +} GF_ODCom; + +/*the default bcommand*/ +typedef struct { + BASE_OD_COMMAND + u32 dataSize; + char *data; +} GF_BaseODCom; + +/*Object Descriptor Update +NB: the list can contain OD or IOD, except internally in the File Format (only MP4_OD)*/ +typedef struct +{ + BASE_OD_COMMAND + GF_List *objectDescriptors; +} GF_ODUpdate; + +/*Object Descriptor Remove*/ +typedef struct +{ + BASE_OD_COMMAND + u32 NbODs; + u16 *OD_ID; +} GF_ODRemove; + +/*Elementary Stream Descriptor Update*/ +typedef struct +{ + BASE_OD_COMMAND + u16 ODID; + GF_List *ESDescriptors; +} GF_ESDUpdate; + +/*Elementary Stream Descriptor Remove*/ +typedef struct { + BASE_OD_COMMAND + u16 ODID; + u32 NbESDs; + u16 *ES_ID; +} GF_ESDRemove; + +/*IPMP Descriptor Update*/ +typedef struct { + BASE_OD_COMMAND + GF_List *IPMPDescList; +} GF_IPMPUpdate; + +/*IPMP Descriptor Remove*/ +typedef struct { + BASE_OD_COMMAND + u32 NbIPMPDs; + /*now this is bad: only IPMPv1 descriptors can be removed at run tim...*/ + u8 *IPMPDescID; +} GF_IPMPRemove; + + + + + + +/******************************************************************** + OD Exports +********************************************************************/ + +/*OD CODEC object - just a simple reader/writer*/ +typedef struct tagODCoDec +{ + GF_BitStream *bs; + GF_List *CommandList; +} GF_ODCodec; + + +/*construction / destruction*/ +GF_ODCodec *gf_odf_codec_new(); +void gf_odf_codec_del(GF_ODCodec *codec); +/* add a command to the codec command list. */ +GF_Err gf_odf_codec_add_com(GF_ODCodec *codec, GF_ODCom *command); +/*encode the current command list - once called the commands are removed or destroyed depending on @cleanup_type: + 0: commands are removed from the list but not destroyed + 1: commands are removed from the list and destroyed + 2: commands are kept in the list and not destroyed +if delete_content is set*/ +GF_Err gf_odf_codec_encode(GF_ODCodec *codec, u32 cleanup_type); +/*get the encoded AU. user is responsible of allocated space*/ +GF_Err gf_odf_codec_get_au(GF_ODCodec *codec, char **outAU, u32 *au_length); +/* set the encoded AU to the codec*/ +GF_Err gf_odf_codec_set_au(GF_ODCodec *codec, const char *au, u32 au_length); +/*decode the previously set-up AU*/ +GF_Err gf_odf_codec_decode(GF_ODCodec *codec); +/*get the first OD command in the list. Once called, the command is removed +from the command list. Return NULL when commandList is empty*/ +GF_ODCom *gf_odf_codec_get_com(GF_ODCodec *codec); + +/*apply a command to the codec command list. Command is duplicated if needed +This is used for state maintenance and RAP generation.*/ +GF_Err gf_odf_codec_apply_com(GF_ODCodec *codec, GF_ODCom *command); + +/************************************************************ + GF_ODCom Functions +************************************************************/ + +/*Commands Creation / Destruction*/ +GF_ODCom *gf_odf_com_new(u8 tag); +GF_Err gf_odf_com_del(GF_ODCom **com); + + +/************************************************************ + Descriptors Functions +************************************************************/ + +/*Descriptors Creation / Destruction*/ +GF_Descriptor *gf_odf_desc_new(u8 tag); +void gf_odf_desc_del(GF_Descriptor *desc); + +/*this is a helper for building a preformatted GF_ESD with decoderConfig, decoderSpecificInfo with no data and +SLConfig descriptor with predefined*/ +GF_ESD *gf_odf_desc_esd_new(u32 sl_predefined); + +/*special function for authoring - convert DSI to BIFSConfig*/ +GF_BIFSConfig *gf_odf_get_bifs_config(GF_DefaultDescriptor *dsi, u8 oti); +/*special function for authoring - convert DSI to LASERConfig*/ +GF_Err gf_odf_get_laser_config(GF_DefaultDescriptor *dsi, GF_LASERConfig *cfg); +/*sepcial function for authoring - convert DSI to TextConfig*/ +GF_Err gf_odf_get_text_config(GF_DefaultDescriptor *dsi, u8 oti, GF_TextConfig *cfg); +/*special function for authoring - convert DSI to UIConfig*/ +GF_Err gf_odf_get_ui_config(GF_DefaultDescriptor *dsi, GF_UIConfig *cfg); +/*converts UIConfig to dsi - does not destroy input descr but does create output one*/ +GF_Err gf_odf_encode_ui_config(GF_UIConfig *cfg, GF_DefaultDescriptor **out_dsi); + +/*simple constructor/destructor*/ +GF_AVCConfig *gf_odf_avc_cfg_new(); +void gf_odf_avc_cfg_del(GF_AVCConfig *cfg); +/*gets GF_AVCConfig from MPEG-4 DSI*/ +GF_AVCConfig *gf_odf_avc_cfg_read(char *dsi, u32 dsi_size); +/*writes GF_AVCConfig as MPEG-4 DSI*/ +GF_Err gf_odf_avc_cfg_write(GF_AVCConfig *cfg, char **outData, u32 *outSize); + +/*destroy the descriptors in a list but not the list*/ +GF_Err gf_odf_desc_list_del(GF_List *descList); + +/*use this function to decode a standalone descriptor +the raw descriptor MUST be formatted with tag and size field!!! +a new desc is created and you must delete it when done*/ +GF_Err gf_odf_desc_read(char *raw_desc, u32 descSize, GF_Descriptor * *outDesc); + +/*use this function to encode a standalone descriptor +the desc will be formatted with tag and size field +the output buffer is allocated and you must delete it when done*/ +GF_Err gf_odf_desc_write(GF_Descriptor *desc, char **outEncDesc, u32 *outSize); + +/*use this function to get the size of a standalone descriptor (including tag and size fields) +return 0 if error*/ +u32 gf_odf_desc_size(GF_Descriptor *desc); + +/*this is usefull to duplicate on the fly a descriptor*/ +GF_Err gf_odf_desc_copy(GF_Descriptor *inDesc, GF_Descriptor **outDesc); + +/*This functions handles internally what desc can be added to another desc +and adds it. NO DUPLICATION of the descriptor, so +once a desc is added to its parent, destroying the parent WILL DESTROY +this descriptor*/ +GF_Err gf_odf_desc_add_desc(GF_Descriptor *parentDesc, GF_Descriptor *newDesc); + +/*returns complete textual description of stream*/ +const char *gf_esd_get_textual_description(GF_ESD *esd); + +const char *gf_afx_get_type_description(u8 afx_code); + + +/*Since IPMP V2, we introduce a new set of functions to read / write a list of descriptors +that have no containers (a bit like an OD command, but for descriptors) +This is usefull for IPMPv2 DecoderSpecificInfo which contains a set of IPMP_Declarators +As it could be used for other purposes we keep it generic +you must create the list yourself, the functions just encode/decode from/to the list*/ + +/*uncompress an encoded list of descriptors. You must pass an empty GF_List structure +to know exactly what was in the buffer*/ +GF_Err gf_odf_desc_list_read(char *raw_list, u32 raw_size, GF_List *descList); +/*compress all descriptors in the list into a single buffer. The buffer is allocated +by the lib and must be destroyed by your app +you must pass (outEncList != NULL && *outEncList == NULL)*/ +GF_Err gf_odf_desc_list_write(GF_List *descList, char **outEncList, u32 *outSize); +/*returns size of encoded desc list*/ +GF_Err gf_odf_desc_list_size(GF_List *descList, u32 *outSize); + +/*retuns NULL if unknown, otherwise value*/ +const char *gf_odf_stream_type_name(u32 streamType); +u32 gf_odf_stream_type_by_name(const char *streamType); + + +#ifndef GPAC_MINIMAL_ODF + + +/************************************************************ + QoS Qualifiers Functions +************************************************************/ + +/*QoS Qualifiers Creation / Destruction*/ +GF_QoS_Default *gf_odf_qos_new(u8 tag); +GF_Err gf_odf_qos_del(GF_QoS_Default **qos); + +/*READ/WRITE functions: QoS qualifiers are special descriptors but follow the same rules as descriptors. +therefore, use gf_odf_desc_read and gf_odf_desc_write for QoS*/ + +/*same function, but for QoS, as a Qualifier IS NOT a descriptor*/ +GF_Err gf_odf_qos_add_qualif(GF_QoS_Descriptor *desc, GF_QoS_Default *qualif); + + + +/* + OCI Stream AU is a list of OCI event (like OD AU is a list of OD commands) +*/ + +typedef struct __tag_oci_event OCIEvent; + +OCIEvent *gf_oci_event_new(u16 EventID); +void gf_oci_event_del(OCIEvent *event); + +GF_Err gf_oci_event_set_start_time(OCIEvent *event, u8 Hours, u8 Minutes, u8 Seconds, u8 HundredSeconds, u8 IsAbsoluteTime); +GF_Err gf_oci_event_set_duration(OCIEvent *event, u8 Hours, u8 Minutes, u8 Seconds, u8 HundredSeconds); +GF_Err gf_oci_event_add_desc(OCIEvent *event, GF_Descriptor *oci_desc); + +GF_Err gf_oci_event_get_id(OCIEvent *event, u16 *ID); +GF_Err gf_oci_event_get_start_time(OCIEvent *event, u8 *Hours, u8 *Minutes, u8 *Seconds, u8 *HundredSeconds, u8 *IsAbsoluteTime); +GF_Err gf_oci_event_get_duration(OCIEvent *event, u8 *Hours, u8 *Minutes, u8 *Seconds, u8 *HundredSeconds); +u32 gf_oci_event_get_desc_count(OCIEvent *event); +GF_Descriptor *gf_oci_event_get_desc(OCIEvent *event, u32 DescIndex); +GF_Err gf_oci_event_rem_desc(OCIEvent *event, u32 DescIndex); + + + +typedef struct __tag_oci_codec OCICodec; + +/*construction / destruction +IsEncoder specifies an OCI Event encoder +version is for future extensions, and only 0x01 is valid for now*/ +OCICodec *gf_oci_codec_new(u8 IsEncoder, u8 Version); +void gf_oci_codec_del(OCICodec *codec); + +/* ENCODER FUNCTIONS +add a command to the codec event list. +The event WILL BE DESTROYED BY THE CODEC after encoding*/ +GF_Err gf_oci_codec_add_event(OCICodec *codec, OCIEvent *event); + +/*encode AU. The memory allocation is done in place +WARNING: once this function called, the codec event List is empty +and events destroyed +you must set *outAU = NULL*/ +GF_Err gf_oci_codec_encode(OCICodec *codec, char **outAU, u32 *au_length); + + + +/*Decoder: decode the previously set-up AU +the input buffer is cleared once decoded*/ +GF_Err gf_oci_codec_decode(OCICodec *codec, char *au, u32 au_length); + +/*get the first OCI Event in the list. Once called, the event is removed +from the event list. Return NULL when the event List is empty +you MUST delete events */ +OCIEvent *gf_oci_codec_get_event(OCICodec *codec); + + +#ifndef GPAC_DISABLE_OD_DUMP + +/*OD dump tools*/ +GF_Err gf_odf_dump_au(char *data, u32 dataLength, FILE *trace, u32 indent, Bool XMTDump); +GF_Err gf_odf_dump_com(void *p, FILE *trace, u32 indent, Bool XMTDump); +GF_Err gf_odf_dump_desc(void *ptr, FILE *trace, u32 indent, Bool XMTDump); +GF_Err gf_odf_dump_com_list(GF_List *commandList, FILE *trace, u32 indent, Bool XMTDump); + +/*OCI dump tools*/ +GF_Err gf_oci_dump_event(OCIEvent *ev, FILE *trace, u32 indent, Bool XMTDump); +GF_Err gf_oci_dump_au(u8 version, char *au, u32 au_length, FILE *trace, u32 indent, Bool XMTDump); + +#endif /*GPAC_DISABLE_OD_DUMP*/ + + +#endif /*GPAC_MINIMAL_ODF*/ + +/*OD parsing tools (XMT/BT)*/ +/*returns desc tag based on name*/ +u32 gf_odf_get_tag_by_name(char *descName); + +/*field type for OD/QoS/IPMPX/etc*/ +enum +{ + /*regular type*/ + GF_ODF_FT_DEFAULT = 0, + /*single descriptor type*/ + GF_ODF_FT_OD = 1, + /*descriptor list type*/ + GF_ODF_FT_OD_LIST = 2, + /*IPMP Data type*/ + GF_ODF_FT_IPMPX = 3, + /*IPMP Data list type*/ + GF_ODF_FT_IPMPX_LIST = 4, + /*IPMP ByteArray type*/ + GF_ODF_FT_IPMPX_BA = 5, + /*IPMP ByteArray list type*/ + GF_ODF_FT_IPMPX_BA_LIST = 6 +}; +u32 gf_odf_get_field_type(GF_Descriptor *desc, char *fieldName); + +/*set non-descriptor field value - value string shall be presented without ' or " characters*/ +GF_Err gf_odf_set_field(GF_Descriptor *desc, char *fieldName, char *val); + +#ifndef GPAC_MINIMAL_ODF + + + +/* + IPMPX extensions - IPMP Data only (messages are not supported yet) +*/ + +typedef struct +{ + u32 length; + char *data; +} GF_IPMPX_ByteArray; + +/*IPMPX authentication descriptors*/ +#define GF_IPMPX_AUTH_DESC \ + u8 tag; \ + +typedef struct +{ + GF_IPMPX_AUTH_DESC +} GF_IPMPX_Authentication; + +enum +{ + GF_IPMPX_AUTH_Forbidden_Tag = 0x00, + GF_IPMPX_AUTH_AlgorithmDescr_Tag = 0x01, + GF_IPMPX_AUTH_KeyDescr_Tag = 0x02 +}; + +typedef struct +{ + GF_IPMPX_AUTH_DESC + char *keyBody; + u32 keyBodyLength; +} GF_IPMPX_AUTH_KeyDescriptor; + +typedef struct +{ + GF_IPMPX_AUTH_DESC + /*used if no specAlgoID*/ + u16 regAlgoID; + GF_IPMPX_ByteArray *specAlgoID; + GF_IPMPX_ByteArray *OpaqueData; +} GF_IPMPX_AUTH_AlgorithmDescriptor; + + +/*IPMP data messages*/ +enum +{ + GF_IPMPX_OPAQUE_DATA_TAG = 0x01, + GF_IPMPX_AUDIO_WM_INIT_TAG = 0x02, + GF_IPMPX_VIDEO_WM_INIT_TAG = 0x03, + GF_IPMPX_SEL_DEC_INIT_TAG = 0x04, + GF_IPMPX_KEY_DATA_TAG = 0x05, + GF_IPMPX_AUDIO_WM_SEND_TAG = 0x06, + GF_IPMPX_VIDEO_WM_SEND_TAG = 0x07, + GF_IPMPX_RIGHTS_DATA_TAG = 0x08, + GF_IPMPX_SECURE_CONTAINER_TAG = 0x09, + GF_IPMPX_ADD_TOOL_LISTENER_TAG = 0x0A, + GF_IPMPX_REMOVE_TOOL_LISTENER_TAG = 0x0B, + GF_IPMPX_INIT_AUTHENTICATION_TAG = 0x0C, + GF_IPMPX_MUTUAL_AUTHENTICATION_TAG = 0x0D, + GF_IPMPX_USER_QUERY_TAG = 0x0E, + GF_IPMPX_USER_RESPONSE_TAG = 0x0F, + GF_IPMPX_PARAMETRIC_DESCRIPTION_TAG = 0x10, + GF_IPMPX_PARAMETRIC_CAPS_QUERY_TAG = 0x11, + GF_IPMPX_PARAMETRIC_CAPS_RESPONSE_TAG = 0x12, + /*NO ASSOCIATED STRUCTURE*/ + GF_IPMPX_GET_TOOLS_TAG = 0x13, + GF_IPMPX_GET_TOOLS_RESPONSE_TAG = 0x14, + GF_IPMPX_GET_TOOL_CONTEXT_TAG = 0x15, + GF_IPMPX_GET_TOOL_CONTEXT_RESPONSE_TAG = 0x16, + GF_IPMPX_CONNECT_TOOL_TAG = 0x17, + GF_IPMPX_DISCONNECT_TOOL_TAG = 0x18, + GF_IPMPX_NOTIFY_TOOL_EVENT_TAG = 0x19, + GF_IPMPX_CAN_PROCESS_TAG = 0x1A, + GF_IPMPX_TRUST_SECURITY_METADATA_TAG = 0x1B, + GF_IPMPX_TOOL_API_CONFIG_TAG = 0x1C, + + /*ISMA*/ + GF_IPMPX_ISMACRYP_TAG = 0xD0, + + /*intern ones for parsing (not real datas)*/ + GF_IPMPX_TRUSTED_TOOL_TAG = 0xA1, + GF_IPMPX_TRUST_SPECIFICATION_TAG = 0xA2, + /*emulate algo descriptors as base IPMP classes for parsing...*/ + GF_IPMPX_ALGORITHM_DESCRIPTOR_TAG = 0xA3, + GF_IPMPX_KEY_DESCRIPTOR_TAG = 0xA4, + GF_IPMPX_PARAM_DESCRIPTOR_ITEM_TAG = 0xA5, + GF_IPMPX_SEL_ENC_BUFFER_TAG = 0xA6, + GF_IPMPX_SEL_ENC_FIELD_TAG = 0xA7 +}; + +typedef char GF_IPMPX_Date[5]; + + +#define GF_IPMPX_DATA_BASE \ + u8 tag; \ + u8 Version; \ + u8 dataID; \ + +typedef struct +{ + GF_IPMPX_DATA_BASE +} GF_IPMPX_Data; + +typedef struct +{ + GF_IPMPX_DATA_BASE + u32 Context; + u8 AuthType; +} GF_IPMPX_InitAuthentication; + +/*NOT a real DATA, only used as data for parsing*/ +typedef struct +{ + GF_IPMPX_DATA_BASE + GF_IPMPX_Date startDate; + u8 attackerProfile; + u32 trustedDuration; + GF_IPMPX_ByteArray *CCTrustMetadata; +} GF_IPMPX_TrustSpecification; + +/*NOT a real DATA, only used as data for parsing*/ +typedef struct +{ + GF_IPMPX_DATA_BASE + bin128 toolID; + GF_IPMPX_Date AuditDate; + GF_List *trustSpecifications; +} GF_IPMPX_TrustedTool; + +typedef struct _ipmpx_TrustSecurityMetadata +{ + GF_IPMPX_DATA_BASE + GF_List *TrustedTools; +} GF_IPMPX_TrustSecurityMetadata; + + +typedef struct +{ + GF_IPMPX_DATA_BASE + Bool failedNegotiation; + + GF_List *candidateAlgorithms; + GF_List *agreedAlgorithms; + GF_IPMPX_ByteArray *AuthenticationData; + + /*inclAuthCodes will be set if any of the members is set (cf spec...)*/ + u32 certType; + /*GF_IPMPX_ByteArray list*/ + GF_List *certificates; + GF_IPMPX_AUTH_KeyDescriptor *publicKey; + GF_IPMPX_ByteArray *opaque; + GF_IPMPX_TrustSecurityMetadata *trustData; + GF_IPMPX_ByteArray *authCodes; +} GF_IPMPX_MutualAuthentication; + +typedef struct +{ + GF_IPMPX_DATA_BASE + /*if set MAC is part of the encrypted data*/ + Bool isMACEncrypted; + + GF_IPMPX_ByteArray *encryptedData; + GF_IPMPX_Data *protectedMsg; + GF_IPMPX_ByteArray *MAC; +} GF_IPMPX_SecureContainer; + +typedef struct +{ + GF_List *ipmp_tools; +} GF_IPMPX_GetToolsResponse; + +typedef struct +{ + GF_IPMPX_DATA_BASE + GF_IPMPX_ByteArray *main_class; + GF_IPMPX_ByteArray *subClass; + GF_IPMPX_ByteArray *typeData; + GF_IPMPX_ByteArray *type; + GF_IPMPX_ByteArray *addedData; +} GF_IPMPX_ParametricDescriptionItem; + +typedef struct _tagIPMPXParamDesc +{ + GF_IPMPX_DATA_BASE + GF_IPMPX_ByteArray *descriptionComment; + u8 majorVersion; + u8 minorVersion; + /*list of GF_IPMPX_ParametricDescriptionItem*/ + GF_List *descriptions; +} GF_IPMPX_ParametricDescription; + +typedef struct +{ + GF_IPMPX_DATA_BASE + GF_IPMPX_ParametricDescription *description; +} GF_IPMPX_ToolParamCapabilitiesQuery; + +typedef struct +{ + GF_IPMPX_DATA_BASE + Bool capabilitiesSupported; +} GF_IPMPX_ToolParamCapabilitiesResponse; + + +typedef struct +{ + GF_IPMPX_DATA_BASE + GF_IPMP_Descriptor *toolDescriptor; +} GF_IPMPX_ConnectTool; + +typedef struct +{ + GF_IPMPX_DATA_BASE + u32 IPMP_ToolContextID; +} GF_IPMPX_DisconnectTool; + + +typedef struct +{ + GF_IPMPX_DATA_BASE + u8 scope; + u16 IPMP_DescriptorIDEx; +} GF_IPMPX_GetToolContext; + + +typedef struct +{ + GF_IPMPX_DATA_BASE + u16 OD_ID; + u16 ESD_ID; + u32 IPMP_ToolContextID; +} GF_IPMPX_GetToolContextResponse; + +/*GF_IPMPX_LISTEN_Types*/ +enum +{ + GF_IPMPX_LISTEN_CONNECTED = 0x00, + GF_IPMPX_LISTEN_CONNECTIONFAILED = 0x01, + GF_IPMPX_LISTEN_DISCONNECTED = 0x02, + GF_IPMPX_LISTEN_DISCONNECTIONFAILED = 0x03, + GF_IPMPX_LISTEN_WATERMARKDETECTED = 0x04 +}; + +typedef struct +{ + GF_IPMPX_DATA_BASE + u8 scope; + /*events to listen to*/ + u8 eventTypeCount; + u8 eventType[10]; +} GF_IPMPX_AddToolNotificationListener; + +typedef struct +{ + GF_IPMPX_DATA_BASE + u8 eventTypeCount; + u8 eventType[10]; +} GF_IPMPX_RemoveToolNotificationListener; + +typedef struct +{ + GF_IPMPX_DATA_BASE + u16 OD_ID; + u16 ESD_ID; + u8 eventType; + u32 IPMP_ToolContextID; +} GF_IPMPX_NotifyToolEvent; + +typedef struct +{ + GF_IPMPX_DATA_BASE + Bool canProcess; +} GF_IPMPX_CanProcess; + +typedef struct +{ + GF_IPMPX_DATA_BASE + GF_IPMPX_ByteArray *opaqueData; +} GF_IPMPX_OpaqueData; + + +typedef struct +{ + GF_IPMPX_DATA_BASE + GF_IPMPX_ByteArray *keyBody; + /*flags meaning + hasStartDTS = 1; + hasStartPacketID = 1<<1; + hasExpireDTS = 1<<2; + hasExpirePacketID = 1<<3 + */ + u32 flags; + + u64 startDTS; + u32 startPacketID; + u64 expireDTS; + u32 expirePacketID; + GF_IPMPX_ByteArray *OpaqueData; +} GF_IPMPX_KeyData; + +typedef struct +{ + GF_IPMPX_DATA_BASE + GF_IPMPX_ByteArray *rightsInfo; +} GF_IPMPX_RightsData; + + +/*not a real GF_IPMPX_Data in spec, but emulated as if for parsing*/ +typedef struct +{ + GF_IPMPX_DATA_BASE + bin128 cipher_Id; + u8 syncBoundary; + /*block mode if stream cypher info is NULL*/ + u8 mode; + u16 blockSize; + u16 keySize; + GF_IPMPX_ByteArray *Stream_Cipher_Specific_Init_Info; +} GF_IPMPX_SelEncBuffer; + +/*not a real GF_IPMPX_Data in spec, but emulated as if for parsing*/ +typedef struct +{ + GF_IPMPX_DATA_BASE + u8 field_Id; + u8 field_Scope; + u8 buf; + + u16 mappingTableSize; + u16 *mappingTable; + GF_IPMPX_ByteArray *shuffleSpecificInfo; +} GF_IPMPX_SelEncField; + + +/*mediaTypeExtension*/ +enum +{ + GF_IPMPX_SE_MT_ISO_IEC = 0x00, + GF_IPMPX_SE_MT_ITU = 0x01 + /*the rest is reserved or forbidden*/ +}; + +/*compliance*/ +enum +{ + GF_IPMPX_SE_COMP_FULLY = 0x00, + GF_IPMPX_SE_COMP_VIDEO_PACKETS = 0x01, + GF_IPMPX_SE_COMP_VIDEO_VOP = 0x02, + GF_IPMPX_SE_COMP_VIDEO_NONE = 0x03, + GF_IPMPX_SE_COMP_VIDEO_GOB = 0x04, + /*0x05-2F ISO Reserved for video*/ + GF_IPMPX_SE_COMP_AAC_DF = 0x30, + GF_IPMPX_SE_COMP_AAC_NONE = 0x31 + /* + 0x32 - 0x5F ISO Reserved for audio + 0x60 - 0xCF ISO Reserved + 0xD0 - 0xFE User Defined + 0xFF Forbidden + */ +}; + +/*syncBoundary*/ +enum +{ + GF_IPMPX_SE_SYNC_VID7EO_PACKETS = 0x00, + GF_IPMPX_SE_SYNC_VIDEO_VOP = 0x01, + GF_IPMPX_SE_SYNC_VIDEO_GOV = 0x02, + /*0x03-2F ISO Reserved for video,*/ + GF_IPMPX_SE_SYNC_AAC_DF = 0x30 + /*0x31 - 0x5F ISO Reserved for audio + 0x60 - 0xCF ISO Reserved + 0xD0 - 0xFE User Defined + 0xFF Forbidden + */ +}; + +/*field_Id*/ +enum +{ + GF_IPMPX_SE_FID_VIDEO_MV = 0x00, + GF_IPMPX_SE_FID_VIDEO_DC = 0x01, + GF_IPMPX_SE_FID_VIDEO_DCT_SIGN = 0x02, + GF_IPMPX_SE_FID_VIDEO_DQUANT = 0x03, + GF_IPMPX_SE_FID_VIDEO_DCT_COEF = 0x04, + GF_IPMPX_SE_FID_VIDEO_ALL = 0x05, + /*0x06-2F ISO Reserved for video*/ + GF_IPMPX_SE_FID_AAC_SIGN = 0x30, + GF_IPMPX_SE_FID_AAC_CODEWORDS = 0x31, + GF_IPMPX_SE_FID_AAC_SCALE = 0x32 + /*0x32 - 0x5F ISO Reserved for audio + 0x60 - 0xCF ISO Reserved + 0xD0 - 0xFE User Defined + 0xFF Forbidden*/ +}; + + +typedef struct +{ + GF_IPMPX_DATA_BASE + u8 mediaTypeExtension; + u8 mediaTypeIndication; + u8 profileLevelIndication; + u8 compliance; + + GF_List *SelEncBuffer; + + GF_List *SelEncFields; + + u16 RLE_DataLength; + u16 *RLE_Data; +} GF_IPMPX_SelectiveDecryptionInit; + + +/*watermark init ops*/ +enum +{ + GF_IPMPX_WM_INSERT = 0, + GF_IPMPX_WM_EXTRACT = 1, + GF_IPMPX_WM_REMARK = 2, + GF_IPMPX_WM_DETECT_COMPRESSION = 3 +}; + +/*used for both audio and video WM init*/ +typedef struct +{ + GF_IPMPX_DATA_BASE + /* + for audio: PCM defined (0x01) and all audio objectTypeIndications + for video: YUV defined (0x01) and all visual objectTypeIndications + */ + u8 inputFormat; + u8 requiredOp; + + /*valid for audio WM, inputFormat=0x01*/ + u8 nChannels; + u8 bitPerSample; + u32 frequency; + + /*valid for video WM, inputFormat=0x01*/ + u16 frame_horizontal_size; + u16 frame_vertical_size; + u8 chroma_format; + + u32 wmPayloadLen; + char *wmPayload; + + u16 wmRecipientId; + + u32 opaqueDataSize; + char *opaqueData; +} GF_IPMPX_WatermarkingInit; + + + +/*WM status*/ +enum +{ + GF_IPMPX_WM_PAYLOAD = 0, + GF_IPMPX_WM_NOPAYLOAD = 1, + GF_IPMPX_WM_NONE = 2, + GF_IPMPX_WM_UNKNOWN = 3 +}; + +/*compression status*/ +enum +{ + GF_IPMPX_WM_COMPRESSION = 0, + GF_IPMPX_WM_NO_COMPRESSION = 1, + GF_IPMPX_WM_COMPRESSION_UNKNOWN = 2 +}; + +typedef struct +{ + GF_IPMPX_DATA_BASE + u8 wm_status; + u8 compression_status; + /*if payload is set, status is FORCED to AUDIO_GF_IPMPX_WM_PAYLOAD*/ + GF_IPMPX_ByteArray *payload; + GF_IPMPX_ByteArray *opaqueData; +} GF_IPMPX_SendWatermark; + + +typedef struct +{ + GF_IPMPX_DATA_BASE + /*GPAC only supports non-0 IDs*/ + u32 Instantiation_API_ID; + u32 Messaging_API_ID; + GF_IPMPX_ByteArray *opaqueData; +} GF_IPMPX_ToolAPI_Config; + +typedef struct +{ + GF_IPMPX_DATA_BASE + u8 cryptoSuite; + u8 IV_length; + Bool use_selective_encryption; + u8 key_indicator_length; +} GF_IPMPX_ISMACryp; + + +/*constructor/destructor*/ +GF_IPMPX_Data *gf_ipmpx_data_new(u8 tag); +void gf_ipmpx_data_del(GF_IPMPX_Data *p); + +/*parse from bitstream*/ +GF_Err gf_ipmpx_data_parse(GF_BitStream *bs, GF_IPMPX_Data **out_data); +/*get IPMP_Data contained size (eg without tag & sizeofinstance)*/ +u32 gf_ipmpx_data_size(GF_IPMPX_Data *p); +/*get fulml IPMP_Data encoded size (eg with tag & sizeofinstance)*/ +u32 gf_ipmpx_data_full_size(GF_IPMPX_Data *p); +/*writes IPMP_Data to buffer*/ +GF_Err gf_ipmpx_data_write(GF_BitStream *bs, GF_IPMPX_Data *_p); + +/*returns GF_IPMPX_Tag based on name*/ +u8 gf_ipmpx_get_tag(char *dataName); +/*return values: cf above */ +u32 gf_ipmpx_get_field_type(GF_IPMPX_Data *p, char *fieldName); +GF_Err gf_ipmpx_set_field(GF_IPMPX_Data *desc, char *fieldName, char *val); +/*assign subdata*/ +GF_Err gf_ipmpx_set_sub_data(GF_IPMPX_Data *desc, char *fieldName, GF_IPMPX_Data *subdesc); +/*assign bytearray*/ +GF_Err gf_ipmpx_set_byte_array(GF_IPMPX_Data *p, char *field, char *str); + +/*ipmpx dumper*/ +GF_Err gf_ipmpx_dump_data(GF_IPMPX_Data *_p, FILE *trace, u32 indent, Bool XMTDump); + + +#endif /*GPAC_MINIMAL_ODF*/ + +#ifdef __cplusplus +} +#endif + +#endif /*_GF_MPEG4_ODF_H_*/ diff --git a/src/gpacmp4/gpac/network.h b/src/gpacmp4/gpac/network.h new file mode 100644 index 00000000..b2292ea2 --- /dev/null +++ b/src/gpacmp4/gpac/network.h @@ -0,0 +1,412 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / common tools sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef _GF_NET_H_ +#define _GF_NET_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/*! + * \file + * \brief IP network functions. + */ + + /*! + * \addtogroup net_grp network + * \ingroup utils_grp + * \brief IP Network Functions + * + *This section documents the IP network functions of the GPAC framework. + * @{ + */ + +#include + + +/*! + *\brief URL local test + * + *Tests whether a URL describes a local file or not + *\param url the url to analyze + *\return 1 if the URL describes a local file, 0 otherwise + */ +Bool gf_url_is_local(const char *url); + +/*! + *\brief gets absolute file path + * + *Gets the absolute file path from a relative path and its parent absolute one. This can only be used with file paths. + *\param pathName the relative path name of a file + *\param parentPath the absolute parent path name + *\return absolute path name of the file, or NULL if bad paths are provided. + \note the returned string must be freed by user + */ +char *gf_url_get_absolute_path(const char *pathName, const char *parentPath); +/*concatenates URL and gets back full URL - returned string must be freed by user*/ +/*! + *\brief URL concatenation + * + *Concatenates a relative URL with its parent URL + *\param parentName URL of the parent service + *\param pathName URL of the service + *\return absolute path name of the service, or NULL if bad paths are provided or if the service path is already an absolute one. + \note the returned string must be freed by user + */ +char *gf_url_concatenate(const char *parentName, const char *pathName); + +/*! + *\brief URL encodin + * + *Encodes URL by replacing special characters with their % encodings. + *\param path URL of the service + *\return encoded path name , or NULL if bad paths are provided. + \note the returned string must be freed by user + */ +char *gf_url_percent_encode(const char *path); + +/*! + *\brief URL to file system + * + *Converts a local URL to a file system value. Removes all white spaces and similar + *\param url url to convert + */ +void gf_url_to_fs_path(char *url); + +/*! + *\brief gets UTC time + * + *Gets UTC time since midnight Jan 1970 + *\param sec number of seconds + *\param msec number of milliseconds + */ +void gf_utc_time_since_1970(u32 *sec, u32 *msec); + + +/*! + * \brief NTP seconds from 1900 to 1970 + * \hideinitializer + * + * Macro giving the number of seconds from from 1900 to 1970 +*/ +#define GF_NTP_SEC_1900_TO_1970 2208988800ul + +/*! + *\brief gets NTP time + * + *Gets NTP (Network Time Protocol) in seconds and fractional side + \param sec NTP time in seconds + \param frac fractional NTP time expressed in 1 / (1<<32 - 1) seconds units + */ +void gf_net_get_ntp(u32 *sec, u32 *frac); + +/*! + * Socket options + * \hideinitializer + */ +enum +{ + /*!Reuses port.*/ + GF_SOCK_REUSE_PORT = 1, + /*!Forces IPV6 if available.*/ + GF_SOCK_FORCE_IPV6 = 1<<1 +}; + +/*! + *\brief abstracted socket object + * + *The abstracted socket object allows you to build client and server applications very simply + *with support for unicast and multicast (no IPv6 yet) +*/ +typedef struct __tag_socket GF_Socket; + +/*!Buffer size to pass for IP address retrieval*/ +#define GF_MAX_IP_NAME_LEN 516 + +/*!socket is a TCP socket*/ +#define GF_SOCK_TYPE_TCP 0x01 +/*!socket is a UDP socket*/ +#define GF_SOCK_TYPE_UDP 0x02 + +/*! + *\brief socket constructor + * + *Constructs a socket object + *\param SocketType the socket type to create, either UDP or TCP + *\return the socket object or NULL if network initialization failure + */ +GF_Socket *gf_sk_new(u32 SocketType); +/*! + *\brief socket destructor + * + *Deletes a socket object + *\param sock the socket object + */ +void gf_sk_del(GF_Socket *sock); + +/*! + *\brief reset internal buffer + * + *Forces the internal socket buffer to be reseted (discarded) + *\param sock the socket object + */ +void gf_sk_reset(GF_Socket *sock); +/*! + *\brief socket buffer size control + * + *Sets the size of the internal buffer of the socket. The socket MUST be bound or connected before. + *\param sock the socket object + *\param send_buffer if 0, sets the size of the reception buffer, otherwise sets the size of the emission buffer + *\param new_size new size of the buffer in bytes. + *\warning This operation may fail depending on the provider, hardware... + */ +GF_Err gf_sk_set_buffer_size(GF_Socket *sock, Bool send_buffer, u32 new_size); + +/*! + *\brief blocking mode control + * + *Sets the blocking mode of a socket on or off. A blocking socket will wait for the net operation to be possible + *while a non-blocking one would return an error. By default, sockets are created in blocking mode + *\param sock the socket object + *\param NonBlockingOn set to 1 to use on-blocking sockets, 0 otherwise + */ +GF_Err gf_sk_set_block_mode(GF_Socket *sock, Bool NonBlockingOn); +/*! + *\brief socket binding + * + *Binds the given socket to the specified port. + *\param local_ip the local interface IP address if desired. If NULL, the default interface will be used. + *\param sock the socket object + *\param port port number to bind this socket to + *\param peer_name the remote server address, if NULL, will use localhost + *\param peer_port remote port number to connect the socket to + *\param options list of option for the bind operation. + */ +GF_Err gf_sk_bind(GF_Socket *sock, const char *local_ip, u16 port, const char *peer_name, u16 peer_port, u32 options); +/*! + *\brief connects a socket + * + *Connects a socket to a remote peer on a given port + *\param sock the socket object + *\param peer_name the remote server address (IP or DNS) + *\param port remote port number to connect the socket to + *\param local_ip the local (client) address (IP or DNS) if any, NULL otherwise. + */ +GF_Err gf_sk_connect(GF_Socket *sock, const char *peer_name, u16 port, const char *local_ip); +/*! + *\brief data emission + * + *Sends a buffer on the socket. The socket must be in a bound or connected mode + *\param sock the socket object + *\param buffer the data buffer to send + *\param length the data length to send + */ +GF_Err gf_sk_send(GF_Socket *sock, const char *buffer, u32 length); +/*! + *\brief data reception + * + *Fetches data on a socket. The socket must be in a bound or connected state + *\param sock the socket object + *\param buffer the recpetion buffer where data is written + *\param length the allocated size of the reception buffer + *\param start_from the offset in the reception buffer where to start writing + *\param read the actual number of bytes received + */ +GF_Err gf_sk_receive(GF_Socket *sock, char *buffer, u32 length, u32 start_from, u32 *read); +/*! + *\brief socket listening + * + *Sets the socket in a listening state. This socket must have been bound to a port before + *\param sock the socket object + *\param max_conn the maximum number of simultaneous connection this socket will accept + */ +GF_Err gf_sk_listen(GF_Socket *sock, u32 max_conn); +/*! + *\brief socket accept + * + *Accepts an incomming connection on a listening socket + *\param sock the socket object + *\param new_conn the resulting connection socket object + */ +GF_Err gf_sk_accept(GF_Socket *sock, GF_Socket **new_conn); + +/*! + *\brief server socket mode + * + *Disable the Nable algo (e.g. set TCP_NODELAY) and set the KEEPALIVE on + *\param sock the socket object + *\param server_on sets server mode on or off +*/ +GF_Err gf_sk_server_mode(GF_Socket *sock, Bool server_on); + +/*! + *\brief get local host name + * + *Retrieves local host name. + *\param buffer destination buffer for name. Buffer must be GF_MAX_IP_NAME_LEN long + */ +GF_Err gf_sk_get_host_name(char *buffer); + +/*! + *\brief get local IP + * + *Gets local IP address of a connected socket, typically used for server after an ACCEPT + *\param sock the socket object + *\param buffer destination buffer for IP address. Buffer must be GF_MAX_IP_NAME_LEN long + */ +GF_Err gf_sk_get_local_ip(GF_Socket *sock, char *buffer); +/*! + *\brief get local info + * + *Gets local socket info of a socket + *\param sock the socket object + *\param port local port number of the socket + *\param sock_type socket type (UDP or TCP) + */ +GF_Err gf_sk_get_local_info(GF_Socket *sock, u16 *port, u32 *sock_type); + +/*! + *\brief get remote address + * + *Gets the remote address of a peer. The socket MUST be connected. + *\param sock the socket object + *\param buffer destination buffer for IP address. Buffer must be GF_MAX_IP_NAME_LEN long + */ +GF_Err gf_sk_get_remote_address(GF_Socket *sock, char *buffer); + +/*! + *\brief set remote address + * + *Sets the remote address of a socket. This is used by connectionless sockets using SendTo and ReceiveFrom + *\param sock the socket object + *\param address the remote peer address + *\param port the remote peer port + */ +GF_Err gf_sk_set_remote(GF_Socket *sock, char *address, u16 port); + + +/*! + *\brief multicast setup + * + *Performs multicast setup (BIND and JOIN) for the socket object + *\param sock the socket object + *\param multi_ip_add the multicast IP address + *\param multi_port the multicast port number + *\param TTL the multicast TTL (Time-To-Live) + *\param no_bind if sets, only join the multicast + *\param local_interface_ip the local interface IP address if desired. If NULL, the default interface will be used. + */ +GF_Err gf_sk_setup_multicast(GF_Socket *sock, const char *multi_ip_add, u16 multi_port, u32 TTL, Bool no_bind, char *local_interface_ip); +/*! + *brief multicast address test + * + *tests whether an IP address is a multicast one or not + *\param multi_ip_add the multicast IP address to test + *\return 1 if the address is a multicast one, 0 otherwise + */ +u32 gf_sk_is_multicast_address(const char *multi_ip_add); + +/*! + *\brief send data with wait delay + * + *Sends data with a max wait delay. This is used for http / ftp sockets mainly. The socket must be connected. + *\param sock the socket object + *\param buffer the data buffer to send + *\param length the data length to send + *\param delay_sec the maximum delay in second to wait before aborting + *\return If the operation timeed out, the function will return a GF_IP_SOCK_WOULD_BLOCK error. + */ +GF_Err gf_sk_send_wait(GF_Socket *sock, const char *buffer, u32 length, u32 delay_sec); +/* receive data with a max wait delay of Second - used for http / ftp sockets mainly*/ +/*! + *\brief receive data with wait delay + * + *Fetches data with a max wait delay. This is used for http / ftp sockets mainly. The socket must be connected. + *\param sock the socket object + *\param buffer the recpetion buffer where data is written + *\param length the allocated size of the reception buffer + *\param start_from the offset in the reception buffer where to start writing + *\param read the actual number of bytes received + *\param delay_sec the maximum delay in second to wait before aborting + *\return If the operation timeed out, the function will return a GF_IP_SOCK_WOULD_BLOCK error. + */ +GF_Err gf_sk_receive_wait(GF_Socket *sock, char *buffer, u32 length, u32 start_from, u32 *read, u32 delay_sec); + +/*! + *\brief gets socket handle + * + *Gets the socket low-level handle as used by OpenSSL. + *\param sock the socket object + *\return the socket handle + */ +s32 gf_sk_get_handle(GF_Socket *sock); + + +/*! + *\brief gets ipv6 support + * + *Returns IPV6 support information. + *\return 2 if the machine has IPV6 support, 1 if the library was compiled with IPV6 support, 0 otherwise + */ +u32 gf_net_has_ipv6(); + + +/*! + *\brief checks address type + * + *Checks if an address is an IPV6 or IPV4 one. + *\return true 1 if address is IPV6 one, 0 otherwise + */ +Bool gf_net_is_ipv6(const char *address); + + +/*! + * \brief MobileIP Callback + * + * The gf_net_mobileip_ctrl_cbk type is the type for the callback of the \ref gf_net_set_mobileip_callback function. By default no mobileip is used + * \param cbck Opaque user data. + * \param start boolean indicating wether the MobileIP subsystem should be started or stopped. + * \return Error code if needed. + * + */ +typedef GF_Err (*gf_net_mobileip_ctrl_cbk)(Bool start); + +/*! + *\brief Assigns MobileIP callback + * + *Assigns the MobileIP control callback. + *\param _mobip_cbk MobileIP control callback + */ +void gf_net_mobileip_set_callback(gf_net_mobileip_ctrl_cbk _mobip_cbk, const char *MobileIP); + +/*! @} */ + +#ifdef __cplusplus +} +#endif + + +#endif /*_GF_NET_H_*/ + diff --git a/src/gpacmp4/gpac/setup.h b/src/gpacmp4/gpac/setup.h new file mode 100644 index 00000000..3fbe32d5 --- /dev/null +++ b/src/gpacmp4/gpac/setup.h @@ -0,0 +1,504 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / general OS configuration file + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef _GF_SETUP_H_ +#define _GF_SETUP_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/*This is to handle cases where config.h is generated at the root of the gpac build tree (./configure) +This is only needed when building libgpac and modules when libgpac is not installed*/ +#ifdef GPAC_HAVE_CONFIG_H +# include "config.h" +#else +# include +#endif + + +/*WIN32 and WinCE config*/ +#if defined(WIN32) || defined(_WIN32_WCE) + +/*common win32 parts*/ +#include +#include + + +typedef unsigned __int64 u64; +typedef unsigned int u32; +typedef unsigned short u16; +typedef unsigned char u8; +typedef __int64 s64; +typedef int s32; +typedef short s16; +typedef char s8; + +#if defined(__GNUC__) +#define GFINLINE inline +#else +#define GFINLINE __inline +#endif + +#define GF_PATH_SEPARATOR '\\' +#define GF_MAX_PATH 1024 + +/*WINCE config*/ +#if defined(_WIN32_WCE) + +/*win32 assert*/ +#ifndef assert + +void CE_Assert(u32 valid, char *file, u32 line); + +#ifndef NDEBUG +#define assert( t ) CE_Assert((unsigned int) (t), __FILE__, __LINE__ ) +#else +#define assert(t) +#endif + +#endif + + +/*performs wide->char and char->wide conversion on a buffer GF_MAX_PATH long*/ +void CE_WideToChar(unsigned short *w_str, char *str); +void CE_CharToWide(char *str, unsigned short *w_str); + + +#define strdup _strdup +#define stricmp _stricmp +#define strnicmp _strnicmp +#define strupr _strupr +#define mkdir _mkdir +#define snprintf _snprintf +#define memccpy _memccpy + + + +#ifndef _PTRDIFF_T_DEFINED +typedef int ptrdiff_t; +#define PTRDIFF(p1, p2, type) ((p1) - (p2)) +#define _PTRDIFF_T_DEFINED +#endif + +#ifndef _SIZE_T_DEFINED +typedef unsigned int size_t; +#define _SIZE_T_DEFINED +#endif + +#ifndef offsetof +#define offsetof(s,m) ((size_t)&(((s*)0)->m)) +#endif + +#ifndef getenv +#define getenv(a) 0L +#endif + +#define strupr _strupr +#define strlwr _strlwr + +/* +#define GPAC_DISABLE_LOG +*/ +#else /*END WINCE*/ + +/*WIN32 not-WinCE*/ +#include +#include +#include +#include +#include +#include + +#define snprintf _snprintf + +#endif /*END WIN32 non win-ce*/ +/*end WIN32 config*/ + +/*start SYMBIAN config*/ +#elif defined(__SYMBIAN32__) + +#define GFINLINE inline +#define GF_PATH_SEPARATOR '\\' + +/*we must explicitely export our functions...*/ +#define GF_EXPORT EXPORT_C + +#include +#include +#include +#include +#include +#include +#include + +#ifdef __SERIES60_3X__ + +typedef unsigned __int64 u64; +typedef __int64 s64; + +#else + +/*FIXME - we don't have 64bit support here we should get rid of all 64bits divisions*/ +/* +typedef unsigned long long u64; +typedef long long s64; +*/ + +typedef unsigned int u64; +typedef signed int s64; + +#endif /*symbian 8*/ + + +typedef unsigned int u32; +typedef unsigned short u16; +typedef unsigned char u8; +typedef int s32; +typedef short s16; +typedef signed char s8; + +#pragma mpwc_relax on + +#define GF_MAX_PATH 260 + +/*sorry this was developed under w32 :)*/ +#define stricmp strcasecmp +#define strnicmp strncasecmp + +#ifndef strupr +char * my_str_upr(char *str); +#define strupr my_str_upr +#endif + +#ifndef strlwr +char * my_str_lwr(char *str); +#define strlwr my_str_lwr +#endif + +#ifndef DBL_MAX +#include +#define DBL_MAX (__IEEE_DBL_MAXPOWTWO) +#endif + +#ifndef FLT_MAX +#include +#define FLT_MAX (__IEEE_FLT_MAXPOWTWO) +#endif + +#ifndef FLT_EPSILON +#define FLT_EPSILON 1 +#endif + +/*end SYMBIAN config*/ + +#else + +/*UNIX likes*/ + +/*force large file support*/ +#ifndef _FILE_OFFSET_BITS +#define _FILE_OFFSET_BITS 64 +#endif +#ifndef _LARGEFILE_SOURCE +#define _LARGEFILE_SOURCE +#endif +#ifndef _LARGEFILE64_SOURCE +#define _LARGEFILE64_SOURCE +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if __APPLE__ && defined GPAC_IPHONE +#include +#endif + +typedef uint64_t u64; +typedef uint32_t u32; +typedef uint16_t u16; +typedef uint8_t u8; +typedef int64_t s64; +typedef int32_t s32; +typedef int16_t s16; +typedef int8_t s8; + +#define GFINLINE inline + +/*sorry this was developed under w32 :)*/ +#define stricmp strcasecmp +#define strnicmp strncasecmp + +#ifndef strupr +char * my_str_upr(char *str); +#define strupr my_str_upr +#endif + +#ifndef strlwr +char * my_str_lwr(char *str); +#define strlwr my_str_lwr +#endif + +#define GF_PATH_SEPARATOR '/' + +#ifdef PATH_MAX +#define GF_MAX_PATH PATH_MAX +#else +/*PATH_MAX not defined*/ +#define GF_MAX_PATH 1023 +#endif + + +#endif /* end platform specific Win32/WinCE/UNIX*/ + +/*define what's missing*/ +#ifndef NULL +#define NULL 0 +#endif + + +typedef double Double; +typedef float Float; +/* 128 bit IDs */ +typedef u8 bin128[16]; + +#define GF_MAX_DOUBLE DBL_MAX +#define GF_MIN_DOUBLE -GF_MAX_DOUBLE +#define GF_MAX_FLOAT FLT_MAX +#define GF_MIN_FLOAT -GF_MAX_FLOAT +#define GF_EPSILON_FLOAT FLT_EPSILON +#define GF_SHORT_MAX SHRT_MAX +#define GF_SHORT_MIN SHRT_MIN + +#ifndef MIN +#define MIN(X, Y) ((X)<(Y)?(X):(Y)) +#endif +#ifndef MAX +#define MAX(X, Y) ((X)>(Y)?(X):(Y)) +#endif + +#define ABSDIFF(a, b) ( ( (a) > (b) ) ? ((a) - (b)) : ((b) - (a)) ) + +#ifndef ABS +#define ABS(a) ( ( (a) > 0 ) ? (a) : - (a) ) +#endif + +#ifndef Bool +typedef u32 Bool; +#endif + + +/*GPAC memory tracking*/ +#if defined(GPAC_MEMORY_TRACKING) + +void *gf_mem_malloc(size_t size, char *filename, int line); +void *gf_mem_calloc(size_t num, size_t size_of, char *filename, int line); +void *gf_mem_realloc(void *ptr, size_t size, char *filename, int line); +void gf_mem_free(void *ptr, char *filename, int line); +char *gf_mem_strdup(const char *str, char *filename, int line); +void gf_memory_print(void); /*prints the state of current allocations*/ + +#define gf_free(ptr) gf_mem_free(ptr, __FILE__, __LINE__) +#define gf_malloc(size) gf_mem_malloc(size, __FILE__, __LINE__) +#define gf_calloc(num, size_of) gf_mem_calloc(num, size_of, __FILE__, __LINE__) +#define gf_strdup(s) gf_mem_strdup(s, __FILE__, __LINE__) +#define gf_realloc(ptr1, size) gf_mem_realloc(ptr1, size, __FILE__, __LINE__) + +#else + +#define gf_malloc malloc +#define gf_calloc calloc +#define gf_realloc realloc +#define gf_free free +#define gf_strdup strdup + +#endif + + +/*end GPAC memory tracking*/ + +#if (defined (WIN32) || defined (_WIN32_WCE)) && !defined(__GNUC__) + +#define LLD "%I64d" +#define LLU "%I64u" +#define LLX "%I64x" +#define LLXPAD( pad ) "%" pad "I64x" +#define LLD_CAST +#define LLU_CAST +#define PTR_TO_U_CAST (u32) + +#elif defined (__SYMBIAN32__) + +#define LLD "%d" +#define LLU "%u" +#define LLX "%x" +#define LLXPAD( pad ) "%" pad "x" +#define LLD_CAST (u32) +#define LLU_CAST (s32) +#define PTR_TO_U_CAST (u32) + +/*seems that even though _LP64 is defined in OSX, %ll modifiers are still needed*/ +#elif defined(__DARWIN__) || defined(__APPLE__) + +#define LLD "%lld" +#define LLU "%llu" +#define LLX "%llx" +#define LLXPAD( pad ) "%" pad "llx" + +#ifdef __LP64__ /* Mac OS 64 bits */ +#define PTR_TO_U_CAST (u64) +#else +#define PTR_TO_U_CAST (u32) +#endif + +#elif defined(_LP64) /*Unix 64 bits*/ + +#define LLD "%ld" +#define LLU "%lu" +#define LLX "%lx" +#define LLXPAD( pad ) "%" pad "lx" +#define PTR_TO_U_CAST (u64) + +#else /*Unix 32 bits*/ + +#define LLD "%lld" +#define LLU "%llu" +#define LLX "%llx" +#define LLXPAD( pad ) "%" pad "llx" +#define PTR_TO_U_CAST (u32) + +#endif + +#ifndef LLD_CAST +#define LLD_CAST +#endif + +#ifndef LLU_CAST +#define LLU_CAST +#endif + +#ifndef PTR_TO_U_CAST +#define PTR_TO_U_CAST +#endif + + +#ifndef GF_EXPORT +/*use def files for windows or let compiler decide*/ +#define GF_EXPORT +#endif + + + + +/*safety checks on macros*/ + +#ifdef GPAC_DISABLE_VRML +# ifndef GPAC_DISABLE_BIFS +# define GPAC_DISABLE_BIFS +# endif +# ifndef GPAC_DISABLE_QTVR +# define GPAC_DISABLE_QTVR +# endif +# ifndef GPAC_DISABLE_X3D +# define GPAC_DISABLE_X3D +# endif +# ifndef GPAC_DISABLE_LOADER_BT +# define GPAC_DISABLE_LOADER_BT +# endif +# ifndef GPAC_DISABLE_LOADER_XMT +# define GPAC_DISABLE_LOADER_XMT +# endif +#endif + +#ifdef GPAC_DISABLE_SVG +# ifndef GPAC_DISABLE_LASER +# define GPAC_DISABLE_LASER +# endif +#endif + + +#ifdef GPAC_DISABLE_AV_PARSERS +# ifndef GPAC_DISABLE_MPEG2PS +# define GPAC_DISABLE_MPEG2PS +# endif +#endif + +#ifdef GPAC_DISABLE_ISOM +# ifndef GPAC_DISABLE_ISOM_WRITE +# define GPAC_DISABLE_ISOM_WRITE +# endif +# ifndef GPAC_DISABLE_ISOM_HINTING +# define GPAC_DISABLE_ISOM_HINTING +# endif +# ifndef GPAC_DISABLE_ISOM_FRAGMENTS +# define GPAC_DISABLE_ISOM_FRAGMENTS +# endif +# ifndef GPAC_DISABLE_SCENE_ENCODER +# define GPAC_DISABLE_SCENE_ENCODER +# endif +# ifndef GPAC_DISABLE_ISOM_DUMP +# define GPAC_DISABLE_ISOM_DUMP +# endif +#endif + +#ifdef GPAC_DISABLE_ISOM_WRITE +# ifndef GPAC_DISABLE_MEDIA_IMPORT +# define GPAC_DISABLE_MEDIA_IMPORT +# endif +#endif + +#ifdef GPAC_DISABLE_STREAMING +# ifndef GPAC_DISABLE_ISOM_HINTING +# define GPAC_DISABLE_ISOM_HINTING +# endif +#endif + +#ifdef GPAC_DISABLE_BIFS +# ifndef GPAC_DISABLE_BIFS_ENC +# define GPAC_DISABLE_BIFS_ENC +# endif +#endif + +#if defined(GPAC_DISABLE_BIFS_ENC) && defined(GPAC_DISABLE_LASER) +# ifndef GPAC_DISABLE_LOADER_ISOM +# define GPAC_DISABLE_LOADER_ISOM +# endif +# ifndef GPAC_DISABLE_SENG +# define GPAC_DISABLE_SENG +# endif +#endif + + +#ifdef __cplusplus +} +#endif + +#endif /*_GF_SETUP_H_*/ diff --git a/src/gpacmp4/gpac/sync_layer.h b/src/gpacmp4/gpac/sync_layer.h new file mode 100644 index 00000000..3c3284c7 --- /dev/null +++ b/src/gpacmp4/gpac/sync_layer.h @@ -0,0 +1,128 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / SL header file + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef _GF_SYNC_LAYER_H_ +#define _GF_SYNC_LAYER_H_ + +#ifdef __cplusplus +extern "C" { +#endif + + +/*the Sync Layer config descriptor*/ +typedef struct +{ + /*base descriptor*/ + u8 tag; + + u8 predefined; + u8 useAccessUnitStartFlag; + u8 useAccessUnitEndFlag; + u8 useRandomAccessPointFlag; + u8 hasRandomAccessUnitsOnlyFlag; + u8 usePaddingFlag; + u8 useTimestampsFlag; + u8 useIdleFlag; + u8 durationFlag; + u32 timestampResolution; + u32 OCRResolution; + u8 timestampLength; + u8 OCRLength; + u8 AULength; + u8 instantBitrateLength; + u8 degradationPriorityLength; + u8 AUSeqNumLength; + u8 packetSeqNumLength; + u32 timeScale; + u16 AUDuration; + u16 CUDuration; + u64 startDTS; + u64 startCTS; +} GF_SLConfig; + +/*************************************** + SLConfig Tag +***************************************/ +enum +{ + SLPredef_Null = 0x01, + SLPredef_MP4 = 0x02, + /*intern to GPAC, means NO SL at all (for streams unable to handle AU reconstruction a timing)*/ + SLPredef_SkipSL = 0xF0 +}; + +/*set SL predefined (assign all fields according to sl->predefined value)*/ +GF_Err gf_odf_slc_set_pref(GF_SLConfig *sl); + + +typedef struct +{ + u8 accessUnitStartFlag; + u8 accessUnitEndFlag; + u8 paddingFlag; + u8 randomAccessPointFlag; + u8 OCRflag; + u8 idleFlag; + u8 decodingTimeStampFlag; + u8 compositionTimeStampFlag; + u8 instantBitrateFlag; + u8 degradationPriorityFlag; + + u8 paddingBits; + u16 packetSequenceNumber; + u64 objectClockReference; + u16 AU_sequenceNumber; + u64 decodingTimeStamp; + u64 compositionTimeStamp; + u16 accessUnitLength; + u32 instantBitrate; + u16 degradationPriority; + + /*this is NOT part of standard SL, only used internally: signals duration of access unit if known + this is usefull for streams with very random updates, to prevent buffering for instance a subtitle stream + which is likely to have no updates during the first minutes... expressed in media timescale*/ + u32 au_duration; + /*ISMACryp extensions*/ + u8 isma_encrypted; + u64 isma_BSO; + /*version_number are pushed from m2ts sections to the mpeg4sl layer so as to handle mpeg4 stream dependencies*/ + u8 m2ts_version_number_plus_one; + u8 m2ts_pcr; +} GF_SLHeader; + + +/*packetize SL-PDU. If PDU is NULL or size 0, only writes the SL header*/ +void gf_sl_packetize(GF_SLConfig* slConfig, GF_SLHeader *Header, char *PDU, u32 size, char **outPacket, u32 *OutSize); +/*gets SL header size in bytes*/ +u32 gf_sl_get_header_size(GF_SLConfig* slConfig, GF_SLHeader *Header); + +/*depacketize SL-PDU*/ +void gf_sl_depacketize(GF_SLConfig *slConfig, GF_SLHeader *Header, const char *PDU, u32 PDULength, u32 *HeaderLen); + + +#ifdef __cplusplus +} +#endif + +#endif /*_GF_SYNC_LAYER_H_*/ diff --git a/src/gpacmp4/gpac/tools.h b/src/gpacmp4/gpac/tools.h new file mode 100644 index 00000000..4100ee46 --- /dev/null +++ b/src/gpacmp4/gpac/tools.h @@ -0,0 +1,812 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / common tools sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef _GF_TOOLS_H_ +#define _GF_TOOLS_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include + + +/*! \file "gpac/tools.h" + * \brief Base definitions and functions of GPAC. + * + * This file contains basic functions and core definitions of the GPAC framework. This file is + * usually included by all GPAC header files since it contains the error definitions. +*/ + +/*! \defgroup utils_grp utils + * You will find in this module the documentation of all tools used in GPAC. +*/ + +/*! \addtogroup tools_grp base utils + * \ingroup utils_grp + * \brief Base definitions and functions of GPAC. + * + * This section documents some very basic functions and core definitions of the GPAC framework. + * @{ + */ + +/*! + * \brief GPAC Version + * \hideinitializer + * + * Macro giving GPAC version expressed as a printable string +*/ +/*KEEP SPACE SEPARATORS FOR MAKE / GREP (SEE MAIN MAKEFILE)!!!, and NO SPACE in GPAC_VERSION for proper install*/ +#define GPAC_VERSION "0.4.6-DEV" + +#include +#define GPAC_FULL_VERSION GPAC_VERSION"-rev"GPAC_SVN_REVISION + +/*! + * \brief GPAC Version + * \hideinitializer + * + * Macro giving GPAC version expressed as an integer, where version X.Y.Z is coded as 0x00XXYYZZ +*/ +#define GPAC_VERSION_INT 0x00000406 + +/*! + * \brief Stringizer + * \hideinitializer + * + * Macro transforming its input name into a string +*/ +#define gf_stringizer(x) #x + +/*! + * \brief Memory allocation + * \hideinitializer + * + * Macro allocating memory and zero-ing it +*/ +#define GF_SAFEALLOC(__ptr, __struct) { __ptr = (__struct *) gf_malloc(sizeof(__struct)); if (__ptr) memset((void *) __ptr, 0, sizeof(__struct)); } + +/*! + * \brief 4CC Formatting + * \hideinitializer + * + * Macro formating a 4-character code (or 4CC) "abcd" as 0xAABBCCDD +*/ +#ifndef GF_4CC +#define GF_4CC(a,b,c,d) (((a)<<24)|((b)<<16)|((c)<<8)|(d)) +#endif + +/*! + * \brief GPAC feature list + * + * returns the list of features enabled/disabled in this GPAC build. +*/ +const char *gpac_features(); + +/*! + * \brief 4CC Printing + * + * returns a 4CC printable form +*/ +const char *gf_4cc_to_str(u32 type); + +/*! + * \brief asprintf() portable implementation + * + * similar to sprintf, except it allows the string on the + * \note asprintf implementation for windows +*/ +int gf_asprintf(char **buffer, const char *fmt, ...); + +/*! + * \brief large file opening + * + * Opens a large file (>4GB) + * \param file_name Same semantics as gf_f64_open + * \param mode Same semantics as gf_f64_open + * \return stream handle of the file object + * \note You only need to call this function if you're suspecting the file to be a large one (usually only media files), otherwise use regular stdio. +*/ +FILE *gf_f64_open(const char *file_name, const char *mode); +/*! + * \brief large file position query + * + * Queries the current read/write position in a large file + * \param f Same semantics as ftell + * \return position in the file + * \note You only need to call this function if you're suspecting the file to be a large one (usually only media files), otherwise use regular stdio. +*/ +u64 gf_f64_tell(FILE *f); +/*! + * \brief large file seeking + * + * Seeks the current read/write position in a large file + * \param f Same semantics as gf_f64_seek + * \param pos Same semantics as gf_f64_seek + * \param whence Same semantics as gf_f64_seek + * \return new position in the file + * \note You only need to call this function if you're suspecting the file to be a large one (usually only media files), otherwise use regular stdio. +*/ +u64 gf_f64_seek(FILE *f, s64 pos, s32 whence); + +/*! @} */ + + +/*! \addtogroup errors_grp error codes + * \ingroup utils_grp + * \brief Errors used in GPAC. + * + * This section documents all error codes used in the GPAC framework. Most of the GPAC's functions will use these as + * return values, and some of these errors are also used for state communication with the different modules of the framework. + * @{ + */ + +/*! + * GPAC Error + * \hideinitializer + * + * positive values are warning and info, 0 means no error and negative values are errors + */ +typedef enum +{ + /*!Message from any scripting engine used in the presentation (ECMAScript, MPEG-J, ...) (Info).*/ + GF_SCRIPT_INFO = 3, + /*!Indicates an data frame has several AU packed (not MPEG-4 compliant). This is used by decoders to force + multiple decoding of the same data frame (Info).*/ + GF_PACKED_FRAMES = 2, + /*!Indicates the end of a stream or of a file (Info).*/ + GF_EOS = 1, + /*! + \n\n + */ + /*!Operation success (no error).*/ + GF_OK = 0, + /*!\n*/ + /*!One of the input parameter is not correct or cannot be used in the current operating mode of the framework.*/ + GF_BAD_PARAM = -1, + /*! Memory allocation failure.*/ + GF_OUT_OF_MEM = -2, + /*! Input/Output failure (disk access, system call failures)*/ + GF_IO_ERR = -3, + /*! The desired feature or operation is not supported by the framework*/ + GF_NOT_SUPPORTED = -4, + /*! Input data has been corrupted*/ + GF_CORRUPTED_DATA = -5, + /*! A modification was attempted on a scene node which could not be found*/ + GF_SG_UNKNOWN_NODE = -6, + /*! The PROTO node interface does not match the nodes using it*/ + GF_SG_INVALID_PROTO = -7, + /*! An error occured in the scripting engine*/ + GF_SCRIPT_ERROR = -8, + /*! Buffer is too small to contain decoded data. Decoders shall use this error whenever they need to resize their output memory buffers*/ + GF_BUFFER_TOO_SMALL = -9, + /*! Bitstream is not compliant to the specfication it refers to*/ + GF_NON_COMPLIANT_BITSTREAM = -10, + /*! No decoders could be found to handle the desired media type*/ + GF_CODEC_NOT_FOUND = -11, + /*! The URL is not properly formatted or cannot be found*/ + GF_URL_ERROR = -12, + /*! An service error has occured at the local side*/ + GF_SERVICE_ERROR = -13, + /*! A service error has occured at the remote (server) side*/ + GF_REMOTE_SERVICE_ERROR = -14, + /*! The desired stream could not be found in the service*/ + GF_STREAM_NOT_FOUND = -15, + /*! The IsoMedia file is not a valid one*/ + GF_ISOM_INVALID_FILE = -20, + /*! The IsoMedia file is not complete. Either the file is being downloaded, or it has been truncated*/ + GF_ISOM_INCOMPLETE_FILE = -21, + /*! The media in this IsoMedia track is not valid (usually due to a broken stream description)*/ + GF_ISOM_INVALID_MEDIA = -22, + /*! The requested operation cannot happen in the current opening mode of the IsoMedia file*/ + GF_ISOM_INVALID_MODE = -23, + /*! This IsoMedia track refers to media outside the file in an unknown way*/ + GF_ISOM_UNKNOWN_DATA_REF = -24, + + /*! An invalid MPEG-4 Object Descriptor was found*/ + GF_ODF_INVALID_DESCRIPTOR = -30, + /*! An MPEG-4 Object Descriptor was found or added to a forbidden descriptor*/ + GF_ODF_FORBIDDEN_DESCRIPTOR = -31, + /*! An invalid MPEG-4 BIFS command was detected*/ + GF_ODF_INVALID_COMMAND = -32, + /*! The scene has been encoded using an unknown BIFS version*/ + GF_BIFS_UNKNOWN_VERSION = -33, + + /*! The remote IP address could not be solved*/ + GF_IP_ADDRESS_NOT_FOUND = -40, + /*! The connection to the remote peer has failed*/ + GF_IP_CONNECTION_FAILURE = -41, + /*! The network operation has failed*/ + GF_IP_NETWORK_FAILURE = -42, + /*! The network connection has been closed*/ + GF_IP_CONNECTION_CLOSED = -43, + /*! The network operation has failed because no data is available*/ + GF_IP_NETWORK_EMPTY = -44, + /*! The network operation has been discarded because it would be a blocking one*/ + GF_IP_SOCK_WOULD_BLOCK = -45, + /*! UDP connection did not receive any data at all. Signaled by client services to reconfigure network if possible*/ + GF_IP_UDP_TIMEOUT = -46, + + /*! Authentication with the remote host has failed*/ + GF_AUTHENTICATION_FAILURE = -50, + /*! Script not ready for playback */ + GF_SCRIPT_NOT_READY = -51 +} GF_Err; + +/*! + * \brief Error Printing + * + * Returns a printable version of a given error + * \param e Error code requested + * \return String representing the error +*/ +const char *gf_error_to_string(GF_Err e); + +/*! @} */ + +/*! \addtogroup log_grp logging tools + * \ingroup utils_grp + * @{ + */ + +/*! + * GPAC Log Levels + * \hideinitializer + * + * These levels describes messages priority used when filtering logs + */ +enum +{ + /*! Disable all Log message*/ + GF_LOG_QUIET = 0, + /*! Log message describes an error*/ + GF_LOG_ERROR, + /*! Log message describes a warning*/ + GF_LOG_WARNING, + /*! Log message is informational (state, etc..)*/ + GF_LOG_INFO, + /*! Log message is a debug info*/ + GF_LOG_DEBUG +}; + +/*! + * \brief Log exits at first error assignment + * + * When GF_LOG_ERROR happens, program leaves with instruction exit(1); + * \param strict strict behaviour when encoutering a serious error. + * + */ +void gf_log_set_strict_error(Bool strict); + +/*! + * \brief gets string-formated log tools + * + * Returns the string-formatted log tools and levels. Returned string shall be freed by the caller. + * \return string-formatted log tools. + * + */ +char *gf_log_get_tools_levels(); + +/*! + * GPAC Log tools + * \hideinitializer + * + * These flags describes which sub-part of GPAC generates the log and are used when filtering logs + */ +enum +{ + /*! Log message from the core library (init, threads, network calls, etc)*/ + GF_LOG_CORE = 0, + /*! Log message from a raw media parser (BIFS, LASeR, A/V formats)*/ + GF_LOG_CODING, + /*! Log message from a bitstream parser (IsoMedia, MPEG-2 TS, OGG, ...)*/ + GF_LOG_CONTAINER, + /*! Log message from the network/service stack (messages & co)*/ + GF_LOG_NETWORK, + /*! Log message from the RTP/RTCP stack (TS info) and packet structure & hinting (debug)*/ + GF_LOG_RTP, + /*! Log message from authoring subsystem (file manip, import/export)*/ + GF_LOG_AUTHOR, + /*! Log message from the sync layer of the terminal*/ + GF_LOG_SYNC, + /*! Log message from a codec*/ + GF_LOG_CODEC, + /*! Log message from any XML parser (context loading, etc)*/ + GF_LOG_PARSER, + /*! Log message from the terminal/compositor, indicating media object state*/ + GF_LOG_MEDIA, + /*! Log message from the scene graph/scene manager (handling of nodes and attribute modif, DOM core)*/ + GF_LOG_SCENE, + /*! Log message from the scripting engine APIs - does not cover alert() in the script code itself*/ + GF_LOG_SCRIPT, + /*! Log message from event handling*/ + GF_LOG_INTERACT, + /*! Log message from compositor*/ + GF_LOG_COMPOSE, + /*! Log for video object cache */ + GF_LOG_CACHE, + /*! Log message from multimedia I/O devices (audio/video input/output, ...)*/ + GF_LOG_MMIO, + /*! Log for runtime info (times, memory, CPU usage)*/ + GF_LOG_RTI, + /*! Log for SMIL timing and animation*/ + GF_LOG_SMIL, + /*! Log for memory tracker*/ + GF_LOG_MEMORY, + /*! Log for audio compositor*/ + GF_LOG_AUDIO, + /*! Generic Log for modules*/ + GF_LOG_MODULE, + /*! Log for threads and mutexes */ + GF_LOG_MUTEX, + /*! Log for all messages coming from GF_Terminal or script alert()*/ + GF_LOG_CONSOLE, + + /*! special value used to set a level for all tools*/ + GF_LOG_ALL, + GF_LOG_TOOL_MAX = GF_LOG_ALL, +}; + +/*! + * \brief Log modules assignment + * + * Sets the tools to be checked for log filtering. By default no logging is performed. + * \param tool tool to be logged. + * \param level level of logging for this tool. + * + */ +void gf_log_set_tool_level(u32 tool, u32 level); + +/*! + * \brief Log Message Callback + * + * The gf_log_cbk type is the type for the callback of the \ref gf_log_set_callback function. By default all logs are redirected to stdout + * \param cbck Opaque user data. + * \param log_level level of the log. This value is not guaranteed in multi-threaded context. + * \param log_tool tool emitting the log. This value is not guaranteed in multi-threaded context. + * \param fmt message log format. + * \param vlist message log param. + * + */ +typedef void (*gf_log_cbk)(void *cbck, u32 log_level, u32 log_tool, const char* fmt, va_list vlist); + +/*! + * \brief Log overwrite + * + * Assigns a user-defined callback for printing log messages. By default all logs are redirected to stdout + * \param usr_cbk Opaque user data + * \param cbk callback log function + * \return previous callback function +*/ +gf_log_cbk gf_log_set_callback(void *usr_cbk, gf_log_cbk cbk); + + +/*! + \cond DUMMY_DOXY_SECTION +*/ +#ifndef GPAC_DISABLE_LOG +/*note: + to turn log on, change to GPAC_ENABLE_LOG + to turn log off, change to GPAC_DISABLE_LOG + this is needed by configure+sed to modify this file directly +*/ +#define GPAC_ENABLE_LOG +#endif + +/*! + \endcond +*/ + + +/*this is all a bit ugly, but most compilers don't properly handle variadic macros...*/ +void gf_log(const char *fmt, ...); +void gf_log_lt(u32 ll, u32 lt); + + +/*! + * \brief Log level checking + * + * Checks if a given tool is logged for the given level + * \param log_tool tool to check + * \param log_level level to check + * \return 1 if logged, 0 otherwise +*/ +Bool gf_log_tool_level_on(u32 log_tool, u32 log_level); + +/*! + * \brief Set log tools and levels + * + * Set log tools and levels according to the log_tools_levels string. All previous log settings are discarded. + * \param log_tools_levels string specifying the tools and levels. It is formatted as logToolX@logLevelX:logToolZ@logLevelZ:... + * \return GF_OK or GF_BAD_PARAM +*/ +GF_Err gf_log_set_tools_levels(const char *log_tools_levels); + +/*! + * \brief Modify log tools and levels + * + * Modify log tools and levels according to the log_tools_levels string. Previous log settings are kept. + * \param log_tools_levels string specifying the tools and levels. It is formatted as logToolX@logLevelX:logToolZ@logLevelZ:... + * \return GF_OK or GF_BAD_PARAM +*/ +GF_Err gf_log_modify_tools_levels(const char *val); + +/*! + * \brief Set log level for a given tool + * + * Set log level for a given tool. + * \param tool tool to log + * \param level log level for this tool +*/ +void gf_log_set_tool_level(u32 tool, u32 level); + +#ifdef GPAC_DISABLE_LOG +#define GF_LOG(_ll, _lm, __args) +#else +/*! + * \brief Message logging + * \hideinitializer + * + * Macro for logging messages. Usage is GF_LOG(log_lev, log_module, (fmt, ...)). The log function is only called if log filtering allows it. This avoids fetching logged parameters when the tool is not being logged. +*/ +#define GF_LOG(_log_level, _log_tools, __args) if (gf_log_tool_level_on(_log_tools, _log_level) ) { gf_log_lt(_log_level, _log_tools); gf_log __args ;} +#endif + + +/*! @} */ + +/*! \addtogroup tools_grp + * @{ + */ + + +/*! + * \brief PseudoRandom Integer Generation Initialization + * + * Sets the starting point for generating a series of pseudorandom integers. + * \param Reset Re-initializes the random number generator +*/ +void gf_rand_init(Bool Reset); +/*! + * \brief PseudoRandom Integer Generation + * + * Returns a pseudorandom integer. +*/ +u32 gf_rand(); + +/*! + * \brief user name + * + * Gets current user (login) name. +*/ +void gf_get_user_name(char *buf, u32 buf_size); + +/*! + * \brief Directory Enumeration Callback + * + * The gf_enum_dir_item type is the type for the callback of the \ref gf_enum_directory function + * \param cbck Opaque user data. + * \param item_name File or directory name. + * \param item_path File or directory full path and name from filesystem root. + * \return 1 to abort enumeration, 0 to continue enumeration. + * + */ +typedef Bool (*gf_enum_dir_item)(void *cbck, char *item_name, char *item_path); +/*! + * \brief Directory enumeration + * + * Enumerates a directory content. Feedback is provided by the enum_dir_item function + * \param dir Directory to enumerate + * \param enum_directory If set, only directories will be enumerated, otherwise only files are. + * \param enum_dir \ref gf_enum_dir_item callback function for enumeration. + * \param cbck Opaque user data passed to callback function. + * \param filter optional filter for file extensions. If a file extension without the dot '.' character is not found in the + * filter the file will be skipped. +*/ +GF_Err gf_enum_directory(const char *dir, Bool enum_directory, gf_enum_dir_item enum_dir, void *cbck, const char *filter); + + +/*! + * \brief File Deletion + * + * Deletes a file from the disk. + * \param fileName absolute name of the file or name relative to the current working directory. +*/ +GF_Err gf_delete_file(const char *fileName); + +/*! + * \brief File Move + * + * Moves or renames a file or directory. + * \param fileName absolute path of the file / directory to move or rename + * \param newFileName absolute new path/name of the file / directory +*/ +void gf_move_file(const char *fileName, const char *newFileName); + +/*! + * \brief Temporary File Creation + * + * Creates a new temporary file in binary mode + * \return stream handle to the new file ressoucre + */ +FILE *gf_temp_file_new(); + + +/*! + * \brief File Modification Time + * + * Returns the modification time of the given file. The exact meaning of this value is system dependent + * \param filename file to check + * \return modification time of the file + */ +u64 gf_file_modification_time(const char *filename); + +/*! + * \brief Progress formatting + * + * Signals progress in GPAC's operations. Note that progress signaling with this function is not thread-safe, the main purpose is to use it for authoring tools only. + * \param title title string of the progress, or NULL for no progress + * \param done Current amount performed of the action. + * \param total Total amount of the action. + */ +void gf_set_progress(const char *title, u64 done, u64 total); + +/*! + * \brief Progress Callback + * + * The gf_on_progress_cbk type is the type for the callback of the \ref gf_set_progress_callback function + * \param cbck Opaque user data. + * \param title preogress title. + * \param done Current amount performed of the action + * \param total Total amount of the action. + * + */ +typedef void (*gf_on_progress_cbk)(const void *cbck, const char *title, u64 done, u64 total); + +/*! + * \brief Progress overwriting + * + * Iverwrites the progress signaling function by a user-defined one. + * \param user_cbk Opaque user data + * \param prog_cbk new callback function to use. Passing NULL restore default GPAC stdout notification. + */ +void gf_set_progress_callback(void *user_cbk, gf_on_progress_cbk prog_cbk); + + +/*! + * \brief Prompt checking + * + * Checks if a character is pending in the prompt buffer. + * \return 1 if a character is ready to be fetched, 0 otherwise. + * \note Function not available under WindowsCE nor SymbianOS +*/ +Bool gf_prompt_has_input(); + +/*! + * \brief Prompt character flush + * + * Returns the current character entered at prompt if any. + * \return value of the character. + * \note Function not available under WindowsCE nor SymbianOS +*/ +char gf_prompt_get_char(); + + +/*! + * \brief turns prompt echo on/off + * + * Turns the prompt character echo on/off - this is usefull when entering passwords. + * \param echo_off indicates whether echo should be turned on or off. + * \note Function not available under WindowsCE nor SymbianOS +*/ +void gf_prompt_set_echo_off(Bool echo_off); + +/*! + *\addtogroup cpu_grp Time tools + *\ingroup utils_grp + *\brief System time and CPU functions + * + *This section documents time functionalities and CPU management in GPAC. + * @{ + */ + + +/*! + * \brief System setup + * + * Inits the system high-resolution clock if any, and CPU usage manager. It is strongly recommended to call this + * function before calling any other GPAC functions, since on some systems (like winCE) it may result in a better memory usage estimation. + * \note This can be called several times but only the first call will result in system setup. + */ +void gf_sys_init(Bool enable_memory_tracker); +/*! + * \brief System closing + * + * Closes the system high-resolution clock and any CPU associated ressources. + * \note This can be called several times but the system will be closed when no more users are counted. + */ +void gf_sys_close(); +/*! + * \brief System clock query + * + * Gets the system clock time. + * \return System clock value since initialization in milliseconds. + */ +u32 gf_sys_clock(); + +/*! + * \brief Sleeps thread/process + * + * Locks calling thread/process execution for a given time. + * \param ms Amount of time to sleep in milliseconds. + */ +void gf_sleep(u32 ms); + +/*! + * \brief CRC32 compute + * + * Computes the CRC32 value of a buffer. + * \param data buffer + * \param size buffer size + * \return computed CRC32 + */ +u32 gf_crc_32(char *data, u32 size); + + +/*!\brief run-time system info object + * + *The Run-Time Info object is used to get CPU and memory occupation of the calling process. + *All time values are expressed in milliseconds (accuracy is not guaranteed). +*/ +typedef struct +{ + /*!start of the sampling period*/ + u32 sampling_instant; + /*!duration of the sampling period*/ + u32 sampling_period_duration; + /*!total amount of time (User+kernel) spent in CPU for all processes as evaluated at the end of the sampling period*/ + u32 total_cpu_time; + /*!total amount of time (User+kernel) spent in CPU for the calling process as evaluated at the end of the sampling period*/ + u32 process_cpu_time; + /*!amount of time (User+kernel) spent in CPU for all processes during the sampling period*/ + u32 total_cpu_time_diff; + /*!total amount of time (User+kernel) spent in CPU for the calling process during the sampling period*/ + u32 process_cpu_time_diff; + /*!total amount of idle time during the sampling period.*/ + u32 cpu_idle_time; + /*!percentage (from 0 to 100) of CPU usage during the sampling period.*/ + u32 total_cpu_usage; + /*!percentage (from 0 to 100) of the CPU usage by the calling process during the sampling period.*/ + u32 process_cpu_usage; + /*!calling process ID*/ + u32 pid; + /*!calling process thread count if known*/ + u32 thread_count; + /*!size of calling process allocated heaps*/ + u64 process_memory; + /*!total physical memory in system*/ + u64 physical_memory; + /*!available physical memory in system*/ + u64 physical_memory_avail; + /*!total memory currently allocated by gpac*/ + u64 gpac_memory; +} GF_SystemRTInfo; + +/*! + * Selection flags for run-time info retrieval + * \hideinitializer + */ +enum +{ + /*!Indicates all processes' times must be fetched. If not set, only the current process times will be retrieved, and the + thread count and total times won't be available*/ + GF_RTI_ALL_PROCESSES_TIMES = 1, + /*!Indicates the process allocated heap size must be fetch. If not set, only the system physical memory is fetched. + Fetching the entire ocess allocated memory can have a large impact on performances*/ + GF_RTI_PROCESS_MEMORY = 1<<1, + /*!Indicates that only system memory should be fetched. When set, all refreshing info is ignored*/ + GF_RTI_SYSTEM_MEMORY_ONLY = 1<<2 +}; + +/*! + * \brief Gets Run-Time info + * + * Gets CPU and memory usage info for the calling process and the system. Information gathering + * is controled through timeout values. + * \param refresh_time_ms refresh time period in milliseconds. If the last sampling was done less than this period ago, the run-time info is not refreshed. + * \param rti holder to the run-time info structure to update. + * \param flags specify which info is to be retrieved. + * \return 1 if info has been updated, 0 otherwise. + * \note You should not try to use a too small refresh time. Typical values are 500 ms or one second. + */ +Bool gf_sys_get_rti(u32 refresh_time_ms, GF_SystemRTInfo *rti, u32 flags); + + +Bool gf_sys_get_battery_state(Bool *onBattery, u32 *onCharge, u32 *level, u32 *batteryLifeTime, u32 *batteryFullLifeTime); + +typedef struct _GF_GlobalLock_opaque GF_GlobalLock; + +/*! + * This function allows the user to create a global lock for all GPAC instances. + * This allow to disable some features for other instances for instance. + * \param resourceName The name of the resource to lock + * \return false if resource has been locked, true if resource could not be locked + */ +GF_GlobalLock * gf_global_resource_lock(const char * resourceName); + +/*! + * Unlock a previouly locked resource + * \param lock The resource to unlock + * \return GF_OK if evertything went fine + */ +GF_Err gf_global_resource_unlock(GF_GlobalLock * lock); + + +/** + * Gets a newly allocated string containing the default cache directory. + * It is the responsibility of the caller to free the string. + * \return a fully qualified path to the default cache directory + */ +char * gf_get_default_cache_directory(); + + + +/** + * Compresses a data buffer in place using zlib. Buffer may be reallocated in the process. + * \param data pointer to the data buffer to be compressed + * \param data_len length of the data buffer to be compressed + * \param out_size pointer for output buffer size + * \return GF_OK if evertything went fine + */ +GF_Err gf_gz_compress_payload(char **data, u32 data_len, u32 *out_size); + +/** + * Decompresses a data buffer using zlib. + * \param data data buffer to be decompressed + * \param data_len length of the data buffer to be decompressed + * \param uncompressed_data pointer to the uncompressed data buffer. It is the responsibility of the caller to free this buffer. + * \param out_size size of the uncompressed buffer + * \return GF_OK if evertything went fine + */ +GF_Err gf_gz_decompress_payload(char *data, u32 data_len, char **uncompressed_data, u32 *out_size); + + +#ifdef GPAC_ANDROID +typedef void (*fm_callback_func)(void *cbk_obj, u32 type, u32 param, int *value); +extern void gf_fm_request_set_callback(void *cbk_obj, fm_callback_func cbk_func); +void gf_fm_request_call(u32 type, u32 param, int *value); +#endif //GPAC_ANDROID + +/*! @} */ + + +/*! @} */ + +#ifdef __cplusplus +} +#endif + + +#endif /*_GF_CORE_H_*/ + diff --git a/src/gpacmp4/gpac/utf.h b/src/gpacmp4/gpac/utf.h new file mode 100644 index 00000000..0863b1af --- /dev/null +++ b/src/gpacmp4/gpac/utf.h @@ -0,0 +1,99 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / common tools sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef _GF_UTF_H_ +#define _GF_UTF_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/*! + * \file + * \brief UTF functions. + */ + +/*! + * \addtogroup utf_grp UTF + * \ingroup utils_grp + * \brief UTF encoding functions + * + *This section documents the UTF functions of the GPAC framework.\n + *The wide characters in GPAC are unsignad shorts, in other words GPAC only supports UTF8 and UTF16 coding styles. + *\note these functions are just ports of libutf8 library tools into GPAC. + * @{ + */ + +#include + +/*! + *\brief wide-char to multibyte conversion + * + *Converts a wide-char string to a multibyte string + *\param dst multibyte destination buffer + *\param dst_len multibyte destination buffer size + *\param srcp address of the wide-char string. This will be set to the next char to be converted in the input buffer if not enough space in the destination, or NULL if conversion was completed. + *\return length (in byte) of the multibyte string or -1 if error. + */ +size_t gf_utf8_wcstombs(char* dst, size_t dst_len, const unsigned short** srcp); +/*converts UTF8 string to wide char string - returns (-1) if error. set @srcp to next char to be +converted if not enough space*/ +/*! + *\brief multibyte to wide-char conversion + * + *Converts a multibyte string to a wide-char string + *\param dst wide-char destination buffer + *\param dst_len wide-char destination buffer size + *\param srcp address of the multibyte character buffer. This will be set to the next char to be converted in the input buffer if not enough space in the destination, or NULL if conversion was completed. + *\return length (in unsigned short) of the wide-char string or -1 if error. + */ +size_t gf_utf8_mbstowcs(unsigned short* dst, size_t dst_len, const char** srcp); +/*! + *\brief wide-char string length + * + *Returns the length in character of a wide-char string + *\param s the wide-char string + *\return the wide-char string length + */ +size_t gf_utf8_wcslen(const unsigned short *s); + +/*! + *\brief string bidi reordering + * + *Performs a simple reordering of words in the string based on each word direction, so that glyphs are sorted in display order. + *\param utf_string the wide-char string + *\param len the len of the wide-char string + *\return 1 if the main direction is right-to-left, 0 otherwise + */ +Bool gf_utf8_reorder_bidi(u16 *utf_string, u32 len); + +/*! @} */ + +#ifdef __cplusplus +} +#endif + + +#endif /*_GF_UTF_H_*/ + diff --git a/src/gpacmp4/gpac/version.h b/src/gpacmp4/gpac/version.h new file mode 100644 index 00000000..818da2eb --- /dev/null +++ b/src/gpacmp4/gpac/version.h @@ -0,0 +1 @@ +#define GPAC_SVN_REVISION "3718M" diff --git a/src/gpacmp4/gpac_ogg.c b/src/gpacmp4/gpac_ogg.c new file mode 100644 index 00000000..967d2bca --- /dev/null +++ b/src/gpacmp4/gpac_ogg.c @@ -0,0 +1,1328 @@ + + +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2002 * + * by the Xiph.Org Foundation http://www.xiph.org/ * + * * + ******************************************************************** + + function: code raw [Vorbis] packets into framed OggSquish stream and + decode Ogg streams back into raw packets + + note: The CRC code is directly derived from public domain code by + Ross Williams (ross@guest.adelaide.edu.au). See docs/framing.html + for details. + + ********************************************************************/ + + +#include + +#ifndef GPAC_DISABLE_OGG + +#define BUFFER_INCREMENT 256 + +static u32 mask[]= +{0x00000000,0x00000001,0x00000003,0x00000007,0x0000000f, + 0x0000001f,0x0000003f,0x0000007f,0x000000ff,0x000001ff, + 0x000003ff,0x000007ff,0x00000fff,0x00001fff,0x00003fff, + 0x00007fff,0x0000ffff,0x0001ffff,0x0003ffff,0x0007ffff, + 0x000fffff,0x001fffff,0x003fffff,0x007fffff,0x00ffffff, + 0x01ffffff,0x03ffffff,0x07ffffff,0x0fffffff,0x1fffffff, + 0x3fffffff,0x7fffffff,0xffffffff }; + +static u32 mask8B[]= +{0x00,0x80,0xc0,0xe0,0xf0,0xf8,0xfc,0xfe,0xff}; + +void oggpack_writeinit(oggpack_buffer *b){ + memset(b,0,sizeof(*b)); + b->ptr = b->buffer = (unsigned char *)gf_malloc(BUFFER_INCREMENT); + b->buffer[0]='\0'; + b->storage=BUFFER_INCREMENT; +} + +void oggpackB_writeinit(oggpack_buffer *b){ + oggpack_writeinit(b); +} + +void oggpack_writetrunc(oggpack_buffer *b,s32 bits){ + s32 bytes=bits>>3; + bits-=bytes*8; + b->ptr=b->buffer+bytes; + b->endbit=bits; + b->endbyte=bytes; + *b->ptr&=mask[bits]; +} + +void oggpackB_writetrunc(oggpack_buffer *b,s32 bits){ + s32 bytes=bits>>3; + bits-=bytes*8; + b->ptr=b->buffer+bytes; + b->endbit=bits; + b->endbyte=bytes; + *b->ptr&=mask8B[bits]; +} + +/* Takes only up to 32 bits. */ +void oggpack_write(oggpack_buffer *b,u32 value,s32 bits){ + if(b->endbyte+4>=b->storage){ + b->buffer = (unsigned char *)gf_realloc(b->buffer,b->storage+BUFFER_INCREMENT); + b->storage+=BUFFER_INCREMENT; + b->ptr=b->buffer+b->endbyte; + } + + value&=mask[bits]; + bits+=b->endbit; + + b->ptr[0]|=value<endbit; + + if(bits>=8){ + b->ptr[1]=value>>(8-b->endbit); + if(bits>=16){ + b->ptr[2]=value>>(16-b->endbit); + if(bits>=24){ + b->ptr[3]=value>>(24-b->endbit); + if(bits>=32){ + if(b->endbit) + b->ptr[4]=value>>(32-b->endbit); + else + b->ptr[4]=0; + } + } + } + } + + b->endbyte+=bits/8; + b->ptr+=bits/8; + b->endbit=bits&7; +} + +/* Takes only up to 32 bits. */ +void oggpackB_write(oggpack_buffer *b,u32 value,s32 bits){ + if(b->endbyte+4>=b->storage){ + b->buffer = (unsigned char *)gf_realloc(b->buffer,b->storage+BUFFER_INCREMENT); + b->storage+=BUFFER_INCREMENT; + b->ptr=b->buffer+b->endbyte; + } + + value=(value&mask[bits])<<(32-bits); + bits+=b->endbit; + + b->ptr[0]|=value>>(24+b->endbit); + + if(bits>=8){ + b->ptr[1]=value>>(16+b->endbit); + if(bits>=16){ + b->ptr[2]=value>>(8+b->endbit); + if(bits>=24){ + b->ptr[3]=value>>(b->endbit); + if(bits>=32){ + if(b->endbit) + b->ptr[4]=value<<(8-b->endbit); + else + b->ptr[4]=0; + } + } + } + } + + b->endbyte+=bits/8; + b->ptr+=bits/8; + b->endbit=bits&7; +} + +void oggpack_writealign(oggpack_buffer *b){ + s32 bits=8-b->endbit; + if(bits<8) + oggpack_write(b,0,bits); +} + +void oggpackB_writealign(oggpack_buffer *b){ + s32 bits=8-b->endbit; + if(bits<8) + oggpackB_write(b,0,bits); +} + +static void oggpack_writecopy_helper(oggpack_buffer *b, + void *source, + s32 bits, + void (*w)(oggpack_buffer *, + u32, + s32), + s32 msb){ + unsigned char *ptr=(unsigned char *)source; + + s32 bytes=bits/8; + bits-=bytes*8; + + if(b->endbit){ + s32 i; + /* unaligned copy. Do it the hard way. */ + for(i=0;iendbyte+bytes+1>=b->storage){ + b->storage=b->endbyte+bytes+BUFFER_INCREMENT; + b->buffer = (unsigned char *)gf_realloc(b->buffer,b->storage); + b->ptr=b->buffer+b->endbyte; + } + + memmove(b->ptr,source,bytes); + b->ptr+=bytes; + b->buffer+=bytes; + *b->ptr=0; + + } + if(bits){ + if(msb) + w(b,(u32)(ptr[bytes]>>(8-bits)),bits); + else + w(b,(u32)(ptr[bytes]),bits); + } +} + +void oggpack_writecopy(oggpack_buffer *b,void *source,s32 bits){ + oggpack_writecopy_helper(b,source,bits,oggpack_write,0); +} + +void oggpackB_writecopy(oggpack_buffer *b,void *source,s32 bits){ + oggpack_writecopy_helper(b,source,bits,oggpackB_write,1); +} + +void oggpack_reset(oggpack_buffer *b){ + b->ptr=b->buffer; + b->buffer[0]=0; + b->endbit=b->endbyte=0; +} + +void oggpackB_reset(oggpack_buffer *b){ + oggpack_reset(b); +} + +void oggpack_writeclear(oggpack_buffer *b){ + gf_free(b->buffer); + memset(b,0,sizeof(*b)); +} + +void oggpackB_writeclear(oggpack_buffer *b){ + oggpack_writeclear(b); +} + +void oggpack_readinit(oggpack_buffer *b,unsigned char *buf,s32 bytes){ + memset(b,0,sizeof(*b)); + b->buffer=b->ptr=buf; + b->storage=bytes; +} + +void oggpackB_readinit(oggpack_buffer *b,unsigned char *buf,s32 bytes){ + oggpack_readinit(b,buf,bytes); +} + +/* Read in bits without advancing the bitptr; bits <= 32 */ +s32 oggpack_look(oggpack_buffer *b,s32 bits){ + u32 ret; + u32 m=mask[bits]; + + bits+=b->endbit; + + if(b->endbyte+4>=b->storage){ + /* not the main path */ + if(b->endbyte*8+bits>b->storage*8)return(-1); + } + + ret=b->ptr[0]>>b->endbit; + if(bits>8){ + ret|=b->ptr[1]<<(8-b->endbit); + if(bits>16){ + ret|=b->ptr[2]<<(16-b->endbit); + if(bits>24){ + ret|=b->ptr[3]<<(24-b->endbit); + if(bits>32 && b->endbit) + ret|=b->ptr[4]<<(32-b->endbit); + } + } + } + return(m&ret); +} + +/* Read in bits without advancing the bitptr; bits <= 32 */ +s32 oggpackB_look(oggpack_buffer *b,s32 bits){ + u32 ret; + s32 m=32-bits; + + bits+=b->endbit; + + if(b->endbyte+4>=b->storage){ + /* not the main path */ + if(b->endbyte*8+bits>b->storage*8)return(-1); + } + + ret=b->ptr[0]<<(24+b->endbit); + if(bits>8){ + ret|=b->ptr[1]<<(16+b->endbit); + if(bits>16){ + ret|=b->ptr[2]<<(8+b->endbit); + if(bits>24){ + ret|=b->ptr[3]<<(b->endbit); + if(bits>32 && b->endbit) + ret|=b->ptr[4]>>(8-b->endbit); + } + } + } + return (ret>>(m>>1))>>((m+1)>>1); +} + +s32 oggpack_look1(oggpack_buffer *b){ + if(b->endbyte>=b->storage)return(-1); + return((b->ptr[0]>>b->endbit)&1); +} + +s32 oggpackB_look1(oggpack_buffer *b){ + if(b->endbyte>=b->storage)return(-1); + return((b->ptr[0]>>(7-b->endbit))&1); +} + +void oggpack_adv(oggpack_buffer *b,s32 bits){ + bits+=b->endbit; + b->ptr+=bits/8; + b->endbyte+=bits/8; + b->endbit=bits&7; +} + +void oggpackB_adv(oggpack_buffer *b,s32 bits){ + oggpack_adv(b,bits); +} + +void oggpack_adv1(oggpack_buffer *b){ + if(++(b->endbit)>7){ + b->endbit=0; + b->ptr++; + b->endbyte++; + } +} + +void oggpackB_adv1(oggpack_buffer *b){ + oggpack_adv1(b); +} + +/* bits <= 32 */ +s32 oggpack_read(oggpack_buffer *b,s32 bits){ + u32 ret; + u32 m=mask[bits]; + + bits+=b->endbit; + + if(b->endbyte+4>=b->storage){ + /* not the main path */ + ret=(u32) (-1); + if(b->endbyte*8+bits>b->storage*8)goto overflow; + } + + ret=b->ptr[0]>>b->endbit; + if(bits>8){ + ret|=b->ptr[1]<<(8-b->endbit); + if(bits>16){ + ret|=b->ptr[2]<<(16-b->endbit); + if(bits>24){ + ret|=b->ptr[3]<<(24-b->endbit); + if(bits>32 && b->endbit){ + ret|=b->ptr[4]<<(32-b->endbit); + } + } + } + } + ret&=m; + + overflow: + + b->ptr+=bits/8; + b->endbyte+=bits/8; + b->endbit=bits&7; + return(ret); +} + +/* bits <= 32 */ +s32 oggpackB_read(oggpack_buffer *b,s32 bits){ + u32 ret; + s32 m=32-bits; + + bits+=b->endbit; + + if(b->endbyte+4>=b->storage){ + /* not the main path */ + ret=(u32) (-1); + if(b->endbyte*8+bits>b->storage*8)goto overflow; + } + + ret=b->ptr[0]<<(24+b->endbit); + if(bits>8){ + ret|=b->ptr[1]<<(16+b->endbit); + if(bits>16){ + ret|=b->ptr[2]<<(8+b->endbit); + if(bits>24){ + ret|=b->ptr[3]<<(b->endbit); + if(bits>32 && b->endbit) + ret|=b->ptr[4]>>(8-b->endbit); + } + } + } + ret=(ret>>(m>>1))>>((m+1)>>1); + + overflow: + + b->ptr+=bits/8; + b->endbyte+=bits/8; + b->endbit=bits&7; + return(ret); +} + +s32 oggpack_read1(oggpack_buffer *b){ + u32 ret; + + if(b->endbyte>=b->storage){ + /* not the main path */ + ret=(u32) (-1); + goto overflow; + } + + ret=(b->ptr[0]>>b->endbit)&1; + + overflow: + + b->endbit++; + if(b->endbit>7){ + b->endbit=0; + b->ptr++; + b->endbyte++; + } + return(ret); +} + +s32 oggpackB_read1(oggpack_buffer *b){ + u32 ret; + + if(b->endbyte>=b->storage){ + /* not the main path */ + ret=(u32) (-1); + goto overflow; + } + + ret=(b->ptr[0]>>(7-b->endbit))&1; + + overflow: + + b->endbit++; + if(b->endbit>7){ + b->endbit=0; + b->ptr++; + b->endbyte++; + } + return(ret); +} + +s32 oggpack_bytes(oggpack_buffer *b){ + return(b->endbyte+(b->endbit+7)/8); +} + +s32 oggpack_bits(oggpack_buffer *b){ + return(b->endbyte*8+b->endbit); +} + +s32 oggpackB_bytes(oggpack_buffer *b){ + return oggpack_bytes(b); +} + +s32 oggpackB_bits(oggpack_buffer *b){ + return oggpack_bits(b); +} + +unsigned char *oggpack_get_buffer(oggpack_buffer *b){ + return(b->buffer); +} + +unsigned char *oggpackB_get_buffer(oggpack_buffer *b){ + return oggpack_get_buffer(b); +} + + +#undef BUFFER_INCREMENT + +/* A complete description of Ogg framing exists in docs/framing.html */ + +s32 ogg_page_version(ogg_page *og){ + return((s32)(og->header[4])); +} + +s32 ogg_page_continued(ogg_page *og){ + return((s32)(og->header[5]&0x01)); +} + +s32 ogg_page_bos(ogg_page *og){ + return((s32)(og->header[5]&0x02)); +} + +s32 ogg_page_eos(ogg_page *og){ + return((s32)(og->header[5]&0x04)); +} + +s64 ogg_page_granulepos(ogg_page *og){ + unsigned char *page=og->header; + s64 granulepos=page[13]&(0xff); + granulepos= (granulepos<<8)|(page[12]&0xff); + granulepos= (granulepos<<8)|(page[11]&0xff); + granulepos= (granulepos<<8)|(page[10]&0xff); + granulepos= (granulepos<<8)|(page[9]&0xff); + granulepos= (granulepos<<8)|(page[8]&0xff); + granulepos= (granulepos<<8)|(page[7]&0xff); + granulepos= (granulepos<<8)|(page[6]&0xff); + return(granulepos); +} + +s32 ogg_page_serialno(ogg_page *og){ + return(og->header[14] | + (og->header[15]<<8) | + (og->header[16]<<16) | + (og->header[17]<<24)); +} + +s32 ogg_page_pageno(ogg_page *og){ + return(og->header[18] | + (og->header[19]<<8) | + (og->header[20]<<16) | + (og->header[21]<<24)); +} + + + +/* returns the number of packets that are completed on this page (if + the leading packet is begun on a previous page, but ends on this + page, it's counted */ + +/* NOTE: +If a page consists of a packet begun on a previous page, and a new +packet begun (but not completed) on this page, the return will be: + ogg_page_packets(page) ==1, + ogg_page_continued(page) !=0 + +If a page happens to be a single packet that was begun on a +previous page, and spans to the next page (in the case of a three or +more page packet), the return will be: + ogg_page_packets(page) ==0, + ogg_page_continued(page) !=0 +*/ + +s32 ogg_page_packets(ogg_page *og){ + s32 i,n=og->header[26],count=0; + for(i=0;iheader[27+i]<255)count++; + return(count); +} + + +#if 0 +/* helper to initialize lookup for direct-table CRC (illustrative; we + use the static init below) */ + +static u32 _ogg_crc_entry(u32 index){ + s32 i; + u32 r; + + r = index << 24; + for (i=0; i<8; i++) + if (r & 0x80000000UL) + r = (r << 1) ^ 0x04c11db7; /* The same as the ethernet generator + polynomial, although we use an + unreflected alg and an init/final + of 0, not 0xffffffff */ + else + r<<=1; + return (r & 0xffffffffUL); +} +#endif + +static u32 crc_lookup[256]={ + 0x00000000,0x04c11db7,0x09823b6e,0x0d4326d9, + 0x130476dc,0x17c56b6b,0x1a864db2,0x1e475005, + 0x2608edb8,0x22c9f00f,0x2f8ad6d6,0x2b4bcb61, + 0x350c9b64,0x31cd86d3,0x3c8ea00a,0x384fbdbd, + 0x4c11db70,0x48d0c6c7,0x4593e01e,0x4152fda9, + 0x5f15adac,0x5bd4b01b,0x569796c2,0x52568b75, + 0x6a1936c8,0x6ed82b7f,0x639b0da6,0x675a1011, + 0x791d4014,0x7ddc5da3,0x709f7b7a,0x745e66cd, + 0x9823b6e0,0x9ce2ab57,0x91a18d8e,0x95609039, + 0x8b27c03c,0x8fe6dd8b,0x82a5fb52,0x8664e6e5, + 0xbe2b5b58,0xbaea46ef,0xb7a96036,0xb3687d81, + 0xad2f2d84,0xa9ee3033,0xa4ad16ea,0xa06c0b5d, + 0xd4326d90,0xd0f37027,0xddb056fe,0xd9714b49, + 0xc7361b4c,0xc3f706fb,0xceb42022,0xca753d95, + 0xf23a8028,0xf6fb9d9f,0xfbb8bb46,0xff79a6f1, + 0xe13ef6f4,0xe5ffeb43,0xe8bccd9a,0xec7dd02d, + 0x34867077,0x30476dc0,0x3d044b19,0x39c556ae, + 0x278206ab,0x23431b1c,0x2e003dc5,0x2ac12072, + 0x128e9dcf,0x164f8078,0x1b0ca6a1,0x1fcdbb16, + 0x018aeb13,0x054bf6a4,0x0808d07d,0x0cc9cdca, + 0x7897ab07,0x7c56b6b0,0x71159069,0x75d48dde, + 0x6b93dddb,0x6f52c06c,0x6211e6b5,0x66d0fb02, + 0x5e9f46bf,0x5a5e5b08,0x571d7dd1,0x53dc6066, + 0x4d9b3063,0x495a2dd4,0x44190b0d,0x40d816ba, + 0xaca5c697,0xa864db20,0xa527fdf9,0xa1e6e04e, + 0xbfa1b04b,0xbb60adfc,0xb6238b25,0xb2e29692, + 0x8aad2b2f,0x8e6c3698,0x832f1041,0x87ee0df6, + 0x99a95df3,0x9d684044,0x902b669d,0x94ea7b2a, + 0xe0b41de7,0xe4750050,0xe9362689,0xedf73b3e, + 0xf3b06b3b,0xf771768c,0xfa325055,0xfef34de2, + 0xc6bcf05f,0xc27dede8,0xcf3ecb31,0xcbffd686, + 0xd5b88683,0xd1799b34,0xdc3abded,0xd8fba05a, + 0x690ce0ee,0x6dcdfd59,0x608edb80,0x644fc637, + 0x7a089632,0x7ec98b85,0x738aad5c,0x774bb0eb, + 0x4f040d56,0x4bc510e1,0x46863638,0x42472b8f, + 0x5c007b8a,0x58c1663d,0x558240e4,0x51435d53, + 0x251d3b9e,0x21dc2629,0x2c9f00f0,0x285e1d47, + 0x36194d42,0x32d850f5,0x3f9b762c,0x3b5a6b9b, + 0x0315d626,0x07d4cb91,0x0a97ed48,0x0e56f0ff, + 0x1011a0fa,0x14d0bd4d,0x19939b94,0x1d528623, + 0xf12f560e,0xf5ee4bb9,0xf8ad6d60,0xfc6c70d7, + 0xe22b20d2,0xe6ea3d65,0xeba91bbc,0xef68060b, + 0xd727bbb6,0xd3e6a601,0xdea580d8,0xda649d6f, + 0xc423cd6a,0xc0e2d0dd,0xcda1f604,0xc960ebb3, + 0xbd3e8d7e,0xb9ff90c9,0xb4bcb610,0xb07daba7, + 0xae3afba2,0xaafbe615,0xa7b8c0cc,0xa379dd7b, + 0x9b3660c6,0x9ff77d71,0x92b45ba8,0x9675461f, + 0x8832161a,0x8cf30bad,0x81b02d74,0x857130c3, + 0x5d8a9099,0x594b8d2e,0x5408abf7,0x50c9b640, + 0x4e8ee645,0x4a4ffbf2,0x470cdd2b,0x43cdc09c, + 0x7b827d21,0x7f436096,0x7200464f,0x76c15bf8, + 0x68860bfd,0x6c47164a,0x61043093,0x65c52d24, + 0x119b4be9,0x155a565e,0x18197087,0x1cd86d30, + 0x029f3d35,0x065e2082,0x0b1d065b,0x0fdc1bec, + 0x3793a651,0x3352bbe6,0x3e119d3f,0x3ad08088, + 0x2497d08d,0x2056cd3a,0x2d15ebe3,0x29d4f654, + 0xc5a92679,0xc1683bce,0xcc2b1d17,0xc8ea00a0, + 0xd6ad50a5,0xd26c4d12,0xdf2f6bcb,0xdbee767c, + 0xe3a1cbc1,0xe760d676,0xea23f0af,0xeee2ed18, + 0xf0a5bd1d,0xf464a0aa,0xf9278673,0xfde69bc4, + 0x89b8fd09,0x8d79e0be,0x803ac667,0x84fbdbd0, + 0x9abc8bd5,0x9e7d9662,0x933eb0bb,0x97ffad0c, + 0xafb010b1,0xab710d06,0xa6322bdf,0xa2f33668, + 0xbcb4666d,0xb8757bda,0xb5365d03,0xb1f740b4}; + +/* init the encode/decode logical stream state */ + +s32 ogg_stream_init(ogg_stream_state *os,s32 serialno){ + if(os){ + memset(os,0,sizeof(*os)); + os->body_storage=16*1024; + os->body_data = (unsigned char *)gf_malloc(os->body_storage*sizeof(*os->body_data)); + + os->lacing_storage=1024; + os->lacing_vals=(s32 *)gf_malloc(os->lacing_storage*sizeof(*os->lacing_vals)); + os->granule_vals=(s64*)gf_malloc(os->lacing_storage*sizeof(*os->granule_vals)); + + os->serialno=serialno; + + return(0); + } + return(-1); +} + +/* _clear does not free os, only the non-flat storage within */ +s32 ogg_stream_clear(ogg_stream_state *os){ + if(os){ + if(os->body_data)gf_free(os->body_data); + if(os->lacing_vals)gf_free(os->lacing_vals); + if(os->granule_vals)gf_free(os->granule_vals); + + memset(os,0,sizeof(*os)); + } + return(0); +} + +s32 ogg_stream_destroy(ogg_stream_state *os){ + if(os){ + ogg_stream_clear(os); + gf_free(os); + } + return(0); +} + +/* Helpers for ogg_stream_encode; this keeps the structure and + what's happening fairly clear */ + +static void _os_body_expand(ogg_stream_state *os,s32 needed){ + if(os->body_storage<=os->body_fill+needed){ + os->body_storage+=(needed+1024); + os->body_data = (unsigned char *)gf_realloc(os->body_data,os->body_storage*sizeof(*os->body_data)); + } +} + +static void _os_lacing_expand(ogg_stream_state *os,s32 needed){ + if(os->lacing_storage<=os->lacing_fill+needed){ + os->lacing_storage+=(needed+32); + os->lacing_vals=(s32*)gf_realloc(os->lacing_vals,os->lacing_storage*sizeof(*os->lacing_vals)); + os->granule_vals=(s64*)gf_realloc(os->granule_vals,os->lacing_storage*sizeof(*os->granule_vals)); + } +} + +/* checksum the page */ +/* Direct table CRC; note that this will be faster in the future if we + perform the checksum silmultaneously with other copies */ + +void ogg_page_checksum_set(ogg_page *og){ + if(og){ + u32 crc_reg=0; + s32 i; + + /* safety; needed for API behavior, but not framing code */ + og->header[22]=0; + og->header[23]=0; + og->header[24]=0; + og->header[25]=0; + + for(i=0;iheader_len;i++) + crc_reg=(crc_reg<<8)^crc_lookup[((crc_reg >> 24)&0xff)^og->header[i]]; + for(i=0;ibody_len;i++) + crc_reg=(crc_reg<<8)^crc_lookup[((crc_reg >> 24)&0xff)^og->body[i]]; + + og->header[22]=crc_reg&0xff; + og->header[23]=(crc_reg>>8)&0xff; + og->header[24]=(crc_reg>>16)&0xff; + og->header[25]=(crc_reg>>24)&0xff; + } +} + +/* submit data to the internal buffer of the framing engine */ +s32 ogg_stream_packetin(ogg_stream_state *os,ogg_packet *op){ + s32 lacing_vals=op->bytes/255+1,i; + + if(os->body_returned){ + /* advance packet data according to the body_returned pointer. We + had to keep it around to return a pointer into the buffer last + call */ + + os->body_fill-=os->body_returned; + if(os->body_fill) + memmove(os->body_data,os->body_data+os->body_returned, + os->body_fill); + os->body_returned=0; + } + + /* make sure we have the buffer storage */ + _os_body_expand(os,op->bytes); + _os_lacing_expand(os,lacing_vals); + + /* Copy in the submitted packet. Yes, the copy is a waste; this is + the liability of overly clean abstraction for the time being. It + will actually be fairly easy to eliminate the extra copy in the + future */ + + memcpy(os->body_data+os->body_fill,op->packet,op->bytes); + os->body_fill+=op->bytes; + + /* Store lacing vals for this packet */ + for(i=0;ilacing_vals[os->lacing_fill+i]=255; + os->granule_vals[os->lacing_fill+i]=os->granulepos; + } + os->lacing_vals[os->lacing_fill+i]=(op->bytes)%255; + os->granulepos=os->granule_vals[os->lacing_fill+i]=op->granulepos; + + /* flag the first segment as the beginning of the packet */ + os->lacing_vals[os->lacing_fill]|= 0x100; + + os->lacing_fill+=lacing_vals; + + /* for the sake of completeness */ + os->packetno++; + + if(op->e_o_s)os->e_o_s=1; + + return(0); +} + +/* This will flush remaining packets into a page (returning nonzero), + even if there is not enough data to trigger a flush normally + (undersized page). If there are no packets or partial packets to + flush, ogg_stream_flush returns 0. Note that ogg_stream_flush will + try to flush a normal sized page like ogg_stream_pageout; a call to + ogg_stream_flush does not guarantee that all packets have flushed. + Only a return value of 0 from ogg_stream_flush indicates all packet + data is flushed into pages. + + since ogg_stream_flush will flush the last page in a stream even if + it's undersized, you almost certainly want to use ogg_stream_pageout + (and *not* ogg_stream_flush) unless you specifically need to flush + an page regardless of size in the middle of a stream. */ + +s32 ogg_stream_flush(ogg_stream_state *os,ogg_page *og){ + s32 i; + s32 vals=0; + s32 maxvals=(os->lacing_fill>255?255:os->lacing_fill); + s32 bytes=0; + s32 acc=0; + s64 granule_pos=os->granule_vals[0]; + + if(maxvals==0)return(0); + + /* construct a page */ + /* decide how many segments to include */ + + /* If this is the initial header case, the first page must only include + the initial header packet */ + if(os->b_o_s==0){ /* 'initial header page' case */ + granule_pos=0; + for(vals=0;valslacing_vals[vals]&0x0ff)<255){ + vals++; + break; + } + } + }else{ + for(vals=0;vals4096)break; + acc+=os->lacing_vals[vals]&0x0ff; + granule_pos=os->granule_vals[vals]; + } + } + + /* construct the header in temp storage */ + memcpy(os->header,"OggS",4); + + /* stream structure version */ + os->header[4]=0x00; + + /* continued packet flag? */ + os->header[5]=0x00; + if((os->lacing_vals[0]&0x100)==0)os->header[5]|=0x01; + /* first page flag? */ + if(os->b_o_s==0)os->header[5]|=0x02; + /* last page flag? */ + if(os->e_o_s && os->lacing_fill==vals)os->header[5]|=0x04; + os->b_o_s=1; + + /* 64 bits of PCM position */ + for(i=6;i<14;i++){ + os->header[i]=(u8) (granule_pos&0xff); + granule_pos>>=8; + } + + /* 32 bits of stream serial number */ + { + s32 serialno=os->serialno; + for(i=14;i<18;i++){ + os->header[i]=(serialno&0xff); + serialno>>=8; + } + } + + /* 32 bits of page counter (we have both counter and page header + because this val can roll over) */ + if(os->pageno==-1)os->pageno=0; /* because someone called + stream_reset; this would be a + strange thing to do in an + encode stream, but it has + plausible uses */ + { + s32 pageno=os->pageno++; + for(i=18;i<22;i++){ + os->header[i]=(pageno&0xff); + pageno>>=8; + } + } + + /* zero for computation; filled in later */ + os->header[22]=0; + os->header[23]=0; + os->header[24]=0; + os->header[25]=0; + + /* segment table */ + os->header[26]=vals&0xff; + for(i=0;iheader[i+27]=(os->lacing_vals[i]&0xff); + + /* set pointers in the ogg_page struct */ + og->header=os->header; + og->header_len=os->header_fill=vals+27; + og->body=os->body_data+os->body_returned; + og->body_len=bytes; + + /* advance the lacing data and set the body_returned pointer */ + + os->lacing_fill-=vals; + memmove(os->lacing_vals,os->lacing_vals+vals,os->lacing_fill*sizeof(*os->lacing_vals)); + memmove(os->granule_vals,os->granule_vals+vals,os->lacing_fill*sizeof(*os->granule_vals)); + os->body_returned+=bytes; + + /* calculate the checksum */ + + ogg_page_checksum_set(og); + + /* done */ + return(1); +} + + +/* This constructs pages from buffered packet segments. The pointers +returned are to static buffers; do not free. The returned buffers are +good only until the next call (using the same ogg_stream_state) */ + +s32 ogg_stream_pageout(ogg_stream_state *os, ogg_page *og){ + + if((os->e_o_s&&os->lacing_fill) || /* 'were done, now flush' case */ + os->body_fill-os->body_returned > 4096 ||/* 'page nominal size' case */ + os->lacing_fill>=255 || /* 'segment table full' case */ + (os->lacing_fill&&!os->b_o_s)){ /* 'initial header page' case */ + + return(ogg_stream_flush(os,og)); + } + + /* not enough data to construct a page and not end of stream */ + return(0); +} + +s32 ogg_stream_eos(ogg_stream_state *os){ + return os->e_o_s; +} + +/* DECODING PRIMITIVES: packet streaming layer **********************/ + +/* This has two layers to place more of the multi-serialno and paging + control in the application's hands. First, we expose a data buffer + using ogg_sync_buffer(). The app either copies into the + buffer, or passes it directly to read(), etc. We then call + ogg_sync_wrote() to tell how many bytes we just added. + + Pages are returned (pointers into the buffer in ogg_sync_state) + by ogg_sync_pageout(). The page is then submitted to + ogg_stream_pagein() along with the appropriate + ogg_stream_state* (ie, matching serialno). We then get raw + packets out calling ogg_stream_packetout() with a + ogg_stream_state. See the 'frame-prog.txt' docs for details and + example code. */ + +/* initialize the struct to a known state */ +s32 ogg_sync_init(ogg_sync_state *oy){ + if(oy){ + memset(oy,0,sizeof(*oy)); + } + return(0); +} + +/* clear non-flat storage within */ +s32 ogg_sync_clear(ogg_sync_state *oy){ + if(oy){ + if(oy->data)gf_free(oy->data); + ogg_sync_init(oy); + } + return(0); +} + +s32 ogg_sync_destroy(ogg_sync_state *oy){ + if(oy){ + ogg_sync_clear(oy); + gf_free(oy); + } + return(0); +} + +char *ogg_sync_buffer(ogg_sync_state *oy, s32 size){ + + /* first, clear out any space that has been previously returned */ + if(oy->returned){ + oy->fill-=oy->returned; + if(oy->fill>0) + memmove(oy->data,oy->data+oy->returned,oy->fill); + oy->returned=0; + } + + if(size>oy->storage-oy->fill){ + /* We need to extend the internal buffer */ + s32 newsize=size+oy->fill+4096; /* an extra page to be nice */ + + if(oy->data) + oy->data = (unsigned char *)gf_realloc(oy->data,newsize); + else + oy->data = (unsigned char *)gf_malloc(newsize); + oy->storage=newsize; + } + + /* expose a segment at least as large as requested at the fill mark */ + return((char *)oy->data+oy->fill); +} + +s32 ogg_sync_wrote(ogg_sync_state *oy, s32 bytes){ + if(oy->fill+bytes>oy->storage)return(-1); + oy->fill+=bytes; + return(0); +} + +/* sync the stream. This is meant to be useful for finding page + boundaries. + + return values for this: + -n) skipped n bytes + 0) page not ready; more data (no bytes skipped) + n) page synced at current location; page length n bytes + +*/ + +s32 ogg_sync_pageseek(ogg_sync_state *oy,ogg_page *og){ + unsigned char *page=oy->data+oy->returned; + unsigned char *next; + s32 bytes=oy->fill-oy->returned; + + if(oy->headerbytes==0){ + s32 headerbytes,i; + if(bytes<27)return(0); /* not enough for a header */ + + /* verify capture pattern */ + if(memcmp(page,"OggS",4))goto sync_fail; + + headerbytes=page[26]+27; + if(bytesbodybytes+=page[27+i]; + oy->headerbytes=headerbytes; + } + + if(oy->bodybytes+oy->headerbytes>bytes)return(0); + + /* The whole test page is buffered. Verify the checksum */ + { + /* Grab the checksum bytes, set the header field to zero */ + char chksum[4]; + ogg_page log; + + memcpy(chksum,page+22,4); + memset(page+22,0,4); + + /* set up a temp page struct and recompute the checksum */ + log.header=page; + log.header_len=oy->headerbytes; + log.body=page+oy->headerbytes; + log.body_len=oy->bodybytes; + ogg_page_checksum_set(&log); + + /* Compare */ + if(memcmp(chksum,page+22,4)){ + /* D'oh. Mismatch! Corrupt page (or miscapture and not a page + at all) */ + /* replace the computed checksum with the one actually read in */ + memcpy(page+22,chksum,4); + + /* Bad checksum. Lose sync */ + goto sync_fail; + } + } + + /* yes, have a whole page all ready to go */ + { + unsigned char *page=oy->data+oy->returned; + s32 bytes; + + if(og){ + og->header=page; + og->header_len=oy->headerbytes; + og->body=page+oy->headerbytes; + og->body_len=oy->bodybytes; + } + + oy->unsynced=0; + oy->returned+=(bytes=oy->headerbytes+oy->bodybytes); + oy->headerbytes=0; + oy->bodybytes=0; + return(bytes); + } + + sync_fail: + + oy->headerbytes=0; + oy->bodybytes=0; + + /* search for possible capture */ + next = (unsigned char *)memchr(page+1,'O',bytes-1); + if(!next) + next=oy->data+oy->fill; + + oy->returned=next-oy->data; + return(-(next-page)); +} + +/* sync the stream and get a page. Keep trying until we find a page. + Supress 'sync errors' after reporting the first. + + return values: + -1) recapture (hole in data) + 0) need more data + 1) page returned + + Returns pointers into buffered data; invalidated by next call to + _stream, _clear, _init, or _buffer */ + +s32 ogg_sync_pageout(ogg_sync_state *oy, ogg_page *og){ + + /* all we need to do is verify a page at the head of the stream + buffer. If it doesn't verify, we look for the next potential + frame */ + + while(1){ + s32 ret=ogg_sync_pageseek(oy,og); + if(ret>0){ + /* have a page */ + return(1); + } + if(ret==0){ + /* need more data */ + return(0); + } + + /* head did not start a synced page... skipped some bytes */ + if(!oy->unsynced){ + oy->unsynced=1; + return(-1); + } + + /* loop. keep looking */ + + } +} + +/* add the incoming page to the stream state; we decompose the page + into packet segments here as well. */ + +s32 ogg_stream_pagein(ogg_stream_state *os, ogg_page *og){ + unsigned char *header=og->header; + unsigned char *body=og->body; + s32 bodysize=og->body_len; + s32 segptr=0; + + s32 version=ogg_page_version(og); + s32 continued=ogg_page_continued(og); + s32 bos=ogg_page_bos(og); + s32 eos=ogg_page_eos(og); + s64 granulepos=ogg_page_granulepos(og); + s32 serialno=ogg_page_serialno(og); + s32 pageno=ogg_page_pageno(og); + s32 segments=header[26]; + + /* clean up 'returned data' */ + { + s32 lr=os->lacing_returned; + s32 br=os->body_returned; + + /* body data */ + if(br){ + os->body_fill-=br; + if(os->body_fill) + memmove(os->body_data,os->body_data+br,os->body_fill); + os->body_returned=0; + } + + if(lr){ + /* segment table */ + if(os->lacing_fill-lr){ + memmove(os->lacing_vals,os->lacing_vals+lr, + (os->lacing_fill-lr)*sizeof(*os->lacing_vals)); + memmove(os->granule_vals,os->granule_vals+lr, + (os->lacing_fill-lr)*sizeof(*os->granule_vals)); + } + os->lacing_fill-=lr; + os->lacing_packet-=lr; + os->lacing_returned=0; + } + } + + /* check the serial number */ + if(serialno!=os->serialno)return(-1); + if(version>0)return(-1); + + _os_lacing_expand(os,segments+1); + + /* are we in sequence? */ + if(pageno!=os->pageno){ + s32 i; + + /* unroll previous partial packet (if any) */ + for(i=os->lacing_packet;ilacing_fill;i++) + os->body_fill-=os->lacing_vals[i]&0xff; + os->lacing_fill=os->lacing_packet; + + /* make a note of dropped data in segment table */ + if(os->pageno!=-1){ + os->lacing_vals[os->lacing_fill++]=0x400; + os->lacing_packet++; + } + + /* are we a 'continued packet' page? If so, we'll need to skip + some segments */ + if(continued){ + bos=0; + for(;segptrbody_data+os->body_fill,body,bodysize); + os->body_fill+=bodysize; + } + + { + s32 saved=-1; + while(segptrlacing_vals[os->lacing_fill]=val; + os->granule_vals[os->lacing_fill]=-1; + + if(bos){ + os->lacing_vals[os->lacing_fill]|=0x100; + bos=0; + } + + if(val<255)saved=os->lacing_fill; + + os->lacing_fill++; + segptr++; + + if(val<255)os->lacing_packet=os->lacing_fill; + } + + /* set the granulepos on the last granuleval of the last full packet */ + if(saved!=-1){ + os->granule_vals[saved]=granulepos; + } + + } + + if(eos){ + os->e_o_s=1; + if(os->lacing_fill>0) + os->lacing_vals[os->lacing_fill-1]|=0x200; + } + + os->pageno=pageno+1; + + return(0); +} + +/* clear things to an initial state. Good to call, eg, before seeking */ +s32 ogg_sync_reset(ogg_sync_state *oy){ + oy->fill=0; + oy->returned=0; + oy->unsynced=0; + oy->headerbytes=0; + oy->bodybytes=0; + return(0); +} + +s32 ogg_stream_reset(ogg_stream_state *os){ + os->body_fill=0; + os->body_returned=0; + + os->lacing_fill=0; + os->lacing_packet=0; + os->lacing_returned=0; + + os->header_fill=0; + + os->e_o_s=0; + os->b_o_s=0; + os->pageno=-1; + os->packetno=0; + os->granulepos=0; + + return(0); +} + +s32 ogg_stream_reset_serialno(ogg_stream_state *os,s32 serialno){ + ogg_stream_reset(os); + os->serialno=serialno; + return(0); +} + +static s32 _packetout(ogg_stream_state *os,ogg_packet *op,s32 adv){ + + /* The last part of decode. We have the stream broken into packet + segments. Now we need to group them into packets (or return the + out of sync markers) */ + + s32 ptr=os->lacing_returned; + + if(os->lacing_packet<=ptr)return(0); + + if(os->lacing_vals[ptr]&0x400){ + /* we need to tell the codec there's a gap; it might need to + handle previous packet dependencies. */ + os->lacing_returned++; + os->packetno++; + return(-1); + } + + if(!op && !adv)return(1); /* just using peek as an inexpensive way + to ask if there's a whole packet + waiting */ + + /* Gather the whole packet. We'll have no holes or a partial packet */ + { + s32 size=os->lacing_vals[ptr]&0xff; + s32 bytes=size; + s32 eos=os->lacing_vals[ptr]&0x200; /* last packet of the stream? */ + s32 bos=os->lacing_vals[ptr]&0x100; /* first packet of the stream? */ + + while(size==255){ + s32 val=os->lacing_vals[++ptr]; + size=val&0xff; + if(val&0x200)eos=0x200; + bytes+=size; + } + + if(op){ + op->e_o_s=eos; + op->b_o_s=bos; + op->packet=os->body_data+os->body_returned; + op->packetno=os->packetno; + op->granulepos=os->granule_vals[ptr]; + op->bytes=bytes; + } + + if(adv){ + os->body_returned+=bytes; + os->lacing_returned=ptr+1; + os->packetno++; + } + } + return(1); +} + +s32 ogg_stream_packetout(ogg_stream_state *os,ogg_packet *op){ + return _packetout(os,op,1); +} + +s32 ogg_stream_packetpeek(ogg_stream_state *os,ogg_packet *op){ + return _packetout(os,op,0); +} + +void ogg_packet_clear(ogg_packet *op) { + gf_free(op->packet); + memset(op, 0, sizeof(*op)); +} + +#endif /*GPAC_DISABLE_OGG*/ diff --git a/src/gpacmp4/gpacmp4.vcxproj b/src/gpacmp4/gpacmp4.vcxproj new file mode 100644 index 00000000..3c7f6ca7 --- /dev/null +++ b/src/gpacmp4/gpacmp4.vcxproj @@ -0,0 +1,128 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + {74B397D2-4BE2-4349-9B61-456368E827E7} + Win32Proj + gpacmp4 + + + + Application + true + Unicode + + + Application + false + true + Unicode + + + + + + + + + + + + + true + + + false + + + + NotUsing + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + F:\borrame\gpacmp4\gpacmp4\;%(AdditionalIncludeDirectories) + + + Console + true + + + + + Level3 + Use + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + + + Console + true + true + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/gpacmp4/gpacmp4.vcxproj.filters b/src/gpacmp4/gpacmp4.vcxproj.filters new file mode 100644 index 00000000..9cd62f4a --- /dev/null +++ b/src/gpacmp4/gpacmp4.vcxproj.filters @@ -0,0 +1,153 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + + + + Header Files + + + Header Files + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + \ No newline at end of file diff --git a/src/gpacmp4/gpacmp4.vcxproj.user b/src/gpacmp4/gpacmp4.vcxproj.user new file mode 100644 index 00000000..4355c1a7 --- /dev/null +++ b/src/gpacmp4/gpacmp4.vcxproj.user @@ -0,0 +1,7 @@ + + + + "F:\ConBackup\Closed captions\Comcast\Video Samples\mov\PD59280_KOSHOW20111104FANC3.mov" + WindowsLocalDebugger + + \ No newline at end of file diff --git a/src/gpacmp4/hinting.c b/src/gpacmp4/hinting.c new file mode 100644 index 00000000..942f3afb --- /dev/null +++ b/src/gpacmp4/hinting.c @@ -0,0 +1,986 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / ISO Media File Format sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include + +#if !defined(GPAC_DISABLE_ISOM) && !defined(GPAC_DISABLE_ISOM_HINTING) + +GF_Box *ghnt_New() +{ + GF_HintSampleEntryBox *tmp; + GF_SAFEALLOC(tmp, GF_HintSampleEntryBox); + if (tmp == NULL) return NULL; + tmp->HintDataTable = gf_list_new(); + if (!tmp->HintDataTable) { + gf_free(tmp); + return NULL; + } + //this type is used internally for protocols that share the same base entry + //currently only RTP uses this, but a flexMux could use this entry too... + tmp->type = GF_ISOM_BOX_TYPE_GHNT; + tmp->HintTrackVersion = 1; + tmp->LastCompatibleVersion = 1; + return (GF_Box *)tmp; +} + +void ghnt_del(GF_Box *s) +{ + GF_HintSampleEntryBox *ptr; + + ptr = (GF_HintSampleEntryBox *)s; + gf_isom_box_array_del(ptr->HintDataTable); + if (ptr->hint_sample) gf_isom_hint_sample_del(ptr->hint_sample); + gf_free(ptr); +} + +GF_Err ghnt_Read(GF_Box *s, GF_BitStream *bs) +{ + GF_Box *a; + GF_Err e; + GF_HintSampleEntryBox *ptr = (GF_HintSampleEntryBox *)s; + if (ptr == NULL) return GF_BAD_PARAM; + + if (ptr->size < 16) return GF_ISOM_INVALID_FILE; + + gf_bs_read_data(bs, ptr->reserved, 6); + ptr->dataReferenceIndex = gf_bs_read_u16(bs); + ptr->HintTrackVersion = gf_bs_read_u16(bs); + ptr->LastCompatibleVersion = gf_bs_read_u16(bs); + ptr->MaxPacketSize = gf_bs_read_u32(bs); + ptr->size -= 16; + + while (ptr->size) { + e = gf_isom_parse_box(&a, bs); + if (e) return e; + e = gf_list_add(ptr->HintDataTable, a); + if (e) return e; + ptr->size -= a->size; + } + return GF_OK; +} + +#ifndef GPAC_DISABLE_ISOM_WRITE + +GF_Err ghnt_Write(GF_Box *s, GF_BitStream *bs) +{ + GF_Err e; + GF_HintSampleEntryBox *ptr = (GF_HintSampleEntryBox *)s; + + e = gf_isom_box_write_header(s, bs); + if (e) return e; + gf_bs_write_data(bs, ptr->reserved, 6); + gf_bs_write_u16(bs, ptr->dataReferenceIndex); + gf_bs_write_u16(bs, ptr->HintTrackVersion); + gf_bs_write_u16(bs, ptr->LastCompatibleVersion); + gf_bs_write_u32(bs, ptr->MaxPacketSize); + return gf_isom_box_array_write(s, ptr->HintDataTable, bs); +} + +GF_Err ghnt_Size(GF_Box *s) +{ + GF_Err e; + GF_HintSampleEntryBox *ptr = (GF_HintSampleEntryBox *)s; + + e = gf_isom_box_get_size(s); + if (e) return e; + ptr->size += 16; + e = gf_isom_box_array_size(s, ptr->HintDataTable); + if (e) return e; + return GF_OK; +} + + +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + + +GF_HintSample *gf_isom_hint_sample_new(u32 ProtocolType) +{ + GF_HintSample *tmp; + u8 type; + + switch (ProtocolType) { + case GF_ISOM_BOX_TYPE_RTP_STSD: + type = GF_ISMO_HINT_RTP; + break; + default: + return NULL; + } + GF_SAFEALLOC(tmp, GF_HintSample); + tmp->packetTable = gf_list_new(); + tmp->HintType = type; + return tmp; +} + +void gf_isom_hint_sample_del(GF_HintSample *ptr) +{ + GF_HintPacket *pck; + + while (gf_list_count(ptr->packetTable)) { + pck = (GF_HintPacket *)gf_list_get(ptr->packetTable, 0); + gf_isom_hint_pck_del(ptr->HintType, pck); + gf_list_rem(ptr->packetTable, 0); + } + gf_list_del(ptr->packetTable); + if (ptr->AdditionalData) gf_free(ptr->AdditionalData); + + if (ptr->sample_cache) { + while (gf_list_count(ptr->sample_cache)) { + GF_HintDataCache *hdc = (GF_HintDataCache *)gf_list_get(ptr->sample_cache, 0); + gf_list_rem(ptr->sample_cache, 0); + if (hdc->samp) gf_isom_sample_del(&hdc->samp); + gf_free(hdc); + } + gf_list_del(ptr->sample_cache); + } + gf_free(ptr); +} + +GF_Err gf_isom_hint_sample_read(GF_HintSample *ptr, GF_BitStream *bs, u32 sampleSize) +{ + u16 entryCount, i; + GF_HintPacket *pck; + GF_Err e; + u64 sizeIn, sizeOut; + + sizeIn = gf_bs_available(bs); + + entryCount = gf_bs_read_u16(bs); + ptr->reserved = gf_bs_read_u16(bs); + + for (i = 0; i < entryCount; i++) { + pck = gf_isom_hint_pck_new(ptr->HintType); + e = gf_isom_hint_pck_read(ptr->HintType, pck, bs); + if (e) return e; + gf_list_add(ptr->packetTable, pck); + } + + sizeOut = gf_bs_available(bs) - sizeIn; + + //do we have some more data after the packets ?? + if ((u32)sizeOut < sampleSize) { + ptr->dataLength = sampleSize - (u32)sizeOut; + ptr->AdditionalData = (char*)gf_malloc(sizeof(char) * ptr->dataLength); + gf_bs_read_data(bs, ptr->AdditionalData, ptr->dataLength); + } + return GF_OK; +} + + +#ifndef GPAC_DISABLE_ISOM_WRITE + +GF_Err gf_isom_hint_sample_write(GF_HintSample *ptr, GF_BitStream *bs) +{ + u32 count, i; + GF_HintPacket *pck; + GF_Err e; + + count = gf_list_count(ptr->packetTable); + gf_bs_write_u16(bs, count); + gf_bs_write_u16(bs, ptr->reserved); + //write the packet table + for (i=0; ipacketTable, i); + e = gf_isom_hint_pck_write(ptr->HintType, pck, bs); + if (e) return e; + } + //write additional data + if (ptr->AdditionalData) { + gf_bs_write_data(bs, ptr->AdditionalData, ptr->dataLength); + } + return GF_OK; +} + + +u32 gf_isom_hint_sample_size(GF_HintSample *ptr) +{ + u32 size, count, i; + GF_HintPacket *pck; + + size = 4; + count = gf_list_count(ptr->packetTable); + for (i=0; ipacketTable, i); + size += gf_isom_hint_pck_size(ptr->HintType, pck); + } + size += ptr->dataLength; + return size; +} + +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + + + +GF_HintPacket *gf_isom_hint_pck_new(u8 HintType) +{ + switch (HintType) { + case GF_ISMO_HINT_RTP: + return (GF_HintPacket *) gf_isom_hint_rtp_new(); + default: + return NULL; + } +} + +void gf_isom_hint_pck_del(u8 HintType, GF_HintPacket *ptr) +{ + switch (HintType) { + case GF_ISMO_HINT_RTP: + gf_isom_hint_rtp_del((GF_RTPPacket *)ptr); + break; + default: + break; + } +} + +GF_Err gf_isom_hint_pck_read(u8 HintType, GF_HintPacket *ptr, GF_BitStream *bs) +{ + switch (HintType) { + case GF_ISMO_HINT_RTP: + return gf_isom_hint_rtp_read((GF_RTPPacket *)ptr, bs); + default: + return GF_NOT_SUPPORTED; + } +} + +#ifndef GPAC_DISABLE_ISOM_WRITE + +GF_Err gf_isom_hint_pck_write(u8 HintType, GF_HintPacket *ptr, GF_BitStream *bs) +{ + switch (HintType) { + case GF_ISMO_HINT_RTP: + return gf_isom_hint_rtp_write((GF_RTPPacket *)ptr, bs); + default: + return GF_NOT_SUPPORTED; + } +} + +u32 gf_isom_hint_pck_size(u8 HintType, GF_HintPacket *ptr) +{ + switch (HintType) { + case GF_ISMO_HINT_RTP: + return gf_isom_hint_rtp_size((GF_RTPPacket *)ptr); + default: + return 0; + } +} + +GF_Err gf_isom_hint_pck_offset(u8 HintType, GF_HintPacket *ptr, u32 offset, u32 HintSampleNumber) +{ + switch (HintType) { + case GF_ISMO_HINT_RTP: + return gf_isom_hint_rtp_offset((GF_RTPPacket *)ptr, offset, HintSampleNumber); + default: + return GF_NOT_SUPPORTED; + } +} + +GF_Err gf_isom_hint_pck_add_dte(u8 HintType, GF_HintPacket *ptr, GF_GenericDTE *dte, u8 AtBegin) +{ + switch (HintType) { + case GF_ISMO_HINT_RTP: + if (AtBegin) + return gf_list_insert( ((GF_RTPPacket *)ptr)->DataTable, dte, 0); + else + return gf_list_add( ((GF_RTPPacket *)ptr)->DataTable, dte); + + default: + return GF_NOT_SUPPORTED; + } +} + +u32 gf_isom_hint_pck_length(u8 HintType, GF_HintPacket *ptr) +{ + switch (HintType) { + case GF_ISMO_HINT_RTP: + return gf_isom_hint_rtp_length((GF_RTPPacket *)ptr); + default: + return 0; + } +} + + +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + + + +/******************************************************************** + Creation of DataTable entries in the RTP sample +********************************************************************/ + +GF_GenericDTE *New_EmptyDTE() +{ + GF_EmptyDTE *dte = (GF_EmptyDTE *)gf_malloc(sizeof(GF_EmptyDTE)); + dte->source = 0; + return (GF_GenericDTE *)dte; +} + +GF_GenericDTE *New_ImmediateDTE() +{ + GF_ImmediateDTE *dte; + GF_SAFEALLOC(dte, GF_ImmediateDTE); + if (dte) { + dte->source = 1; + dte->dataLength = 0; + } + return (GF_GenericDTE *)dte; +} + +GF_GenericDTE *New_SampleDTE() +{ + GF_SampleDTE *dte = (GF_SampleDTE *)gf_malloc(sizeof(GF_SampleDTE)); + dte->source = 2; + //can be -1 in QT , so init at -2 + dte->trackRefIndex = (s8) -2; + dte->dataLength = 0; + dte->sampleNumber = 0; + dte->samplesPerComp = 1; + dte->byteOffset = 0; + dte->bytesPerComp = 1; + return (GF_GenericDTE *)dte; +} + +GF_GenericDTE *New_StreamDescDTE() +{ + GF_StreamDescDTE *dte = (GF_StreamDescDTE *)gf_malloc(sizeof(GF_StreamDescDTE)); + dte->source = 3; + dte->byteOffset = 0; + dte->dataLength = 0; + dte->reserved = 0; + dte->streamDescIndex = 0; + //can be -1 in QT , so init at -2 + dte->trackRefIndex = (s8) -2; + return (GF_GenericDTE *)dte; +} + +//creation of DTEs +GF_GenericDTE *NewDTE(u8 type) +{ + switch (type) { + case 0: + return New_EmptyDTE(); + case 1: + return New_ImmediateDTE(); + case 2: + return New_SampleDTE(); + case 3: + return New_StreamDescDTE(); + default: + return NULL; + } +} + +/******************************************************************** + Deletion of DataTable entries in the RTP sample +********************************************************************/ +void Del_EmptyDTE(GF_EmptyDTE *dte) +{ gf_free(dte); } + +void Del_ImmediateDTE(GF_ImmediateDTE *dte) +{ gf_free(dte); } + +void Del_SampleDTE(GF_SampleDTE *dte) +{ gf_free(dte); } + +void Del_StreamDescDTE(GF_StreamDescDTE *dte) +{ gf_free(dte); } + +//deletion of DTEs +void DelDTE(GF_GenericDTE *dte) +{ + switch (dte->source) { + case 0: + Del_EmptyDTE((GF_EmptyDTE *)dte); + break; + case 1: + Del_ImmediateDTE((GF_ImmediateDTE *)dte); + break; + case 2: + Del_SampleDTE((GF_SampleDTE *)dte); + break; + case 3: + Del_StreamDescDTE((GF_StreamDescDTE *)dte); + break; + default: + return; + } +} + + + +/******************************************************************** + Reading of DataTable entries in the RTP sample +********************************************************************/ +GF_Err Read_EmptyDTE(GF_EmptyDTE *dte, GF_BitStream *bs) +{ + char empty[15]; + //empty but always 15 bytes !!! + gf_bs_read_data(bs, empty, 15); + return GF_OK; +} + +GF_Err Read_ImmediateDTE(GF_ImmediateDTE *dte, GF_BitStream *bs) +{ + dte->dataLength = gf_bs_read_u8(bs); + if (dte->dataLength > 14) return GF_ISOM_INVALID_FILE; + gf_bs_read_data(bs, dte->data, dte->dataLength); + if (dte->dataLength < 14) gf_bs_skip_bytes(bs, 14 - dte->dataLength); + return GF_OK; +} + +GF_Err Read_SampleDTE(GF_SampleDTE *dte, GF_BitStream *bs) +{ + dte->trackRefIndex = gf_bs_read_u8(bs); + dte->dataLength = gf_bs_read_u16(bs); + dte->sampleNumber = gf_bs_read_u32(bs); + dte->byteOffset = gf_bs_read_u32(bs); + dte->bytesPerComp = gf_bs_read_u16(bs); + dte->samplesPerComp = gf_bs_read_u16(bs); + return GF_OK; +} + +GF_Err Read_StreamDescDTE(GF_StreamDescDTE *dte, GF_BitStream *bs) +{ + dte->trackRefIndex = gf_bs_read_u8(bs); + dte->dataLength = gf_bs_read_u16(bs); + dte->streamDescIndex = gf_bs_read_u32(bs); + dte->byteOffset = gf_bs_read_u32(bs); + dte->reserved = gf_bs_read_u32(bs); + return GF_OK; +} + +GF_Err ReadDTE(GF_GenericDTE *dte, GF_BitStream *bs) +{ + switch (dte->source) { + case 0: + //nothing to o, it is an empty entry + return Read_EmptyDTE((GF_EmptyDTE *)dte, bs); + case 1: + return Read_ImmediateDTE((GF_ImmediateDTE *)dte, bs); + case 2: + return Read_SampleDTE((GF_SampleDTE *)dte, bs); + case 3: + return Read_StreamDescDTE((GF_StreamDescDTE *)dte, bs); + default: + return GF_ISOM_INVALID_FILE; + } +} + +/******************************************************************** + Writing of DataTable entries in the RTP sample +********************************************************************/ +GF_Err Write_EmptyDTE(GF_EmptyDTE *dte, GF_BitStream *bs) +{ + gf_bs_write_u8(bs, dte->source); + //empty but always 15 bytes !!! + gf_bs_write_data(bs, "empty hint DTE", 15); + return GF_OK; +} + +GF_Err Write_ImmediateDTE(GF_ImmediateDTE *dte, GF_BitStream *bs) +{ + char data[14]; + gf_bs_write_u8(bs, dte->source); + gf_bs_write_u8(bs, dte->dataLength); + gf_bs_write_data(bs, dte->data, dte->dataLength); + if (dte->dataLength < 14) { + memset(data, 0, 14); + gf_bs_write_data(bs, data, 14 - dte->dataLength); + } + return GF_OK; +} + +GF_Err Write_SampleDTE(GF_SampleDTE *dte, GF_BitStream *bs) +{ + gf_bs_write_u8(bs, dte->source); + gf_bs_write_u8(bs, dte->trackRefIndex); + gf_bs_write_u16(bs, dte->dataLength); + gf_bs_write_u32(bs, dte->sampleNumber); + gf_bs_write_u32(bs, dte->byteOffset); + gf_bs_write_u16(bs, dte->bytesPerComp); + gf_bs_write_u16(bs, dte->samplesPerComp); + return GF_OK; +} + +GF_Err Write_StreamDescDTE(GF_StreamDescDTE *dte, GF_BitStream *bs) +{ + gf_bs_write_u8(bs, dte->source); + + gf_bs_write_u8(bs, dte->trackRefIndex); + gf_bs_write_u16(bs, dte->dataLength); + gf_bs_write_u32(bs, dte->streamDescIndex); + gf_bs_write_u32(bs, dte->byteOffset); + gf_bs_write_u32(bs, dte->reserved); + return GF_OK; +} + +GF_Err WriteDTE(GF_GenericDTE *dte, GF_BitStream *bs) +{ + switch (dte->source) { + case 0: + //nothing to do, it is an empty entry + return Write_EmptyDTE((GF_EmptyDTE *)dte, bs); + case 1: + return Write_ImmediateDTE((GF_ImmediateDTE *)dte, bs); + case 2: + return Write_SampleDTE((GF_SampleDTE *)dte, bs); + case 3: + return Write_StreamDescDTE((GF_StreamDescDTE *)dte, bs); + default: + return GF_ISOM_INVALID_FILE; + } +} + +GF_Err OffsetDTE(GF_GenericDTE *dte, u32 offset, u32 HintSampleNumber) +{ + GF_SampleDTE *sDTE; + //offset shifting is only true for intra sample reference + switch (dte->source) { + case 2: + break; + default: + return GF_OK; + } + + sDTE = (GF_SampleDTE *)dte; + //we only adjust for intra HintTrack reference + if (sDTE->trackRefIndex != (s8) -1) return GF_OK; + //and in the same sample + if (sDTE->sampleNumber != HintSampleNumber) return GF_OK; + sDTE->byteOffset += offset; + return GF_OK; +} + +GF_RTPPacket *gf_isom_hint_rtp_new() +{ + GF_RTPPacket *tmp; + GF_SAFEALLOC(tmp, GF_RTPPacket); + if (!tmp) return NULL; + tmp->TLV = gf_list_new(); + tmp->DataTable = gf_list_new(); + return tmp; +} + +void gf_isom_hint_rtp_del(GF_RTPPacket *ptr) +{ + GF_GenericDTE *p; + //the DTE + while (gf_list_count(ptr->DataTable)) { + p = (GF_GenericDTE *)gf_list_get(ptr->DataTable, 0); + DelDTE(p); + gf_list_rem(ptr->DataTable, 0); + } + gf_list_del(ptr->DataTable); + //the TLV + gf_isom_box_array_del(ptr->TLV); + gf_free(ptr); +} + +GF_Err gf_isom_hint_rtp_read(GF_RTPPacket *ptr, GF_BitStream *bs) +{ + GF_Err e; + u8 hasTLV, type; + u16 i, count; + u32 TLVsize, tempSize; + GF_GenericDTE *dte; + GF_Box *a; + + ptr->relativeTransTime = gf_bs_read_u32(bs); + //RTP Header + //1- reserved fields + gf_bs_read_int(bs, 2); + ptr->P_bit = gf_bs_read_int(bs, 1); + ptr->X_bit = gf_bs_read_int(bs, 1); + gf_bs_read_int(bs, 4); + ptr->M_bit = gf_bs_read_int(bs, 1); + ptr->payloadType = gf_bs_read_int(bs, 7); + + ptr->SequenceNumber = gf_bs_read_u16(bs); + gf_bs_read_int(bs, 13); + hasTLV = gf_bs_read_int(bs, 1); + ptr->B_bit = gf_bs_read_int(bs, 1); + ptr->R_bit = gf_bs_read_int(bs, 1); + count = gf_bs_read_u16(bs); + + //read the TLV + if (hasTLV) { + tempSize = 4; //TLVsize includes its field length + TLVsize = gf_bs_read_u32(bs); + while (tempSize < TLVsize) { + e = gf_isom_parse_box(&a, bs); + if (e) return e; + gf_list_add(ptr->TLV, a); + tempSize += (u32) a->size; + } + if (tempSize != TLVsize) return GF_ISOM_INVALID_FILE; + } + + //read the DTEs + for (i=0; idataLength) add_it = 1; + break; + case 2: + if ( ((GF_SampleDTE *)dte)->dataLength) add_it = 1; + break; + case 3: + if ( ((GF_StreamDescDTE *)dte)->dataLength) add_it = 1; + break; + } + if (add_it) + gf_list_add(ptr->DataTable, dte); + else + DelDTE(dte); + } + return GF_OK; +} + +GF_Err gf_isom_hint_rtp_offset(GF_RTPPacket *ptr, u32 offset, u32 HintSampleNumber) +{ + u32 count, i; + GF_GenericDTE *dte; + GF_Err e; + + count = gf_list_count(ptr->DataTable); + for (i=0; iDataTable, i); + e = OffsetDTE(dte, offset, HintSampleNumber); + if (e) return e; + } + return GF_OK; +} + +//Gets the REAL size of the packet once rebuild, but without CSRC fields in the +//header +u32 gf_isom_hint_rtp_length(GF_RTPPacket *ptr) +{ + u32 size, count, i; + GF_GenericDTE *dte; + + //64 bit header + size = 8; + //32 bit SSRC + size += 4; + count = gf_list_count(ptr->DataTable); + for (i=0; iDataTable, i); + switch (dte->source) { + case 0: + break; + case 1: + size += ((GF_ImmediateDTE *)dte)->dataLength; + break; + case 2: + size += ((GF_SampleDTE *)dte)->dataLength; + break; + case 3: + size += ((GF_StreamDescDTE *)dte)->dataLength; + break; + } + } + return size; +} + + +#ifndef GPAC_DISABLE_ISOM_WRITE + +u32 gf_isom_hint_rtp_size(GF_RTPPacket *ptr) +{ + GF_Box none; + u32 size, count; + //the RTP Header size and co + size = 12; + //the extra table size + count = gf_list_count(ptr->TLV); + if (count) { + none.size = 4; //WE INCLUDE THE SIZE FIELD LENGTH + none.type = 0; + //REMEMBER THAT TLV ENTRIES ARE 4-BYTES ALIGNED !!! + gf_isom_box_array_size(&none, ptr->TLV); + size += (u32) none.size; + } + //the DTE (each entry is 16 bytes) + count = gf_list_count(ptr->DataTable); + size += count * 16; + return size; +} + +GF_Err gf_isom_hint_rtp_write(GF_RTPPacket *ptr, GF_BitStream *bs) +{ + GF_Err e; + u32 TLVcount, DTEcount, i; + GF_Box none; + GF_GenericDTE *dte; + + gf_bs_write_u32(bs, ptr->relativeTransTime); + //RTP Header +// gf_bs_write_int(bs, 2, 2); + //version is 2 + gf_bs_write_int(bs, 2, 2); + gf_bs_write_int(bs, ptr->P_bit, 1); + gf_bs_write_int(bs, ptr->X_bit, 1); + gf_bs_write_int(bs, 0, 4); + gf_bs_write_int(bs, ptr->M_bit, 1); + gf_bs_write_int(bs, ptr->payloadType, 7); + + gf_bs_write_u16(bs, ptr->SequenceNumber); + gf_bs_write_int(bs, 0, 13); + TLVcount = gf_list_count(ptr->TLV); + DTEcount = gf_list_count(ptr->DataTable); + gf_bs_write_int(bs, TLVcount ? 1 : 0, 1); + gf_bs_write_int(bs, ptr->B_bit, 1); + gf_bs_write_int(bs, ptr->R_bit, 1); + + gf_bs_write_u16(bs, DTEcount); + + if (TLVcount) { + //first write the size of the table ... + none.size = 4; //WE INCLUDE THE SIZE FIELD LENGTH + none.type = 0; + gf_isom_box_array_size(&none, ptr->TLV); + gf_bs_write_u32(bs, (u32) none.size); + e = gf_isom_box_array_write(&none, ptr->TLV, bs); + if (e) return e; + } + //write the DTE... + for (i = 0; i < DTEcount; i++) { + dte = (GF_GenericDTE *)gf_list_get(ptr->DataTable, i); + e = WriteDTE(dte, bs); + if (e) return e; + } + return GF_OK; +} + +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + +GF_EXPORT +GF_Err gf_isom_reset_hint_reader(GF_ISOFile *the_file, u32 trackNumber, u32 sample_start, u32 ts_offset, u32 sn_offset, u32 ssrc) +{ + GF_Err e; + GF_TrackBox *trak; + GF_HintSampleEntryBox *entry; + + trak = gf_isom_get_track_from_file(the_file, trackNumber); + if (!trak) return GF_BAD_PARAM; + + if (!sample_start) return GF_BAD_PARAM; + if (sample_start>=trak->Media->information->sampleTable->SampleSize->sampleCount) return GF_BAD_PARAM; + + e = Media_GetSampleDesc(trak->Media, 1, (GF_SampleEntryBox **) &entry, NULL); + if (e) return e; + switch (entry->type) { + case GF_ISOM_BOX_TYPE_RTP_STSD: + break; + default: + return GF_NOT_SUPPORTED; + } + + entry->hint_ref = NULL; + e = Track_FindRef(trak, GF_ISOM_REF_HINT, &entry->hint_ref); + if (e) return e; + + entry->cur_sample = sample_start; + entry->pck_sn = 1 + sn_offset; + entry->ssrc = ssrc; + entry->ts_offset = ts_offset; + if (entry->hint_sample) gf_isom_hint_sample_del(entry->hint_sample); + entry->hint_sample = NULL; + return GF_OK; +} + +static GF_Err gf_isom_load_next_hint_sample(GF_ISOFile *the_file, u32 trackNumber, GF_TrackBox *trak, GF_HintSampleEntryBox *entry) +{ + GF_BitStream *bs; + u32 descIdx; + GF_ISOSample *samp; + + if (!entry->cur_sample) return GF_BAD_PARAM; + if (entry->cur_sample>trak->Media->information->sampleTable->SampleSize->sampleCount) return GF_EOS; + + samp = gf_isom_get_sample(the_file, trackNumber, entry->cur_sample, &descIdx); + if (!samp) return GF_IO_ERR; + entry->cur_sample++; + + if (entry->hint_sample) gf_isom_hint_sample_del(entry->hint_sample); + + bs = gf_bs_new(samp->data, samp->dataLength, GF_BITSTREAM_READ); + entry->hint_sample = gf_isom_hint_sample_new(entry->type); + gf_isom_hint_sample_read(entry->hint_sample, bs, samp->dataLength); + gf_bs_del(bs); + entry->hint_sample->TransmissionTime = samp->DTS; + gf_isom_sample_del(&samp); + entry->hint_sample->sample_cache = gf_list_new(); + return GF_OK; +} + +static GF_ISOSample *gf_isom_get_data_sample(GF_HintSample *hsamp, GF_TrackBox *trak, u32 sample_num) +{ + GF_ISOSample *samp; + GF_HintDataCache *hdc; + u32 i, count; + count = gf_list_count(hsamp->sample_cache); + for (i=0; isample_cache, i); + if ((hdc->sample_num==sample_num) && (hdc->trak==trak)) return hdc->samp; + } + + samp = gf_isom_sample_new(); + Media_GetSample(trak->Media, sample_num, &samp, &i, 0, NULL); + if (!samp) return NULL; + GF_SAFEALLOC(hdc, GF_HintDataCache); + hdc->samp = samp; + hdc->sample_num = sample_num; + hdc->trak = trak; + /*we insert all new samples, since they're more likely to be fetched next (except for audio + interleaving and other multiplex)*/ + gf_list_insert(hsamp->sample_cache, hdc, 0); + return samp; +} + +GF_EXPORT +GF_Err gf_isom_next_hint_packet(GF_ISOFile *the_file, u32 trackNumber, char **pck_data, u32 *pck_size, Bool *disposable, Bool *repeated, u32 *trans_ts, u32 *sample_num) +{ + GF_RTPPacket *pck; + GF_Err e; + GF_BitStream *bs; + GF_TrackBox *trak, *ref_trak; + GF_HintSampleEntryBox *entry; + u32 i, count, ts; + s32 cts_off; + + *pck_data = NULL; + *pck_size = 0; + if (trans_ts) *trans_ts = 0; + if (disposable) *disposable = 0; + if (repeated) *repeated = 0; + if (sample_num) *sample_num = 0; + + trak = gf_isom_get_track_from_file(the_file, trackNumber); + if (!trak) return GF_BAD_PARAM; + e = Media_GetSampleDesc(trak->Media, 1, (GF_SampleEntryBox **) &entry, NULL); + if (e) return e; + switch (entry->type) { + case GF_ISOM_BOX_TYPE_RTP_STSD: + break; + default: + return GF_NOT_SUPPORTED; + } + + if (!entry->hint_sample) { + e = gf_isom_load_next_hint_sample(the_file, trackNumber, trak, entry); + if (e) return e; + } + pck = (GF_RTPPacket *)gf_list_get(entry->hint_sample->packetTable, 0); + gf_list_rem(entry->hint_sample->packetTable, 0); + if (!pck) return GF_BAD_PARAM; + + bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); + /*write RTP header*/ + gf_bs_write_int(bs, 2, 2); /*version*/ + gf_bs_write_int(bs, pck->P_bit, 1); /*P bit*/ + gf_bs_write_int(bs, pck->X_bit, 1); /*X bit*/ + gf_bs_write_int(bs, 0, 4); /*CSRC count*/ + gf_bs_write_int(bs, pck->M_bit, 1); /*M bit*/ + gf_bs_write_int(bs, pck->payloadType, 7); /*payt*/ + gf_bs_write_u16(bs, entry->pck_sn); /*seq num*/ + entry->pck_sn++; + + /*look for CTS offset in TLV*/ + cts_off = 0; + count = gf_list_count(pck->TLV); + for (i=0; iTLV, i); + if (rtpo->type == GF_ISOM_BOX_TYPE_RTPO) { + cts_off = rtpo->timeOffset; + break; + } + } + /*TS - TODO check TS wrapping*/ + ts = (u32) (entry->hint_sample->TransmissionTime + pck->relativeTransTime + entry->ts_offset + cts_off); + gf_bs_write_u32(bs, ts ); + gf_bs_write_u32(bs, entry->ssrc); /*SSRC*/ + + /*then build all data*/ + count = gf_list_count(pck->DataTable); + for (i=0; iDataTable, i); + switch (dte->source) { + /*empty*/ + case 0: + break; + /*immediate data*/ + case 1: + gf_bs_write_data(bs, ((GF_ImmediateDTE *)dte)->data, ((GF_ImmediateDTE *)dte)->dataLength); + break; + /*sample data*/ + case 2: + { + GF_ISOSample *samp; + GF_SampleDTE *sdte = (GF_SampleDTE *)dte; + /*get track if not this one*/ + if (sdte->trackRefIndex != (s8) -1) { + if (!entry->hint_ref || !entry->hint_ref->trackIDs) { + gf_isom_hint_rtp_del(pck); + gf_bs_del(bs); + return GF_ISOM_INVALID_FILE; + } + ref_trak = gf_isom_get_track_from_id(trak->moov, entry->hint_ref->trackIDs[(u32)sdte->trackRefIndex]); + } else { + ref_trak = trak; + } + samp = gf_isom_get_data_sample(entry->hint_sample, ref_trak, sdte->sampleNumber); + if (!samp) { + gf_isom_hint_rtp_del(pck); + gf_bs_del(bs); + return GF_IO_ERR; + } + gf_bs_write_data(bs, samp->data + sdte->byteOffset, sdte->dataLength); + } + break; + /*sample desc data - currently NOT SUPPORTED !!!*/ + case 3: + break; + } + } + if (trans_ts) *trans_ts = ts; + if (disposable) *disposable = pck->B_bit; + if (repeated) *repeated = pck->R_bit; + if (sample_num) *sample_num = entry->cur_sample-1; + + gf_bs_get_content(bs, pck_data, pck_size); + gf_bs_del(bs); + gf_isom_hint_rtp_del(pck); + if (!gf_list_count(entry->hint_sample->packetTable)) { + gf_isom_hint_sample_del(entry->hint_sample); + entry->hint_sample = NULL; + } + return GF_OK; +} + +#endif /* !defined(GPAC_DISABLE_ISOM) && !defined(GPAC_DISABLE_ISOM_HINTING)*/ diff --git a/src/gpacmp4/ipmpx_code.c b/src/gpacmp4/ipmpx_code.c new file mode 100644 index 00000000..145799b7 --- /dev/null +++ b/src/gpacmp4/ipmpx_code.c @@ -0,0 +1,2021 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / MPEG-4 ObjectDescriptor sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include + +#ifndef GPAC_MINIMAL_ODF + + +#define GF_IPMPX_DATA_ALLOC(__ptr, __stname, __tag) \ + __ptr = (__stname*)gf_malloc(sizeof(__stname)); \ + if (!__ptr) return NULL; \ + memset(__ptr, 0, sizeof(__stname)); \ + ((GF_IPMPX_Data *)__ptr)->tag = __tag; \ + ((GF_IPMPX_Data *)__ptr)->Version = 0x01; \ + + +#define GF_IPMPX_DELETE_ARRAY(__ar) if (__ar) { if (__ar->data) gf_free(__ar->data); gf_free(__ar); } + +u32 get_field_size(u32 size_desc) +{ + if (size_desc < 0x00000080) return 1; + else if (size_desc < 0x00004000) return 2; + else if (size_desc < 0x00200000) return 3; + else return 4; +} + +void write_var_size(GF_BitStream *bs, u32 size) +{ + unsigned char vals[4]; + u32 length = size; + + vals[3] = (unsigned char) (length & 0x7f); length >>= 7; + vals[2] = (unsigned char) ((length & 0x7f) | 0x80); length >>= 7; + vals[1] = (unsigned char) ((length & 0x7f) | 0x80); length >>= 7; + vals[0] = (unsigned char) ((length & 0x7f) | 0x80); + if (size < 0x00000080) { + gf_bs_write_int(bs, vals[3], 8); + } else if (size < 0x00004000) { + gf_bs_write_int(bs, vals[2], 8); + gf_bs_write_int(bs, vals[3], 8); + } else if (size < 0x00200000) { + gf_bs_write_int(bs, vals[1], 8); + gf_bs_write_int(bs, vals[2], 8); + gf_bs_write_int(bs, vals[3], 8); + } else if (size < 0x10000000) { + gf_bs_write_int(bs, vals[0], 8); + gf_bs_write_int(bs, vals[1], 8); + gf_bs_write_int(bs, vals[2], 8); + gf_bs_write_int(bs, vals[3], 8); + } +} + +GF_IPMPX_ByteArray *GF_IPMPX_GetByteArray(GF_BitStream *bs) +{ + GF_IPMPX_ByteArray *ba; + u32 val, size; + size = 0; + do { + val = gf_bs_read_int(bs, 8); + size <<= 7; + size |= val & 0x7F; + } while ( val & 0x80 ); + if (!size) return NULL; + ba = (GF_IPMPX_ByteArray*)gf_malloc(sizeof(GF_IPMPX_ByteArray)); + ba->data = (char*)gf_malloc(sizeof(char)*size); + gf_bs_read_data(bs, ba->data, size); + ba->length = size; + return ba; +} + +u32 GF_IPMPX_GetByteArraySize(GF_IPMPX_ByteArray *ba) +{ + if (!ba) return 1; + return ba->length + get_field_size(ba->length); +} + +void GF_IPMPX_WriteByteArray(GF_BitStream *bs, GF_IPMPX_ByteArray *ba) +{ + if (!ba || !ba->data) { + write_var_size(bs, 0); + } else { + write_var_size(bs, ba->length); + gf_bs_write_data(bs, ba->data, ba->length); + } +} + +void GF_IPMPX_AUTH_Delete(GF_IPMPX_Authentication *auth) +{ + if (!auth) return; + switch (auth->tag) { + case GF_IPMPX_AUTH_AlgorithmDescr_Tag: + { + GF_IPMPX_AUTH_AlgorithmDescriptor *p = (GF_IPMPX_AUTH_AlgorithmDescriptor *)auth; + GF_IPMPX_DELETE_ARRAY(p->specAlgoID); + GF_IPMPX_DELETE_ARRAY(p->OpaqueData); + gf_free(p); + } + break; + case GF_IPMPX_AUTH_KeyDescr_Tag: + { + GF_IPMPX_AUTH_KeyDescriptor *p = (GF_IPMPX_AUTH_KeyDescriptor *)auth; + if (p->keyBody) gf_free(p->keyBody); + gf_free(p); + } + break; + } +} + +u32 GF_IPMPX_AUTH_Size(GF_IPMPX_Authentication *auth) +{ + u32 size; + if (!auth) return 0; + switch (auth->tag) { + case GF_IPMPX_AUTH_AlgorithmDescr_Tag: + { + GF_IPMPX_AUTH_AlgorithmDescriptor *p = (GF_IPMPX_AUTH_AlgorithmDescriptor *)auth; + size = 1 + (p->specAlgoID ? GF_IPMPX_GetByteArraySize(p->specAlgoID) : 2); + size += GF_IPMPX_GetByteArraySize(p->OpaqueData); + return size; + } + break; + case GF_IPMPX_AUTH_KeyDescr_Tag: + { + GF_IPMPX_AUTH_KeyDescriptor *p = (GF_IPMPX_AUTH_KeyDescriptor *)auth; + size = p->keyBodyLength; + return size; + } + break; + default: + return 0; + } +} + +u32 GF_IPMPX_AUTH_FullSize(GF_IPMPX_Authentication *auth) +{ + u32 size = GF_IPMPX_AUTH_Size(auth); + size += get_field_size(size); + size += 1;/*tag*/ + return size; +} + +GF_Err WriteGF_IPMPX_AUTH(GF_BitStream *bs, GF_IPMPX_Authentication *auth) +{ + u32 size; + + if (!auth) return GF_OK; + gf_bs_write_int(bs, auth->tag, 8); + + size = GF_IPMPX_AUTH_Size(auth); + write_var_size(bs, size); + + switch (auth->tag) { + case GF_IPMPX_AUTH_AlgorithmDescr_Tag: + { + GF_IPMPX_AUTH_AlgorithmDescriptor *p = (GF_IPMPX_AUTH_AlgorithmDescriptor *)auth; + gf_bs_write_int(bs, p->specAlgoID ? 0 : 1, 1); + gf_bs_write_int(bs, 0, 7); + if (p->specAlgoID) { + GF_IPMPX_WriteByteArray(bs, p->specAlgoID); + } else { + gf_bs_write_int(bs, p->regAlgoID, 16); + } + GF_IPMPX_WriteByteArray(bs, p->OpaqueData); + } + break; + case GF_IPMPX_AUTH_KeyDescr_Tag: + { + GF_IPMPX_AUTH_KeyDescriptor *p = (GF_IPMPX_AUTH_KeyDescriptor *)auth; + /*tag*/ + gf_bs_write_data(bs, p->keyBody, p->keyBodyLength); + } + break; + default: + break; + } + return GF_OK; +} + +GF_Err GF_IPMPX_AUTH_Parse(GF_BitStream *bs, GF_IPMPX_Authentication **auth) +{ + u32 val, size, tag; + + tag = gf_bs_read_int(bs, 8); + size = 0; + do { + val = gf_bs_read_int(bs, 8); + size <<= 7; + size |= val & 0x7F; + } while ( val & 0x80 ); + /*don't know if tolerated in IPMPX*/ + if (!size) return GF_OK; + + switch (tag) { + case GF_IPMPX_AUTH_KeyDescr_Tag: + { + GF_IPMPX_AUTH_KeyDescriptor *p; + GF_SAFEALLOC(p, GF_IPMPX_AUTH_KeyDescriptor); + if (!p) return GF_OUT_OF_MEM; + p->tag = tag; + p->keyBodyLength = size; + p->keyBody = (char*)gf_malloc(sizeof(char)*size); + gf_bs_read_data(bs, p->keyBody, size); + *auth = (GF_IPMPX_Authentication *)p; + return GF_OK; + } + break; + case GF_IPMPX_AUTH_AlgorithmDescr_Tag: + { + Bool isReg; + GF_IPMPX_AUTH_AlgorithmDescriptor *p; + GF_SAFEALLOC(p, GF_IPMPX_AUTH_AlgorithmDescriptor); + if (!p) return GF_OUT_OF_MEM; + p->tag = tag; + isReg = gf_bs_read_int(bs, 1); + gf_bs_read_int(bs, 7); + if (isReg) { + p->regAlgoID = gf_bs_read_int(bs, 16); + } else { + p->specAlgoID = GF_IPMPX_GetByteArray(bs); + } + p->OpaqueData = GF_IPMPX_GetByteArray(bs); + *auth = (GF_IPMPX_Authentication *)p; + return GF_OK; + } + break; + default: + break; + } + return GF_NON_COMPLIANT_BITSTREAM; +} + + +GF_Err GF_IPMPX_ReadData(GF_BitStream *bs, GF_IPMPX_Data *_p, u32 size); + +u32 gf_ipmpx_data_full_size(GF_IPMPX_Data *p) +{ + u32 size; + if (!p) return 0; + size = gf_ipmpx_data_size(p); + size += 5; /*Version and dataID*/ + size += get_field_size(size); + size += 1;/*tag*/ + return size; +} + +GF_Err gf_ipmpx_data_parse(GF_BitStream *bs, GF_IPMPX_Data **out_data) +{ + GF_Err e; + u32 val, size; + u8 tag; + GF_IPMPX_Data *p; + + *out_data = NULL; + tag = gf_bs_read_int(bs, 8); + size = 0; + do { + val = gf_bs_read_int(bs, 8); + size <<= 7; + size |= val & 0x7F; + } while ( val & 0x80 ); + /*don't know if tolerated in IPMPX*/ + if (!size) return GF_OK; + + p = gf_ipmpx_data_new(tag); + if (!p) return GF_NON_COMPLIANT_BITSTREAM; + p->Version = gf_bs_read_int(bs, 8); + p->dataID = gf_bs_read_int(bs, 32); + e = GF_IPMPX_ReadData(bs, p, size); + if (e) { + gf_ipmpx_data_del(p); + return e; + } + *out_data = p; + return GF_OK; +} + +GF_Err GF_IPMPX_WriteBase(GF_BitStream *bs, GF_IPMPX_Data *p) +{ + u32 size; + + if (!p) return GF_BAD_PARAM; + size = gf_ipmpx_data_size(p); + size += 5; /*Version & dataID*/ + gf_bs_write_int(bs, p->tag, 8); + write_var_size(bs, size); + gf_bs_write_int(bs, p->Version, 8); + gf_bs_write_int(bs, p->dataID, 32); + return GF_OK; +} + + +static GF_IPMPX_Data *NewGF_IPMPX_InitAuthentication() +{ + GF_IPMPX_InitAuthentication *ptr; + GF_IPMPX_DATA_ALLOC(ptr, GF_IPMPX_InitAuthentication, GF_IPMPX_INIT_AUTHENTICATION_TAG); + return (GF_IPMPX_Data *) ptr; +} +static void DelGF_IPMPX_InitAuthentication(GF_IPMPX_Data *_p) +{ + GF_IPMPX_InitAuthentication *p = (GF_IPMPX_InitAuthentication*)_p; + gf_free(p); +} +static GF_Err ReadGF_IPMPX_InitAuthentication(GF_BitStream *bs, GF_IPMPX_Data *_p, u32 size) +{ + GF_IPMPX_InitAuthentication *p = (GF_IPMPX_InitAuthentication*)_p; + p->Context = gf_bs_read_int(bs, 32); + p->AuthType = gf_bs_read_int(bs, 8); + return GF_OK; +} +static u32 SizeGF_IPMPX_InitAuthentication(GF_IPMPX_Data *_p) +{ + return 5; +} +static GF_Err WriteGF_IPMPX_InitAuthentication(GF_BitStream *bs, GF_IPMPX_Data *_p) +{ + GF_IPMPX_InitAuthentication *p = (GF_IPMPX_InitAuthentication*)_p; + gf_bs_write_int(bs, p->Context, 32); + gf_bs_write_int(bs, p->AuthType, 8); + return GF_OK; +} + + +static GF_IPMPX_Data *NewGF_IPMPX_MutualAuthentication() +{ + GF_IPMPX_MutualAuthentication *ptr; + GF_IPMPX_DATA_ALLOC(ptr, GF_IPMPX_MutualAuthentication, GF_IPMPX_MUTUAL_AUTHENTICATION_TAG); + ptr->candidateAlgorithms = gf_list_new(); + ptr->agreedAlgorithms = gf_list_new(); + ptr->certificates = gf_list_new(); + return (GF_IPMPX_Data *) ptr; +} + +static void delete_algo_list(GF_List *algos) +{ + u32 i; + for (i=0;icandidateAlgorithms); + delete_algo_list(p->agreedAlgorithms); + GF_IPMPX_DELETE_ARRAY(p->AuthenticationData); + GF_IPMPX_DELETE_ARRAY(p->opaque); + GF_IPMPX_DELETE_ARRAY(p->authCodes); + gf_ipmpx_data_del((GF_IPMPX_Data *)p->trustData); + GF_IPMPX_AUTH_Delete((GF_IPMPX_Authentication*)p->publicKey); + while (gf_list_count(p->certificates)) { + GF_IPMPX_ByteArray *ba = (GF_IPMPX_ByteArray *)gf_list_get(p->certificates, 0); + gf_list_rem(p->certificates, 0); + GF_IPMPX_DELETE_ARRAY(ba); + } + gf_list_del(p->certificates); + gf_free(p); +} +static GF_Err ReadGF_IPMPX_MutualAuthentication(GF_BitStream *bs, GF_IPMPX_Data *_p, u32 size) +{ + GF_Err e; + u32 i, count; + Bool requestNegotiation, successNegotiation, inclAuthenticationData, inclAuthCodes; + GF_IPMPX_MutualAuthentication *p = (GF_IPMPX_MutualAuthentication *)_p; + + requestNegotiation = gf_bs_read_int(bs, 1); + successNegotiation = gf_bs_read_int(bs, 1); + p->failedNegotiation = gf_bs_read_int(bs, 1); + inclAuthenticationData = gf_bs_read_int(bs, 1); + inclAuthCodes = gf_bs_read_int(bs, 1); + gf_bs_read_int(bs, 3); + + if (requestNegotiation) { + count = gf_bs_read_int(bs, 8); + for (i=0; icandidateAlgorithms, auth); + } + } + if (successNegotiation) { + count = gf_bs_read_int(bs, 8); + for (i=0; iagreedAlgorithms, auth); + } + } + if (inclAuthenticationData) p->AuthenticationData = GF_IPMPX_GetByteArray(bs); + if (inclAuthCodes) { + /*unspecified in spec, IM1 uses 8 bits*/ + u32 type = gf_bs_read_int(bs, 8); + switch (type) { + case 0x01: + count = gf_bs_read_int(bs, 8); + p->certType = gf_bs_read_int(bs, 32); + for (i=0; icertificates, ipd); + } + break; + case 0x02: + e = GF_IPMPX_AUTH_Parse(bs, (GF_IPMPX_Authentication**) &p->publicKey); + if (e) return e; + break; + case 0xFE: + p->opaque = GF_IPMPX_GetByteArray(bs); + break; + default: + break; + } + e = gf_ipmpx_data_parse(bs, (GF_IPMPX_Data **) &p->trustData); + if (e) return e; + p->authCodes = GF_IPMPX_GetByteArray(bs); + } + return GF_OK; +} +static u32 SizeGF_IPMPX_MutualAuthentication(GF_IPMPX_Data *_p) +{ + u32 size, i, count; + GF_IPMPX_MutualAuthentication *p = (GF_IPMPX_MutualAuthentication *)_p; + size = 1; + count = gf_list_count(p->candidateAlgorithms); + if (count) { + size += 1; + for (i=0; icandidateAlgorithms, i); + size += GF_IPMPX_AUTH_FullSize(ip_auth); + } + } + count = gf_list_count(p->agreedAlgorithms); + if (count) { + size += 1; + for (i=0; iagreedAlgorithms, i); + size += GF_IPMPX_AUTH_FullSize(ip_auth); + } + } + if (p->AuthenticationData) size += GF_IPMPX_GetByteArraySize(p->AuthenticationData); + + count = gf_list_count(p->certificates); + if (count || p->opaque || p->publicKey) { + size += 1; + /*type 1*/ + if (count) { + size += 1+4; + for (i=0; icertificates, i); + size += GF_IPMPX_GetByteArraySize(ba); + } + } + /*type 2*/ + else if (p->publicKey) { + size += GF_IPMPX_AUTH_FullSize((GF_IPMPX_Authentication*)p->publicKey); + } + /*type 0xFE*/ + else if (p->opaque) { + size += GF_IPMPX_GetByteArraySize(p->opaque); + } + size += gf_ipmpx_data_full_size((GF_IPMPX_Data *)p->trustData); + size += GF_IPMPX_GetByteArraySize(p->authCodes); + } + return size; +} +static GF_Err WriteGF_IPMPX_MutualAuthentication(GF_BitStream *bs, GF_IPMPX_Data *_p) +{ + u32 i, count; + GF_IPMPX_MutualAuthentication *p = (GF_IPMPX_MutualAuthentication *)_p; + + gf_bs_write_int(bs, gf_list_count(p->candidateAlgorithms) ? 1 : 0, 1); + gf_bs_write_int(bs, gf_list_count(p->agreedAlgorithms) ? 1 : 0, 1); + gf_bs_write_int(bs, p->failedNegotiation ? 1 : 0, 1); + gf_bs_write_int(bs, p->AuthenticationData ? 1 : 0, 1); + if (gf_list_count(p->certificates) || p->opaque || p->publicKey) { + gf_bs_write_int(bs, 1, 1); + } else { + gf_bs_write_int(bs, 0, 1); + } + gf_bs_write_int(bs, 0, 3); + + count = gf_list_count(p->candidateAlgorithms); + if (count) { + gf_bs_write_int(bs, count, 8); + for (i=0; icandidateAlgorithms, i); + WriteGF_IPMPX_AUTH(bs, ip_auth); + } + } + count = gf_list_count(p->agreedAlgorithms); + if (count) { + gf_bs_write_int(bs, count, 8); + for (i=0; iagreedAlgorithms, i); + WriteGF_IPMPX_AUTH(bs, ip_auth); + } + } + if (p->AuthenticationData) GF_IPMPX_WriteByteArray(bs, p->AuthenticationData); + + count = gf_list_count(p->certificates); + if (count || p->opaque || p->publicKey) { + /*type 1*/ + if (count) { + gf_bs_write_int(bs, 0x01, 8); + + gf_bs_write_int(bs, count, 8); + gf_bs_write_int(bs, p->certType, 32); + for (i=0; icertificates, i); + if (ipd) GF_IPMPX_WriteByteArray(bs, ipd); + } + } + /*type 2*/ + else if (p->publicKey) { + gf_bs_write_int(bs, 0x02, 8); + WriteGF_IPMPX_AUTH(bs, (GF_IPMPX_Authentication *) p->publicKey); + } + /*type 0xFE*/ + else if (p->opaque) { + gf_bs_write_int(bs, 0xFE, 8); + GF_IPMPX_WriteByteArray(bs, p->opaque); + } + gf_ipmpx_data_write(bs, (GF_IPMPX_Data *)p->trustData); + GF_IPMPX_WriteByteArray(bs, p->authCodes); + } + return GF_OK; +} + +static GF_IPMPX_Data *NewGF_IPMPX_TrustSecurityMetadata() +{ + GF_IPMPX_TrustSecurityMetadata *ptr; + GF_IPMPX_DATA_ALLOC(ptr, GF_IPMPX_TrustSecurityMetadata, GF_IPMPX_TRUST_SECURITY_METADATA_TAG); + ptr->TrustedTools = gf_list_new(); + return (GF_IPMPX_Data *) ptr; +} +static void DelGF_IPMPX_TrustSecurityMetadata(GF_IPMPX_Data *_p) +{ + GF_IPMPX_TrustSecurityMetadata *p = (GF_IPMPX_TrustSecurityMetadata *)_p; + while (gf_list_count(p->TrustedTools)) { + GF_IPMPX_TrustedTool *tt = (GF_IPMPX_TrustedTool *)gf_list_get(p->TrustedTools, 0); + gf_list_rem(p->TrustedTools, 0); + while (gf_list_count(tt->trustSpecifications)) { + GF_IPMPX_TrustSpecification *tts = (GF_IPMPX_TrustSpecification *)gf_list_get(tt->trustSpecifications, 0); + gf_list_rem(tt->trustSpecifications, 0); + GF_IPMPX_DELETE_ARRAY(tts->CCTrustMetadata); + gf_free(tts); + } + gf_list_del(tt->trustSpecifications); + gf_free(tt); + } + gf_list_del(p->TrustedTools); + gf_free(p); +} +static GF_Err ReadGF_IPMPX_TrustSecurityMetadata(GF_BitStream *bs, GF_IPMPX_Data *_p, u32 size) +{ + u32 nbTools, nbSpec; + GF_IPMPX_TrustSecurityMetadata *p = (GF_IPMPX_TrustSecurityMetadata *)_p; + nbTools = gf_bs_read_int(bs, 16); + while (nbTools) { + GF_IPMPX_TrustedTool *tt = (GF_IPMPX_TrustedTool *)gf_malloc(sizeof(GF_IPMPX_TrustedTool)); + if (!tt) return GF_OUT_OF_MEM; + memset(tt, 0, sizeof(GF_IPMPX_TrustedTool)); + tt->tag = GF_IPMPX_TRUSTED_TOOL_TAG; + nbTools--; + gf_list_add(p->TrustedTools, tt); + gf_bs_read_data(bs, (char*)tt->toolID, 16); + gf_bs_read_data(bs, tt->AuditDate, 5); + tt->trustSpecifications = gf_list_new(); + nbSpec = gf_bs_read_int(bs, 16); + while (nbSpec) { + Bool has_cc; + GF_IPMPX_TrustSpecification *tts = (GF_IPMPX_TrustSpecification *)gf_malloc(sizeof(GF_IPMPX_TrustSpecification)); + if (!tts) return GF_OUT_OF_MEM; + memset(tts, 0, sizeof(GF_IPMPX_TrustSpecification)); + tts->tag = GF_IPMPX_TRUST_SPECIFICATION_TAG; + nbSpec--; + gf_list_add(tt->trustSpecifications, tts); + has_cc = gf_bs_read_int(bs, 1); + gf_bs_read_int(bs, 7); + if (has_cc) { + tts->CCTrustMetadata = GF_IPMPX_GetByteArray(bs); + } else { + gf_bs_read_data(bs, tts->startDate, 5); + tts->attackerProfile = gf_bs_read_int(bs, 2); + gf_bs_read_int(bs, 6); + tts->trustedDuration = gf_bs_read_int(bs, 32); + } + } + } + return GF_OK; +} +static u32 SizeGF_IPMPX_TrustSecurityMetadata(GF_IPMPX_Data *_p) +{ + u32 size, i, j; + GF_IPMPX_TrustSecurityMetadata *p = (GF_IPMPX_TrustSecurityMetadata *)_p; + size = 2; + for (i=0;iTrustedTools); i++) { + GF_IPMPX_TrustedTool *tt = (GF_IPMPX_TrustedTool *)gf_list_get(p->TrustedTools, i); + size += 23; + for (j=0; jtrustSpecifications); j++) { + GF_IPMPX_TrustSpecification *tts = (GF_IPMPX_TrustSpecification *)gf_list_get(tt->trustSpecifications, j); + size += 1; + if (tts->CCTrustMetadata) size += GF_IPMPX_GetByteArraySize(tts->CCTrustMetadata); + else size += 10; + } + } + return size; +} +static GF_Err WriteGF_IPMPX_TrustSecurityMetadata(GF_BitStream *bs, GF_IPMPX_Data *_p) +{ + u32 i, j, c1, c2; + GF_IPMPX_TrustSecurityMetadata *p = (GF_IPMPX_TrustSecurityMetadata *)_p; + + c1 = gf_list_count(p->TrustedTools); + gf_bs_write_int(bs, c1, 16); + for (i=0;iTrustedTools, i); + gf_bs_write_data(bs, (char*)tt->toolID, 16); + gf_bs_write_data(bs, (char*)tt->AuditDate, 5); + c2 = gf_list_count(tt->trustSpecifications); + gf_bs_write_int(bs, c2, 16); + + for (j=0; jtrustSpecifications, j); + gf_bs_write_int(bs, tts->CCTrustMetadata ? 1 : 0, 1); + gf_bs_write_int(bs, 0, 7); + if (tts->CCTrustMetadata) GF_IPMPX_WriteByteArray(bs, tts->CCTrustMetadata); + else { + gf_bs_write_data(bs, tts->startDate, 5); + gf_bs_write_int(bs, tts->attackerProfile, 2); + gf_bs_write_int(bs, 0, 6); + gf_bs_write_int(bs, tts->trustedDuration, 32); + } + } + } + return GF_OK; +} + +static GF_IPMPX_Data *NewGF_IPMPX_SecureContainer() +{ + GF_IPMPX_SecureContainer *ptr; + GF_IPMPX_DATA_ALLOC(ptr, GF_IPMPX_SecureContainer, GF_IPMPX_SECURE_CONTAINER_TAG); + return (GF_IPMPX_Data *) ptr; +} +static void DelGF_IPMPX_SecureContainer(GF_IPMPX_Data *_p) +{ + GF_IPMPX_SecureContainer *p = (GF_IPMPX_SecureContainer *)_p; + GF_IPMPX_DELETE_ARRAY(p->encryptedData); + GF_IPMPX_DELETE_ARRAY(p->MAC); + gf_ipmpx_data_del(p->protectedMsg); + gf_free(p); +} +static GF_Err ReadGF_IPMPX_SecureContainer(GF_BitStream *bs, GF_IPMPX_Data *_p, u32 size) +{ + Bool has_enc, has_mac; + GF_IPMPX_SecureContainer *p = (GF_IPMPX_SecureContainer *)_p; + has_enc = gf_bs_read_int(bs, 1); + has_mac = gf_bs_read_int(bs, 1); + p->isMACEncrypted = gf_bs_read_int(bs, 1); + gf_bs_read_int(bs, 5); + if (has_enc) { + p->encryptedData = GF_IPMPX_GetByteArray(bs); + if (has_mac && !p->isMACEncrypted) p->MAC = GF_IPMPX_GetByteArray(bs); + } else { + GF_Err e = gf_ipmpx_data_parse(bs, &p->protectedMsg); + if (e) return e; + if (has_mac) p->MAC = GF_IPMPX_GetByteArray(bs); + } + return GF_OK; +} +static u32 SizeGF_IPMPX_SecureContainer(GF_IPMPX_Data *_p) +{ + u32 size = 1; + GF_IPMPX_SecureContainer *p = (GF_IPMPX_SecureContainer *)_p; + if (p->MAC) p->isMACEncrypted = 0; + if (p->encryptedData) { + size += GF_IPMPX_GetByteArraySize(p->encryptedData); + if (p->MAC) size += GF_IPMPX_GetByteArraySize(p->MAC); + } else { + size += gf_ipmpx_data_full_size(p->protectedMsg); + if (p->MAC) size += GF_IPMPX_GetByteArraySize(p->MAC); + } + return size; +} +static GF_Err WriteGF_IPMPX_SecureContainer(GF_BitStream *bs, GF_IPMPX_Data *_p) +{ + GF_IPMPX_SecureContainer *p = (GF_IPMPX_SecureContainer *)_p; + if (p->MAC) p->isMACEncrypted = 0; + gf_bs_write_int(bs, p->encryptedData ? 1 : 0, 1); + gf_bs_write_int(bs, (p->MAC || p->isMACEncrypted) ? 1 : 0, 1); + gf_bs_write_int(bs, p->isMACEncrypted, 1); + gf_bs_write_int(bs, 0, 5); + if (p->encryptedData) { + GF_IPMPX_WriteByteArray(bs, p->encryptedData); + if (p->MAC) GF_IPMPX_WriteByteArray(bs, p->MAC); + } else { + GF_Err e = gf_ipmpx_data_write(bs, p->protectedMsg); + if (e) return e; + if (p->MAC) GF_IPMPX_WriteByteArray(bs, p->MAC); + } + return GF_OK; +} + +static GF_IPMPX_Data *NewGF_IPMPX_GetToolsResponse() +{ + GF_IPMPX_GetToolsResponse *ptr; + GF_IPMPX_DATA_ALLOC(ptr, GF_IPMPX_GetToolsResponse, GF_IPMPX_GET_TOOLS_RESPONSE_TAG); + ptr->ipmp_tools = gf_list_new(); + return (GF_IPMPX_Data *) ptr; +} +static void DelGF_IPMPX_GetToolsResponse(GF_IPMPX_Data *_p) +{ + GF_IPMPX_GetToolsResponse *p = (GF_IPMPX_GetToolsResponse *)_p; + while (gf_list_count(p->ipmp_tools)) { + /*IPMPTools are descriptors*/ + GF_Descriptor *d = (GF_Descriptor *)gf_list_get(p->ipmp_tools, 0); + gf_list_rem(p->ipmp_tools, 0); + gf_odf_desc_del((GF_Descriptor*)d); + } + gf_list_del(p->ipmp_tools); + gf_free(p); +} +static GF_Err ReadGF_IPMPX_GetToolsResponse(GF_BitStream *bs, GF_IPMPX_Data *_p, u32 size) +{ + u32 NbBytes = 0; + GF_IPMPX_GetToolsResponse *p = (GF_IPMPX_GetToolsResponse *)_p; + + while (size>NbBytes) { + u32 desc_size, start_o; + GF_Descriptor *desc; + GF_Err e; + start_o = (u32) gf_bs_get_position(bs); + e = gf_odf_parse_descriptor(bs, &desc, &desc_size); + if (e) return e; + gf_list_add(p->ipmp_tools, desc); + NbBytes += (u32) gf_bs_get_position(bs) - start_o; + } + if (sizeipmp_tools, &size); + return size; +} +static GF_Err WriteGF_IPMPX_GetToolsResponse(GF_BitStream *bs, GF_IPMPX_Data *_p) +{ + u32 i; + GF_IPMPX_GetToolsResponse *p = (GF_IPMPX_GetToolsResponse *)_p; + + for (i=0; iipmp_tools); i++) { + GF_Descriptor *desc = (GF_Descriptor *)gf_list_get(p->ipmp_tools, i); + gf_odf_write_descriptor(bs, desc); + } + return GF_OK; +} +static GF_IPMPX_Data *NewGF_IPMPX_ParametricDescription() +{ + GF_IPMPX_ParametricDescription *ptr; + GF_IPMPX_DATA_ALLOC(ptr, GF_IPMPX_ParametricDescription, GF_IPMPX_PARAMETRIC_DESCRIPTION_TAG); + ptr->descriptions = gf_list_new(); + return (GF_IPMPX_Data *) ptr; +} +static void DelGF_IPMPX_ParametricDescription(GF_IPMPX_Data *_p) +{ + GF_IPMPX_ParametricDescription *p = (GF_IPMPX_ParametricDescription *)_p; + GF_IPMPX_DELETE_ARRAY(p->descriptionComment); + while (gf_list_count(p->descriptions)) { + GF_IPMPX_ParametricDescriptionItem *it = (GF_IPMPX_ParametricDescriptionItem *)gf_list_get(p->descriptions, 0); + gf_list_rem(p->descriptions, 0); + GF_IPMPX_DELETE_ARRAY(it->main_class); + GF_IPMPX_DELETE_ARRAY(it->subClass); + GF_IPMPX_DELETE_ARRAY(it->typeData); + GF_IPMPX_DELETE_ARRAY(it->type); + GF_IPMPX_DELETE_ARRAY(it->addedData); + gf_free(it); + } + gf_list_del(p->descriptions); + gf_free(p); +} +static GF_Err ReadGF_IPMPX_ParametricDescription(GF_BitStream *bs, GF_IPMPX_Data *_p, u32 size) +{ + u32 count; + GF_IPMPX_ParametricDescription *p = (GF_IPMPX_ParametricDescription *)_p; + p->descriptionComment = GF_IPMPX_GetByteArray(bs); + p->majorVersion = gf_bs_read_int(bs, 8); + p->minorVersion = gf_bs_read_int(bs, 8); + count = gf_bs_read_int(bs, 32); + while (count) { + GF_IPMPX_ParametricDescriptionItem *it = (GF_IPMPX_ParametricDescriptionItem *)gf_malloc(sizeof(GF_IPMPX_ParametricDescriptionItem)); + gf_list_add(p->descriptions, it); + count--; + it->main_class = GF_IPMPX_GetByteArray(bs); + it->subClass = GF_IPMPX_GetByteArray(bs); + it->typeData = GF_IPMPX_GetByteArray(bs); + it->type = GF_IPMPX_GetByteArray(bs); + it->addedData = GF_IPMPX_GetByteArray(bs); + } + return GF_OK; +} +static u32 SizeGF_IPMPX_ParametricDescription(GF_IPMPX_Data *_p) +{ + u32 size, i; + GF_IPMPX_ParametricDescription *p = (GF_IPMPX_ParametricDescription *)_p; + size = GF_IPMPX_GetByteArraySize(p->descriptionComment); + size += 6; + for (i=0; idescriptions); i++) { + GF_IPMPX_ParametricDescriptionItem *it = (GF_IPMPX_ParametricDescriptionItem *)gf_list_get(p->descriptions, i); + size += GF_IPMPX_GetByteArraySize(it->main_class); + size += GF_IPMPX_GetByteArraySize(it->subClass); + size += GF_IPMPX_GetByteArraySize(it->typeData); + size += GF_IPMPX_GetByteArraySize(it->type); + size += GF_IPMPX_GetByteArraySize(it->addedData); + } + return size; +} +static GF_Err WriteGF_IPMPX_ParametricDescription(GF_BitStream *bs, GF_IPMPX_Data *_p) +{ + u32 i; + GF_IPMPX_ParametricDescription *p = (GF_IPMPX_ParametricDescription *)_p; + GF_IPMPX_WriteByteArray(bs, p->descriptionComment); + gf_bs_write_int(bs, p->majorVersion, 8); + gf_bs_write_int(bs, p->minorVersion, 8); + gf_bs_write_int(bs, gf_list_count(p->descriptions), 32); + + for (i=0; idescriptions); i++) { + GF_IPMPX_ParametricDescriptionItem *it = (GF_IPMPX_ParametricDescriptionItem *)gf_list_get(p->descriptions, i); + GF_IPMPX_WriteByteArray(bs, it->main_class); + GF_IPMPX_WriteByteArray(bs, it->subClass); + GF_IPMPX_WriteByteArray(bs, it->typeData); + GF_IPMPX_WriteByteArray(bs, it->type); + GF_IPMPX_WriteByteArray(bs, it->addedData); + } + return GF_OK; +} +static GF_IPMPX_Data *NewGF_IPMPX_ToolParamCapabilitiesQuery() +{ + GF_IPMPX_ToolParamCapabilitiesQuery*ptr; + GF_IPMPX_DATA_ALLOC(ptr, GF_IPMPX_ToolParamCapabilitiesQuery, GF_IPMPX_PARAMETRIC_CAPS_QUERY_TAG); + return (GF_IPMPX_Data *) ptr; +} +static void DelGF_IPMPX_ToolParamCapabilitiesQuery(GF_IPMPX_Data *_p) +{ + GF_IPMPX_ToolParamCapabilitiesQuery *p = (GF_IPMPX_ToolParamCapabilitiesQuery *)_p; + gf_ipmpx_data_del((GF_IPMPX_Data *) p->description); + gf_free(p); +} +static GF_Err ReadGF_IPMPX_ToolParamCapabilitiesQuery(GF_BitStream *bs, GF_IPMPX_Data *_p, u32 size) +{ + GF_IPMPX_ToolParamCapabilitiesQuery *p = (GF_IPMPX_ToolParamCapabilitiesQuery*)_p; + return gf_ipmpx_data_parse(bs, (GF_IPMPX_Data **) &p->description); +} +static u32 SizeGF_IPMPX_ToolParamCapabilitiesQuery(GF_IPMPX_Data *_p) +{ + GF_IPMPX_ToolParamCapabilitiesQuery *p = (GF_IPMPX_ToolParamCapabilitiesQuery*)_p; + return gf_ipmpx_data_full_size((GF_IPMPX_Data *) p->description); +} +static GF_Err WriteGF_IPMPX_ToolParamCapabilitiesQuery(GF_BitStream *bs, GF_IPMPX_Data *_p) +{ + GF_IPMPX_ToolParamCapabilitiesQuery *p = (GF_IPMPX_ToolParamCapabilitiesQuery*)_p; + return gf_ipmpx_data_write(bs, (GF_IPMPX_Data *) p->description); +} +static GF_IPMPX_Data *NewGF_IPMPX_ToolParamCapabilitiesResponse() +{ + GF_IPMPX_ToolParamCapabilitiesResponse*ptr; + GF_IPMPX_DATA_ALLOC(ptr, GF_IPMPX_ToolParamCapabilitiesResponse, GF_IPMPX_PARAMETRIC_CAPS_RESPONSE_TAG); + return (GF_IPMPX_Data *) ptr; +} +static void DelGF_IPMPX_ToolParamCapabilitiesResponse(GF_IPMPX_Data *_p) +{ + gf_free(_p); +} +static GF_Err ReadGF_IPMPX_ToolParamCapabilitiesResponse(GF_BitStream *bs, GF_IPMPX_Data *_p, u32 size) +{ + GF_IPMPX_ToolParamCapabilitiesResponse *p = (GF_IPMPX_ToolParamCapabilitiesResponse*)_p; + p->capabilitiesSupported = gf_bs_read_int(bs, 1); + gf_bs_read_int(bs, 7); + return GF_OK; +} +static u32 SizeGF_IPMPX_ToolParamCapabilitiesResponse(GF_IPMPX_Data *_p) +{ + return 1; +} +static GF_Err WriteGF_IPMPX_ToolParamCapabilitiesResponse(GF_BitStream *bs, GF_IPMPX_Data *_p) +{ + GF_IPMPX_ToolParamCapabilitiesResponse*p = (GF_IPMPX_ToolParamCapabilitiesResponse*)_p; + gf_bs_write_int(bs, p->capabilitiesSupported, 1); + gf_bs_write_int(bs, 0, 7); + return GF_OK; +} + +static GF_IPMPX_Data *NewGF_IPMPX_ConnectTool() +{ + GF_IPMPX_ConnectTool*ptr; + GF_IPMPX_DATA_ALLOC(ptr, GF_IPMPX_ConnectTool, GF_IPMPX_CONNECT_TOOL_TAG); + return (GF_IPMPX_Data *) ptr; +} +static void DelGF_IPMPX_ConnectTool(GF_IPMPX_Data *_p) +{ + GF_IPMPX_ConnectTool *p = (GF_IPMPX_ConnectTool*)_p; + if (p->toolDescriptor) gf_odf_desc_del((GF_Descriptor *)p->toolDescriptor); + gf_free(p); +} +static GF_Err ReadGF_IPMPX_ConnectTool(GF_BitStream *bs, GF_IPMPX_Data *_p, u32 size) +{ + u32 dsize; + GF_IPMPX_ConnectTool*p = (GF_IPMPX_ConnectTool*)_p; + return gf_odf_parse_descriptor(bs, (GF_Descriptor **) &p->toolDescriptor, &dsize); +} +static u32 SizeGF_IPMPX_ConnectTool(GF_IPMPX_Data *_p) +{ + u32 size; + GF_IPMPX_ConnectTool*p = (GF_IPMPX_ConnectTool*)_p; + gf_odf_size_descriptor((GF_Descriptor *)p->toolDescriptor, &size); + size += gf_odf_size_field_size(size); + return size; +} +static GF_Err WriteGF_IPMPX_ConnectTool(GF_BitStream *bs, GF_IPMPX_Data *_p) +{ + GF_IPMPX_ConnectTool*p = (GF_IPMPX_ConnectTool*)_p; + gf_odf_write_descriptor(bs, (GF_Descriptor *)p->toolDescriptor); + return GF_OK; +} +static GF_IPMPX_Data *NewGF_IPMPX_DisconnectTool() +{ + GF_IPMPX_DisconnectTool*ptr; + GF_IPMPX_DATA_ALLOC(ptr, GF_IPMPX_DisconnectTool, GF_IPMPX_DISCONNECT_TOOL_TAG); + return (GF_IPMPX_Data *) ptr; +} +static void DelGF_IPMPX_DisconnectTool(GF_IPMPX_Data *_p) +{ + gf_free(_p); +} +static GF_Err ReadGF_IPMPX_DisconnectTool(GF_BitStream *bs, GF_IPMPX_Data *_p, u32 size) +{ + GF_IPMPX_DisconnectTool*p = (GF_IPMPX_DisconnectTool*)_p; + p->IPMP_ToolContextID = gf_bs_read_int(bs, 32); + return GF_OK; +} +static u32 SizeGF_IPMPX_DisconnectTool(GF_IPMPX_Data *_p) +{ + return 4; +} +static GF_Err WriteGF_IPMPX_DisconnectTool(GF_BitStream *bs, GF_IPMPX_Data *_p) +{ + GF_IPMPX_DisconnectTool*p = (GF_IPMPX_DisconnectTool*)_p; + gf_bs_write_int(bs, p->IPMP_ToolContextID, 32); + return GF_OK; +} + +static GF_IPMPX_Data *NewGF_IPMPX_GetToolContext() +{ + GF_IPMPX_GetToolContext*ptr; + GF_IPMPX_DATA_ALLOC(ptr, GF_IPMPX_GetToolContext, GF_IPMPX_GET_TOOL_CONTEXT_TAG); + return (GF_IPMPX_Data *) ptr; +} +static void DelGF_IPMPX_GetToolContext(GF_IPMPX_Data *_p) +{ + gf_free(_p); +} +static GF_Err ReadGF_IPMPX_GetToolContext(GF_BitStream *bs, GF_IPMPX_Data *_p, u32 size) +{ + Bool has_idex; + GF_IPMPX_GetToolContext*p = (GF_IPMPX_GetToolContext*)_p; + p->scope = gf_bs_read_int(bs, 3); + has_idex = gf_bs_read_int(bs, 1); + gf_bs_read_int(bs, 4); + if (has_idex) p->IPMP_DescriptorIDEx = gf_bs_read_int(bs, 16); + + return GF_OK; +} +static u32 SizeGF_IPMPX_GetToolContext(GF_IPMPX_Data *_p) +{ + GF_IPMPX_GetToolContext*p = (GF_IPMPX_GetToolContext*)_p; + return p->IPMP_DescriptorIDEx ? 3 : 1; +} +static GF_Err WriteGF_IPMPX_GetToolContext(GF_BitStream *bs, GF_IPMPX_Data *_p) +{ + GF_IPMPX_GetToolContext*p = (GF_IPMPX_GetToolContext*)_p; + gf_bs_write_int(bs, p->scope, 3); + gf_bs_write_int(bs, p->IPMP_DescriptorIDEx ? 1 : 0, 1); + gf_bs_write_int(bs, 0, 4); + if (p->IPMP_DescriptorIDEx) gf_bs_write_int(bs, p->IPMP_DescriptorIDEx, 16); + return GF_OK; +} +static GF_IPMPX_Data *NewGF_IPMPX_GetToolContextResponse() +{ + GF_IPMPX_GetToolContextResponse*ptr; + GF_IPMPX_DATA_ALLOC(ptr, GF_IPMPX_GetToolContextResponse, GF_IPMPX_GET_TOOL_CONTEXT_RESPONSE_TAG); + return (GF_IPMPX_Data *) ptr; +} +static void DelGF_IPMPX_GetToolContextResponse(GF_IPMPX_Data *_p) +{ + gf_free(_p); +} +static GF_Err ReadGF_IPMPX_GetToolContextResponse(GF_BitStream *bs, GF_IPMPX_Data *_p, u32 size) +{ + Bool has_esid; + GF_IPMPX_GetToolContextResponse*p = (GF_IPMPX_GetToolContextResponse*)_p; + has_esid = gf_bs_read_int(bs, 1); + gf_bs_read_int(bs, 5); + p->OD_ID = gf_bs_read_int(bs, 10); + if (has_esid) p->ESD_ID = gf_bs_read_int(bs, 16); + p->IPMP_ToolContextID = gf_bs_read_int(bs, 32); + return GF_OK; +} +static u32 SizeGF_IPMPX_GetToolContextResponse(GF_IPMPX_Data *_p) +{ + GF_IPMPX_GetToolContextResponse*p = (GF_IPMPX_GetToolContextResponse*)_p; + return p->ESD_ID ? 8 : 6; +} +static GF_Err WriteGF_IPMPX_GetToolContextResponse(GF_BitStream *bs, GF_IPMPX_Data *_p) +{ + GF_IPMPX_GetToolContextResponse*p = (GF_IPMPX_GetToolContextResponse*)_p; + gf_bs_write_int(bs, p->ESD_ID ? 1 : 0, 1); + gf_bs_write_int(bs, 0, 5); + gf_bs_write_int(bs, p->OD_ID, 10); + if (p->ESD_ID) gf_bs_write_int(bs, p->ESD_ID, 16); + gf_bs_write_int(bs, p->IPMP_ToolContextID, 32); + return GF_OK; +} + + +static GF_IPMPX_Data *NewGF_IPMPX_AddToolNotificationListener() +{ + GF_IPMPX_AddToolNotificationListener*ptr; + GF_IPMPX_DATA_ALLOC(ptr, GF_IPMPX_AddToolNotificationListener, GF_IPMPX_ADD_TOOL_LISTENER_TAG); + return (GF_IPMPX_Data *) ptr; +} +static void DelGF_IPMPX_AddToolNotificationListener(GF_IPMPX_Data *_p) +{ + gf_free(_p); +} +static GF_Err ReadGF_IPMPX_AddToolNotificationListener(GF_BitStream *bs, GF_IPMPX_Data *_p, u32 size) +{ + u32 i; + GF_IPMPX_AddToolNotificationListener*p = (GF_IPMPX_AddToolNotificationListener*)_p; + p->scope = gf_bs_read_int(bs, 3); + gf_bs_read_int(bs, 5); + p->eventTypeCount = gf_bs_read_int(bs, 8); + for (i=0;ieventTypeCount; i++) p->eventType[i] = gf_bs_read_int(bs, 8); + return GF_OK; +} +static u32 SizeGF_IPMPX_AddToolNotificationListener(GF_IPMPX_Data *_p) +{ + GF_IPMPX_AddToolNotificationListener*p = (GF_IPMPX_AddToolNotificationListener*)_p; + return p->eventTypeCount + 2; +} +static GF_Err WriteGF_IPMPX_AddToolNotificationListener(GF_BitStream *bs, GF_IPMPX_Data *_p) +{ + u32 i; + GF_IPMPX_AddToolNotificationListener*p = (GF_IPMPX_AddToolNotificationListener*)_p; + gf_bs_write_int(bs, p->scope, 3); + gf_bs_write_int(bs, 0, 5); + gf_bs_write_int(bs, p->eventTypeCount, 8); + for (i=0;ieventTypeCount; i++) gf_bs_write_int(bs, p->eventType[i], 8); + return GF_OK; +} +static GF_IPMPX_Data *NewGF_IPMPX_RemoveToolNotificationListener() +{ + GF_IPMPX_RemoveToolNotificationListener*ptr; + GF_IPMPX_DATA_ALLOC(ptr, GF_IPMPX_RemoveToolNotificationListener, GF_IPMPX_REMOVE_TOOL_LISTENER_TAG); + return (GF_IPMPX_Data *) ptr; +} +static void DelGF_IPMPX_RemoveToolNotificationListener(GF_IPMPX_Data *_p) +{ + gf_free(_p); +} +static GF_Err ReadGF_IPMPX_RemoveToolNotificationListener(GF_BitStream *bs, GF_IPMPX_Data *_p, u32 size) +{ + u32 i; + GF_IPMPX_RemoveToolNotificationListener*p = (GF_IPMPX_RemoveToolNotificationListener*)_p; + p->eventTypeCount = gf_bs_read_int(bs, 8); + for (i=0;ieventTypeCount; i++) p->eventType[i] = gf_bs_read_int(bs, 8); + return GF_OK; +} +static u32 SizeGF_IPMPX_RemoveToolNotificationListener(GF_IPMPX_Data *_p) +{ + GF_IPMPX_RemoveToolNotificationListener*p = (GF_IPMPX_RemoveToolNotificationListener*)_p; + return p->eventTypeCount + 1; +} +static GF_Err WriteGF_IPMPX_RemoveToolNotificationListener(GF_BitStream *bs, GF_IPMPX_Data *_p) +{ + u32 i; + GF_IPMPX_RemoveToolNotificationListener*p = (GF_IPMPX_RemoveToolNotificationListener*)_p; + gf_bs_write_int(bs, p->eventTypeCount, 8); + for (i=0;ieventTypeCount; i++) gf_bs_write_int(bs, p->eventType[i], 8); + return GF_OK; +} + +static GF_IPMPX_Data *NewGF_IPMPX_NotifyToolEvent() +{ + GF_IPMPX_NotifyToolEvent*ptr; + GF_IPMPX_DATA_ALLOC(ptr, GF_IPMPX_NotifyToolEvent, GF_IPMPX_NOTIFY_TOOL_EVENT_TAG); + return (GF_IPMPX_Data *) ptr; +} +static void DelGF_IPMPX_NotifyToolEvent(GF_IPMPX_Data *_p) +{ + gf_free(_p); +} +static GF_Err ReadGF_IPMPX_NotifyToolEvent(GF_BitStream *bs, GF_IPMPX_Data *_p, u32 size) +{ + Bool has_id; + GF_IPMPX_NotifyToolEvent*p = (GF_IPMPX_NotifyToolEvent*)_p; + has_id = gf_bs_read_int(bs, 1); + gf_bs_read_int(bs, 7); + if (has_id) { + p->OD_ID = gf_bs_read_int(bs, 10); + gf_bs_read_int(bs, 6); + p->ESD_ID = gf_bs_read_int(bs, 16); + } + p->eventType = gf_bs_read_int(bs, 8); + p->IPMP_ToolContextID = gf_bs_read_int(bs, 32); + return GF_OK; +} +static u32 SizeGF_IPMPX_NotifyToolEvent(GF_IPMPX_Data *_p) +{ + GF_IPMPX_NotifyToolEvent*p = (GF_IPMPX_NotifyToolEvent*)_p; + return (p->OD_ID || p->ESD_ID) ? 10 : 6; +} +static GF_Err WriteGF_IPMPX_NotifyToolEvent(GF_BitStream *bs, GF_IPMPX_Data *_p) +{ + GF_IPMPX_NotifyToolEvent*p = (GF_IPMPX_NotifyToolEvent*)_p; + gf_bs_write_int(bs, (p->OD_ID || p->ESD_ID) ? 1 : 0, 1); + gf_bs_write_int(bs, 0, 7); + if (p->OD_ID || p->ESD_ID) { + gf_bs_write_int(bs, p->OD_ID, 10); + gf_bs_write_int(bs, 0, 6); + gf_bs_write_int(bs, p->ESD_ID, 16); + } + gf_bs_write_int(bs, p->eventType, 8); + gf_bs_write_int(bs, p->IPMP_ToolContextID, 32); + return GF_OK; +} +static GF_IPMPX_Data *NewGF_IPMPX_CanProcess() +{ + GF_IPMPX_CanProcess*ptr; + GF_IPMPX_DATA_ALLOC(ptr, GF_IPMPX_CanProcess, GF_IPMPX_CAN_PROCESS_TAG); + return (GF_IPMPX_Data *) ptr; +} +static void DelGF_IPMPX_CanProcess(GF_IPMPX_Data *_p) +{ + gf_free(_p); +} +static GF_Err ReadGF_IPMPX_CanProcess(GF_BitStream *bs, GF_IPMPX_Data *_p, u32 size) +{ + GF_IPMPX_CanProcess*p = (GF_IPMPX_CanProcess*)_p; + p->canProcess = gf_bs_read_int(bs, 1); + gf_bs_read_int(bs, 7); + return GF_OK; +} +static u32 SizeGF_IPMPX_CanProcess(GF_IPMPX_Data *_p) +{ + return 1; +} +static GF_Err WriteGF_IPMPX_CanProcess(GF_BitStream *bs, GF_IPMPX_Data *_p) +{ + GF_IPMPX_CanProcess*p = (GF_IPMPX_CanProcess*)_p; + gf_bs_write_int(bs, p->canProcess ? 1 : 0, 1); + gf_bs_write_int(bs, 0, 7); + return GF_OK; +} +static GF_IPMPX_Data *NewGF_IPMPX_OpaqueData(u8 tag) +{ + GF_IPMPX_OpaqueData*ptr; + GF_IPMPX_DATA_ALLOC(ptr, GF_IPMPX_OpaqueData, tag); + return (GF_IPMPX_Data *) ptr; +} +static void DelGF_IPMPX_OpaqueData(GF_IPMPX_Data *_p) +{ + GF_IPMPX_OpaqueData *p = (GF_IPMPX_OpaqueData*)_p; + GF_IPMPX_DELETE_ARRAY(p->opaqueData); + gf_free(p); +} +static GF_Err ReadGF_IPMPX_OpaqueData(GF_BitStream *bs, GF_IPMPX_Data *_p, u32 size) +{ + GF_IPMPX_OpaqueData*p = (GF_IPMPX_OpaqueData*)_p; + p->opaqueData = GF_IPMPX_GetByteArray(bs); + return GF_OK; +} +static u32 SizeGF_IPMPX_OpaqueData(GF_IPMPX_Data *_p) +{ + GF_IPMPX_OpaqueData*p = (GF_IPMPX_OpaqueData*)_p; + return GF_IPMPX_GetByteArraySize(p->opaqueData); +} +static GF_Err WriteGF_IPMPX_OpaqueData(GF_BitStream *bs, GF_IPMPX_Data *_p) +{ + GF_IPMPX_OpaqueData*p = (GF_IPMPX_OpaqueData*)_p; + GF_IPMPX_WriteByteArray(bs, p->opaqueData); + return GF_OK; +} +static GF_IPMPX_Data *NewGF_IPMPX_KeyData() +{ + GF_IPMPX_KeyData*ptr; + GF_IPMPX_DATA_ALLOC(ptr, GF_IPMPX_KeyData, GF_IPMPX_KEY_DATA_TAG); + return (GF_IPMPX_Data *) ptr; +} +static void DelGF_IPMPX_KeyData(GF_IPMPX_Data *_p) +{ + GF_IPMPX_KeyData*p = (GF_IPMPX_KeyData*)_p; + GF_IPMPX_DELETE_ARRAY(p->keyBody); + GF_IPMPX_DELETE_ARRAY(p->OpaqueData); + gf_free(p); +} +static GF_Err ReadGF_IPMPX_KeyData(GF_BitStream *bs, GF_IPMPX_Data *_p, u32 size) +{ + GF_IPMPX_KeyData*p = (GF_IPMPX_KeyData*)_p; + p->keyBody = GF_IPMPX_GetByteArray(bs); + p->flags = 0; + if (gf_bs_read_int(bs, 1)) p->flags |= 1; + if (gf_bs_read_int(bs, 1)) p->flags |= 1<<1; + if (gf_bs_read_int(bs, 1)) p->flags |= 1<<2; + if (gf_bs_read_int(bs, 1)) p->flags |= 1<<3; + gf_bs_read_int(bs, 4); + if (p->flags & (1)) p->startDTS = gf_bs_read_long_int(bs, 64); + if (p->flags & (1<<1)) p->startPacketID = gf_bs_read_int(bs, 32); + if (p->flags & (1<<2)) p->expireDTS = gf_bs_read_long_int(bs, 64); + if (p->flags & (1<<3)) p->expirePacketID = gf_bs_read_int(bs, 32); + p->OpaqueData = GF_IPMPX_GetByteArray(bs); + return GF_OK; +} +static u32 SizeGF_IPMPX_KeyData(GF_IPMPX_Data *_p) +{ + u32 size = 0; + GF_IPMPX_KeyData*p = (GF_IPMPX_KeyData*)_p; + size += GF_IPMPX_GetByteArraySize(p->keyBody); + size += 1; + if (p->flags & (1)) size += 8; + if (p->flags & (1<<1)) size += 4; + if (p->flags & (1<<2)) size += 8; + if (p->flags & (1<<3)) size += 4; + size += GF_IPMPX_GetByteArraySize(p->OpaqueData); + return size; +} +static GF_Err WriteGF_IPMPX_KeyData(GF_BitStream *bs, GF_IPMPX_Data *_p) +{ + GF_IPMPX_KeyData*p = (GF_IPMPX_KeyData*)_p; + GF_IPMPX_WriteByteArray(bs, p->keyBody); + gf_bs_write_int(bs, (p->flags & (1)) ? 1 : 0, 1); + gf_bs_write_int(bs, (p->flags & (1<<1)) ? 1 : 0, 1); + gf_bs_write_int(bs, (p->flags & (1<<2)) ? 1 : 0, 1); + gf_bs_write_int(bs, (p->flags & (1<<3)) ? 1 : 0, 1); + gf_bs_write_int(bs, 0, 4); + if (p->flags & (1)) gf_bs_write_long_int(bs, p->startDTS, 64); + if (p->flags & (1<<1)) gf_bs_write_int(bs, p->startPacketID, 32); + if (p->flags & (1<<2)) gf_bs_write_long_int(bs, p->expireDTS, 64); + if (p->flags & (1<<3)) gf_bs_write_int(bs, p->expirePacketID, 32); + GF_IPMPX_WriteByteArray(bs, p->OpaqueData); + return GF_OK; +} + +static GF_IPMPX_Data *NewGF_IPMPX_SelectiveDecryptionInit() +{ + GF_IPMPX_SelectiveDecryptionInit*ptr; + GF_IPMPX_DATA_ALLOC(ptr, GF_IPMPX_SelectiveDecryptionInit, GF_IPMPX_SEL_DEC_INIT_TAG); + ptr->SelEncBuffer = gf_list_new(); + ptr->SelEncFields = gf_list_new(); + return (GF_IPMPX_Data *) ptr; +} +static void DelGF_IPMPX_SelectiveDecryptionInit(GF_IPMPX_Data *_p) +{ + GF_IPMPX_SelectiveDecryptionInit*p = (GF_IPMPX_SelectiveDecryptionInit*)_p; + while (gf_list_count(p->SelEncBuffer)) { + GF_IPMPX_SelEncBuffer *sb = (GF_IPMPX_SelEncBuffer *)gf_list_get(p->SelEncBuffer, 0); + gf_list_rem(p->SelEncBuffer, 0); + GF_IPMPX_DELETE_ARRAY(sb->Stream_Cipher_Specific_Init_Info); + gf_free(sb); + } + gf_list_del(p->SelEncBuffer); + while (gf_list_count(p->SelEncFields)) { + GF_IPMPX_SelEncField*sf = (GF_IPMPX_SelEncField*)gf_list_get(p->SelEncFields, 0); + gf_list_rem(p->SelEncFields, 0); + GF_IPMPX_DELETE_ARRAY(sf->shuffleSpecificInfo); + if (sf->mappingTable) gf_free(sf->mappingTable); + gf_free(sf); + } + gf_list_del(p->SelEncFields); + if (p->RLE_Data) gf_free(p->RLE_Data); + gf_free(p); +} +static GF_Err ReadGF_IPMPX_SelectiveDecryptionInit(GF_BitStream *bs, GF_IPMPX_Data *_p, u32 size) +{ + u32 count, i; + Bool is_spec; + GF_IPMPX_SelectiveDecryptionInit*p = (GF_IPMPX_SelectiveDecryptionInit*)_p; + p->mediaTypeExtension = gf_bs_read_int(bs, 8); + p->mediaTypeIndication = gf_bs_read_int(bs, 8); + p->profileLevelIndication = gf_bs_read_int(bs, 8); + p->compliance = gf_bs_read_int(bs, 8); + count = gf_bs_read_int(bs, 8); + while (count) { + Bool is_block; + GF_IPMPX_SelEncBuffer *sb; + GF_SAFEALLOC(sb, GF_IPMPX_SelEncBuffer); + gf_list_add(p->SelEncBuffer, sb); + count--; + gf_bs_read_data(bs, (char*)sb->cipher_Id, 16); + sb->syncBoundary = gf_bs_read_int(bs, 8); + is_block = gf_bs_read_int(bs, 1); + gf_bs_read_int(bs, 7); + if (is_block) { + sb->mode = gf_bs_read_int(bs, 8); + sb->blockSize = gf_bs_read_int(bs, 16); + sb->keySize = gf_bs_read_int(bs, 16); + } else { + sb->Stream_Cipher_Specific_Init_Info = GF_IPMPX_GetByteArray(bs); + } + } + is_spec = gf_bs_read_int(bs, 1); + gf_bs_read_int(bs, 7); + if (is_spec) { + Bool is_map; + count = gf_bs_read_int(bs, 8); + while (count) { + GF_IPMPX_SelEncField *sf; + GF_SAFEALLOC(sf, GF_IPMPX_SelEncField); + gf_list_add(p->SelEncFields, sf); + count--; + sf->field_Id = gf_bs_read_int(bs, 8); + sf->field_Scope = gf_bs_read_int(bs, 3); + gf_bs_read_int(bs, 5); + sf->buf = gf_bs_read_int(bs, 8); + is_map = gf_bs_read_int(bs, 1); + gf_bs_read_int(bs, 7); + if (is_map) { + Bool sendMapTable = gf_bs_read_int(bs, 1); + Bool isShuffled = gf_bs_read_int(bs, 1); + gf_bs_read_int(bs, 6); + if (sendMapTable) { + sf->mappingTableSize = gf_bs_read_int(bs, 16); + sf->mappingTable = (u16*)gf_malloc(sizeof(u16) * sf->mappingTableSize); + for (i=0; imappingTableSize; i++) sf->mappingTable[i] = gf_bs_read_int(bs, 16); + } + if (isShuffled) sf->shuffleSpecificInfo = GF_IPMPX_GetByteArray(bs); + } + } + } else { + p->RLE_DataLength = gf_bs_read_int(bs, 16); + p->RLE_Data = (u16*)gf_malloc(sizeof(u16)*p->RLE_DataLength); + for (i=0; iRLE_DataLength; i++) p->RLE_Data[i] = gf_bs_read_int(bs, 16); + } + return GF_OK; +} +static u32 SizeGF_IPMPX_SelectiveDecryptionInit(GF_IPMPX_Data *_p) +{ + u32 size, i; + GF_IPMPX_SelectiveDecryptionInit*p = (GF_IPMPX_SelectiveDecryptionInit*)_p; + + size = 5; + for (i=0; iSelEncBuffer); i++) { + GF_IPMPX_SelEncBuffer *sb = (GF_IPMPX_SelEncBuffer *)gf_list_get(p->SelEncBuffer, i); + size += 18; + if (sb->Stream_Cipher_Specific_Init_Info) { + size += GF_IPMPX_GetByteArraySize(sb->Stream_Cipher_Specific_Init_Info); + } else { + size += 5; + } + } + size += 1; + if (p->RLE_Data) { + size += 2 + 2*p->RLE_DataLength; + } else { + size += 1; + for (i=0; iSelEncFields); i++) { + GF_IPMPX_SelEncField *sf = (GF_IPMPX_SelEncField *)gf_list_get(p->SelEncFields, i); + size += 4; + if (sf->mappingTable || sf->shuffleSpecificInfo) { + size += 1; + if (sf->mappingTable) size += 2 + 2*sf->mappingTableSize; + if (sf->shuffleSpecificInfo) size += GF_IPMPX_GetByteArraySize(sf->shuffleSpecificInfo); + } + } + } + return size; +} +static GF_Err WriteGF_IPMPX_SelectiveDecryptionInit(GF_BitStream *bs, GF_IPMPX_Data *_p) +{ + u32 count, i; + GF_IPMPX_SelectiveDecryptionInit*p = (GF_IPMPX_SelectiveDecryptionInit*)_p; + + gf_bs_write_int(bs, p->mediaTypeExtension, 8); + gf_bs_write_int(bs, p->mediaTypeIndication, 8); + gf_bs_write_int(bs, p->profileLevelIndication, 8); + gf_bs_write_int(bs, p->compliance, 8); + + count = gf_list_count(p->SelEncBuffer); + gf_bs_write_int(bs, count, 8); + for (i=0; iSelEncBuffer, i); + gf_bs_write_data(bs, (char*)sb->cipher_Id, 16); + gf_bs_write_int(bs, sb->syncBoundary, 8); + gf_bs_write_int(bs, sb->Stream_Cipher_Specific_Init_Info ? 0 : 1, 1); + gf_bs_write_int(bs, 0, 7); + if (sb->Stream_Cipher_Specific_Init_Info) { + GF_IPMPX_WriteByteArray(bs, sb->Stream_Cipher_Specific_Init_Info); + } else { + gf_bs_write_int(bs, sb->mode, 8); + gf_bs_write_int(bs, sb->blockSize, 16); + gf_bs_write_int(bs, sb->keySize, 16); + } + } + gf_bs_write_int(bs, p->RLE_Data ? 0 : 1, 1); + gf_bs_write_int(bs, 0, 7); + if (p->RLE_Data) { + gf_bs_write_int(bs, p->RLE_DataLength, 16); + for (i=0; iRLE_DataLength; i++) gf_bs_write_int(bs, p->RLE_Data[i], 16); + } else { + count = gf_list_count(p->SelEncFields); + gf_bs_write_int(bs, count, 8); + for (i=0; iSelEncFields, i); + gf_bs_write_int(bs, sf->field_Id, 8); + gf_bs_write_int(bs, sf->field_Scope, 3); + gf_bs_write_int(bs, 0, 5); + gf_bs_write_int(bs, sf->buf, 8); + gf_bs_write_int(bs, (sf->mappingTable || sf->shuffleSpecificInfo) ? 1 : 0, 1); + gf_bs_write_int(bs, 0, 7); + if (sf->mappingTable || sf->shuffleSpecificInfo) { + gf_bs_write_int(bs, sf->mappingTable ? 1 : 0, 1); + gf_bs_write_int(bs, sf->shuffleSpecificInfo ? 1 : 0, 1); + gf_bs_write_int(bs, 0, 6); + if (sf->mappingTable) { + gf_bs_write_int(bs, sf->mappingTableSize, 16); + for (i=0; imappingTableSize; i++) gf_bs_write_int(bs, sf->mappingTable[i], 16); + } + if (sf->shuffleSpecificInfo) GF_IPMPX_WriteByteArray(bs, sf->shuffleSpecificInfo); + } + } + } + return GF_OK; +} +static GF_IPMPX_Data *NewGF_IPMPX_WatermarkingInit(u8 tag) +{ + GF_IPMPX_WatermarkingInit *ptr; + GF_IPMPX_DATA_ALLOC(ptr, GF_IPMPX_WatermarkingInit, tag); + return (GF_IPMPX_Data *) ptr; +} +static void DelGF_IPMPX_WatermarkingInit(GF_IPMPX_Data *_p) +{ + GF_IPMPX_WatermarkingInit *p = (GF_IPMPX_WatermarkingInit*)_p; + if (p->wmPayload) gf_free(p->wmPayload); + if (p->opaqueData) gf_free(p->opaqueData); + gf_free(p); +} +static GF_Err ReadGF_IPMPX_WatermarkingInit(GF_BitStream *bs, GF_IPMPX_Data *_p, u32 size) +{ + Bool has_opaque_data; + GF_IPMPX_WatermarkingInit *p = (GF_IPMPX_WatermarkingInit*)_p; + + p->inputFormat = gf_bs_read_int(bs, 8); + p->requiredOp = gf_bs_read_int(bs, 4); + has_opaque_data = gf_bs_read_int(bs, 1); + gf_bs_read_int(bs, 3); + if (p->inputFormat==0x01) { + if (p->tag == GF_IPMPX_AUDIO_WM_INIT_TAG) { + p->nChannels = gf_bs_read_int(bs, 8); + p->bitPerSample = gf_bs_read_int(bs, 8); + p->frequency = gf_bs_read_int(bs, 32); + } else { + p->frame_horizontal_size = gf_bs_read_int(bs, 16); + p->frame_vertical_size = gf_bs_read_int(bs, 16); + p->chroma_format = gf_bs_read_int(bs, 8); + } + } + switch (p->requiredOp) { + case GF_IPMPX_WM_INSERT: + case GF_IPMPX_WM_REMARK: + p->wmPayloadLen = gf_bs_read_int(bs, 16); + p->wmPayload = (char*)gf_malloc(sizeof(u8) * p->wmPayloadLen); + gf_bs_read_data(bs, p->wmPayload, p->wmPayloadLen); + break; + case GF_IPMPX_WM_EXTRACT: + case GF_IPMPX_WM_DETECT_COMPRESSION: + p->wmRecipientId = gf_bs_read_int(bs, 16); + break; + } + if (has_opaque_data) { + p->opaqueDataSize = gf_bs_read_int(bs, 16); + p->opaqueData = (char*)gf_malloc(sizeof(u8) * p->wmPayloadLen); + gf_bs_read_data(bs, p->opaqueData, p->opaqueDataSize); + } + return GF_OK; +} +static u32 SizeGF_IPMPX_WatermarkingInit(GF_IPMPX_Data *_p) +{ + u32 size; + GF_IPMPX_WatermarkingInit *p = (GF_IPMPX_WatermarkingInit*)_p; + size = 2; + if (p->inputFormat==0x01) size += (p->tag == GF_IPMPX_AUDIO_WM_INIT_TAG) ? 6 : 5; + switch (p->requiredOp) { + case GF_IPMPX_WM_INSERT: + case GF_IPMPX_WM_REMARK: + size += 2+p->wmPayloadLen; + break; + case GF_IPMPX_WM_EXTRACT: + case GF_IPMPX_WM_DETECT_COMPRESSION: + size += 2; + break; + } + if (p->opaqueData) size += p->opaqueDataSize + 2; + return size; +} +static GF_Err WriteGF_IPMPX_WatermarkingInit(GF_BitStream *bs, GF_IPMPX_Data *_p) +{ + GF_IPMPX_WatermarkingInit*p = (GF_IPMPX_WatermarkingInit*)_p; + + gf_bs_write_int(bs, p->inputFormat, 8); + gf_bs_write_int(bs, p->requiredOp, 4); + gf_bs_write_int(bs, p->opaqueData ? 1 : 0, 1); + gf_bs_write_int(bs, 0, 3); + if (p->inputFormat==0x01) { + if (p->tag == GF_IPMPX_AUDIO_WM_INIT_TAG) { + gf_bs_write_int(bs, p->nChannels, 8); + gf_bs_write_int(bs, p->bitPerSample, 8); + gf_bs_write_int(bs, p->frequency, 32); + } else { + gf_bs_write_int(bs, p->frame_horizontal_size, 16); + gf_bs_write_int(bs, p->frame_vertical_size, 16); + gf_bs_write_int(bs, p->chroma_format, 8); + } + } + switch (p->requiredOp) { + case GF_IPMPX_WM_INSERT: + case GF_IPMPX_WM_REMARK: + gf_bs_write_int(bs, p->wmPayloadLen, 16); + gf_bs_write_data(bs, p->wmPayload, p->wmPayloadLen); + break; + case GF_IPMPX_WM_EXTRACT: + case GF_IPMPX_WM_DETECT_COMPRESSION: + gf_bs_write_int(bs, p->wmRecipientId, 16); + break; + } + if (p->opaqueData) { + gf_bs_write_int(bs, p->opaqueDataSize, 16); + gf_bs_write_data(bs, p->opaqueData, p->opaqueDataSize); + } + return GF_OK; +} +static GF_IPMPX_Data *NewGF_IPMPX_SendWatermark(u8 tag) +{ + GF_IPMPX_SendWatermark*ptr; + GF_IPMPX_DATA_ALLOC(ptr, GF_IPMPX_SendWatermark, tag); + return (GF_IPMPX_Data *) ptr; +} +static void DelGF_IPMPX_SendWatermark(GF_IPMPX_Data *_p) +{ + GF_IPMPX_SendWatermark*p = (GF_IPMPX_SendWatermark*)_p; + GF_IPMPX_DELETE_ARRAY(p->payload); + GF_IPMPX_DELETE_ARRAY(p->opaqueData); + gf_free(p); +} +static GF_Err ReadGF_IPMPX_SendWatermark(GF_BitStream *bs, GF_IPMPX_Data *_p, u32 size) +{ + Bool has_op_data; + GF_IPMPX_SendWatermark *p = (GF_IPMPX_SendWatermark*)_p; + p->wm_status = gf_bs_read_int(bs, 2); + p->compression_status = gf_bs_read_int(bs, 2); + has_op_data = gf_bs_read_int(bs, 1); + gf_bs_read_int(bs, 3); + if (p->wm_status==GF_IPMPX_WM_PAYLOAD) p->payload = GF_IPMPX_GetByteArray(bs); + if (has_op_data) p->opaqueData = GF_IPMPX_GetByteArray(bs); + return GF_OK; +} +static u32 SizeGF_IPMPX_SendWatermark(GF_IPMPX_Data *_p) +{ + u32 size; + GF_IPMPX_SendWatermark *p = (GF_IPMPX_SendWatermark*)_p; + size = 1; + if (p->payload) size += GF_IPMPX_GetByteArraySize(p->payload); + if (p->opaqueData) size += GF_IPMPX_GetByteArraySize(p->opaqueData); + return size; +} +static GF_Err WriteGF_IPMPX_SendWatermark(GF_BitStream *bs, GF_IPMPX_Data *_p) +{ + GF_IPMPX_SendWatermark*p = (GF_IPMPX_SendWatermark*)_p; + if (p->payload) p->wm_status = GF_IPMPX_WM_PAYLOAD; + gf_bs_write_int(bs, p->wm_status, 2); + gf_bs_write_int(bs, p->compression_status, 2); + gf_bs_write_int(bs, p->opaqueData ? 1 : 0, 1); + gf_bs_write_int(bs, 0, 3); + if (p->payload) GF_IPMPX_WriteByteArray(bs, p->payload); + if (p->opaqueData) GF_IPMPX_WriteByteArray(bs, p->opaqueData); + return GF_OK; +} + +static GF_IPMPX_Data *NewGF_IPMPX_ToolAPI_Config() +{ + GF_IPMPX_ToolAPI_Config*ptr; + GF_IPMPX_DATA_ALLOC(ptr, GF_IPMPX_ToolAPI_Config, GF_IPMPX_TOOL_API_CONFIG_TAG); + return (GF_IPMPX_Data *) ptr; +} +static void DelGF_IPMPX_ToolAPI_Config(GF_IPMPX_Data *_p) +{ + GF_IPMPX_ToolAPI_Config*p = (GF_IPMPX_ToolAPI_Config*)_p; + GF_IPMPX_DELETE_ARRAY(p->opaqueData); + gf_free(p); +} +static GF_Err ReadGF_IPMPX_ToolAPI_Config(GF_BitStream *bs, GF_IPMPX_Data *_p, u32 size) +{ + Bool has_i, has_m; + GF_IPMPX_ToolAPI_Config*p = (GF_IPMPX_ToolAPI_Config*)_p; + has_i = gf_bs_read_int(bs, 1); + has_m = gf_bs_read_int(bs, 1); + gf_bs_read_int(bs, 6); + if (has_i) p->Instantiation_API_ID = gf_bs_read_int(bs, 32); + if (has_m) p->Messaging_API_ID = gf_bs_read_int(bs, 32); + p->opaqueData = GF_IPMPX_GetByteArray(bs); + return GF_OK; +} +static u32 SizeGF_IPMPX_ToolAPI_Config(GF_IPMPX_Data *_p) +{ + u32 size; + GF_IPMPX_ToolAPI_Config *p = (GF_IPMPX_ToolAPI_Config*)_p; + size = 1; + if (p->Instantiation_API_ID) size += 4; + if (p->Messaging_API_ID ) size += 4; + size += GF_IPMPX_GetByteArraySize(p->opaqueData); + return size; +} +static GF_Err WriteGF_IPMPX_ToolAPI_Config(GF_BitStream *bs, GF_IPMPX_Data *_p) +{ + GF_IPMPX_ToolAPI_Config*p = (GF_IPMPX_ToolAPI_Config*)_p; + gf_bs_write_int(bs, p->Instantiation_API_ID ? 1 : 0, 1); + gf_bs_write_int(bs, p->Messaging_API_ID ? 1 : 0, 1); + gf_bs_write_int(bs, 0, 6); + if (p->Instantiation_API_ID) gf_bs_write_int(bs, p->Instantiation_API_ID, 32); + if (p->Messaging_API_ID) gf_bs_write_int(bs, p->Messaging_API_ID, 32); + GF_IPMPX_WriteByteArray(bs, p->opaqueData); + return GF_OK; +} + +static GF_IPMPX_Data *NewGF_IPMPX_ISMACryp() +{ + GF_IPMPX_ISMACryp *ptr; + GF_IPMPX_DATA_ALLOC(ptr, GF_IPMPX_ISMACryp, GF_IPMPX_ISMACRYP_TAG); + return (GF_IPMPX_Data *) ptr; +} +static void DelGF_IPMPX_ISMACryp(GF_IPMPX_Data *_p) +{ + GF_IPMPX_ISMACryp*p = (GF_IPMPX_ISMACryp*)_p; + gf_free(p); +} +static GF_Err ReadGF_IPMPX_ISMACryp(GF_BitStream *bs, GF_IPMPX_Data *_p, u32 size) +{ + GF_IPMPX_ISMACryp*p = (GF_IPMPX_ISMACryp*)_p; + p->cryptoSuite = gf_bs_read_int(bs, 8); + p->IV_length = gf_bs_read_int(bs, 8); + p->use_selective_encryption = gf_bs_read_int(bs, 1); + gf_bs_read_int(bs, 7); + p->key_indicator_length = gf_bs_read_int(bs, 8); + return GF_OK; +} +static u32 SizeGF_IPMPX_ISMACryp(GF_IPMPX_Data *_p) +{ + return 4; +} +static GF_Err WriteGF_IPMPX_ISMACryp(GF_BitStream *bs, GF_IPMPX_Data *_p) +{ + GF_IPMPX_ISMACryp*p = (GF_IPMPX_ISMACryp*)_p; + gf_bs_write_int(bs, p->cryptoSuite, 8); + gf_bs_write_int(bs, p->IV_length, 8); + gf_bs_write_int(bs, p->use_selective_encryption ? 1 : 0, 1); + gf_bs_write_int(bs, 0, 7); + gf_bs_write_int(bs, p->key_indicator_length, 8); + return GF_OK; +} + +GF_IPMPX_Data *gf_ipmpx_data_new(u8 tag) +{ + switch (tag) { + case GF_IPMPX_RIGHTS_DATA_TAG: + case GF_IPMPX_OPAQUE_DATA_TAG: return NewGF_IPMPX_OpaqueData(tag); + case GF_IPMPX_KEY_DATA_TAG: return NewGF_IPMPX_KeyData(); + case GF_IPMPX_SECURE_CONTAINER_TAG: return NewGF_IPMPX_SecureContainer(); + case GF_IPMPX_ADD_TOOL_LISTENER_TAG: return NewGF_IPMPX_AddToolNotificationListener(); + case GF_IPMPX_REMOVE_TOOL_LISTENER_TAG: return NewGF_IPMPX_RemoveToolNotificationListener(); + case GF_IPMPX_INIT_AUTHENTICATION_TAG: return NewGF_IPMPX_InitAuthentication(); + case GF_IPMPX_MUTUAL_AUTHENTICATION_TAG: return NewGF_IPMPX_MutualAuthentication(); + case GF_IPMPX_PARAMETRIC_DESCRIPTION_TAG: return NewGF_IPMPX_ParametricDescription(); + case GF_IPMPX_PARAMETRIC_CAPS_QUERY_TAG: return NewGF_IPMPX_ToolParamCapabilitiesQuery(); + case GF_IPMPX_PARAMETRIC_CAPS_RESPONSE_TAG: return NewGF_IPMPX_ToolParamCapabilitiesResponse(); + case GF_IPMPX_GET_TOOLS_RESPONSE_TAG: return NewGF_IPMPX_GetToolsResponse(); + case GF_IPMPX_GET_TOOL_CONTEXT_TAG: return NewGF_IPMPX_GetToolContext(); + case GF_IPMPX_GET_TOOL_CONTEXT_RESPONSE_TAG: return NewGF_IPMPX_GetToolContextResponse(); + case GF_IPMPX_CONNECT_TOOL_TAG: return NewGF_IPMPX_ConnectTool(); + case GF_IPMPX_DISCONNECT_TOOL_TAG: return NewGF_IPMPX_DisconnectTool(); + case GF_IPMPX_NOTIFY_TOOL_EVENT_TAG: return NewGF_IPMPX_NotifyToolEvent(); + case GF_IPMPX_CAN_PROCESS_TAG: return NewGF_IPMPX_CanProcess(); + case GF_IPMPX_TRUST_SECURITY_METADATA_TAG: return NewGF_IPMPX_TrustSecurityMetadata(); + case GF_IPMPX_ISMACRYP_TAG: return NewGF_IPMPX_ISMACryp(); + case GF_IPMPX_GET_TOOLS_TAG: + { + GF_IPMPX_Data *p; + GF_IPMPX_DATA_ALLOC(p, GF_IPMPX_Data, GF_IPMPX_GET_TOOLS_TAG); + return p; + } + case GF_IPMPX_TRUSTED_TOOL_TAG: + { + GF_IPMPX_TrustedTool *p; + GF_IPMPX_DATA_ALLOC(p, GF_IPMPX_TrustedTool, GF_IPMPX_TRUSTED_TOOL_TAG); + p->trustSpecifications = gf_list_new(); + return (GF_IPMPX_Data *)p; + } + case GF_IPMPX_TRUST_SPECIFICATION_TAG: + { + GF_IPMPX_TrustSpecification *p; + GF_IPMPX_DATA_ALLOC(p, GF_IPMPX_TrustSpecification, GF_IPMPX_TRUST_SPECIFICATION_TAG); + return (GF_IPMPX_Data *)p; + } + case GF_IPMPX_TOOL_API_CONFIG_TAG: return NewGF_IPMPX_ToolAPI_Config(); + case GF_IPMPX_SEL_DEC_INIT_TAG: return NewGF_IPMPX_SelectiveDecryptionInit(); + case GF_IPMPX_AUDIO_WM_INIT_TAG: + case GF_IPMPX_VIDEO_WM_INIT_TAG: + return NewGF_IPMPX_WatermarkingInit(tag); + case GF_IPMPX_AUDIO_WM_SEND_TAG: + case GF_IPMPX_VIDEO_WM_SEND_TAG: + return NewGF_IPMPX_SendWatermark(tag); + + case GF_IPMPX_ALGORITHM_DESCRIPTOR_TAG: + { + GF_IPMPX_AUTH_AlgorithmDescriptor *p = (GF_IPMPX_AUTH_AlgorithmDescriptor *)gf_malloc(sizeof(GF_IPMPX_AUTH_AlgorithmDescriptor)); + if (!p) return NULL; + memset(p, 0, sizeof(GF_IPMPX_AUTH_AlgorithmDescriptor)); + p->tag = GF_IPMPX_ALGORITHM_DESCRIPTOR_TAG; + return (GF_IPMPX_Data *) p; + } + case GF_IPMPX_KEY_DESCRIPTOR_TAG: + { + GF_IPMPX_AUTH_KeyDescriptor *p = (GF_IPMPX_AUTH_KeyDescriptor *)gf_malloc(sizeof(GF_IPMPX_AUTH_KeyDescriptor)); + if (!p) return NULL; + memset(p, 0, sizeof(GF_IPMPX_AUTH_KeyDescriptor)); + p->tag = GF_IPMPX_KEY_DESCRIPTOR_TAG; + return (GF_IPMPX_Data *) p; + } + + case GF_IPMPX_PARAM_DESCRIPTOR_ITEM_TAG: + { + GF_IPMPX_AUTH_KeyDescriptor *p = (GF_IPMPX_AUTH_KeyDescriptor *)gf_malloc(sizeof(GF_IPMPX_ParametricDescriptionItem)); + if (!p) return NULL; + memset(p, 0, sizeof(GF_IPMPX_ParametricDescriptionItem)); + p->tag = GF_IPMPX_PARAM_DESCRIPTOR_ITEM_TAG; + return (GF_IPMPX_Data *) p; + } + case GF_IPMPX_SEL_ENC_BUFFER_TAG: + { + GF_IPMPX_SelEncBuffer*p = (GF_IPMPX_SelEncBuffer*)gf_malloc(sizeof(GF_IPMPX_SelEncBuffer)); + if (!p) return NULL; + memset(p, 0, sizeof(GF_IPMPX_SelEncBuffer)); + p->tag = GF_IPMPX_SEL_ENC_BUFFER_TAG; + return (GF_IPMPX_Data *) p; + } + case GF_IPMPX_SEL_ENC_FIELD_TAG: + { + GF_IPMPX_SelEncField*p = (GF_IPMPX_SelEncField*)gf_malloc(sizeof(GF_IPMPX_SelEncField)); + if (!p) return NULL; + memset(p, 0, sizeof(GF_IPMPX_SelEncField)); + p->tag = GF_IPMPX_SEL_ENC_FIELD_TAG; + return (GF_IPMPX_Data *) p; + } + +/* + case GF_IPMPX_USER_QUERY_TAG: return NewGF_IPMPX_UserQuery(); + case GF_IPMPX_USER_RESPONSE_TAG: return NewGF_IPMPX_UserQueryResponse(); +*/ + default: return NULL; + } + +} + +void gf_ipmpx_data_del(GF_IPMPX_Data *_p) +{ + if (!_p) return; + switch (_p->tag) { + case GF_IPMPX_RIGHTS_DATA_TAG: + case GF_IPMPX_OPAQUE_DATA_TAG: DelGF_IPMPX_OpaqueData(_p); return; + case GF_IPMPX_KEY_DATA_TAG: DelGF_IPMPX_KeyData(_p); return; + case GF_IPMPX_SECURE_CONTAINER_TAG: DelGF_IPMPX_SecureContainer(_p); return; + case GF_IPMPX_ADD_TOOL_LISTENER_TAG: DelGF_IPMPX_AddToolNotificationListener(_p); return; + case GF_IPMPX_REMOVE_TOOL_LISTENER_TAG: DelGF_IPMPX_RemoveToolNotificationListener(_p); return; + case GF_IPMPX_INIT_AUTHENTICATION_TAG: DelGF_IPMPX_InitAuthentication(_p); return; + case GF_IPMPX_MUTUAL_AUTHENTICATION_TAG: DelGF_IPMPX_MutualAuthentication(_p); return; + case GF_IPMPX_PARAMETRIC_DESCRIPTION_TAG: DelGF_IPMPX_ParametricDescription(_p); return; + case GF_IPMPX_PARAMETRIC_CAPS_QUERY_TAG: DelGF_IPMPX_ToolParamCapabilitiesQuery(_p); return; + case GF_IPMPX_PARAMETRIC_CAPS_RESPONSE_TAG: DelGF_IPMPX_ToolParamCapabilitiesResponse(_p); return; + case GF_IPMPX_GET_TOOLS_RESPONSE_TAG: DelGF_IPMPX_GetToolsResponse(_p); return; + case GF_IPMPX_GET_TOOL_CONTEXT_TAG: DelGF_IPMPX_GetToolContext(_p); return; + case GF_IPMPX_GET_TOOL_CONTEXT_RESPONSE_TAG: DelGF_IPMPX_GetToolContextResponse(_p); return; + case GF_IPMPX_CONNECT_TOOL_TAG: DelGF_IPMPX_ConnectTool(_p); return; + case GF_IPMPX_DISCONNECT_TOOL_TAG: DelGF_IPMPX_DisconnectTool(_p); return; + case GF_IPMPX_NOTIFY_TOOL_EVENT_TAG: DelGF_IPMPX_NotifyToolEvent(_p); return; + case GF_IPMPX_CAN_PROCESS_TAG: DelGF_IPMPX_CanProcess(_p); return; + case GF_IPMPX_TRUST_SECURITY_METADATA_TAG: DelGF_IPMPX_TrustSecurityMetadata(_p); return; + case GF_IPMPX_TOOL_API_CONFIG_TAG: DelGF_IPMPX_ToolAPI_Config(_p); return; + case GF_IPMPX_ISMACRYP_TAG: DelGF_IPMPX_ISMACryp(_p); return; + case GF_IPMPX_SEL_DEC_INIT_TAG: DelGF_IPMPX_SelectiveDecryptionInit(_p); return; + case GF_IPMPX_AUDIO_WM_INIT_TAG: + case GF_IPMPX_VIDEO_WM_INIT_TAG: + DelGF_IPMPX_WatermarkingInit(_p); return; + case GF_IPMPX_AUDIO_WM_SEND_TAG: + case GF_IPMPX_VIDEO_WM_SEND_TAG: + DelGF_IPMPX_SendWatermark(_p); return; + +/* + case GF_IPMPX_USER_QUERY_TAG: DelGF_IPMPX_UserQuery(_p); return; + case GF_IPMPX_USER_RESPONSE_TAG: DelGF_IPMPX_UserQueryResponse(_p); return; +*/ + case GF_IPMPX_TRUSTED_TOOL_TAG: + { + GF_IPMPX_TrustedTool *p = (GF_IPMPX_TrustedTool *)_p; + while (gf_list_count(p->trustSpecifications)) { + GF_IPMPX_Data *ts = (GF_IPMPX_Data *)gf_list_get(p->trustSpecifications, 0); + gf_list_rem(p->trustSpecifications, 0); + gf_ipmpx_data_del(ts); + } + gf_list_del(p->trustSpecifications); + gf_free(p); + return; + } + case GF_IPMPX_TRUST_SPECIFICATION_TAG: + { + GF_IPMPX_TrustSpecification *p = (GF_IPMPX_TrustSpecification *)_p; + GF_IPMPX_DELETE_ARRAY(p->CCTrustMetadata); + gf_free(p); + return; + } + case GF_IPMPX_PARAM_DESCRIPTOR_ITEM_TAG: + { + GF_IPMPX_ParametricDescriptionItem *p = (GF_IPMPX_ParametricDescriptionItem*)_p; + GF_IPMPX_DELETE_ARRAY(p->main_class); + GF_IPMPX_DELETE_ARRAY(p->subClass); + GF_IPMPX_DELETE_ARRAY(p->typeData); + GF_IPMPX_DELETE_ARRAY(p->type); + GF_IPMPX_DELETE_ARRAY(p->addedData); + gf_free(p); + return; + } + case GF_IPMPX_ALGORITHM_DESCRIPTOR_TAG: + _p->tag = GF_IPMPX_AUTH_AlgorithmDescr_Tag; + GF_IPMPX_AUTH_Delete((GF_IPMPX_Authentication*)_p); + return; + case GF_IPMPX_KEY_DESCRIPTOR_TAG: + _p->tag = GF_IPMPX_AUTH_KeyDescr_Tag; + GF_IPMPX_AUTH_Delete((GF_IPMPX_Authentication*)_p); + return; + + case GF_IPMPX_SEL_ENC_BUFFER_TAG: + { + GF_IPMPX_SelEncBuffer*p = (GF_IPMPX_SelEncBuffer*)_p; + GF_IPMPX_DELETE_ARRAY(p->Stream_Cipher_Specific_Init_Info); + gf_free(p); + return; + } + case GF_IPMPX_SEL_ENC_FIELD_TAG: + { + GF_IPMPX_SelEncField*p = (GF_IPMPX_SelEncField*)_p; + GF_IPMPX_DELETE_ARRAY(p->shuffleSpecificInfo); + if (p->mappingTable) gf_free(p->mappingTable); + gf_free(p); + return; + } + + case GF_IPMPX_GET_TOOLS_TAG: + default: + gf_free(_p); + return; + } +} + +GF_Err GF_IPMPX_ReadData(GF_BitStream *bs, GF_IPMPX_Data *_p, u32 read) +{ + switch (_p->tag) { + case GF_IPMPX_RIGHTS_DATA_TAG: + case GF_IPMPX_OPAQUE_DATA_TAG: return ReadGF_IPMPX_OpaqueData(bs, _p, read); + case GF_IPMPX_KEY_DATA_TAG: return ReadGF_IPMPX_KeyData(bs, _p, read); + case GF_IPMPX_SECURE_CONTAINER_TAG: return ReadGF_IPMPX_SecureContainer(bs, _p, read); + case GF_IPMPX_ADD_TOOL_LISTENER_TAG: return ReadGF_IPMPX_AddToolNotificationListener(bs, _p, read); + case GF_IPMPX_REMOVE_TOOL_LISTENER_TAG: return ReadGF_IPMPX_RemoveToolNotificationListener(bs, _p, read); + case GF_IPMPX_INIT_AUTHENTICATION_TAG: return ReadGF_IPMPX_InitAuthentication(bs, _p, read); + case GF_IPMPX_MUTUAL_AUTHENTICATION_TAG: return ReadGF_IPMPX_MutualAuthentication(bs, _p, read); + case GF_IPMPX_PARAMETRIC_DESCRIPTION_TAG: return ReadGF_IPMPX_ParametricDescription(bs, _p, read); + case GF_IPMPX_PARAMETRIC_CAPS_QUERY_TAG: return ReadGF_IPMPX_ToolParamCapabilitiesQuery(bs, _p, read); + case GF_IPMPX_PARAMETRIC_CAPS_RESPONSE_TAG: return ReadGF_IPMPX_ToolParamCapabilitiesResponse(bs, _p, read); + case GF_IPMPX_GET_TOOLS_RESPONSE_TAG: return ReadGF_IPMPX_GetToolsResponse(bs, _p, read); + case GF_IPMPX_GET_TOOL_CONTEXT_TAG: return ReadGF_IPMPX_GetToolContext(bs, _p, read); + case GF_IPMPX_GET_TOOL_CONTEXT_RESPONSE_TAG: return ReadGF_IPMPX_GetToolContextResponse(bs, _p, read); + case GF_IPMPX_CONNECT_TOOL_TAG: return ReadGF_IPMPX_ConnectTool(bs, _p, read); + case GF_IPMPX_DISCONNECT_TOOL_TAG: return ReadGF_IPMPX_DisconnectTool(bs, _p, read); + case GF_IPMPX_NOTIFY_TOOL_EVENT_TAG: return ReadGF_IPMPX_NotifyToolEvent(bs, _p, read); + case GF_IPMPX_CAN_PROCESS_TAG: return ReadGF_IPMPX_CanProcess(bs, _p, read); + case GF_IPMPX_TRUST_SECURITY_METADATA_TAG: return ReadGF_IPMPX_TrustSecurityMetadata(bs, _p, read); + case GF_IPMPX_TOOL_API_CONFIG_TAG: return ReadGF_IPMPX_ToolAPI_Config(bs, _p, read); + case GF_IPMPX_ISMACRYP_TAG: return ReadGF_IPMPX_ISMACryp(bs, _p, read); + case GF_IPMPX_SEL_DEC_INIT_TAG: return ReadGF_IPMPX_SelectiveDecryptionInit(bs, _p, read); + case GF_IPMPX_AUDIO_WM_INIT_TAG: + case GF_IPMPX_VIDEO_WM_INIT_TAG: + return ReadGF_IPMPX_WatermarkingInit(bs, _p, read); + case GF_IPMPX_AUDIO_WM_SEND_TAG: + case GF_IPMPX_VIDEO_WM_SEND_TAG: + return ReadGF_IPMPX_SendWatermark(bs, _p, read); + +/* + case GF_IPMPX_USER_QUERY_TAG: return ReadGF_IPMPX_UserQuery(bs, _p, read); + case GF_IPMPX_USER_RESPONSE_TAG: return ReadGF_IPMPX_UserQueryResponse(bs, _p, read); +*/ + case GF_IPMPX_GET_TOOLS_TAG: return GF_OK; + default: return GF_BAD_PARAM; + } +} + +u32 gf_ipmpx_data_size(GF_IPMPX_Data *_p) +{ + switch (_p->tag) { + case GF_IPMPX_RIGHTS_DATA_TAG: + case GF_IPMPX_OPAQUE_DATA_TAG: return SizeGF_IPMPX_OpaqueData(_p); + case GF_IPMPX_KEY_DATA_TAG: return SizeGF_IPMPX_KeyData(_p); + case GF_IPMPX_SECURE_CONTAINER_TAG: return SizeGF_IPMPX_SecureContainer(_p); + case GF_IPMPX_ADD_TOOL_LISTENER_TAG: return SizeGF_IPMPX_AddToolNotificationListener(_p); + case GF_IPMPX_REMOVE_TOOL_LISTENER_TAG: return SizeGF_IPMPX_RemoveToolNotificationListener(_p); + case GF_IPMPX_INIT_AUTHENTICATION_TAG: return SizeGF_IPMPX_InitAuthentication(_p); + case GF_IPMPX_MUTUAL_AUTHENTICATION_TAG: return SizeGF_IPMPX_MutualAuthentication(_p); + case GF_IPMPX_PARAMETRIC_DESCRIPTION_TAG: return SizeGF_IPMPX_ParametricDescription(_p); + case GF_IPMPX_PARAMETRIC_CAPS_QUERY_TAG: return SizeGF_IPMPX_ToolParamCapabilitiesQuery(_p); + case GF_IPMPX_PARAMETRIC_CAPS_RESPONSE_TAG: return SizeGF_IPMPX_ToolParamCapabilitiesResponse(_p); + case GF_IPMPX_GET_TOOLS_RESPONSE_TAG: return SizeGF_IPMPX_GetToolsResponse(_p); + case GF_IPMPX_GET_TOOL_CONTEXT_TAG: return SizeGF_IPMPX_GetToolContext(_p); + case GF_IPMPX_GET_TOOL_CONTEXT_RESPONSE_TAG: return SizeGF_IPMPX_GetToolContextResponse(_p); + case GF_IPMPX_CONNECT_TOOL_TAG: return SizeGF_IPMPX_ConnectTool(_p); + case GF_IPMPX_DISCONNECT_TOOL_TAG: return SizeGF_IPMPX_DisconnectTool(_p); + case GF_IPMPX_NOTIFY_TOOL_EVENT_TAG: return SizeGF_IPMPX_NotifyToolEvent(_p); + case GF_IPMPX_CAN_PROCESS_TAG: return SizeGF_IPMPX_CanProcess(_p); + case GF_IPMPX_TRUST_SECURITY_METADATA_TAG: return SizeGF_IPMPX_TrustSecurityMetadata(_p); + case GF_IPMPX_TOOL_API_CONFIG_TAG: return SizeGF_IPMPX_ToolAPI_Config(_p); + case GF_IPMPX_ISMACRYP_TAG: return SizeGF_IPMPX_ISMACryp(_p); + case GF_IPMPX_SEL_DEC_INIT_TAG: return SizeGF_IPMPX_SelectiveDecryptionInit(_p); + case GF_IPMPX_AUDIO_WM_INIT_TAG: + case GF_IPMPX_VIDEO_WM_INIT_TAG: + return SizeGF_IPMPX_WatermarkingInit(_p); + case GF_IPMPX_AUDIO_WM_SEND_TAG: + case GF_IPMPX_VIDEO_WM_SEND_TAG: + return SizeGF_IPMPX_SendWatermark(_p); + +/* + case GF_IPMPX_USER_QUERY_TAG: return SizeGF_IPMPX_UserQuery(_p); + case GF_IPMPX_USER_RESPONSE_TAG: return SizeGF_IPMPX_UserQueryResponse(_p); +*/ + case GF_IPMPX_GET_TOOLS_TAG: return 0; + default: return GF_BAD_PARAM; + } +} + +GF_Err gf_ipmpx_data_write(GF_BitStream *bs, GF_IPMPX_Data *_p) +{ + GF_Err e; + if (!_p) return GF_OK; + e = GF_IPMPX_WriteBase(bs, _p); + if (e) return e; + switch (_p->tag) { + case GF_IPMPX_RIGHTS_DATA_TAG: + case GF_IPMPX_OPAQUE_DATA_TAG: return WriteGF_IPMPX_OpaqueData(bs, _p); + case GF_IPMPX_KEY_DATA_TAG: return WriteGF_IPMPX_KeyData(bs, _p); + case GF_IPMPX_SECURE_CONTAINER_TAG: return WriteGF_IPMPX_SecureContainer(bs, _p); + case GF_IPMPX_ADD_TOOL_LISTENER_TAG: return WriteGF_IPMPX_AddToolNotificationListener(bs, _p); + case GF_IPMPX_REMOVE_TOOL_LISTENER_TAG: return WriteGF_IPMPX_RemoveToolNotificationListener(bs, _p); + case GF_IPMPX_INIT_AUTHENTICATION_TAG: return WriteGF_IPMPX_InitAuthentication(bs, _p); + case GF_IPMPX_MUTUAL_AUTHENTICATION_TAG: return WriteGF_IPMPX_MutualAuthentication(bs, _p); + case GF_IPMPX_PARAMETRIC_DESCRIPTION_TAG: return WriteGF_IPMPX_ParametricDescription(bs, _p); + case GF_IPMPX_PARAMETRIC_CAPS_QUERY_TAG: return WriteGF_IPMPX_ToolParamCapabilitiesQuery(bs, _p); + case GF_IPMPX_PARAMETRIC_CAPS_RESPONSE_TAG: return WriteGF_IPMPX_ToolParamCapabilitiesResponse(bs, _p); + case GF_IPMPX_GET_TOOLS_RESPONSE_TAG: return WriteGF_IPMPX_GetToolsResponse(bs, _p); + case GF_IPMPX_GET_TOOL_CONTEXT_TAG: return WriteGF_IPMPX_GetToolContext(bs, _p); + case GF_IPMPX_GET_TOOL_CONTEXT_RESPONSE_TAG: return WriteGF_IPMPX_GetToolContextResponse(bs, _p); + case GF_IPMPX_CONNECT_TOOL_TAG: return WriteGF_IPMPX_ConnectTool(bs, _p); + case GF_IPMPX_DISCONNECT_TOOL_TAG: return WriteGF_IPMPX_DisconnectTool(bs, _p); + case GF_IPMPX_NOTIFY_TOOL_EVENT_TAG: return WriteGF_IPMPX_NotifyToolEvent(bs, _p); + case GF_IPMPX_CAN_PROCESS_TAG: return WriteGF_IPMPX_CanProcess(bs, _p); + case GF_IPMPX_TRUST_SECURITY_METADATA_TAG: return WriteGF_IPMPX_TrustSecurityMetadata(bs, _p); + case GF_IPMPX_TOOL_API_CONFIG_TAG: return WriteGF_IPMPX_ToolAPI_Config(bs, _p); + case GF_IPMPX_ISMACRYP_TAG: return WriteGF_IPMPX_ISMACryp(bs, _p); + case GF_IPMPX_SEL_DEC_INIT_TAG: return WriteGF_IPMPX_SelectiveDecryptionInit(bs, _p); + case GF_IPMPX_AUDIO_WM_INIT_TAG: + case GF_IPMPX_VIDEO_WM_INIT_TAG: + return WriteGF_IPMPX_WatermarkingInit(bs, _p); + case GF_IPMPX_AUDIO_WM_SEND_TAG: + case GF_IPMPX_VIDEO_WM_SEND_TAG: + return WriteGF_IPMPX_SendWatermark(bs, _p); + +/* + case GF_IPMPX_USER_QUERY_TAG: return WriteGF_IPMPX_UserQuery(bs, _p); + case GF_IPMPX_USER_RESPONSE_TAG: return WriteGF_IPMPX_UserQueryResponse(bs, _p); +*/ + case GF_IPMPX_GET_TOOLS_TAG: return GF_OK; + default: return GF_BAD_PARAM; + } +} + +#endif /*GPAC_MINIMAL_ODF*/ diff --git a/src/gpacmp4/ipmpx_parse.c b/src/gpacmp4/ipmpx_parse.c new file mode 100644 index 00000000..980b48db --- /dev/null +++ b/src/gpacmp4/ipmpx_parse.c @@ -0,0 +1,681 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / MPEG-4 ObjectDescriptor sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include "../disable_warnings.h" +#include + +#ifndef GPAC_MINIMAL_ODF + +void GF_IPMPX_AUTH_Delete(GF_IPMPX_Authentication *auth); + + +u8 gf_ipmpx_get_tag(char *dataName) +{ + if (!stricmp(dataName, "IPMP_KeyData")) return GF_IPMPX_KEY_DATA_TAG; + else if (!stricmp(dataName, "IPMP_RightsData")) return GF_IPMPX_RIGHTS_DATA_TAG; + else if (!stricmp(dataName, "IPMP_OpaqueData")) return GF_IPMPX_OPAQUE_DATA_TAG; + else if (!stricmp(dataName, "IPMP_SecureContainer")) return GF_IPMPX_SECURE_CONTAINER_TAG; + else if (!stricmp(dataName, "IPMP_InitAuthentication")) return GF_IPMPX_INIT_AUTHENTICATION_TAG; + else if (!stricmp(dataName, "IPMP_TrustSecurityMetadata")) return GF_IPMPX_TRUST_SECURITY_METADATA_TAG; + else if (!stricmp(dataName, "IPMP_TrustedTool")) return GF_IPMPX_TRUSTED_TOOL_TAG; + else if (!stricmp(dataName, "IPMP_TrustSpecification")) return GF_IPMPX_TRUST_SPECIFICATION_TAG; + else if (!stricmp(dataName, "IPMP_MutualAuthentication")) return GF_IPMPX_MUTUAL_AUTHENTICATION_TAG; + else if (!stricmp(dataName, "IPMP_AlgorithmDescriptor")) return GF_IPMPX_ALGORITHM_DESCRIPTOR_TAG; + else if (!stricmp(dataName, "IPMP_KeyDescriptor")) return GF_IPMPX_KEY_DESCRIPTOR_TAG; + else if (!stricmp(dataName, "IPMP_GetToolsResponse")) return GF_IPMPX_GET_TOOLS_RESPONSE_TAG; + else if (!stricmp(dataName, "IPMP_ParametricDescription")) return GF_IPMPX_PARAMETRIC_DESCRIPTION_TAG; + else if (!stricmp(dataName, "IPMP_ParametricDescriptionItem")) return GF_IPMPX_PARAM_DESCRIPTOR_ITEM_TAG; + else if (!stricmp(dataName, "IPMP_ToolParamCapabilitiesQuery")) return GF_IPMPX_PARAMETRIC_CAPS_QUERY_TAG; + else if (!stricmp(dataName, "IPMP_ToolParamCapabilitiesResponse")) return GF_IPMPX_PARAMETRIC_CAPS_RESPONSE_TAG; + else if (!stricmp(dataName, "IPMP_ConnectTool")) return GF_IPMPX_CONNECT_TOOL_TAG; + else if (!stricmp(dataName, "IPMP_DisconnectTool")) return GF_IPMPX_DISCONNECT_TOOL_TAG; + else if (!stricmp(dataName, "IPMP_GetToolContext")) return GF_IPMPX_GET_TOOL_CONTEXT_TAG; + else if (!stricmp(dataName, "IPMP_GetToolContextResponse")) return GF_IPMPX_GET_TOOL_CONTEXT_RESPONSE_TAG; + else if (!stricmp(dataName, "IPMP_AddToolNotificationListener")) return GF_IPMPX_ADD_TOOL_LISTENER_TAG; + else if (!stricmp(dataName, "IPMP_RemoveToolNotificationListener")) return GF_IPMPX_REMOVE_TOOL_LISTENER_TAG; + else if (!stricmp(dataName, "IPMP_NotifyToolEvent")) return GF_IPMPX_NOTIFY_TOOL_EVENT_TAG; + else if (!stricmp(dataName, "IPMP_CanProcess")) return GF_IPMPX_CAN_PROCESS_TAG; + else if (!stricmp(dataName, "IPMP_ToolAPI_Config")) return GF_IPMPX_TOOL_API_CONFIG_TAG; + else if (!stricmp(dataName, "IPMP_AudioWatermarkingInit")) return GF_IPMPX_AUDIO_WM_INIT_TAG; + else if (!stricmp(dataName, "IPMP_VideoWatermarkingInit")) return GF_IPMPX_VIDEO_WM_INIT_TAG; + else if (!stricmp(dataName, "IPMP_SendAudioWatermark")) return GF_IPMPX_AUDIO_WM_SEND_TAG; + else if (!stricmp(dataName, "IPMP_SendVideoWatermark")) return GF_IPMPX_VIDEO_WM_SEND_TAG; + else if (!stricmp(dataName, "IPMP_SelectiveDecryptionInit")) return GF_IPMPX_SEL_DEC_INIT_TAG; + else if (!stricmp(dataName, "IPMP_SelectiveBuffer")) return GF_IPMPX_SEL_ENC_BUFFER_TAG; + else if (!stricmp(dataName, "IPMP_SelectiveField")) return GF_IPMPX_SEL_ENC_FIELD_TAG; + else if (!stricmp(dataName, "ISMACryp_Data")) return GF_IPMPX_ISMACRYP_TAG; + return 0; +} + +u32 gf_ipmpx_get_field_type(GF_IPMPX_Data *p, char *fieldName) +{ + switch (p->tag) { + case GF_IPMPX_KEY_DATA_TAG: + if (!stricmp(fieldName, "keyBody")|| !stricmp(fieldName, "opaqueData")) return GF_ODF_FT_IPMPX_BA; + break; + case GF_IPMPX_RIGHTS_DATA_TAG: + if (!stricmp(fieldName, "rightsInfo")) return GF_ODF_FT_IPMPX_BA; + break; + case GF_IPMPX_OPAQUE_DATA_TAG: + if (!stricmp(fieldName, "OpaqueData")) return GF_ODF_FT_IPMPX_BA; + break; + case GF_IPMPX_SECURE_CONTAINER_TAG: + if (!stricmp(fieldName, "encryptedData") || !stricmp(fieldName, "MAC")) return GF_ODF_FT_IPMPX_BA; + else if (!stricmp(fieldName, "protectedMsg")) return GF_ODF_FT_IPMPX; + break; + case GF_IPMPX_TRUST_SECURITY_METADATA_TAG: + if (!stricmp(fieldName, "trustedTools")) return GF_ODF_FT_IPMPX_LIST; + break; + case GF_IPMPX_TRUSTED_TOOL_TAG: + if (!stricmp(fieldName, "trustSpecifications")) return GF_ODF_FT_IPMPX_LIST; + break; + case GF_IPMPX_TRUST_SPECIFICATION_TAG: + if (!stricmp(fieldName, "CCTrustMetadata")) return GF_ODF_FT_IPMPX_BA; + break; + case GF_IPMPX_MUTUAL_AUTHENTICATION_TAG: + if (!stricmp(fieldName, "candidateAlgorithms") || !stricmp(fieldName, "agreedAlgorithms")) return GF_ODF_FT_IPMPX_LIST; + else if (!stricmp(fieldName, "certificates")) return GF_ODF_FT_IPMPX_BA_LIST; + else if (!stricmp(fieldName, "publicKey") || !stricmp(fieldName, "trustData")) return GF_ODF_FT_IPMPX; + else if (!stricmp(fieldName, "authCodes") || !stricmp(fieldName, "opaque") || !stricmp(fieldName, "AuthenticationData")) + return GF_ODF_FT_IPMPX_BA; + break; + case GF_IPMPX_ALGORITHM_DESCRIPTOR_TAG: + if (!stricmp(fieldName, "specAlgoID") || !stricmp(fieldName, "OpaqueData")) return GF_ODF_FT_IPMPX_BA; + break; + case GF_IPMPX_GET_TOOLS_RESPONSE_TAG: + if (!stricmp(fieldName, "ipmp_tools")) return GF_ODF_FT_OD_LIST; + break; + case GF_IPMPX_PARAMETRIC_DESCRIPTION_TAG: + if (!stricmp(fieldName, "descriptionComment")) return GF_ODF_FT_IPMPX_BA; + else if (!stricmp(fieldName, "descriptions")) return GF_ODF_FT_IPMPX_LIST; + break; + case GF_IPMPX_PARAM_DESCRIPTOR_ITEM_TAG: + /*all is IPMPX data*/ + return GF_ODF_FT_IPMPX_BA; + case GF_IPMPX_PARAMETRIC_CAPS_QUERY_TAG: + if (!stricmp(fieldName, "description")) return GF_ODF_FT_IPMPX; + break; + case GF_IPMPX_CONNECT_TOOL_TAG: + if (!stricmp(fieldName, "toolDescriptor")) return GF_ODF_FT_OD; + break; + case GF_IPMPX_TOOL_API_CONFIG_TAG: + if (!stricmp(fieldName, "opaqueData")) return GF_ODF_FT_IPMPX_BA; + break; + case GF_IPMPX_AUDIO_WM_SEND_TAG: + case GF_IPMPX_VIDEO_WM_SEND_TAG: + if (!stricmp(fieldName, "payload") || !stricmp(fieldName, "opaqueData")) return GF_ODF_FT_IPMPX_BA; + break; + case GF_IPMPX_SEL_DEC_INIT_TAG: + if (!stricmp(fieldName, "SelectiveBuffers") || !stricmp(fieldName, "SelectiveFields")) return GF_ODF_FT_IPMPX_LIST; + break; + case GF_IPMPX_SEL_ENC_BUFFER_TAG: + if (!stricmp(fieldName, "StreamCipher")) return GF_ODF_FT_IPMPX_BA; + break; + case GF_IPMPX_SEL_ENC_FIELD_TAG: + if (!stricmp(fieldName, "shuffleSpecificInfo")) return GF_ODF_FT_IPMPX_BA; + break; + default: + break; + } + return 0; +} + +#include + +void GF_IPMPX_ParseBinData(char *val, char **out_data, u32 *out_data_size) +{ + u32 i, c, len; + char s[3]; + + if (val[0] != '%') { + len = *out_data_size = strlen(val); + *out_data = (char*)gf_malloc(sizeof(char) * len); + memcpy(*out_data, val, sizeof(char) * len); + return; + } + + len = strlen(val) / 3; + if (*out_data) gf_free(*out_data); + *out_data_size = len; + *out_data = (char*)gf_malloc(sizeof(char) * len); + s[2] = 0; + for (i=0; itag) { + case GF_IPMPX_KEY_DATA_TAG: + { + GF_IPMPX_KeyData *p = (GF_IPMPX_KeyData*)_p; + if (!stricmp(fieldName, "hasStartDTS")) { + if (!stricmp(val, "false") || !stricmp(val, "0") ) p->flags &= ~1; + else p->flags |= 1; + ret = 1; + } + else if (!stricmp(fieldName, "hasStartPacketID")) { + if (!stricmp(val, "false") || !stricmp(val, "0") ) p->flags &= ~2; + else p->flags |= 2; + ret = 1; + } + else if (!stricmp(fieldName, "hasExpireDTS")) { + if (!stricmp(val, "false") || !stricmp(val, "0") ) p->flags &= ~4; + else p->flags |= 4; + ret = 1; + } + else if (!stricmp(fieldName, "hasExpirePacketID")) { + if (!stricmp(val, "false") || !stricmp(val, "0") ) p->flags &= ~8; + else p->flags |= 8; + ret = 1; + } + else if (!stricmp(fieldName, "startDTS")) GET_U64(p->startDTS) + else if (!stricmp(fieldName, "startPacketID")) GET_U32(p->startPacketID) + else if (!stricmp(fieldName, "expireDTS")) GET_U32(p->expireDTS) + else if (!stricmp(fieldName, "expirePacketID")) GET_U32(p->expirePacketID) + } + break; + case GF_IPMPX_SECURE_CONTAINER_TAG: + { + GF_IPMPX_SecureContainer*p = (GF_IPMPX_SecureContainer*)_p; + if (!stricmp(fieldName, "isMACEncrypted")) GET_BOOL(p->isMACEncrypted) + } + break; + case GF_IPMPX_INIT_AUTHENTICATION_TAG: + { + GF_IPMPX_InitAuthentication *p = (GF_IPMPX_InitAuthentication*)_p; + if (!stricmp(fieldName, "Context")) GET_U32(p->Context) + else if (!stricmp(fieldName, "AuthType")) GET_U8(p->AuthType) + } + break; + case GF_IPMPX_TRUSTED_TOOL_TAG: + { + GF_IPMPX_TrustedTool *p = (GF_IPMPX_TrustedTool*)_p; + if (!stricmp(fieldName, "toolID")) { GF_IPMPX_ParseBin128(val, &p->toolID); ret = 1; } + else if (!stricmp(fieldName, "AuditDate")) { GF_IPMPX_ParseDate(val, &p->AuditDate); ret = 1; } + } + break; + case GF_IPMPX_TRUST_SPECIFICATION_TAG: + { + GF_IPMPX_TrustSpecification *p = (GF_IPMPX_TrustSpecification*)_p; + if (!stricmp(fieldName, "startDate")) { GF_IPMPX_ParseDate(val, &p->startDate); ret = 1; } + else if (!stricmp(fieldName, "attackerProfile")) GET_U8(p->attackerProfile) + else if (!stricmp(fieldName, "trustedDuration")) GET_U32(p->trustedDuration) + } + break; + case GF_IPMPX_ALGORITHM_DESCRIPTOR_TAG: + { + GF_IPMPX_AUTH_AlgorithmDescriptor *p = (GF_IPMPX_AUTH_AlgorithmDescriptor *)_p; + if (!stricmp(fieldName, "regAlgoID")) GET_U16(p->regAlgoID) + } + break; + case GF_IPMPX_KEY_DESCRIPTOR_TAG: + { + GF_IPMPX_AUTH_KeyDescriptor *p = (GF_IPMPX_AUTH_KeyDescriptor *)_p; + if (!stricmp(fieldName, "keyBody")) { + u32 s; + GF_IPMPX_ParseBinData(val, &p->keyBody, &s); + p->keyBodyLength = s; + ret = 1; + } + } + break; + case GF_IPMPX_PARAMETRIC_DESCRIPTION_TAG: + { + GF_IPMPX_ParametricDescription*p = (GF_IPMPX_ParametricDescription*)_p; + if (!stricmp(fieldName, "majorVersion")) GET_U8(p->majorVersion) + else if (!stricmp(fieldName, "minorVersion")) GET_U8(p->minorVersion) + } + break; + case GF_IPMPX_PARAMETRIC_CAPS_RESPONSE_TAG: + { + GF_IPMPX_ToolParamCapabilitiesResponse*p = (GF_IPMPX_ToolParamCapabilitiesResponse*)_p; + if (!stricmp(fieldName, "capabilitiesSupported")) GET_BOOL(p->capabilitiesSupported) + } + break; + case GF_IPMPX_DISCONNECT_TOOL_TAG: + { + GF_IPMPX_DisconnectTool*p = (GF_IPMPX_DisconnectTool*)_p; + if (!stricmp(fieldName, "IPMP_ToolContextID")) GET_U16(p->IPMP_ToolContextID) + } + break; + case GF_IPMPX_GET_TOOL_CONTEXT_TAG: + { + GF_IPMPX_GetToolContext*p = (GF_IPMPX_GetToolContext*)_p; + if (!stricmp(fieldName, "scope")) GET_U8(p->scope) + else if (!stricmp(fieldName, "IPMP_DescriptorIDEx")) GET_U16(p->IPMP_DescriptorIDEx) + } + break; + case GF_IPMPX_GET_TOOL_CONTEXT_RESPONSE_TAG: + { + GF_IPMPX_GetToolContextResponse*p = (GF_IPMPX_GetToolContextResponse*)_p; + if (!stricmp(fieldName, "OD_ID")) GET_U16(p->OD_ID) + else if (!stricmp(fieldName, "ESD_ID")) GET_U16(p->ESD_ID) + else if (!stricmp(fieldName, "IPMP_ToolContextID")) GET_U16(p->IPMP_ToolContextID) + } + break; + case GF_IPMPX_ADD_TOOL_LISTENER_TAG: + { + GF_IPMPX_AddToolNotificationListener*p = (GF_IPMPX_AddToolNotificationListener*)_p; + if (!stricmp(fieldName, "eventType")) return GF_IPMPX_ParseEventType(val, p->eventType, &p->eventTypeCount); + else if (!stricmp(fieldName, "scope")) GET_U8(p->scope) + } + break; + case GF_IPMPX_REMOVE_TOOL_LISTENER_TAG: + { + GF_IPMPX_RemoveToolNotificationListener*p = (GF_IPMPX_RemoveToolNotificationListener*)_p; + if (!stricmp(fieldName, "eventType")) return GF_IPMPX_ParseEventType(val, p->eventType, &p->eventTypeCount); + } + break; + case GF_IPMPX_NOTIFY_TOOL_EVENT_TAG: + { + GF_IPMPX_NotifyToolEvent*p = (GF_IPMPX_NotifyToolEvent*)_p; + if (!stricmp(fieldName, "OD_ID")) GET_U16(p->OD_ID) + else if (!stricmp(fieldName, "ESD_ID")) GET_U16(p->ESD_ID) + else if (!stricmp(fieldName, "IPMP_ToolContextID")) GET_U16(p->IPMP_ToolContextID) + else if (!stricmp(fieldName, "eventType")) GET_U8(p->eventType) + } + break; + case GF_IPMPX_CAN_PROCESS_TAG: + { + GF_IPMPX_CanProcess*p = (GF_IPMPX_CanProcess*)_p; + if (!stricmp(fieldName, "canProcess")) GET_BOOL(p->canProcess) + } + break; + case GF_IPMPX_TOOL_API_CONFIG_TAG: + { + GF_IPMPX_ToolAPI_Config*p = (GF_IPMPX_ToolAPI_Config*)_p; + if (!stricmp(fieldName, "Instantiation_API_ID")) GET_U32(p->Instantiation_API_ID) + else if (!stricmp(fieldName, "Messaging_API_ID")) GET_U32(p->Messaging_API_ID) + } + break; + case GF_IPMPX_AUDIO_WM_INIT_TAG: + case GF_IPMPX_VIDEO_WM_INIT_TAG: + { + GF_IPMPX_WatermarkingInit *p = (GF_IPMPX_WatermarkingInit*)_p; + if (!stricmp(fieldName, "inputFormat")) GET_U8(p->inputFormat) + else if (!stricmp(fieldName, "requiredOp")) GET_U8(p->requiredOp) + else if (!stricmp(fieldName, "nChannels")) GET_U8(p->nChannels) + else if (!stricmp(fieldName, "bitPerSample")) GET_U8(p->bitPerSample) + else if (!stricmp(fieldName, "frequency")) GET_U32(p->frequency) + else if (!stricmp(fieldName, "frame_horizontal_size")) GET_U16(p->frame_horizontal_size) + else if (!stricmp(fieldName, "frame_vertical_size")) GET_U16(p->frame_vertical_size) + else if (!stricmp(fieldName, "chroma_format")) GET_U8(p->chroma_format) + else if (!stricmp(fieldName, "wmPayload")) { ret=1; GF_IPMPX_ParseBinData(val, &p->wmPayload, &p->wmPayloadLen); } + else if (!stricmp(fieldName, "opaqueData")) { ret=1; GF_IPMPX_ParseBinData(val, &p->opaqueData, &p->opaqueDataSize); } + else if (!stricmp(fieldName, "wmRecipientId")) GET_U16(p->wmRecipientId) + } + break; + case GF_IPMPX_AUDIO_WM_SEND_TAG: + case GF_IPMPX_VIDEO_WM_SEND_TAG: + { + GF_IPMPX_SendWatermark *p = (GF_IPMPX_SendWatermark*)_p; + if (!stricmp(fieldName, "wm_status")) GET_U8(p->wm_status) + else if (!stricmp(fieldName, "compression_status")) GET_U8(p->compression_status) + } + break; + case GF_IPMPX_SEL_DEC_INIT_TAG: + { + GF_IPMPX_SelectiveDecryptionInit*p = (GF_IPMPX_SelectiveDecryptionInit*)_p; + if (!stricmp(fieldName, "mediaTypeExtension")) GET_U8(p->mediaTypeExtension) + else if (!stricmp(fieldName, "mediaTypeExtension")) GET_U8(p->mediaTypeExtension) + else if (!stricmp(fieldName, "mediaTypeIndication")) GET_U8(p->mediaTypeIndication) + else if (!stricmp(fieldName, "profileLevelIndication")) GET_U8(p->profileLevelIndication) + else if (!stricmp(fieldName, "compliance")) GET_U8(p->compliance) + else if (!stricmp(fieldName, "RLE_Data")) { ret=1; gf_ipmpx_data_parse_16(val, &p->RLE_Data, &p->RLE_DataLength); } + } + break; + case GF_IPMPX_SEL_ENC_BUFFER_TAG: + { + GF_IPMPX_SelEncBuffer*p = (GF_IPMPX_SelEncBuffer*)_p; + if (!stricmp(fieldName, "cipher_Id")) { GF_IPMPX_ParseBin128(val, &p->cipher_Id); ret = 1; } + else if (!stricmp(fieldName, "syncBoundary")) GET_U8(p->syncBoundary) + else if (!stricmp(fieldName, "mode")) GET_U8(p->mode) + else if (!stricmp(fieldName, "blockSize")) GET_U16(p->blockSize) + else if (!stricmp(fieldName, "keySize")) GET_U16(p->keySize) + } + break; + case GF_IPMPX_SEL_ENC_FIELD_TAG: + { + GF_IPMPX_SelEncField*p = (GF_IPMPX_SelEncField*)_p; + if (!stricmp(fieldName, "field_Id")) GET_U8(p->field_Id) + else if (!stricmp(fieldName, "field_Scope")) GET_U8(p->field_Scope) + else if (!stricmp(fieldName, "buf")) GET_U8(p->buf) + else if (!stricmp(fieldName, "mappingTable")) { ret=1; gf_ipmpx_data_parse_16(val, &p->mappingTable, &p->mappingTableSize); } + } + break; + case GF_IPMPX_ISMACRYP_TAG: + { + GF_IPMPX_ISMACryp*p = (GF_IPMPX_ISMACryp*)_p; + if (!stricmp(fieldName, "crypto_suite")) GET_U8(p->cryptoSuite) + else if (!stricmp(fieldName, "IV_length")) GET_U8(p->IV_length) + else if (!stricmp(fieldName, "selective_encryption")) GET_BOOL(p->use_selective_encryption) + else if (!stricmp(fieldName, "key_indicator_length")) GET_U8(p->key_indicator_length) + } + break; + } + return ret ? GF_OK : GF_BAD_PARAM; +} + +GF_Err gf_ipmpx_set_sub_data(GF_IPMPX_Data *_p, char *fieldName, GF_IPMPX_Data *sp) +{ + switch (_p->tag) { + case GF_IPMPX_SECURE_CONTAINER_TAG: + { + GF_IPMPX_SecureContainer*p = (GF_IPMPX_SecureContainer*)_p; + if (p->protectedMsg) gf_ipmpx_data_del(p->protectedMsg); + p->protectedMsg = sp; + return GF_OK; + } + break; + case GF_IPMPX_TRUST_SECURITY_METADATA_TAG: + { + GF_IPMPX_TrustSecurityMetadata *p = (GF_IPMPX_TrustSecurityMetadata*)_p; + if (!sp || (sp->tag!=GF_IPMPX_TRUSTED_TOOL_TAG) ) return GF_BAD_PARAM; + gf_list_add(p->TrustedTools, sp); + return GF_OK; + } + break; + case GF_IPMPX_TRUSTED_TOOL_TAG: + { + GF_IPMPX_TrustedTool *p = (GF_IPMPX_TrustedTool *)_p; + if (!sp || (sp->tag!=GF_IPMPX_TRUST_SPECIFICATION_TAG) ) return GF_BAD_PARAM; + gf_list_add(p->trustSpecifications, sp); + return GF_OK; + } + break; + case GF_IPMPX_MUTUAL_AUTHENTICATION_TAG: + { + GF_IPMPX_MutualAuthentication *p = (GF_IPMPX_MutualAuthentication *)_p; + if (!sp) return GF_BAD_PARAM; + switch (sp->tag) { + case GF_IPMPX_ALGORITHM_DESCRIPTOR_TAG: + sp->tag = GF_IPMPX_AUTH_AlgorithmDescr_Tag; + if (!stricmp(fieldName, "candidateAlgorithms")) return gf_list_add(p->agreedAlgorithms, sp); + else if (!stricmp(fieldName, "agreedAlgorithms")) return gf_list_add(p->agreedAlgorithms, sp); + else return GF_BAD_PARAM; + case GF_IPMPX_KEY_DESCRIPTOR_TAG: + sp->tag = GF_IPMPX_AUTH_KeyDescr_Tag; + if (!stricmp(fieldName, "candidateAlgorithms")) return gf_list_add(p->agreedAlgorithms, sp); + else if (!stricmp(fieldName, "agreedAlgorithms")) return gf_list_add(p->agreedAlgorithms, sp); + else if (!stricmp(fieldName, "publicKey")) { + if (p->publicKey) GF_IPMPX_AUTH_Delete((GF_IPMPX_Authentication *) p->publicKey); + p->publicKey = (GF_IPMPX_AUTH_KeyDescriptor *) sp; + return GF_OK; + } + else return GF_BAD_PARAM; + case GF_IPMPX_TRUST_SECURITY_METADATA_TAG: + if (p->trustData) gf_ipmpx_data_del((GF_IPMPX_Data *)p->trustData); + p->trustData = (GF_IPMPX_TrustSecurityMetadata*)sp; + return GF_OK; + default: + return GF_BAD_PARAM; + } + } + break; + case GF_IPMPX_PARAMETRIC_DESCRIPTION_TAG: + { + GF_IPMPX_ParametricDescription*p = (GF_IPMPX_ParametricDescription*)_p; + if (!sp || (sp->tag!=GF_IPMPX_PARAM_DESCRIPTOR_ITEM_TAG) ) return GF_BAD_PARAM; + if (!stricmp(fieldName, "descriptions")) return gf_list_add(p->descriptions, sp); + else return GF_BAD_PARAM; + } + break; + case GF_IPMPX_PARAMETRIC_CAPS_QUERY_TAG: + { + GF_IPMPX_ToolParamCapabilitiesQuery*p = (GF_IPMPX_ToolParamCapabilitiesQuery*)_p; + if (!sp || (sp->tag!=GF_IPMPX_PARAMETRIC_DESCRIPTION_TAG) ) return GF_BAD_PARAM; + if (stricmp(fieldName, "description")) return GF_BAD_PARAM; + if (p->description) gf_ipmpx_data_del((GF_IPMPX_Data *)p->description); + p->description = (GF_IPMPX_ParametricDescription*)sp; + return GF_OK; + } + break; + case GF_IPMPX_SEL_DEC_INIT_TAG: + { + GF_IPMPX_SelectiveDecryptionInit*p = (GF_IPMPX_SelectiveDecryptionInit*)_p; + if (!sp) return GF_BAD_PARAM; + if (sp->tag==GF_IPMPX_SEL_ENC_BUFFER_TAG) return gf_list_add(p->SelEncBuffer, sp); + else if (sp->tag==GF_IPMPX_SEL_ENC_FIELD_TAG) return gf_list_add(p->SelEncFields, sp); + else return GF_BAD_PARAM; + } + break; + } + return GF_BAD_PARAM; +} + +GF_Err gf_ipmpx_set_byte_array(GF_IPMPX_Data *p, char *field, char *str) +{ + GF_IPMPX_ByteArray *d; + GF_IPMPX_ByteArray **dest; + d = (GF_IPMPX_ByteArray*)gf_malloc(sizeof(GF_IPMPX_ByteArray)); + d->length = strlen(str); + d->data = (char*)gf_malloc(sizeof(char)*d->length); + memcpy(d->data, str, d->length); + + dest = NULL; + switch (p->tag) { + case GF_IPMPX_KEY_DATA_TAG: + if (!stricmp(field, "keyBody")) dest = & ((GF_IPMPX_KeyData*)p)->keyBody; + else if (!stricmp(field, "opaqueData")) dest = & ((GF_IPMPX_KeyData*)p)->OpaqueData; + break; + case GF_IPMPX_RIGHTS_DATA_TAG: + if (!stricmp(field, "rightsInfo")) dest = & ((GF_IPMPX_RightsData*)p)->rightsInfo; + break; + case GF_IPMPX_OPAQUE_DATA_TAG: + if (!stricmp(field, "opaqueData")) dest = & ((GF_IPMPX_OpaqueData*)p)->opaqueData; + break; + case GF_IPMPX_SECURE_CONTAINER_TAG: + if (!stricmp(field, "encryptedData")) dest = & ((GF_IPMPX_SecureContainer*)p)->encryptedData; + else if (!stricmp(field, "MAC")) dest = & ((GF_IPMPX_SecureContainer*)p)->MAC; + break; + case GF_IPMPX_TRUST_SPECIFICATION_TAG: + if (!stricmp(field, "CCTrustMetadata")) dest = & ((GF_IPMPX_TrustSpecification*)p)->CCTrustMetadata; + break; + case GF_IPMPX_MUTUAL_AUTHENTICATION_TAG: + if (!stricmp(field, "AuthenticationData")) dest = & ((GF_IPMPX_MutualAuthentication*)p)->AuthenticationData; + else if (!stricmp(field, "opaque")) dest = & ((GF_IPMPX_MutualAuthentication*)p)->opaque; + else if (!stricmp(field, "authCodes")) dest = & ((GF_IPMPX_MutualAuthentication*)p)->authCodes; + else if (!stricmp(field, "certificates")) { + gf_list_add(((GF_IPMPX_MutualAuthentication*)p)->certificates, d); + return GF_OK; + } + break; + case GF_IPMPX_PARAMETRIC_DESCRIPTION_TAG: + if (!stricmp(field, "descriptionComment")) dest = & ((GF_IPMPX_ParametricDescription*)p)->descriptionComment; + break; + case GF_IPMPX_PARAM_DESCRIPTOR_ITEM_TAG: + if (!stricmp(field, "class")) dest = & ((GF_IPMPX_ParametricDescriptionItem*)p)->main_class; + else if (!stricmp(field, "subClass")) dest = & ((GF_IPMPX_ParametricDescriptionItem*)p)->subClass; + else if (!stricmp(field, "typeData")) dest = & ((GF_IPMPX_ParametricDescriptionItem*)p)->typeData; + else if (!stricmp(field, "type")) dest = & ((GF_IPMPX_ParametricDescriptionItem*)p)->type; + else if (!stricmp(field, "addedData")) dest = & ((GF_IPMPX_ParametricDescriptionItem*)p)->addedData; + break; + case GF_IPMPX_TOOL_API_CONFIG_TAG: + if (!stricmp(field, "opaqueData")) dest = & ((GF_IPMPX_ToolAPI_Config*)p)->opaqueData; + break; + case GF_IPMPX_AUDIO_WM_SEND_TAG: + case GF_IPMPX_VIDEO_WM_SEND_TAG: + if (!stricmp(field, "payload")) dest = & ((GF_IPMPX_SendWatermark *)p)->payload; + else if (!stricmp(field, "opaqueData")) dest = & ((GF_IPMPX_SendWatermark*)p)->opaqueData; + break; + case GF_IPMPX_SEL_ENC_BUFFER_TAG: + if (!stricmp(field, "StreamCipher")) dest = & ((GF_IPMPX_SelEncBuffer*)p)->Stream_Cipher_Specific_Init_Info; + break; + case GF_IPMPX_SEL_ENC_FIELD_TAG: + if (!stricmp(field, "shuffleSpecificInfo")) dest = & ((GF_IPMPX_SelEncField*)p)->shuffleSpecificInfo; + break; + } + if (!dest) { + gf_free(d->data); + gf_free(d); + return GF_BAD_PARAM; + } + if ( (*dest) ) { + if ((*dest)->data) gf_free((*dest)->data); + gf_free((*dest)); + } + (*dest) = d; + return GF_OK; +} + +#endif /*GPAC_MINIMAL_ODF*/ + diff --git a/src/gpacmp4/isom_intern.c b/src/gpacmp4/isom_intern.c new file mode 100644 index 00000000..acd87358 --- /dev/null +++ b/src/gpacmp4/isom_intern.c @@ -0,0 +1,845 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / ISO Media File Format sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ +#include "../disable_warnings.h" +#include +#include +#ifndef _WIN32 +#include +#endif + +#ifndef GPAC_DISABLE_ISOM + +/************************************************************** + Some Local functions for movie creation +**************************************************************/ +GF_Err gf_isom_parse_root_box(GF_Box **outBox, GF_BitStream *bs, u64 *bytesExpected, Bool progressive_mode); + +#ifndef GPAC_DISABLE_ISOM_FRAGMENTS +GF_Err MergeTrack(GF_TrackBox *trak, GF_TrackFragmentBox *traf, u64 moof_offset, Bool is_first_merge); + +GF_Err MergeFragment(GF_MovieFragmentBox *moof, GF_ISOFile *mov) +{ + u32 i, j; + u64 MaxDur; + GF_TrackFragmentBox *traf; + GF_TrackBox *trak; + + MaxDur = 0; + + //we shall have a MOOV and its MVEX BEFORE any MOOF + if (!mov->moov || !mov->moov->mvex) return GF_ISOM_INVALID_FILE; + //and all fragments must be continous + if (mov->NextMoofNumber && (mov->NextMoofNumber >= moof->mfhd->sequence_number)) return GF_ISOM_INVALID_FILE; + + i=0; + while ((traf = (GF_TrackFragmentBox*)gf_list_enum(moof->TrackList, &i))) { + if (!traf->tfhd) { + trak = NULL; + traf->trex = NULL; + } else { + trak = gf_isom_get_track_from_id(mov->moov, traf->tfhd->trackID); + j=0; + while ((traf->trex = (GF_TrackExtendsBox*)gf_list_enum(mov->moov->mvex->TrackExList, &j))) { + if (traf->trex->trackID == traf->tfhd->trackID) break; + traf->trex = NULL; + } + } + if (!trak || !traf->trex) return GF_ISOM_INVALID_FILE; + + MergeTrack(trak, traf, mov->current_top_box_start, !mov->first_moof_merged); + + //update trak duration + SetTrackDuration(trak); + if (trak->Header->duration > MaxDur) + MaxDur = trak->Header->duration; + } + + mov->first_moof_merged = 1; + mov->NextMoofNumber = moof->mfhd->sequence_number; + //update movie duration + if (mov->moov->mvhd->duration < MaxDur) mov->moov->mvhd->duration = MaxDur; + return GF_OK; +} + +#endif + + +GF_Err gf_isom_parse_movie_boxes(GF_ISOFile *mov, u64 *bytesMissing, Bool progressive_mode) +{ + GF_Box *a; + u64 totSize; + GF_Err e = GF_OK; + + totSize = 0; + + +#ifndef GPAC_DISABLE_ISOM_FRAGMENTS + /*restart from where we stopped last*/ + totSize = mov->current_top_box_start; + gf_bs_seek(mov->movieFileMap->bs, mov->current_top_box_start); +#endif + + + /*while we have some data, parse our boxes*/ + while (gf_bs_available(mov->movieFileMap->bs)) { + *bytesMissing = 0; +#ifndef GPAC_DISABLE_ISOM_FRAGMENTS + mov->current_top_box_start = gf_bs_get_position(mov->movieFileMap->bs); +#endif + + e = gf_isom_parse_root_box(&a, mov->movieFileMap->bs, bytesMissing, progressive_mode); + + if (e >= 0) { + e = GF_OK; + } else if (e == GF_ISOM_INCOMPLETE_FILE) { + /*our mdat is uncomplete, only valid for READ ONLY files...*/ + if (mov->openMode != GF_ISOM_OPEN_READ) { + return GF_ISOM_INVALID_FILE; + } + return e; + } else { + return e; + } + + switch (a->type) { + /*MOOV box*/ + case GF_ISOM_BOX_TYPE_MOOV: + if (mov->moov) return GF_ISOM_INVALID_FILE; + mov->moov = (GF_MovieBox *)a; + /*set our pointer to the movie*/ + mov->moov->mov = mov; +#ifndef GPAC_DISABLE_ISOM_FRAGMENTS + if (mov->moov->mvex) mov->moov->mvex->mov = mov; +#endif + e = gf_list_add(mov->TopBoxes, a); + if (e) return e; + totSize += a->size; + break; + + /*META box*/ + case GF_ISOM_BOX_TYPE_META: + if (mov->meta) return GF_ISOM_INVALID_FILE; + mov->meta = (GF_MetaBox *)a; + e = gf_list_add(mov->TopBoxes, a); + if (e) return e; + totSize += a->size; + break; + + /*we only keep the MDAT in READ for dump purposes*/ + case GF_ISOM_BOX_TYPE_MDAT: + totSize += a->size; + if (mov->openMode == GF_ISOM_OPEN_READ) { + if (!mov->mdat) { + mov->mdat = (GF_MediaDataBox *) a; + e = gf_list_add(mov->TopBoxes, mov->mdat); + if (e) return e; + } +#ifndef GPAC_DISABLE_ISOM_FRAGMENTS + else if (mov->FragmentsFlags & GF_ISOM_FRAG_READ_DEBUG) gf_list_add(mov->TopBoxes, a); +#endif + else gf_isom_box_del(a); + } + /*if we don't have any MDAT yet, create one (edit-write mode) + We only work with one mdat, but we're puting it at the place + of the first mdat found when opening a file for editing*/ + else if (!mov->mdat && (mov->openMode != GF_ISOM_OPEN_READ) && (mov->openMode != GF_ISOM_OPEN_CAT_FRAGMENTS)) { + gf_isom_box_del(a); + mov->mdat = (GF_MediaDataBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_MDAT); + e = gf_list_add(mov->TopBoxes, mov->mdat); + if (e) return e; + } else { + gf_isom_box_del(a); + } + break; + case GF_ISOM_BOX_TYPE_FTYP: + /*ONE AND ONLY ONE FTYP*/ + if (mov->brand) { + gf_isom_box_del(a); + return GF_ISOM_INVALID_FILE; + } + mov->brand = (GF_FileTypeBox *)a; + totSize += a->size; + e = gf_list_add(mov->TopBoxes, a); + break; + + case GF_ISOM_BOX_TYPE_PDIN: + /*ONE AND ONLY ONE PDIN*/ + if (mov->pdin) { + gf_isom_box_del(a); + return GF_ISOM_INVALID_FILE; + } + mov->pdin = (GF_ProgressiveDownloadBox *) a; + totSize += a->size; + e = gf_list_add(mov->TopBoxes, a); + break; + + +#ifndef GPAC_DISABLE_ISOM_FRAGMENTS + case GF_ISOM_BOX_TYPE_STYP: + if (((GF_SegmentTypeBox *)a)->majorBrand == GF_4CC('i', 's', 's', 's') || + ((GF_SegmentTypeBox *)a)->majorBrand == GF_4CC('i', 'm', 's', 's')) mov->is_index_segment = 1; + + case GF_ISOM_BOX_TYPE_SIDX: + totSize += a->size; + if (mov->FragmentsFlags & GF_ISOM_FRAG_READ_DEBUG) { + e = gf_list_add(mov->TopBoxes, a); + } else { + gf_isom_box_del(a); + } + break; + + case GF_ISOM_BOX_TYPE_MOOF: + ((GF_MovieFragmentBox *)a)->mov = mov; + + totSize += a->size; + mov->moof = (GF_MovieFragmentBox *) a; + /*read & debug: store at root level*/ + if (mov->FragmentsFlags & GF_ISOM_FRAG_READ_DEBUG) { + gf_list_add(mov->TopBoxes, a); + } else if (mov->openMode==GF_ISOM_OPEN_CAT_FRAGMENTS) { + mov->NextMoofNumber = mov->moof->mfhd->sequence_number+1; + mov->moof = NULL; + gf_isom_box_del(a); + } else { + /*merge all info*/ + e = MergeFragment((GF_MovieFragmentBox *)a, mov); + gf_isom_box_del(a); + if (e) { + GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[iso file] Error merging fragment: %s\n", gf_error_to_string(e) )); + } + } + break; +#endif + case GF_4CC('j','P',' ',' '): + { + GF_UnknownBox *box = (GF_UnknownBox*)a; + u8 *c = (u8 *) box->data; + if ((box->dataSize==4) + && (GF_4CC(c[0],c[1],c[2],c[3])==(u32)0x0D0A870A)) + mov->is_jp2 = 1; + + gf_isom_box_del(a); + } + break; + + default: + totSize += a->size; + e = gf_list_add(mov->TopBoxes, a); + break; + } + +#ifndef GPAC_DISABLE_ISOM_FRAGMENTS +// mov->current_top_box_start = gf_bs_get_position(mov->movieFileMap->bs); +#endif + } + + /*we need at least moov or meta*/ + if (!mov->moov && !mov->meta +#ifndef GPAC_DISABLE_ISOM_FRAGMENTS + && !mov->moof && !mov->is_index_segment +#endif + ) return GF_ISOM_INVALID_FILE; + /*we MUST have movie header*/ + if (mov->moov && !mov->moov->mvhd) return GF_ISOM_INVALID_FILE; + /*we MUST have meta handler*/ + if (mov->meta && !mov->meta->handler) return GF_ISOM_INVALID_FILE; + +#ifndef GPAC_DISABLE_ISOM_WRITE + + if (mov->moov) { + /*set the default interleaving time*/ + mov->interleavingTime = mov->moov->mvhd->timeScale; + +#ifndef GPAC_DISABLE_ISOM_FRAGMENTS + /*in edit mode with successfully loaded fragments, delete all fragment signaling since + file is no longer fragmented*/ + if ((mov->openMode > GF_ISOM_OPEN_READ) && (mov->openMode != GF_ISOM_OPEN_CAT_FRAGMENTS) && mov->moov->mvex) { + gf_isom_box_del((GF_Box *)mov->moov->mvex); + mov->moov->mvex = NULL; + } +#endif + + } +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + + return GF_OK; +} + + +GF_ISOFile *gf_isom_new_movie() +{ + GF_ISOFile *mov = (GF_ISOFile*)gf_malloc(sizeof(GF_ISOFile)); + if (mov == NULL) { + gf_isom_set_last_error(NULL, GF_OUT_OF_MEM); + return NULL; + } + memset(mov, 0, sizeof(GF_ISOFile)); + + /*init the boxes*/ + mov->TopBoxes = gf_list_new(); + if (!mov->TopBoxes) { + gf_isom_set_last_error(NULL, GF_OUT_OF_MEM); + gf_free(mov); + return NULL; + } + + /*default storage mode is flat*/ + mov->storageMode = GF_ISOM_STORE_FLAT; + return mov; +} + +//Create and parse the movie for READ - EDIT only +GF_ISOFile *gf_isom_open_file(const char *fileName, u32 OpenMode, const char *tmp_dir) +{ + GF_Err e; + u64 bytes; + GF_ISOFile *mov = gf_isom_new_movie(); + if (! mov) return NULL; + + mov->fileName = gf_strdup(fileName); + mov->openMode = OpenMode; + + if ( (OpenMode == GF_ISOM_OPEN_READ) || (OpenMode == GF_ISOM_OPEN_READ_DUMP) ) { + //always in read ... + mov->openMode = GF_ISOM_OPEN_READ; + mov->es_id_default_sync = -1; + //for open, we do it the regular way and let the GF_DataMap assign the appropriate struct + //this can be FILE (the only one supported...) as well as remote + //(HTTP, ...),not suported yet + //the bitstream IS PART OF the GF_DataMap + //as this is read-only, use a FileMapping. this is the only place where + //we use file mapping + e = gf_isom_datamap_new(fileName, NULL, GF_ISOM_DATA_MAP_READ_ONLY, &mov->movieFileMap); + if (e) { + gf_isom_set_last_error(NULL, e); + gf_isom_delete_movie(mov); + return NULL; + } + +#ifndef GPAC_DISABLE_ISOM_FRAGMENTS + if (OpenMode == GF_ISOM_OPEN_READ_DUMP) mov->FragmentsFlags |= GF_ISOM_FRAG_READ_DEBUG; +#endif + + } else { + +#ifdef GPAC_DISABLE_ISOM_WRITE + //not allowed for READ_ONLY lib + gf_isom_delete_movie(mov); + gf_isom_set_last_error(NULL, GF_ISOM_INVALID_MODE); + return NULL; + +#else + + //set a default output name for edited file + mov->finalName = (char*)gf_malloc(strlen(fileName) + 5); + if (!mov->finalName) { + gf_isom_set_last_error(NULL, GF_OUT_OF_MEM); + gf_isom_delete_movie(mov); + return NULL; + } + strcpy(mov->finalName, "out_"); + strcat(mov->finalName, fileName); + + //open the original file with edit tag + e = gf_isom_datamap_new(fileName, NULL, GF_ISOM_DATA_MAP_EDIT, &mov->movieFileMap); + //if the file doesn't exist, we assume it's wanted and create one from scratch + if (e) { + gf_isom_set_last_error(NULL, e); + gf_isom_delete_movie(mov); + return NULL; + } + //and create a temp fileName for the edit + e = gf_isom_datamap_new("mp4_tmp_edit", tmp_dir, GF_ISOM_DATA_MAP_WRITE, & mov->editFileMap); + if (e) { + gf_isom_set_last_error(NULL, e); + gf_isom_delete_movie(mov); + return NULL; + } + + mov->es_id_default_sync = -1; + +#endif + } + + //OK, let's parse the movie... + mov->LastError = gf_isom_parse_movie_boxes(mov, &bytes, 0); + if (mov->LastError) { + gf_isom_set_last_error(NULL, mov->LastError); + gf_isom_delete_movie(mov); + return NULL; + } + + if (OpenMode == GF_ISOM_OPEN_CAT_FRAGMENTS) { + gf_isom_datamap_del(mov->movieFileMap); + /*reopen the movie file map in cat mode*/ + e = gf_isom_datamap_new(fileName, tmp_dir, GF_ISOM_DATA_MAP_CAT, & mov->movieFileMap); + } + 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; + u64 ret; + gf_utc_time_since_1970(&calctime, &msec); + calctime += GF_ISOM_MAC_TIME_OFFSET; + ret = calctime; + return ret; +} + +void gf_isom_delete_movie(GF_ISOFile *mov) +{ + //these are our two main files + if (mov->movieFileMap) gf_isom_datamap_del(mov->movieFileMap); + +#ifndef GPAC_DISABLE_ISOM_WRITE + if (mov->editFileMap) { + gf_isom_datamap_del(mov->editFileMap); + } + if (mov->finalName) gf_free(mov->finalName); +#endif + + gf_isom_box_array_del(mov->TopBoxes); + gf_isom_box_array_del(mov->moof_list); + + + if (mov->fileName) gf_free(mov->fileName); + gf_free(mov); +} + +GF_TrackBox *gf_isom_get_track_from_id(GF_MovieBox *moov, u32 trackID) +{ + u32 i, count; + GF_TrackBox *trak; + if (!moov || !trackID) return NULL; + + count = gf_list_count(moov->trackList); + for (i = 0; itrackList, i); + if (trak->Header->trackID == trackID) return trak; + } + return NULL; +} + +GF_TrackBox *gf_isom_get_track_from_file(GF_ISOFile *movie, u32 trackNumber) +{ + GF_TrackBox *trak; + if (!movie) return NULL; + trak = gf_isom_get_track(movie->moov, trackNumber); + if (!trak) movie->LastError = GF_BAD_PARAM; + return trak; +} + + +//WARNING: MOVIETIME IS EXPRESSED IN MEDIA TS +GF_Err GetMediaTime(GF_TrackBox *trak, u64 movieTime, u64 *MediaTime, s64 *SegmentStartTime, s64 *MediaOffset, u8 *useEdit) +{ +#if 0 + GF_Err e; + u32 sampleNumber, prevSampleNumber; + u64 firstDTS; +#endif + u32 i; + u64 time, lastSampleTime, m_time; + s64 mtime; + GF_EdtsEntry *ent; + Double scale_ts; + GF_SampleTableBox *stbl = trak->Media->information->sampleTable; + + *useEdit = 1; + *MediaTime = 0; + //no segment yet... + *SegmentStartTime = -1; + *MediaOffset = -1; + if (!trak->moov->mvhd->timeScale || !trak->Media->mediaHeader->timeScale) { + return GF_ISOM_INVALID_FILE; + } + + //no samples... + if (!stbl->SampleSize->sampleCount) { + lastSampleTime = 0; + } else { + lastSampleTime = trak->Media->mediaHeader->duration; + } + + //No edits, 1 to 1 mapping + if (! trak->editBox || !trak->editBox->editList) { + *MediaTime = movieTime; + //check this is in our media time line + if (*MediaTime > lastSampleTime) *MediaTime = lastSampleTime; + *useEdit = 0; + return GF_OK; + } + //browse the edit list and get the time + scale_ts = trak->moov->mvhd->timeScale; + scale_ts /= trak->Media->mediaHeader->timeScale; + scale_ts *= ((s64)movieTime ); + m_time = (u64) (scale_ts); + + time = 0; + ent = NULL; + i=0; + while ((ent = (GF_EdtsEntry *)gf_list_enum(trak->editBox->editList->entryList, &i))) { + if (time + ent->segmentDuration > m_time) { + goto ent_found; + } + time += ent->segmentDuration; + } + //we had nothing in the list (strange file but compliant...) + //return the 1 to 1 mapped vale of the last media sample + if (!ent) { + *MediaTime = movieTime; + //check this is in our media time line + if (*MediaTime > lastSampleTime) *MediaTime = lastSampleTime; + *useEdit = 0; + return GF_OK; + } + //request for a bigger time that what we can give: return the last sample (undefined behavior...) + *MediaTime = lastSampleTime; + return GF_OK; + +ent_found: + //OK, we found our entry, set the SegmentTime + *SegmentStartTime = time; + + //we request an empty list, there's no media here... + if (ent->mediaTime < 0) { + *MediaTime = 0; + return GF_OK; + } + //we request a dwell edit + if (! ent->mediaRate) { + *MediaTime = ent->mediaTime; + //no media offset + *MediaOffset = 0; + *useEdit = 2; + return GF_OK; + } + + /*WARNING: this can be "-1" when doing searchForward mode (to prevent jumping to next entry)*/ + mtime = ent->mediaTime + movieTime - (time * trak->Media->mediaHeader->timeScale / trak->moov->mvhd->timeScale); + if (mtime<0) mtime = 0; + *MediaTime = (u64) mtime; + *MediaOffset = ent->mediaTime; + +#if 0 + // + //Sanity check: is the requested time valid ? This is to cope with wrong EditLists + //we have the translated time, but we need to make sure we have a sample at this time ... + //we have to find a COMPOSITION time + e = findEntryForTime(stbl, (u32) *MediaTime, 1, &sampleNumber, &prevSampleNumber); + if (e) return e; + + //first case: our time is after the last sample DTS (it's a broken editList somehow) + //set the media time to the last sample + if (!sampleNumber && !prevSampleNumber) { + *MediaTime = lastSampleTime; + return GF_OK; + } + //get the appropriated sample + if (!sampleNumber) sampleNumber = prevSampleNumber; + + stbl_GetSampleDTS(stbl->TimeToSample, sampleNumber, &DTS); + CTS = 0; + if (stbl->CompositionOffset) stbl_GetSampleCTS(stbl->CompositionOffset, sampleNumber, &CTS); + + //now get the entry sample (the entry time gives the CTS, and we need the DTS + e = findEntryForTime(stbl, (u32) ent->mediaTime, 0, &sampleNumber, &prevSampleNumber); + if (e) return e; + + //oops, the mediaTime indicates a sample that is not in our media ! + if (!sampleNumber && !prevSampleNumber) { + *MediaTime = lastSampleTime; + return GF_ISOM_INVALID_FILE; + } + if (!sampleNumber) sampleNumber = prevSampleNumber; + + stbl_GetSampleDTS(stbl->TimeToSample, sampleNumber, &firstDTS); + + //and store the "time offset" of the desired sample in this segment + //this is weird, used to rebuild the timeStamp when reading from the track, not the + //media ... + *MediaOffset = firstDTS; +#endif + return GF_OK; +} + +GF_Err GetNextMediaTime(GF_TrackBox *trak, u64 movieTime, u64 *OutMovieTime) +{ + u32 i; + u64 time; + GF_EdtsEntry *ent; + + *OutMovieTime = 0; + if (! trak->editBox || !trak->editBox->editList) return GF_BAD_PARAM; + + time = 0; + ent = NULL; + i=0; + while ((ent = (GF_EdtsEntry *)gf_list_enum(trak->editBox->editList->entryList, &i))) { + if (time * trak->Media->mediaHeader->timeScale >= movieTime * trak->moov->mvhd->timeScale) { + /*skip empty edits*/ + if (ent->mediaTime >= 0) { + *OutMovieTime = time * trak->Media->mediaHeader->timeScale / trak->moov->mvhd->timeScale; + if (*OutMovieTime>0) *OutMovieTime -= 1; + return GF_OK; + } + } + time += ent->segmentDuration; + } + //request for a bigger time that what we can give: return the last sample (undefined behavior...) + *OutMovieTime = trak->moov->mvhd->duration; + return GF_EOS; +} + +GF_Err GetPrevMediaTime(GF_TrackBox *trak, u64 movieTime, u64 *OutMovieTime) +{ + u32 i; + u64 time; + GF_EdtsEntry *ent; + + *OutMovieTime = 0; + if (! trak->editBox || !trak->editBox->editList) return GF_BAD_PARAM; + + time = 0; + ent = NULL; + i=0; + while ((ent = (GF_EdtsEntry *)gf_list_enum(trak->editBox->editList->entryList, &i))) { + if (ent->mediaTime == -1) { + if ( (time + ent->segmentDuration) * trak->Media->mediaHeader->timeScale >= movieTime * trak->moov->mvhd->timeScale) { + *OutMovieTime = time * trak->Media->mediaHeader->timeScale / trak->moov->mvhd->timeScale; + return GF_OK; + } + continue; + } + /*get the first entry whose end is greater than or equal to the desired time*/ + time += ent->segmentDuration; + if ( time * trak->Media->mediaHeader->timeScale >= movieTime * trak->moov->mvhd->timeScale) { + *OutMovieTime = time * trak->Media->mediaHeader->timeScale / trak->moov->mvhd->timeScale; + return GF_OK; + } + } + *OutMovieTime = 0; + return GF_OK; +} + +#ifndef GPAC_DISABLE_ISOM_WRITE + +void gf_isom_insert_moov(GF_ISOFile *file) +{ + u64 now; + GF_MovieHeaderBox *mvhd; + if (file->moov) return; + + //OK, create our boxes (mvhd, iods, ...) + file->moov = (GF_MovieBox *) moov_New(); + file->moov->mov = file; + //Header SetUp + now = gf_isom_get_mp4time(); + mvhd = (GF_MovieHeaderBox *) mvhd_New(); + mvhd->creationTime = now; + mvhd->modificationTime = now; + mvhd->nextTrackID = 1; + //600 is our default movie TimeScale + mvhd->timeScale = 600; + + file->interleavingTime = mvhd->timeScale; + moov_AddBox((GF_Box*)file->moov, (GF_Box *)mvhd); + gf_list_add(file->TopBoxes, file->moov); +} + +//Create the movie for WRITE only +GF_ISOFile *gf_isom_create_movie(const char *fileName, u32 OpenMode, const char *tmp_dir) +{ + GF_Err e; + + GF_ISOFile *mov = gf_isom_new_movie(); + if (!mov) return NULL; + mov->openMode = OpenMode; + //then set up our movie + + //in WRITE, the input dataMap is ALWAYS NULL + mov->movieFileMap = NULL; + + //but we have the edit one + if (OpenMode == GF_ISOM_OPEN_WRITE) { + //THIS IS NOT A TEMP FILE, WRITE mode is used for "live capture" + //this file will be the final file... + mov->fileName = gf_strdup(fileName); + e = gf_isom_datamap_new(fileName, NULL, GF_ISOM_DATA_MAP_WRITE, & mov->editFileMap); + if (e) goto err_exit; + + /*brand is set to ISOM by default - it may be touched until sample data is added to track*/ + gf_isom_set_brand_info( (GF_ISOFile *) mov, GF_ISOM_BRAND_ISOM, 1); + } else { + //we are in EDIT mode but we are creating the file -> temp file + mov->finalName = (char*)gf_malloc(strlen(fileName) + 1); + strcpy(mov->finalName, fileName); + e = gf_isom_datamap_new("mp4_tmp_edit", tmp_dir, GF_ISOM_DATA_MAP_WRITE, & mov->editFileMap); + if (e) { + gf_isom_set_last_error(NULL, e); + gf_isom_delete_movie(mov); + return NULL; + } + //brand is set to ISOM by default + gf_isom_set_brand_info( (GF_ISOFile *) mov, GF_ISOM_BRAND_ISOM, 1); + } + + //create an MDAT + mov->mdat = (GF_MediaDataBox *) mdat_New(); + gf_list_add(mov->TopBoxes, mov->mdat); + + //default behaviour is capture mode, no interleaving (eg, no rewrite of mdat) + mov->storageMode = GF_ISOM_STORE_FLAT; + return mov; + +err_exit: + gf_isom_set_last_error(NULL, e); + if (mov) gf_isom_delete_movie(mov); + return NULL; +} + +GF_EdtsEntry *CreateEditEntry(u64 EditDuration, u64 MediaTime, u8 EditMode) +{ + GF_EdtsEntry *ent; + + ent = (GF_EdtsEntry*)gf_malloc(sizeof(GF_EdtsEntry)); + if (!ent) return NULL; + + switch (EditMode) { + case GF_ISOM_EDIT_EMPTY: + ent->mediaRate = 1; + ent->mediaTime = -1; + break; + + case GF_ISOM_EDIT_DWELL: + ent->mediaRate = 0; + ent->mediaTime = MediaTime; + break; + default: + ent->mediaRate = 1; + ent->mediaTime = MediaTime; + break; + } + ent->segmentDuration = EditDuration; + return ent; +} + +GF_Err gf_isom_add_subsample_info(GF_SubSampleInformationBox *sub_samples, u32 sampleNumber, u32 subSampleSize, u8 priority, u32 reserved, Bool discardable) +{ + u32 i, count, last_sample; + GF_SampleEntry *pSamp; + GF_SubSampleEntry *pSubSamp; + + pSamp = NULL; + last_sample = 0; + count = gf_list_count(sub_samples->Samples); + for (i=0; iSamples, i); + /*TODO - do we need to support insertion of subsample info ?*/ + if (last_sample + pSamp->sample_delta > sampleNumber) return GF_NOT_SUPPORTED; + if (last_sample + pSamp->sample_delta == sampleNumber) break; + last_sample += pSamp->sample_delta; + pSamp = NULL; + } + + if (!pSamp) { + GF_SAFEALLOC(pSamp, GF_SampleEntry); + if (!pSamp) return GF_OUT_OF_MEM; + pSamp->SubSamples = gf_list_new(); + if (!pSamp->SubSamples ) { + gf_free(pSamp); + return GF_OUT_OF_MEM; + } + pSamp->sample_delta = sampleNumber - last_sample; + gf_list_add(sub_samples->Samples, pSamp); + } + + if ((subSampleSize>0xFFFF) && !sub_samples->version) { + sub_samples->version = 1; + } + /*remove last subsample info*/ + if (!subSampleSize) { + pSubSamp = (GF_SubSampleEntry *) gf_list_last(pSamp->SubSamples); + gf_list_rem_last(pSamp->SubSamples); + gf_free(pSubSamp); + if (!gf_list_count(pSamp->SubSamples)) { + gf_list_del_item(sub_samples->Samples, pSamp); + gf_list_del(pSamp->SubSamples); + gf_free(pSamp); + } + return GF_OK; + } + /*add subsample*/ + GF_SAFEALLOC(pSubSamp, GF_SubSampleEntry); + if (!pSubSamp) return GF_OUT_OF_MEM; + pSubSamp->subsample_size = subSampleSize; + pSubSamp->subsample_priority = priority; + pSubSamp->reserved = reserved; + pSubSamp->discardable = discardable; + return gf_list_add(pSamp->SubSamples, pSubSamp); +} + +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + +u32 gf_isom_sample_get_subsample_entry(GF_ISOFile *movie, u32 track, u32 sampleNumber, GF_SampleEntry **sub_sample) +{ + u32 i, count, last_sample; + GF_SubSampleInformationBox *sub_samples; + GF_TrackBox *trak = gf_isom_get_track_from_file(movie, track); + if (sub_sample) *sub_sample = NULL; + if (!track) return 0; + if (!trak->Media || !trak->Media->information->sampleTable) return 0; + sub_samples = trak->Media->information->sampleTable->SubSamples; + if (!sub_samples) return 0; + last_sample = 0; + count = gf_list_count(sub_samples->Samples); + for (i=0; iSamples, i); + if (last_sample + pSamp->sample_delta == sampleNumber) { + if (sub_sample) *sub_sample = pSamp; + return gf_list_count(pSamp->SubSamples); + } + last_sample += pSamp->sample_delta; + } + return 0; +} +#endif /*GPAC_DISABLE_ISOM*/ diff --git a/src/gpacmp4/isom_read.c b/src/gpacmp4/isom_read.c new file mode 100644 index 00000000..0c69b0d3 --- /dev/null +++ b/src/gpacmp4/isom_read.c @@ -0,0 +1,2666 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / ISO Media File Format sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include "../disable_warnings.h" +#include +#include + +#ifndef GPAC_DISABLE_ISOM + +//the only static var. Used to store any error happening while opening a movie +static GF_Err MP4_API_IO_Err; + +void gf_isom_set_last_error(GF_ISOFile *movie, GF_Err error) +{ + if (!movie) { + MP4_API_IO_Err = error; + } else { + movie->LastError = error; + } +} + + +GF_EXPORT +GF_Err gf_isom_last_error(GF_ISOFile *the_file) +{ + if (!the_file) return MP4_API_IO_Err; + return the_file->LastError; +} + +GF_EXPORT +u8 gf_isom_get_mode(GF_ISOFile *the_file) +{ + if (!the_file) return 0; + return the_file->openMode; +} + +u64 gf_isom_get_file_size(GF_ISOFile *the_file) +{ + if (!the_file) return 0; + if (the_file->movieFileMap) return gf_bs_get_size(the_file->movieFileMap->bs); + if (the_file->editFileMap) return gf_bs_get_size(the_file->editFileMap->bs); + return 0; +} + +/************************************************************** + Sample Manip +**************************************************************/ + +//creates a new empty sample +GF_EXPORT +GF_ISOSample *gf_isom_sample_new() +{ + GF_ISOSample *tmp; + GF_SAFEALLOC(tmp, GF_ISOSample); + return tmp; +} + +//delete a sample +GF_EXPORT +void gf_isom_sample_del(GF_ISOSample **samp) +{ + if (! *samp) return; + if ((*samp)->data && (*samp)->dataLength) gf_free((*samp)->data); + gf_free(*samp); + *samp = NULL; +} + +GF_EXPORT +Bool gf_isom_probe_file(const char *fileName) +{ + unsigned char data[4]; + u32 type; + FILE *f = gf_f64_open(fileName, "rb"); + if (!f) return 0; + type = 0; + if (fread(data, 1, 4, f) == 4) { + if (fread(data, 1, 4, f) == 4) { + type = GF_4CC(data[0], data[1], data[2], data[3]); + } + } + fclose(f); + switch (type) { + case GF_ISOM_BOX_TYPE_MOOV: + case GF_ISOM_BOX_TYPE_MDAT: + case GF_ISOM_BOX_TYPE_FTYP: +#ifndef GPAC_DISABLE_ISOM_FRAGMENTS + case GF_ISOM_BOX_TYPE_STYP: +#endif + case GF_ISOM_BOX_TYPE_FREE: + case GF_ISOM_BOX_TYPE_SKIP: + case GF_ISOM_BOX_TYPE_UDTA: + case GF_ISOM_BOX_TYPE_META: + case GF_ISOM_BOX_TYPE_VOID: + case GF_4CC('j','P',' ',' '): + case GF_4CC('w','i','d','e'): + return 1; + default: + return 0; + } +} + +/************************************************************** + File Opening in streaming mode + the file map is regular (through FILE handles) +**************************************************************/ +GF_EXPORT +GF_Err gf_isom_open_progressive(const char *fileName, GF_ISOFile **the_file, u64 *BytesMissing) +{ + GF_Err e; + GF_ISOFile *movie; + + *BytesMissing = 0; + *the_file = NULL; + + movie = gf_isom_new_movie(); + if (!movie) return GF_OUT_OF_MEM; + + movie->fileName = gf_strdup(fileName); + movie->openMode = GF_ISOM_OPEN_READ; + //do NOT use FileMapping on incomplete files + e = gf_isom_datamap_new(fileName, NULL, GF_ISOM_DATA_MAP_READ, &movie->movieFileMap); + if (e) { + gf_isom_delete_movie(movie); + return e; + } + +#ifndef GPAC_DISABLE_ISOM_WRITE + movie->editFileMap = NULL; + movie->finalName = NULL; +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + + e = gf_isom_parse_movie_boxes(movie, BytesMissing, 1); + if (e == GF_ISOM_INCOMPLETE_FILE) { + //if we have a moov, we're fine + if (movie->moov) { + *the_file = (GF_ISOFile *)movie; + return GF_OK; + } + //if not, delete the movie + gf_isom_delete_movie(movie); + return e; + } else if (e) { + //if not, delete the movie + gf_isom_delete_movie(movie); + return e; + } + //OK, let's return + *the_file = (GF_ISOFile *)movie; + return GF_OK; +} + +/************************************************************** + File Reading +**************************************************************/ + +GF_EXPORT +GF_ISOFile *gf_isom_open(const char *fileName, u32 OpenMode, const char *tmp_dir) +{ + GF_ISOFile *movie; + MP4_API_IO_Err = GF_OK; + + switch (OpenMode & 0xFF) { + case GF_ISOM_OPEN_READ_DUMP: + case GF_ISOM_OPEN_READ: + movie = gf_isom_open_file(fileName, OpenMode, NULL); + break; + +#ifndef GPAC_DISABLE_ISOM_WRITE + + case GF_ISOM_OPEN_WRITE: + movie = gf_isom_create_movie(fileName, OpenMode, tmp_dir); + break; + case GF_ISOM_OPEN_EDIT: + case GF_ISOM_OPEN_CAT_FRAGMENTS: + movie = gf_isom_open_file(fileName, OpenMode, tmp_dir); + break; + case GF_ISOM_WRITE_EDIT: + movie = gf_isom_create_movie(fileName, OpenMode, tmp_dir); + break; + +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + + default: + return NULL; + } + return (GF_ISOFile *) movie; +} + +GF_EXPORT +GF_Err gf_isom_close(GF_ISOFile *movie) +{ + GF_Err e; + if (movie == NULL) return GF_ISOM_INVALID_FILE; + e = GF_OK; + +#ifndef GPAC_DISABLE_ISOM_WRITE + + //write our movie to the file + if (movie->openMode != GF_ISOM_OPEN_READ) { + gf_isom_get_duration(movie); +#ifndef GPAC_DISABLE_ISOM_FRAGMENTS + //movie fragment mode, just store the fragment + if ( (movie->openMode == GF_ISOM_OPEN_WRITE) && (movie->FragmentsFlags & GF_ISOM_FRAG_WRITE_READY) ) { + e = gf_isom_close_fragments(movie); + if (e) return e; + } else +#endif + e = WriteToFile(movie); + } + +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + + //free and return; + gf_isom_delete_movie(movie); + return e; +} + + +GF_EXPORT +Bool gf_isom_has_root_od(GF_ISOFile *movie) +{ + if (!movie || !movie->moov || !movie->moov->iods || !movie->moov->iods->descriptor) return 0; + return 1; +} + +//this funct is used for exchange files, where the iods contains an OD +GF_EXPORT +GF_Descriptor *gf_isom_get_root_od(GF_ISOFile *movie) +{ + GF_Descriptor *desc; + GF_ObjectDescriptor *od; + GF_InitialObjectDescriptor *iod; + GF_IsomObjectDescriptor *isom_od; + GF_IsomInitialObjectDescriptor *isom_iod; + GF_ESD *esd; + GF_ES_ID_Inc *inc; + u32 i; + u8 useIOD; + + if (!movie || !movie->moov) return NULL; + if (!movie->moov->iods) return NULL; + + od = NULL; + iod = NULL; + + switch (movie->moov->iods->descriptor->tag) { + case GF_ODF_ISOM_OD_TAG: + od = (GF_ObjectDescriptor*)gf_malloc(sizeof(GF_ObjectDescriptor)); + memset(od, 0, sizeof(GF_ObjectDescriptor)); + od->ESDescriptors = gf_list_new(); + useIOD = 0; + break; + case GF_ODF_ISOM_IOD_TAG: + iod = (GF_InitialObjectDescriptor*)gf_malloc(sizeof(GF_InitialObjectDescriptor)); + memset(iod, 0, sizeof(GF_InitialObjectDescriptor)); + iod->ESDescriptors = gf_list_new(); + useIOD = 1; + break; + default: + return NULL; + } + + //duplicate our descriptor + movie->LastError = gf_odf_desc_copy((GF_Descriptor *) movie->moov->iods->descriptor, &desc); + if (movie->LastError) return NULL; + + if (!useIOD) { + isom_od = (GF_IsomObjectDescriptor *)desc; + od->objectDescriptorID = isom_od->objectDescriptorID; + od->extensionDescriptors = isom_od->extensionDescriptors; + isom_od->extensionDescriptors = NULL; + od->IPMP_Descriptors = isom_od->IPMP_Descriptors; + isom_od->IPMP_Descriptors = NULL; + od->OCIDescriptors = isom_od->OCIDescriptors; + isom_od->OCIDescriptors = NULL; + od->URLString = isom_od->URLString; + isom_od->URLString = NULL; + od->tag = GF_ODF_OD_TAG; + //then recreate the desc in Inc + i=0; + while ((inc = (GF_ES_ID_Inc*)gf_list_enum(isom_od->ES_ID_IncDescriptors, &i))) { + movie->LastError = GetESDForTime(movie->moov, inc->trackID, 0, &esd); + if (!movie->LastError) movie->LastError = gf_list_add(od->ESDescriptors, esd); + if (movie->LastError) { + gf_odf_desc_del(desc); + gf_odf_desc_del((GF_Descriptor *) od); + return NULL; + } + } + gf_odf_desc_del(desc); + return (GF_Descriptor *)od; + } else { + isom_iod = (GF_IsomInitialObjectDescriptor *)desc; + iod->objectDescriptorID = isom_iod->objectDescriptorID; + iod->extensionDescriptors = isom_iod->extensionDescriptors; + isom_iod->extensionDescriptors = NULL; + iod->IPMP_Descriptors = isom_iod->IPMP_Descriptors; + isom_iod->IPMP_Descriptors = NULL; + iod->OCIDescriptors = isom_iod->OCIDescriptors; + isom_iod->OCIDescriptors = NULL; + iod->URLString = isom_iod->URLString; + isom_iod->URLString = NULL; + iod->tag = GF_ODF_IOD_TAG; + + iod->audio_profileAndLevel = isom_iod->audio_profileAndLevel; + iod->graphics_profileAndLevel = isom_iod->graphics_profileAndLevel; + iod->inlineProfileFlag = isom_iod->inlineProfileFlag; + iod->OD_profileAndLevel = isom_iod->OD_profileAndLevel; + iod->scene_profileAndLevel = isom_iod->scene_profileAndLevel; + iod->visual_profileAndLevel = isom_iod->visual_profileAndLevel; + iod->IPMPToolList = isom_iod->IPMPToolList; + isom_iod->IPMPToolList = NULL; + + //then recreate the desc in Inc + i=0; + while ((inc = (GF_ES_ID_Inc*)gf_list_enum(isom_iod->ES_ID_IncDescriptors, &i))) { + movie->LastError = GetESDForTime(movie->moov, inc->trackID, 0, &esd); + if (!movie->LastError) movie->LastError = gf_list_add(iod->ESDescriptors, esd); + if (movie->LastError) { + gf_odf_desc_del(desc); + gf_odf_desc_del((GF_Descriptor *) iod); + return NULL; + } + } + gf_odf_desc_del(desc); + return (GF_Descriptor *)iod; + } +} + + +GF_EXPORT +u32 gf_isom_get_track_count(GF_ISOFile *movie) +{ + if (!movie || !movie->moov) return 0; + + if (! movie->moov->trackList) { + movie->LastError = GF_ISOM_INVALID_FILE; + return 0; + } + return gf_list_count(movie->moov->trackList); +} + + +GF_EXPORT +u32 gf_isom_get_track_id(GF_ISOFile *movie, u32 trackNumber) +{ + GF_TrackBox *trak; + if (!movie) return 0; + trak = gf_isom_get_track_from_file(movie, trackNumber); + if (!trak) return 0; + return trak->Header->trackID; +} + + +GF_EXPORT +u32 gf_isom_get_track_by_id(GF_ISOFile *the_file, u32 trackID) +{ + GF_TrackBox *trak; + u32 count; + u32 i; + if (the_file == NULL) return 0; + + count = gf_isom_get_track_count(the_file); + if (!count) return 0; + for (i = 0; i < count; i++) { + trak = gf_isom_get_track_from_file(the_file, i+1); + if (!trak) return 0; + if (trak->Header->trackID == trackID) return i+1; + } + return 0; +} + +//return the timescale of the movie, 0 if error +GF_EXPORT +Bool gf_isom_has_movie(GF_ISOFile *file) +{ + if (file && file->moov) return 1; + return 0; +} + +#ifndef GPAC_DISABLE_ISOM +GF_EXPORT +Bool gf_isom_has_segment(GF_ISOFile *file, u32 *brand, u32 *version) +{ +#ifndef GPAC_DISABLE_ISOM_FRAGMENTS + u32 i; + GF_Box *a; + i = 0; + while (NULL != (a = (GF_Box *) gf_list_enum(file->TopBoxes, &i))) { +#ifndef GPAC_DISABLE_ISOM_FRAGMENTS + if (a->type == GF_ISOM_BOX_TYPE_STYP) { + GF_SegmentTypeBox *styp = (GF_SegmentTypeBox *)a; + *brand = styp->majorBrand; + *version = styp->minorVersion; + return 1; + } +#endif + } +#endif + return 0; +} +#endif + +//return the timescale of the movie, 0 if error +GF_EXPORT +u32 gf_isom_get_timescale(GF_ISOFile *movie) +{ + if (!movie || !movie->moov) return 0; + return movie->moov->mvhd->timeScale; +} + +//return the duration of the movie, 0 if error +GF_EXPORT +u64 gf_isom_get_duration(GF_ISOFile *movie) +{ +#ifndef GPAC_DISABLE_ISOM_WRITE + u32 i; + u64 maxDur; + GF_TrackBox *trak; +#endif + + if (!movie || !movie->moov) return 0; + + //if file was open in Write or Edit mode, recompute the duration + //the duration of a movie is the MaxDuration of all the tracks... + +#ifndef GPAC_DISABLE_ISOM_WRITE + + if (movie->openMode != GF_ISOM_OPEN_READ) { + maxDur = 0; + i=0; + while ((trak = (GF_TrackBox *)gf_list_enum(movie->moov->trackList, &i))) { + if( (movie->LastError = SetTrackDuration(trak)) ) return 0; + if (trak->Header->duration > maxDur) + maxDur = trak->Header->duration; + } + movie->moov->mvhd->duration = maxDur; + } + +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + + return movie->moov->mvhd->duration; +} + +//return the creation info of the movie +GF_EXPORT +GF_Err gf_isom_get_creation_time(GF_ISOFile *movie, u64 *creationTime, u64 *modificationTime) +{ + if (!movie || !movie->moov) return GF_BAD_PARAM; + + if (creationTime) *creationTime = movie->moov->mvhd->creationTime; + if (creationTime) *modificationTime = movie->moov->mvhd->modificationTime; + return GF_OK; +} + + +//check the presence of a track in IOD. 0: NO, 1: YES, 2: ERROR +GF_EXPORT +u8 gf_isom_is_track_in_root_od(GF_ISOFile *movie, u32 trackNumber) +{ + u32 i; + u32 trackID; + GF_Descriptor *desc; + GF_ES_ID_Inc *inc; + GF_List *inc_list; + if (!movie) return 2; + if (!movie->moov || !movie->moov->iods) return 0; + + desc = movie->moov->iods->descriptor; + switch (desc->tag) { + case GF_ODF_ISOM_IOD_TAG: + inc_list = ((GF_IsomInitialObjectDescriptor *)desc)->ES_ID_IncDescriptors; + break; + case GF_ODF_ISOM_OD_TAG: + inc_list = ((GF_IsomObjectDescriptor *)desc)->ES_ID_IncDescriptors; + break; + //files without IOD are possible ! + default: + return 0; + } + trackID = gf_isom_get_track_id(movie, trackNumber); + if (!trackID) return 2; + i=0; + while ((inc = (GF_ES_ID_Inc*)gf_list_enum(inc_list, &i))) { + if (inc->trackID == trackID) return 1; + } + return 0; +} + + + +//gets the enable flag of a track +//0: NO, 1: yes, 2: error +GF_EXPORT +u8 gf_isom_is_track_enabled(GF_ISOFile *the_file, u32 trackNumber) +{ + GF_TrackBox *trak; + trak = gf_isom_get_track_from_file(the_file, trackNumber); + + if (!trak) return 2; + return (trak->Header->flags & 1) ? 1 : 0; +} + + +//get the track duration +//return 0 if bad param +GF_EXPORT +u64 gf_isom_get_track_duration(GF_ISOFile *movie, u32 trackNumber) +{ + GF_TrackBox *trak; + trak = gf_isom_get_track_from_file(movie, trackNumber); + if (!trak) return 0; + +#ifndef GPAC_DISABLE_ISOM_WRITE + /*in all modes except dump recompute duration in case headers are wrong*/ + if (movie->openMode != GF_ISOM_OPEN_READ_DUMP) { + SetTrackDuration(trak); + } +#endif + return trak->Header->duration; +} + +GF_EXPORT +GF_Err gf_isom_get_media_language(GF_ISOFile *the_file, u32 trackNumber, char *three_char_code) +{ + GF_TrackBox *trak; + trak = gf_isom_get_track_from_file(the_file, trackNumber); + if (!trak) return GF_BAD_PARAM; + memcpy(three_char_code, trak->Media->mediaHeader->packedLanguage, sizeof(char)*4); + return GF_OK; +} + + +//Return the number of track references of a track for a given ReferenceType +//return -1 if error +GF_EXPORT +s32 gf_isom_get_reference_count(GF_ISOFile *movie, u32 trackNumber, u32 referenceType) +{ + GF_TrackBox *trak; + GF_TrackReferenceTypeBox *dpnd; + trak = gf_isom_get_track_from_file(movie, trackNumber); + if (!trak) return -1; + if (!trak->References) return 0; + if (movie->openMode == GF_ISOM_OPEN_WRITE) { + movie->LastError = GF_ISOM_INVALID_MODE; + return -1; + } + + dpnd = NULL; + if ( (movie->LastError = Track_FindRef(trak, referenceType, &dpnd)) ) return -1; + if (!dpnd) return 0; + return dpnd->trackIDCount; +} + + +//Return the referenced track number for a track and a given ReferenceType and Index +//return -1 if error, 0 if the reference is a NULL one, or the trackNumber +GF_EXPORT +GF_Err gf_isom_get_reference(GF_ISOFile *movie, u32 trackNumber, u32 referenceType, u32 referenceIndex, u32 *refTrack) +{ + GF_Err e; + GF_TrackBox *trak; + GF_TrackReferenceTypeBox *dpnd; + u32 refTrackNum; + trak = gf_isom_get_track_from_file(movie, trackNumber); + + *refTrack = 0; + if (!trak || !trak->References) return GF_BAD_PARAM; + + dpnd = NULL; + e = Track_FindRef(trak, referenceType, &dpnd); + if (e) return e; + if (!dpnd) return GF_BAD_PARAM; + + if (referenceIndex > dpnd->trackIDCount) return GF_BAD_PARAM; + + //the spec allows a NULL reference + //(ex, to force desync of a track, set a sync ref with ID = 0) + if (dpnd->trackIDs[referenceIndex - 1] == 0) return GF_OK; + + refTrackNum = gf_isom_get_tracknum_from_id(movie->moov, dpnd->trackIDs[referenceIndex-1]); + + //if the track was not found, this means the file is broken !!! + if (! refTrackNum) return GF_ISOM_INVALID_FILE; + *refTrack = refTrackNum; + return GF_OK; +} + +//Return the number of track references of a track for a given ReferenceType +//return -1 if error +GF_EXPORT +Bool gf_isom_has_track_reference(GF_ISOFile *movie, u32 trackNumber, u32 referenceType, u32 refTrackID) +{ + u32 i; + GF_TrackBox *trak; + GF_TrackReferenceTypeBox *dpnd; + trak = gf_isom_get_track_from_file(movie, trackNumber); + if (!trak) return 0; + if (!trak->References) return 0; + + dpnd = NULL; + if ( (movie->LastError = Track_FindRef(trak, referenceType, &dpnd)) ) return 0; + if (!dpnd) return 0; + for (i=0; itrackIDCount; i++) { + if (dpnd->trackIDs[i]==refTrackID) return 1; + } + return 0; +} + + + +//Return the media time given the absolute time in the Movie +GF_EXPORT +GF_Err gf_isom_get_media_time(GF_ISOFile *the_file, u32 trackNumber, u32 movieTime, u64 *MediaTime) +{ + GF_TrackBox *trak; + u8 useEdit; + s64 SegmentStartTime, mediaOffset; + trak = gf_isom_get_track_from_file(the_file, trackNumber); + if (!trak || !MediaTime) return GF_BAD_PARAM;; + + SegmentStartTime = 0; + return GetMediaTime(trak, movieTime, MediaTime, &SegmentStartTime, &mediaOffset, &useEdit); +} + + +//Get the stream description index (eg, the ESD) for a given time IN MEDIA TIMESCALE +//return 0 if error or if empty +GF_EXPORT +u32 gf_isom_get_sample_description_index(GF_ISOFile *movie, u32 trackNumber, u64 for_time) +{ + u32 streamDescIndex; + GF_TrackBox *trak; + trak = gf_isom_get_track_from_file(movie, trackNumber); + if (!trak) return 0; + + if ( (movie->LastError = Media_GetSampleDescIndex(trak->Media, for_time, &streamDescIndex)) ) { + return 0; + } + return streamDescIndex; +} + +//Get the number of "streams" stored in the media - a media can have several stream descriptions... +GF_EXPORT +u32 gf_isom_get_sample_description_count(GF_ISOFile *the_file, u32 trackNumber) +{ + GF_TrackBox *trak; + trak = gf_isom_get_track_from_file(the_file, trackNumber); + if (!trak) return 0; + + return gf_list_count(trak->Media->information->sampleTable->SampleDescription->boxList); +} + + +//Get the GF_ESD given the StreamDescriptionIndex +//THE DESCRIPTOR IS DUPLICATED, SO HAS TO BE DELETED BY THE APP +GF_EXPORT +GF_ESD *gf_isom_get_esd(GF_ISOFile *movie, u32 trackNumber, u32 StreamDescriptionIndex) +{ + GF_ESD *esd; + GF_Err e; + e = GetESD(movie->moov, gf_isom_get_track_id(movie, trackNumber), StreamDescriptionIndex, &esd); + if (e && (e!= GF_ISOM_INVALID_MEDIA)) { + movie->LastError = e; + return NULL; + } + + return esd; +} + +//Get the decoderConfigDescriptor given the SampleDescriptionIndex +//THE DESCRIPTOR IS DUPLICATED, SO HAS TO BE DELETED BY THE APP +GF_EXPORT +GF_DecoderConfig *gf_isom_get_decoder_config(GF_ISOFile *the_file, u32 trackNumber, u32 StreamDescriptionIndex) +{ + GF_TrackBox *trak; + GF_ESD *esd; + GF_Descriptor *decInfo; + trak = gf_isom_get_track_from_file(the_file, trackNumber); + if (!trak) return NULL; + //get the ESD (possibly emulated) + Media_GetESD(trak->Media, StreamDescriptionIndex, &esd, 0); + if (!esd) return NULL; + decInfo = (GF_Descriptor *) esd->decoderConfig; + esd->decoderConfig = NULL; + gf_odf_desc_del((GF_Descriptor *) esd); + return (GF_DecoderConfig *)decInfo; +} + + +//get the media duration (without edit) +//return 0 if bad param +GF_EXPORT +u64 gf_isom_get_media_duration(GF_ISOFile *movie, u32 trackNumber) +{ + GF_TrackBox *trak; + trak = gf_isom_get_track_from_file(movie, trackNumber); + if (!trak) return 0; + + +#ifndef GPAC_DISABLE_ISOM_WRITE + + /*except in dump mode always recompute the duration*/ + if (movie->openMode != GF_ISOM_OPEN_READ_DUMP) { + if ( (movie->LastError = Media_SetDuration(trak)) ) return 0; + } + +#endif + + return trak->Media->mediaHeader->duration; +} + +//Get the timeScale of the media. All samples DTS/CTS are expressed in this timeScale +GF_EXPORT +u32 gf_isom_get_media_timescale(GF_ISOFile *the_file, u32 trackNumber) +{ + GF_TrackBox *trak; + trak = gf_isom_get_track_from_file(the_file, trackNumber); + if (!trak) return 0; + return trak->Media->mediaHeader->timeScale; +} + + +GF_EXPORT +u32 gf_isom_get_copyright_count(GF_ISOFile *mov) +{ + GF_UserDataMap *map; + if (!mov || !mov->moov || !mov->moov->udta) return 0; + map = udta_getEntry(mov->moov->udta, GF_ISOM_BOX_TYPE_CPRT, NULL); + if (!map) return 0; + return gf_list_count(map->boxList); +} + +GF_EXPORT +GF_Err gf_isom_get_copyright(GF_ISOFile *mov, u32 Index, const char **threeCharCode, const char **notice) +{ + GF_UserDataMap *map; + GF_CopyrightBox *cprt; + + if (!mov || !mov->moov || !Index) return GF_BAD_PARAM; + + if (!mov->moov->udta) return GF_OK; + map = udta_getEntry(mov->moov->udta, GF_ISOM_BOX_TYPE_CPRT, NULL); + if (!map) return GF_OK; + + if (Index > gf_list_count(map->boxList)) return GF_BAD_PARAM; + + cprt = (GF_CopyrightBox*)gf_list_get(map->boxList, Index-1); + (*threeCharCode) = cprt->packedLanguageCode; + (*notice) = cprt->notice; + return GF_OK; +} + +GF_EXPORT +GF_Err gf_isom_get_watermark(GF_ISOFile *mov, bin128 UUID, u8** data, u32* length) +{ + GF_UserDataMap *map; + GF_UnknownUUIDBox *wm; + + if (!mov) return GF_BAD_PARAM; + if (!mov->moov || !mov->moov->udta) return GF_NOT_SUPPORTED; + + map = udta_getEntry(mov->moov->udta, GF_ISOM_BOX_TYPE_UUID, (bin128 *) & UUID); + if (!map) return GF_NOT_SUPPORTED; + + wm = (GF_UnknownUUIDBox*)gf_list_get(map->boxList, 0); + if (!wm) return GF_NOT_SUPPORTED; + + *data = (u8 *) gf_malloc(sizeof(char)*wm->dataSize); + memcpy(*data, wm->data, wm->dataSize); + *length = wm->dataSize; + return GF_OK; +} + +GF_EXPORT +u32 gf_isom_get_chapter_count(GF_ISOFile *movie, u32 trackNumber) +{ + GF_UserDataMap *map; + GF_ChapterListBox *lst; + GF_UserDataBox *udta; + + if (!movie || !movie->moov) return 0; + + udta = NULL; + if (trackNumber) { + GF_TrackBox *trak = gf_isom_get_track_from_file(movie, trackNumber); + if (!trak) return 0; + udta = trak->udta; + } else { + udta = movie->moov->udta; + } + if (!udta) return 0; + map = udta_getEntry(udta, GF_ISOM_BOX_TYPE_CHPL, NULL); + if (!map) return 0; + lst = (GF_ChapterListBox *)gf_list_get(map->boxList, 0); + if (!lst) return 0; + return gf_list_count(lst->list); +} + +GF_EXPORT +GF_Err gf_isom_get_chapter(GF_ISOFile *movie, u32 trackNumber, u32 Index, u64 *chapter_time, const char **name) +{ + GF_UserDataMap *map; + GF_ChapterListBox *lst; + GF_ChapterEntry *ce; + GF_UserDataBox *udta; + + if (!movie || !movie->moov) return GF_BAD_PARAM; + + udta = NULL; + if (trackNumber) { + GF_TrackBox *trak = gf_isom_get_track_from_file(movie, trackNumber); + if (!trak) return GF_BAD_PARAM; + udta = trak->udta; + } else { + udta = movie->moov->udta; + } + if (!udta) return GF_BAD_PARAM; + map = udta_getEntry(movie->moov->udta, GF_ISOM_BOX_TYPE_CHPL, NULL); + if (!map) return GF_BAD_PARAM; + lst = (GF_ChapterListBox *)gf_list_get(map->boxList, 0); + if (!lst) return GF_BAD_PARAM; + + ce = (GF_ChapterEntry *)gf_list_get(lst->list, Index-1); + if (!ce) return GF_BAD_PARAM; + if (chapter_time) { + *chapter_time = ce->start_time; + *chapter_time /= 10000L; + } + if (name) *name = ce->name; + return GF_OK; +} + + +GF_EXPORT +u32 gf_isom_get_media_type(GF_ISOFile *movie, u32 trackNumber) +{ + GF_TrackBox *trak; + trak = gf_isom_get_track_from_file(movie, trackNumber); + if (!trak) return GF_BAD_PARAM; + return trak->Media->handler->handlerType; +} + +Bool IsMP4Description(u32 entryType) +{ + switch (entryType) { + case GF_ISOM_BOX_TYPE_MP4S: + case GF_ISOM_BOX_TYPE_MP4A: + case GF_ISOM_BOX_TYPE_MP4V: + case GF_ISOM_BOX_TYPE_ENCA: + case GF_ISOM_BOX_TYPE_ENCV: + case GF_ISOM_BOX_TYPE_ENCS: + return 1; + default: + return 0; + } +} + +Bool IsMP4EncryptedDescription(u32 entryType) +{ + switch (entryType) { + case GF_ISOM_BOX_TYPE_ENCA: + case GF_ISOM_BOX_TYPE_ENCV: + case GF_ISOM_BOX_TYPE_ENCS: + return 1; + default: + return 0; + } +} + +GF_EXPORT +u8 gf_isom_is_track_encrypted(GF_ISOFile *the_file, u32 trackNumber) +{ + GF_TrackBox *trak; + GF_Box *entry; + trak = gf_isom_get_track_from_file(the_file, trackNumber); + if (!trak) return 2; + entry = (GF_Box*)gf_list_get(trak->Media->information->sampleTable->SampleDescription->boxList, 0); + if (!entry) return 2; + return IsMP4EncryptedDescription(entry->type); +} + +GF_EXPORT +u32 gf_isom_get_media_subtype(GF_ISOFile *the_file, u32 trackNumber, u32 DescriptionIndex) +{ + GF_TrackBox *trak; + GF_Box *entry; + trak = gf_isom_get_track_from_file(the_file, trackNumber); + if (!trak || !DescriptionIndex) return 0; + entry = (GF_Box*)gf_list_get(trak->Media->information->sampleTable->SampleDescription->boxList, DescriptionIndex-1); + if (!entry) return 0; + + //filter MPEG sub-types + if (IsMP4Description(entry->type)) { + if (IsMP4EncryptedDescription(entry->type)) return GF_ISOM_SUBTYPE_MPEG4_CRYP; + else return GF_ISOM_SUBTYPE_MPEG4; + } + if (entry->type == GF_ISOM_BOX_TYPE_GNRV) { + return ((GF_GenericVisualSampleEntryBox *)entry)->EntryType; + } + else if (entry->type == GF_ISOM_BOX_TYPE_GNRA) { + return ((GF_GenericAudioSampleEntryBox *)entry)->EntryType; + } + else if (entry->type == GF_ISOM_BOX_TYPE_GNRM) { + return ((GF_GenericSampleEntryBox *)entry)->EntryType; + } + return entry->type; +} + +GF_EXPORT +u32 gf_isom_get_mpeg4_subtype(GF_ISOFile *the_file, u32 trackNumber, u32 DescriptionIndex) +{ + GF_TrackBox *trak; + GF_Box *entry; + trak = gf_isom_get_track_from_file(the_file, trackNumber); + if (!trak || !DescriptionIndex) return 0; + entry = (GF_Box*)gf_list_get(trak->Media->information->sampleTable->SampleDescription->boxList, DescriptionIndex-1); + if (!entry) return 0; + + //filter MPEG sub-types + if (!IsMP4Description(entry->type)) return 0; + return entry->type; +} + +//Get the HandlerDescription name. +GF_EXPORT +GF_Err gf_isom_get_handler_name(GF_ISOFile *the_file, u32 trackNumber, const char **outName) +{ + GF_TrackBox *trak; + trak = gf_isom_get_track_from_file(the_file, trackNumber); + if (!trak || !outName) return GF_BAD_PARAM; + *outName = trak->Media->handler->nameUTF8; + return GF_OK; +} + +//Check the DataReferences of this track +GF_EXPORT +GF_Err gf_isom_check_data_reference(GF_ISOFile *the_file, u32 trackNumber, u32 StreamDescriptionIndex) +{ + GF_Err e; + u32 drefIndex; + GF_TrackBox *trak; + + if (!StreamDescriptionIndex || !trackNumber) return GF_BAD_PARAM; + trak = gf_isom_get_track_from_file(the_file, trackNumber); + if (!trak) return GF_BAD_PARAM; + + e = Media_GetSampleDesc(trak->Media, StreamDescriptionIndex , NULL, &drefIndex); + if (e) return e; + if (!drefIndex) return GF_BAD_PARAM; + return Media_CheckDataEntry(trak->Media, drefIndex); +} + +//get the location of the data. If URL && URN are NULL, the data is in this file +GF_EXPORT +GF_Err gf_isom_get_data_reference(GF_ISOFile *the_file, u32 trackNumber, u32 StreamDescriptionIndex, const char **outURL, const char **outURN) +{ + GF_TrackBox *trak; + GF_DataEntryURLBox *url; + GF_DataEntryURNBox *urn; + u32 drefIndex; + GF_Err e; + + *outURL = *outURN = NULL; + + if (!StreamDescriptionIndex || !trackNumber) return GF_BAD_PARAM; + trak = gf_isom_get_track_from_file(the_file, trackNumber); + if (!trak) return GF_BAD_PARAM; + + e = Media_GetSampleDesc(trak->Media, StreamDescriptionIndex , NULL, &drefIndex); + if (e) return e; + if (!drefIndex) return GF_BAD_PARAM; + + url = (GF_DataEntryURLBox*)gf_list_get(trak->Media->information->dataInformation->dref->boxList, drefIndex - 1); + if (!url) return GF_ISOM_INVALID_FILE; + + *outURL = *outURN = NULL; + if (url->type == GF_ISOM_BOX_TYPE_URL) { + *outURL = url->location; + *outURN = NULL; + } else if (url->type == GF_ISOM_BOX_TYPE_URN) { + urn = (GF_DataEntryURNBox *) url; + *outURN = urn->nameURN; + *outURL = urn->location; + } else { + *outURN = NULL; + *outURL = NULL; + } + return GF_OK; +} + +//Get the number of samples +//return 0 if error or empty +GF_EXPORT +u32 gf_isom_get_sample_count(GF_ISOFile *the_file, u32 trackNumber) +{ + GF_TrackBox *trak; + trak = gf_isom_get_track_from_file(the_file, trackNumber); + if (!trak) return 0; + return trak->Media->information->sampleTable->SampleSize->sampleCount + trak->sample_count_at_seg_start; +} + +u32 gf_isom_get_constant_sample_size(GF_ISOFile *the_file, u32 trackNumber) +{ + GF_TrackBox *trak; + trak = gf_isom_get_track_from_file(the_file, trackNumber); + if (!trak) return 0; + return trak->Media->information->sampleTable->SampleSize->sampleSize; +} + +GF_EXPORT +Bool gf_isom_has_time_offset(GF_ISOFile *the_file, u32 trackNumber) +{ + u32 i; + GF_CompositionOffsetBox *ctts; + GF_TrackBox *trak; + trak = gf_isom_get_track_from_file(the_file, trackNumber); + if (!trak || !trak->Media->information->sampleTable->CompositionOffset) return 0; + + //return true at the first offset found + ctts = trak->Media->information->sampleTable->CompositionOffset; + for (i=0; inb_entries; i++) { + if (ctts->entries[i].decodingOffset && ctts->entries[i].sampleCount) return 1; + } + return 0; +} + +GF_EXPORT +Bool gf_isom_has_sync_shadows(GF_ISOFile *the_file, u32 trackNumber) +{ + GF_TrackBox *trak = gf_isom_get_track_from_file(the_file, trackNumber); + if (!trak) return 0; + if (!trak->Media->information->sampleTable->ShadowSync) return 0; + if (gf_list_count(trak->Media->information->sampleTable->ShadowSync->entries) ) return 1; + return 0; +} + +GF_EXPORT +Bool gf_isom_has_sample_dependency(GF_ISOFile *the_file, u32 trackNumber) +{ + GF_TrackBox *trak = gf_isom_get_track_from_file(the_file, trackNumber); + if (!trak) return 0; + if (!trak->Media->information->sampleTable->SampleDep) return 0; + return 1; +} + +//return a sample give its number, and set the SampleDescIndex of this sample +//this index allows to retrieve the stream description if needed (2 media in 1 track) +//return NULL if error +GF_EXPORT +GF_ISOSample *gf_isom_get_sample(GF_ISOFile *the_file, u32 trackNumber, u32 sampleNumber, u32 *sampleDescriptionIndex) +{ + GF_Err e; + u32 descIndex; + GF_TrackBox *trak; + GF_ISOSample *samp; + trak = gf_isom_get_track_from_file(the_file, trackNumber); + if (!trak) return NULL; + + if (!sampleNumber) return NULL; + samp = gf_isom_sample_new(); + if (!samp) return NULL; + +#ifndef GPAC_DISABLE_ISOM_FRAGMENTS + if (sampleNumber<=trak->sample_count_at_seg_start) return NULL; + sampleNumber -= trak->sample_count_at_seg_start; +#endif + + e = Media_GetSample(trak->Media, sampleNumber, &samp, &descIndex, 0, NULL); + if (e) { + gf_isom_set_last_error(the_file, e); + gf_isom_sample_del(&samp); + return NULL; + } + if (sampleDescriptionIndex) *sampleDescriptionIndex = descIndex; +#ifndef GPAC_DISABLE_ISOM_FRAGMENTS + if (samp) samp->DTS += trak->dts_at_seg_start; +#endif + + return samp; +} + +GF_EXPORT +u32 gf_isom_get_sample_duration(GF_ISOFile *the_file, u32 trackNumber, u32 sampleNumber) +{ + u64 dur; + u64 dts; + GF_TrackBox *trak = gf_isom_get_track_from_file(the_file, trackNumber); + if (!trak || !sampleNumber) return 0; + + stbl_GetSampleDTS(trak->Media->information->sampleTable->TimeToSample, sampleNumber, &dur); + if (sampleNumber == trak->Media->information->sampleTable->SampleSize->sampleCount) { + return (u32) (trak->Media->mediaHeader->duration - dur); + } + + stbl_GetSampleDTS(trak->Media->information->sampleTable->TimeToSample, sampleNumber+1, &dts); + return (u32) (dts - dur); +} + + +GF_EXPORT +u32 gf_isom_get_sample_size(GF_ISOFile *the_file, u32 trackNumber, u32 sampleNumber) +{ + u32 size = 0; + GF_TrackBox *trak = gf_isom_get_track_from_file(the_file, trackNumber); + if (!trak || !sampleNumber) return 0; + + stbl_GetSampleSize(trak->Media->information->sampleTable->SampleSize, sampleNumber, &size); + return size; +} + +GF_EXPORT +u8 gf_isom_get_sample_sync(GF_ISOFile *the_file, u32 trackNumber, u32 sampleNumber) +{ + u8 is_rap; + GF_Err e; + GF_TrackBox *trak = gf_isom_get_track_from_file(the_file, trackNumber); + if (!trak || !sampleNumber) return 0; + + if (! trak->Media->information->sampleTable->SyncSample) return 1; + e = stbl_GetSampleRAP(trak->Media->information->sampleTable->SyncSample, sampleNumber, &is_rap, NULL, NULL); + if (e) return 0; + return is_rap; +} + +//same as gf_isom_get_sample but doesn't fetch media data +GF_EXPORT +GF_ISOSample *gf_isom_get_sample_info(GF_ISOFile *the_file, u32 trackNumber, u32 sampleNumber, u32 *sampleDescriptionIndex, u64 *data_offset) +{ + GF_Err e; + GF_TrackBox *trak; + GF_ISOSample *samp; + trak = gf_isom_get_track_from_file(the_file, trackNumber); + if (!trak) return NULL; + + if (!sampleNumber) return NULL; + samp = gf_isom_sample_new(); + if (!samp) return NULL; + e = Media_GetSample(trak->Media, sampleNumber, &samp, sampleDescriptionIndex, 1, data_offset); + if (e) { + gf_isom_set_last_error(the_file, e); + gf_isom_sample_del(&samp); + return NULL; + } + return samp; +} + +//same as gf_isom_get_sample but doesn't fetch media data +GF_EXPORT +u64 gf_isom_get_sample_dts(GF_ISOFile *the_file, u32 trackNumber, u32 sampleNumber) +{ + u64 dts; + GF_TrackBox *trak; + trak = gf_isom_get_track_from_file(the_file, trackNumber); + if (!trak) return 0; + + if (!sampleNumber) return 0; + if (stbl_GetSampleDTS(trak->Media->information->sampleTable->TimeToSample, sampleNumber, &dts) != GF_OK) return 0; + return dts; +} + + +GF_EXPORT +Bool gf_isom_is_self_contained(GF_ISOFile *the_file, u32 trackNumber, u32 sampleDescriptionIndex) +{ + GF_TrackBox *trak; + trak = gf_isom_get_track_from_file(the_file, trackNumber); + if (!trak) return 0; + return Media_IsSelfContained(trak->Media, sampleDescriptionIndex); +} + +/*retrieves given sample DTS*/ +u32 gf_isom_get_sample_from_dts(GF_ISOFile *the_file, u32 trackNumber, u64 dts) +{ + GF_Err e; + u32 sampleNumber, prevSampleNumber; + GF_TrackBox *trak; + GF_SampleTableBox *stbl; + + trak = gf_isom_get_track_from_file(the_file, trackNumber); + if (!trak) return 0; + + stbl = trak->Media->information->sampleTable; + + e = findEntryForTime(stbl, dts, 1, &sampleNumber, &prevSampleNumber); + if (e) return 0; + return sampleNumber; +} + + +//return a sample given a desired display time IN MEDIA TIME SCALE +//and set the StreamDescIndex of this sample +//this index allows to retrieve the stream description if needed (2 media in 1 track) +//return NULL if error +//WARNING: the sample may not be sync even though the sync was requested (depends on the media) +GF_EXPORT +GF_Err gf_isom_get_sample_for_media_time(GF_ISOFile *the_file, u32 trackNumber, u64 desiredTime, u32 *StreamDescriptionIndex, u8 SearchMode, GF_ISOSample **sample, u32 *SampleNum) +{ + GF_Err e; + u32 sampleNumber, prevSampleNumber, syncNum, shadowSync; + GF_TrackBox *trak; + GF_ISOSample *shadow; + GF_SampleTableBox *stbl; + u8 useShadow, IsSync; + + if (!sample) return GF_BAD_PARAM; + + if (SampleNum) *SampleNum = 0; + trak = gf_isom_get_track_from_file(the_file, trackNumber); + if (!trak) return GF_BAD_PARAM; + + stbl = trak->Media->information->sampleTable; + +#ifndef GPAC_DISABLE_ISOM_FRAGMENTS + if (desiredTime < trak->dts_at_seg_start) return GF_BAD_PARAM; + desiredTime -= trak->dts_at_seg_start; +#endif + + e = findEntryForTime(stbl, desiredTime, 0, &sampleNumber, &prevSampleNumber); + if (e) return e; + + //if no shadow table, reset to sync only + useShadow = 0; + if (!stbl->ShadowSync && (SearchMode == GF_ISOM_SEARCH_SYNC_SHADOW)) + SearchMode = GF_ISOM_SEARCH_SYNC_BACKWARD; + + //if no syncTable, disable syncSearching, as all samples ARE sync + if (! trak->Media->information->sampleTable->SyncSample) { + if (SearchMode == GF_ISOM_SEARCH_SYNC_FORWARD) SearchMode = GF_ISOM_SEARCH_FORWARD; + if (SearchMode == GF_ISOM_SEARCH_SYNC_BACKWARD) SearchMode = GF_ISOM_SEARCH_BACKWARD; + } + + //not found, return EOF or browse backward + if (!sampleNumber && !prevSampleNumber) { + if (SearchMode == GF_ISOM_SEARCH_SYNC_BACKWARD || SearchMode == GF_ISOM_SEARCH_BACKWARD) { + sampleNumber = trak->Media->information->sampleTable->SampleSize->sampleCount; + } + if (!sampleNumber) return GF_EOS; + } + + //check in case we have the perfect sample + IsSync = 0; + + //according to the direction adjust the sampleNum value + switch (SearchMode) { + case GF_ISOM_SEARCH_SYNC_FORWARD: + IsSync = 1; + case GF_ISOM_SEARCH_FORWARD: + //not the exact one + if (!sampleNumber) { + if (prevSampleNumber != stbl->SampleSize->sampleCount) { + sampleNumber = prevSampleNumber + 1; + } else { + sampleNumber = prevSampleNumber; + } + } + break; + + //if dummy mode, reset to default browsing + case GF_ISOM_SEARCH_SYNC_BACKWARD: + IsSync = 1; + case GF_ISOM_SEARCH_SYNC_SHADOW: + case GF_ISOM_SEARCH_BACKWARD: + default: + //first case, not found.... + if (!sampleNumber && !prevSampleNumber) { + sampleNumber = stbl->SampleSize->sampleCount; + } else if (!sampleNumber) { + sampleNumber = prevSampleNumber; + } + break; + } + + //get the sync sample num + if (IsSync) { + //get the SyncNumber + e = Media_FindSyncSample(trak->Media->information->sampleTable, + sampleNumber, &syncNum, SearchMode); + if (e) return e; + if (syncNum) sampleNumber = syncNum; + syncNum = 0; + } + //if we are in shadow mode, get the previous sync sample + //in case we can't find a good SyncShadow + else if (SearchMode == GF_ISOM_SEARCH_SYNC_SHADOW) { + //get the SyncNumber + e = Media_FindSyncSample(trak->Media->information->sampleTable, + sampleNumber, &syncNum, GF_ISOM_SEARCH_SYNC_BACKWARD); + if (e) return e; + } + + + //OK sampleNumber is exactly the sample we need (except for shadow) + + *sample = gf_isom_sample_new(); + if (*sample == NULL) return GF_OUT_OF_MEM; + + //we are in shadow mode, we need to browse both SyncSample and ShadowSyncSample to get + //the desired sample... + if (SearchMode == GF_ISOM_SEARCH_SYNC_SHADOW) { + //get the shadowing number + stbl_GetSampleShadow(stbl->ShadowSync, &sampleNumber, &shadowSync); + //now sampleNumber is the closest previous shadowed sample. + //1- If we have a closer sync sample, use it. + //2- if the shadowSync is 0, we don't have any shadowing, use syncNum + if ((sampleNumber < syncNum) || (!shadowSync)) { + sampleNumber = syncNum; + } else { + //otherwise, we have a better alternate sample in the shadowSync for this sample + useShadow = 1; + } + } + + e = Media_GetSample(trak->Media, sampleNumber, sample, StreamDescriptionIndex, 0, NULL); + if (e) { + gf_isom_sample_del(sample); + return e; + } + //optionally get the sample number + if (SampleNum) { + *SampleNum = sampleNumber; +#ifndef GPAC_DISABLE_ISOM_FRAGMENTS + *SampleNum += trak->sample_count_at_seg_start; +#endif + } + + //in shadow mode, we only get the data of the shadowing sample ! + if (useShadow) { + //we have to use StreamDescriptionIndex in case the sample data is in another desc + //though this is unlikely as non optimized... + shadow = gf_isom_get_sample(the_file, trackNumber, shadowSync, StreamDescriptionIndex); + //if no sample, the shadowSync is broken, return the sample + if (!shadow) return GF_OK; + (*sample)->IsRAP = 1; + gf_free((*sample)->data); + (*sample)->dataLength = shadow->dataLength; + (*sample)->data = shadow->data; + //set data length to 0 to keep the buffer alive... + shadow->dataLength = 0; + gf_isom_sample_del(&shadow); + } + return GF_OK; +} + +GF_EXPORT +GF_Err gf_isom_get_sample_for_movie_time(GF_ISOFile *the_file, u32 trackNumber, u64 movieTime, u32 *StreamDescriptionIndex, u8 SearchMode, GF_ISOSample **sample, u32 *sampleNumber) +{ + GF_Err e; + GF_TrackBox *trak; + u64 mediaTime; + s64 segStartTime, mediaOffset; + u32 sampNum; + u8 useEdit; + + trak = gf_isom_get_track_from_file(the_file, trackNumber); + if (!trak) return GF_BAD_PARAM; + + if (*sample || !sample) return GF_BAD_PARAM; + //check 0-duration tracks (BIFS and co). Check we're not searching forward + if (!trak->Header->duration) { + if (movieTime && ( (SearchMode == GF_ISOM_SEARCH_SYNC_FORWARD) || (SearchMode == GF_ISOM_SEARCH_FORWARD)) ) { + *sample = NULL; + if (sampleNumber) *sampleNumber = 0; + *StreamDescriptionIndex = 0; + return GF_EOS; + } + } + else if (movieTime * trak->moov->mvhd->timeScale > trak->Header->duration * trak->Media->mediaHeader->timeScale) { + *sample = NULL; + if (sampleNumber) *sampleNumber = 0; + *StreamDescriptionIndex = 0; + return GF_EOS; + } + + //get the media time for this movie time... + mediaTime = segStartTime = 0; + *StreamDescriptionIndex = 0; + + e = GetMediaTime(trak, movieTime, &mediaTime, &segStartTime, &mediaOffset, &useEdit); + if (e) return e; + + /*here we check if we were playing or not and return no sample in normal search modes*/ + if (useEdit && mediaOffset == -1) { + if ((SearchMode==GF_ISOM_SEARCH_FORWARD) || (SearchMode==GF_ISOM_SEARCH_BACKWARD)) { + /*get next sample time in MOVIE timescale*/ + if (SearchMode==GF_ISOM_SEARCH_FORWARD) + e = GetNextMediaTime(trak, movieTime, &mediaTime); + else + e = GetPrevMediaTime(trak, movieTime, &mediaTime); + if (e) return e; + return gf_isom_get_sample_for_movie_time(the_file, trackNumber, (u32) mediaTime, StreamDescriptionIndex, GF_ISOM_SEARCH_SYNC_FORWARD, sample, sampleNumber); + } + if (sampleNumber) *sampleNumber = 0; + *sample = gf_isom_sample_new(); + (*sample)->DTS = movieTime; + return GF_OK; + } + /*dwell edit in non-sync mode, fetch next/prev sample depending on mode. + Otherwise return the dwell entry*/ + if (useEdit==2) { + if ((SearchMode==GF_ISOM_SEARCH_FORWARD) || (SearchMode==GF_ISOM_SEARCH_BACKWARD)) { + /*get next sample time in MOVIE timescale*/ + if (SearchMode==GF_ISOM_SEARCH_FORWARD) + e = GetNextMediaTime(trak, movieTime, &mediaTime); + else + e = GetPrevMediaTime(trak, movieTime, &mediaTime); + if (e) return e; + return gf_isom_get_sample_for_movie_time(the_file, trackNumber, (u32) mediaTime, StreamDescriptionIndex, GF_ISOM_SEARCH_SYNC_FORWARD, sample, sampleNumber); + } + } + + //OK, we have a sample so fetch it + e = gf_isom_get_sample_for_media_time(the_file, trackNumber, mediaTime, StreamDescriptionIndex, SearchMode, sample, &sampNum); + if (e) return e; + + //OK, now the trick: we have to rebuild the time stamps, according + //to the media time scale (used by SLConfig) - add the edit start time but stay in + //the track TS + if (useEdit) { + u64 _ts = segStartTime; + _ts *= trak->Media->mediaHeader->timeScale; + _ts /= trak->moov->mvhd->timeScale; + (*sample)->DTS += _ts; + /*watchout, the sample fetched may be before the first sample in the edit list (when seeking)*/ + if ( (*sample)->DTS > (u64) mediaOffset) { + (*sample)->DTS -= (u64) mediaOffset; + } else { + (*sample)->DTS = 0; + } + } + if (sampleNumber) *sampleNumber = sampNum; + return GF_OK; +} + + + +GF_EXPORT +u64 gf_isom_get_missing_bytes(GF_ISOFile *the_file, u32 trackNumber) +{ + GF_TrackBox *trak; + trak = gf_isom_get_track_from_file(the_file, trackNumber); + if (!trak) return 0; + + return trak->Media->BytesMissing; +} + +GF_EXPORT +GF_Err gf_isom_set_sample_padding(GF_ISOFile *the_file, u32 trackNumber, u32 padding_bytes) +{ + GF_TrackBox *trak; + trak = gf_isom_get_track_from_file(the_file, trackNumber); + if (!trak) return GF_BAD_PARAM; + trak->padding_bytes = padding_bytes; + return GF_OK; + +} + +//get the number of edited segment +GF_EXPORT +u32 gf_isom_get_edit_segment_count(GF_ISOFile *the_file, u32 trackNumber) +{ + GF_TrackBox *trak; + trak = gf_isom_get_track_from_file(the_file, trackNumber); + if (!trak) return 0; + + if (!trak->editBox || !trak->editBox->editList) return 0; + return gf_list_count(trak->editBox->editList->entryList); +} + + +//Get the desired segment information +GF_EXPORT +GF_Err gf_isom_get_edit_segment(GF_ISOFile *the_file, u32 trackNumber, u32 SegmentIndex, u64 *EditTime, u64 *SegmentDuration, u64 *MediaTime, u8 *EditMode) +{ + u32 i; + u64 startTime; + GF_TrackBox *trak; + GF_EditListBox *elst; + GF_EdtsEntry *ent; + + ent = NULL; + trak = gf_isom_get_track_from_file(the_file, trackNumber); + if (!trak) return GF_BAD_PARAM; + + if (!trak || !trak->editBox || + !trak->editBox->editList || + (SegmentIndex > gf_list_count(trak->editBox->editList->entryList)) || + !SegmentIndex) + return GF_BAD_PARAM; + + elst = trak->editBox->editList; + startTime = 0; + + for (i = 0; i < SegmentIndex; i++) { + ent = (GF_EdtsEntry*)gf_list_get(elst->entryList, i); + if (i < SegmentIndex-1) startTime += ent->segmentDuration; + } + *EditTime = startTime; + *SegmentDuration = ent->segmentDuration; + if (ent->mediaTime < 0) { + *MediaTime = 0; + *EditMode = GF_ISOM_EDIT_EMPTY; + return GF_OK; + } + if (ent->mediaRate == 0) { + *MediaTime = ent->mediaTime; + *EditMode = GF_ISOM_EDIT_DWELL; + return GF_OK; + } + *MediaTime = ent->mediaTime; + *EditMode = GF_ISOM_EDIT_NORMAL; + return GF_OK; +} + +GF_EXPORT +u8 gf_isom_has_sync_points(GF_ISOFile *the_file, u32 trackNumber) +{ + GF_TrackBox *trak; + + trak = gf_isom_get_track_from_file(the_file, trackNumber); + if (!trak) return 0; + if (trak->Media->information->sampleTable->SyncSample) { + if (!trak->Media->information->sampleTable->SyncSample->nb_entries) return 2; + return 1; + } + return 0; +} + +/*returns number of sync points*/ +GF_EXPORT +u32 gf_isom_get_sync_point_count(GF_ISOFile *the_file, u32 trackNumber) +{ + GF_TrackBox *trak; + trak = gf_isom_get_track_from_file(the_file, trackNumber); + if (!trak) return 0; + if (trak->Media->information->sampleTable->SyncSample) { + return trak->Media->information->sampleTable->SyncSample->nb_entries; + } + return 0; +} + + +GF_EXPORT +GF_Err gf_isom_get_brand_info(GF_ISOFile *movie, u32 *brand, u32 *minorVersion, u32 *AlternateBrandsCount) +{ + if (!movie || !brand) return GF_BAD_PARAM; + if (!movie->brand) { + *brand = 0; + if (minorVersion) *minorVersion = 0; + if (AlternateBrandsCount) *AlternateBrandsCount = 0; + return GF_OK; + } + + *brand = movie->brand->majorBrand; + if (minorVersion) *minorVersion = movie->brand->minorVersion; + if (AlternateBrandsCount) *AlternateBrandsCount = movie->brand->altCount; + return GF_OK; +} + +GF_EXPORT +GF_Err gf_isom_get_alternate_brand(GF_ISOFile *movie, u32 BrandIndex, u32 *brand) +{ + if (!movie || !movie->brand || !brand) return GF_BAD_PARAM; + if (BrandIndex > movie->brand->altCount || !BrandIndex) return GF_BAD_PARAM; + *brand = movie->brand->altBrand[BrandIndex-1]; + return GF_OK; +} + +GF_Err gf_isom_get_sample_padding_bits(GF_ISOFile *the_file, u32 trackNumber, u32 sampleNumber, u8 *NbBits) +{ + GF_TrackBox *trak; + + trak = gf_isom_get_track_from_file(the_file, trackNumber); + if (!trak) return GF_BAD_PARAM; + + + //Padding info + return stbl_GetPaddingBits(trak->Media->information->sampleTable->PaddingBits, + sampleNumber, NbBits); + +} + + +GF_EXPORT +Bool gf_isom_has_padding_bits(GF_ISOFile *the_file, u32 trackNumber) +{ + GF_TrackBox *trak; + + trak = gf_isom_get_track_from_file(the_file, trackNumber); + if (!trak) return 0; + + if (trak->Media->information->sampleTable->PaddingBits) return 1; + return 0; +} + + +GF_EXPORT +u32 gf_isom_get_user_data_count(GF_ISOFile *movie, u32 trackNumber, u32 UserDataType, bin128 UUID) +{ + GF_UserDataMap *map; + GF_TrackBox *trak; + GF_UserDataBox *udta; + bin128 t; + u32 i, count; + + if (!movie || !movie->moov) return 0; + + if (UserDataType == GF_ISOM_BOX_TYPE_UUID) UserDataType = 0; + memset(t, 1, 16); + + if (trackNumber) { + trak = gf_isom_get_track_from_file(movie, trackNumber); + if (!trak) return 0; + udta = trak->udta; + } else { + udta = movie->moov->udta; + } + if (!udta) return 0; + + i=0; + while ((map = (GF_UserDataMap*)gf_list_enum(udta->recordList, &i))) { + count = gf_list_count(map->boxList); + + if ((map->boxType == GF_ISOM_BOX_TYPE_UUID) && !memcmp(map->uuid, UUID, 16)) return count; + else if (map->boxType == UserDataType) return count; + } + return 0; +} + +GF_EXPORT +GF_Err gf_isom_get_user_data(GF_ISOFile *movie, u32 trackNumber, u32 UserDataType, bin128 UUID, u32 UserDataIndex, char **userData, u32 *userDataSize) +{ + GF_UserDataMap *map; + GF_UnknownBox *ptr; + u32 i; + bin128 t; + GF_TrackBox *trak; + GF_UserDataBox *udta; + + if (!movie || !movie->moov) return GF_BAD_PARAM; + + if (trackNumber) { + trak = gf_isom_get_track_from_file(movie, trackNumber); + if (!trak) return GF_BAD_PARAM; + udta = trak->udta; + } else { + udta = movie->moov->udta; + } + if (!udta) return GF_BAD_PARAM; + + if (UserDataType == GF_ISOM_BOX_TYPE_UUID) UserDataType = 0; + memset(t, 1, 16); + + if (!UserDataIndex) return GF_BAD_PARAM; + if (!userData || !userDataSize || *userData) return GF_BAD_PARAM; + + i=0; + while ((map = (GF_UserDataMap*)gf_list_enum(udta->recordList, &i))) { + if ((map->boxType == GF_ISOM_BOX_TYPE_UUID) && !memcmp(map->uuid, UUID, 16)) goto found; + else if (map->boxType == UserDataType) goto found; + + } + return GF_BAD_PARAM; + +found: + + if (UserDataIndex > gf_list_count(map->boxList) ) return GF_BAD_PARAM; + ptr = (GF_UnknownBox*)gf_list_get(map->boxList, UserDataIndex-1); + + //ok alloc the data + *userData = (char *)gf_malloc(sizeof(char)*ptr->dataSize); + if (!*userData) return GF_OUT_OF_MEM; + memcpy(*userData, ptr->data, sizeof(char)*ptr->dataSize); + *userDataSize = ptr->dataSize; + return GF_OK; +} + +GF_EXPORT +void gf_isom_delete(GF_ISOFile *movie) +{ + //free and return; + gf_isom_delete_movie(movie); +} + +GF_EXPORT +GF_Err gf_isom_get_chunks_infos(GF_ISOFile *movie, u32 trackNumber, u32 *dur_min, u32 *dur_avg, u32 *dur_max, u32 *size_min, u32 *size_avg, u32 *size_max) +{ + GF_TrackBox *trak; + u32 i, k, sample_idx, dmin, dmax, smin, smax, tot_chunks; + u64 davg, savg; + GF_SampleToChunkBox *stsc; + GF_TimeToSampleBox *stts; + if (!movie || !trackNumber || !movie->moov) return GF_BAD_PARAM; + trak = gf_isom_get_track_from_file(movie, trackNumber); + if (!trak) return GF_BAD_PARAM; + + stsc = trak->Media->information->sampleTable->SampleToChunk; + stts = trak->Media->information->sampleTable->TimeToSample; + + dmin = smin = (u32) -1; + dmax = smax = 0; + davg = savg = 0; + sample_idx = 1; + tot_chunks = 0; + for (i=0; inb_entries; i++) { + u32 nb_chunk = 0; + while (1) { + u32 chunk_dur = 0; + u32 chunk_size = 0; + for (k=0; kentries[i].samplesPerChunk; k++) { + u64 dts; + u32 dur; + u32 size; + stbl_GetSampleDTS_and_Duration(stts, k+sample_idx, &dts, &dur); + chunk_dur += dur; + stbl_GetSampleSize(trak->Media->information->sampleTable->SampleSize, k+sample_idx, &size); + chunk_size += size; + + } + if (dmin>chunk_dur) dmin = chunk_dur; + if (dmaxchunk_size) smin = chunk_size; + if (smaxentries[i].samplesPerChunk; + if (i+1==stsc->nb_entries) break; + nb_chunk ++; + if (stsc->entries[i].firstChunk + nb_chunk == stsc->entries[i+1].firstChunk) break; + } + } + if (tot_chunks) davg /= tot_chunks; + + if (dur_min) *dur_min = dmin; + if (dur_avg) *dur_avg = (u32) davg; + if (dur_max) *dur_max = dmax; + + if (size_min) *size_min = smin; + if (size_avg) *size_avg = (u32) savg; + if (size_max) *size_max = smax; + return GF_OK; +} + +GF_EXPORT +u32 gf_isom_get_sample_fragment_count(GF_ISOFile *the_file, u32 trackNumber, u32 sampleNumber) +{ + GF_TrackBox *trak; + trak = gf_isom_get_track_from_file(the_file, trackNumber); + if (!trak) return 0; + + //Padding info + return stbl_GetSampleFragmentCount(trak->Media->information->sampleTable->Fragments, sampleNumber); +} + +GF_EXPORT +u16 gf_isom_get_sample_fragment_size(GF_ISOFile *the_file, u32 trackNumber, u32 sampleNumber, u32 FragmentIndex) +{ + GF_TrackBox *trak; + trak = gf_isom_get_track_from_file(the_file, trackNumber); + if (!trak || !FragmentIndex) return 0; + + //Padding info + return stbl_GetSampleFragmentSize(trak->Media->information->sampleTable->Fragments, sampleNumber, FragmentIndex); +} + + +GF_EXPORT +GF_Err gf_isom_get_fragment_defaults(GF_ISOFile *the_file, u32 trackNumber, + u32 *defaultDuration, u32 *defaultSize, u32 *defaultDescriptionIndex, + u32 *defaultRandomAccess, u8 *defaultPadding, u16 *defaultDegradationPriority) +{ + GF_TrackBox *trak; + GF_StscEntry *sc_ent; + u32 i, j, maxValue, value; +#ifndef GPAC_DISABLE_ISOM_FRAGMENTS + GF_TrackExtendsBox *trex; +#endif + GF_SampleTableBox *stbl; + trak = gf_isom_get_track_from_file(the_file, trackNumber); + if (!trak) return GF_BAD_PARAM; + + /*if trex is already set, restore flags*/ +#ifndef GPAC_DISABLE_ISOM_FRAGMENTS + trex = the_file->moov->mvex ? GetTrex(the_file->moov, gf_isom_get_track_id(the_file,trackNumber) ) : NULL; + if (trex) { + trex->track = trak; + + if (defaultDuration) *defaultDuration = trex->def_sample_duration; + if (defaultSize) *defaultSize = trex->def_sample_size; + if (defaultDescriptionIndex) *defaultDescriptionIndex = trex->def_sample_desc_index; + if (defaultRandomAccess) *defaultRandomAccess = GF_ISOM_GET_FRAG_SYNC(trex->def_sample_flags); + if (defaultPadding) *defaultPadding = GF_ISOM_GET_FRAG_PAD(trex->def_sample_flags); + if (defaultDegradationPriority) *defaultDegradationPriority = GF_ISOM_GET_FRAG_DEG(trex->def_sample_flags); + return GF_OK; + } +#endif + + + stbl = trak->Media->information->sampleTable; + //duration + if (defaultDuration) { + maxValue = value = 0; + for (i=0; iTimeToSample->nb_entries; i++) { + if (stbl->TimeToSample->entries[i].sampleCount>maxValue) { + value = stbl->TimeToSample->entries[i].sampleDelta; + maxValue = stbl->TimeToSample->entries[i].sampleCount; + } + } + *defaultDuration = value; + } + //size + if (defaultSize) { + *defaultSize = stbl->SampleSize->sampleSize; + } + //descIndex + if (defaultDescriptionIndex) { + GF_SampleToChunkBox *stsc= stbl->SampleToChunk; + maxValue = value = 0; + for (i=0; inb_entries; i++) { + sc_ent = &stsc->entries[i]; + if ((sc_ent->nextChunk - sc_ent->firstChunk) * sc_ent->samplesPerChunk > maxValue) { + value = sc_ent->sampleDescriptionIndex; + maxValue = (sc_ent->nextChunk - sc_ent->firstChunk) * sc_ent->samplesPerChunk; + } + } + *defaultDescriptionIndex = value ? value : 1; + } + //RAP + if (defaultRandomAccess) { + //no sync table is ALL RAP + *defaultRandomAccess = stbl->SyncSample ? 0 : 1; + if (stbl->SyncSample + && (stbl->SyncSample->nb_entries >= stbl->SampleSize->sampleCount/2)) { + *defaultRandomAccess = 1; + } + } + //defaultPadding + if (defaultPadding) { + *defaultPadding = 0; + if (stbl->PaddingBits) { + maxValue = 0; + for (i=0; iPaddingBits->SampleCount; i++) { + value = 0; + for (j=0; jPaddingBits->SampleCount; j++) { + if (stbl->PaddingBits->padbits[i]==stbl->PaddingBits->padbits[j]) { + value ++; + } + } + if (value>maxValue) { + maxValue = value; + *defaultPadding = stbl->PaddingBits->padbits[i]; + } + } + } + } + //defaultDegradationPriority + if (defaultDegradationPriority) { + *defaultDegradationPriority = 0; + if (stbl->DegradationPriority) { + maxValue = 0; + for (i=0; iDegradationPriority->nb_entries; i++) { + value = 0; + for (j=0; jDegradationPriority->nb_entries; j++) { + if (stbl->DegradationPriority->priorities[i]==stbl->DegradationPriority->priorities[j]) { + value ++; + } + } + if (value>maxValue) { + maxValue = value; + *defaultDegradationPriority = stbl->DegradationPriority->priorities[i]; + } + } + } + } + return GF_OK; +} + + +GF_EXPORT +GF_Err gf_isom_refresh_fragmented(GF_ISOFile *movie, u64 *MissingBytes) +{ + u64 prevsize, size; +#ifdef GPAC_DISABLE_ISOM_FRAGMENTS + return GF_NOT_SUPPORTED; +#else + if (!movie || !movie->moov || !movie->moov->mvex) return GF_BAD_PARAM; + if (movie->openMode != GF_ISOM_OPEN_READ) return GF_BAD_PARAM; + + /*refresh size*/ + size = gf_bs_get_size(movie->movieFileMap->bs); + prevsize = gf_bs_get_refreshed_size(movie->movieFileMap->bs); + if (prevsize==size) return GF_OK; + + //ok parse root boxes + return gf_isom_parse_movie_boxes(movie, MissingBytes, 1); +#endif +} + +GF_EXPORT +GF_Err gf_isom_release_segment(GF_ISOFile *movie, Bool reset_tables) +{ +#ifndef GPAC_DISABLE_ISOM_FRAGMENTS + u32 i; + if (!movie || !movie->moov || !movie->moov->mvex) return GF_BAD_PARAM; + + for (i=0; imoov->trackList); i++) { + GF_TrackBox *trak = (GF_TrackBox *) gf_list_get(movie->moov->trackList, i); + if (trak->Media->information->dataHandler == movie->movieFileMap) { + trak->Media->information->dataHandler = NULL; + } + if (reset_tables) { + u32 type, dur; + u64 dts; + GF_SampleTableBox *stbl = trak->Media->information->sampleTable; + trak->sample_count_at_seg_start += stbl->SampleSize->sampleCount; + if (trak->sample_count_at_seg_start) { + stbl_GetSampleDTS_and_Duration(stbl->TimeToSample, stbl->SampleSize->sampleCount, &dts, &dur); + trak->dts_at_seg_start += dts + dur; + } +#define RECREATE_BOX(_a, __cast) \ + if (_a) { \ + type = _a->type;\ + gf_isom_box_del((GF_Box *)_a);\ + _a = __cast gf_isom_box_new(type);\ + }\ + + RECREATE_BOX(stbl->ChunkOffset, (GF_Box *)); + RECREATE_BOX(stbl->CompositionOffset, (GF_CompositionOffsetBox *)); + RECREATE_BOX(stbl->DegradationPriority, (GF_DegradationPriorityBox *)); + RECREATE_BOX(stbl->PaddingBits, (GF_PaddingBitsBox *)); + RECREATE_BOX(stbl->SampleDep, (GF_SampleDependencyTypeBox *)); + RECREATE_BOX(stbl->SampleSize, (GF_SampleSizeBox *)); + RECREATE_BOX(stbl->SampleToChunk, (GF_SampleToChunkBox *)); + RECREATE_BOX(stbl->ShadowSync, (GF_ShadowSyncBox *)); + RECREATE_BOX(stbl->SyncSample, (GF_SyncSampleBox *)); + RECREATE_BOX(stbl->TimeToSample, (GF_TimeToSampleBox *)); + } + } + + movie->first_moof_merged = 0; + gf_isom_datamap_del(movie->movieFileMap); + movie->movieFileMap = NULL; +#endif + return GF_OK; +} + +GF_Err gf_isom_open_segment(GF_ISOFile *movie, const char *fileName) +{ +#ifdef GPAC_DISABLE_ISOM_FRAGMENTS + return GF_NOT_SUPPORTED; +#else + u64 MissingBytes; + GF_Err e; + u32 i; + if (!movie || !movie->moov || !movie->moov->mvex) return GF_BAD_PARAM; + if (movie->openMode != GF_ISOM_OPEN_READ) return GF_BAD_PARAM; + + if (movie->movieFileMap) + gf_isom_release_segment(movie, 0); + + e = gf_isom_datamap_new(fileName, NULL, GF_ISOM_DATA_MAP_READ_ONLY, &movie->movieFileMap); + if (e) return e; + + for (i=0; imoov->trackList); i++) { + GF_TrackBox *trak = (GF_TrackBox *) gf_list_get(movie->moov->trackList, i); + if (trak->Media->information->dataHandler == NULL) { + trak->Media->information->dataHandler = movie->movieFileMap; + } + } + + movie->current_top_box_start = 0; + //ok parse root boxes + return gf_isom_parse_movie_boxes(movie, &MissingBytes, 1); +#endif +} + + +GF_EXPORT +GF_Err gf_isom_text_set_streaming_mode(GF_ISOFile *movie, Bool do_convert) +{ + if (!movie) return GF_BAD_PARAM; + movie->convert_streaming_text = do_convert; + return GF_OK; +} + + +GF_EXPORT +GF_GenericSampleDescription *gf_isom_get_generic_sample_description(GF_ISOFile *movie, u32 trackNumber, u32 StreamDescriptionIndex) +{ + GF_GenericVisualSampleEntryBox *entry; + GF_GenericAudioSampleEntryBox *gena; + GF_GenericSampleEntryBox *genm; + GF_TrackBox *trak; + GF_GenericSampleDescription *udesc; + trak = gf_isom_get_track_from_file(movie, trackNumber); + if (!trak || !StreamDescriptionIndex) return NULL; + + entry = (GF_GenericVisualSampleEntryBox *)gf_list_get(trak->Media->information->sampleTable->SampleDescription->boxList, StreamDescriptionIndex-1); + //no entry or MPEG entry: + if (!entry || IsMP4Description(entry->type) ) return NULL; + //if we handle the description return false + switch (entry->type) { + case GF_ISOM_SUBTYPE_3GP_AMR: + case GF_ISOM_SUBTYPE_3GP_AMR_WB: + case GF_ISOM_SUBTYPE_3GP_EVRC: + case GF_ISOM_SUBTYPE_3GP_QCELP: + case GF_ISOM_SUBTYPE_3GP_SMV: + case GF_ISOM_SUBTYPE_3GP_H263: + return NULL; + case GF_ISOM_BOX_TYPE_GNRV: + GF_SAFEALLOC(udesc, GF_GenericSampleDescription); + if (entry->EntryType == GF_ISOM_BOX_TYPE_UUID) { + memcpy(udesc->UUID, ((GF_UUIDBox*)entry)->uuid, sizeof(bin128)); + } else { + udesc->codec_tag = entry->EntryType; + } + udesc->version = entry->version; + udesc->revision = entry->revision; + udesc->vendor_code = entry->vendor; + udesc->temporal_quality = entry->temporal_quality; + udesc->spacial_quality = entry->spacial_quality; + udesc->width = entry->Width; + udesc->height = entry->Height; + udesc->h_res = entry->horiz_res; + udesc->v_res = entry->vert_res; + strcpy(udesc->compressor_name, entry->compressor_name); + udesc->depth = entry->bit_depth; + udesc->color_table_index = entry->color_table_index; + if (entry->data_size) { + udesc->extension_buf_size = entry->data_size; + udesc->extension_buf = (char*)gf_malloc(sizeof(char) * entry->data_size); + memcpy(udesc->extension_buf, entry->data, entry->data_size); + } + return udesc; + case GF_ISOM_BOX_TYPE_GNRA: + gena = (GF_GenericAudioSampleEntryBox *)entry; + GF_SAFEALLOC(udesc, GF_GenericSampleDescription); + if (gena->EntryType == GF_ISOM_BOX_TYPE_UUID) { + memcpy(udesc->UUID, ((GF_UUIDBox*)gena)->uuid, sizeof(bin128)); + } else { + udesc->codec_tag = gena->EntryType; + } + udesc->version = gena->version; + udesc->revision = gena->revision; + udesc->vendor_code = gena->vendor; + udesc->samplerate = gena->samplerate_hi; + udesc->bits_per_sample = gena->bitspersample; + udesc->nb_channels = gena->channel_count; + if (gena->data_size) { + udesc->extension_buf_size = gena->data_size; + udesc->extension_buf = (char*)gf_malloc(sizeof(char) * gena->data_size); + memcpy(udesc->extension_buf, gena->data, gena->data_size); + } + return udesc; + case GF_ISOM_BOX_TYPE_GNRM: + genm = (GF_GenericSampleEntryBox *)entry; + GF_SAFEALLOC(udesc, GF_GenericSampleDescription); + if (genm->EntryType == GF_ISOM_BOX_TYPE_UUID) { + memcpy(udesc->UUID, ((GF_UUIDBox*)genm)->uuid, sizeof(bin128)); + } else { + udesc->codec_tag = genm->EntryType; + } + if (genm->data_size) { + udesc->extension_buf_size = genm->data_size; + udesc->extension_buf = (char*)gf_malloc(sizeof(char) * genm->data_size); + memcpy(udesc->extension_buf, genm->data, genm->data_size); + } + return udesc; + } + return NULL; +} + +GF_EXPORT +GF_Err gf_isom_get_visual_info(GF_ISOFile *movie, u32 trackNumber, u32 StreamDescriptionIndex, u32 *Width, u32 *Height) +{ + GF_TrackBox *trak; + GF_SampleEntryBox *entry; + GF_SampleDescriptionBox *stsd; + + trak = gf_isom_get_track_from_file(movie, trackNumber); + if (!trak) return GF_BAD_PARAM; + + stsd = trak->Media->information->sampleTable->SampleDescription; + if (!stsd) return movie->LastError = GF_ISOM_INVALID_FILE; + if (!StreamDescriptionIndex || StreamDescriptionIndex > gf_list_count(stsd->boxList)) return movie->LastError = GF_BAD_PARAM; + + entry = (GF_SampleEntryBox *)gf_list_get(stsd->boxList, StreamDescriptionIndex - 1); + //no support for generic sample entries (eg, no MPEG4 descriptor) + if (entry == NULL) return GF_BAD_PARAM; + + //valid for MPEG visual, JPG and 3GPP H263 + switch (entry->type) { + case GF_ISOM_BOX_TYPE_ENCV: + case GF_ISOM_BOX_TYPE_MP4V: + case GF_ISOM_SUBTYPE_3GP_H263: + case GF_ISOM_BOX_TYPE_AVC1: + case GF_ISOM_BOX_TYPE_AVC2: + case GF_ISOM_BOX_TYPE_SVC1: + case GF_ISOM_BOX_TYPE_GNRV: + *Width = ((GF_VisualSampleEntryBox*)entry)->Width; + *Height = ((GF_VisualSampleEntryBox*)entry)->Height; + return GF_OK; + default: + if (trak->Media->handler->handlerType==GF_ISOM_MEDIA_SCENE) { + *Width = trak->Header->width>>16; + *Height = trak->Header->height>>16; + return GF_OK; + } + return GF_BAD_PARAM; + } +} + +GF_EXPORT +GF_Err gf_isom_get_audio_info(GF_ISOFile *movie, u32 trackNumber, u32 StreamDescriptionIndex, u32 *SampleRate, u32 *Channels, u8 *bitsPerSample) +{ + GF_TrackBox *trak; + GF_SampleEntryBox *entry; + GF_SampleDescriptionBox *stsd; + + trak = gf_isom_get_track_from_file(movie, trackNumber); + if (!trak) return GF_BAD_PARAM; + + stsd = trak->Media->information->sampleTable->SampleDescription; + if (!stsd) return movie->LastError = GF_ISOM_INVALID_FILE; + if (!StreamDescriptionIndex || StreamDescriptionIndex > gf_list_count(stsd->boxList)) return movie->LastError = GF_BAD_PARAM; + + entry = (GF_SampleEntryBox *)gf_list_get(stsd->boxList, StreamDescriptionIndex - 1); + //no support for generic sample entries (eg, no MPEG4 descriptor) + if (entry == NULL) return GF_BAD_PARAM; + + switch (entry->type) { + case GF_ISOM_BOX_TYPE_ENCA: + if (entry->protection_info && (entry->protection_info->original_format->data_format!= GF_ISOM_BOX_TYPE_MP4A)) return GF_ISOM_INVALID_MEDIA; + case GF_ISOM_BOX_TYPE_MP4A: + case GF_ISOM_SUBTYPE_3GP_AMR: + case GF_ISOM_SUBTYPE_3GP_AMR_WB: + case GF_ISOM_SUBTYPE_3GP_EVRC: + case GF_ISOM_SUBTYPE_3GP_QCELP: + case GF_ISOM_SUBTYPE_3GP_SMV: + case GF_ISOM_BOX_TYPE_AC3: + if (SampleRate) (*SampleRate) = ((GF_AudioSampleEntryBox*)entry)->samplerate_hi; + if (Channels) (*Channels) = ((GF_AudioSampleEntryBox*)entry)->channel_count; + if (bitsPerSample) (*bitsPerSample) = (u8) ((GF_AudioSampleEntryBox*)entry)->bitspersample; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + +GF_EXPORT +GF_Err gf_isom_get_pixel_aspect_ratio(GF_ISOFile *movie, u32 trackNumber, u32 StreamDescriptionIndex, u32 *hSpacing, u32 *vSpacing) +{ + GF_TrackBox *trak; + GF_SampleEntryBox *entry; + GF_SampleDescriptionBox *stsd; + + trak = gf_isom_get_track_from_file(movie, trackNumber); + if (!trak) return GF_BAD_PARAM; + + stsd = trak->Media->information->sampleTable->SampleDescription; + if (!stsd) return movie->LastError = GF_ISOM_INVALID_FILE; + if (!StreamDescriptionIndex || StreamDescriptionIndex > gf_list_count(stsd->boxList)) return movie->LastError = GF_BAD_PARAM; + + entry = (GF_SampleEntryBox *)gf_list_get(stsd->boxList, StreamDescriptionIndex - 1); + //no support for generic sample entries (eg, no MPEG4 descriptor) + if (entry == NULL) return GF_BAD_PARAM; + + //valid for MPEG visual, JPG and 3GPP H263 + switch (entry->type) { + case GF_ISOM_BOX_TYPE_ENCV: + case GF_ISOM_BOX_TYPE_MP4V: + case GF_ISOM_SUBTYPE_3GP_H263: + case GF_ISOM_BOX_TYPE_AVC1: + case GF_ISOM_BOX_TYPE_AVC2: + case GF_ISOM_BOX_TYPE_SVC1: + case GF_ISOM_BOX_TYPE_GNRV: + *hSpacing = ((GF_VisualSampleEntryBox*)entry)->pasp ? ((GF_VisualSampleEntryBox*)entry)->pasp->hSpacing : 0; + *vSpacing = ((GF_VisualSampleEntryBox*)entry)->pasp ? ((GF_VisualSampleEntryBox*)entry)->pasp->vSpacing : 0; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + +GF_EXPORT +const char *gf_isom_get_filename(GF_ISOFile *movie) +{ + if (!movie) return NULL; +#ifndef GPAC_DISABLE_ISOM_WRITE + if (movie->finalName && !movie->fileName) return movie->finalName; +#endif + return movie->fileName; +} + + +GF_EXPORT +u8 gf_isom_get_pl_indication(GF_ISOFile *movie, u8 PL_Code) +{ + GF_IsomInitialObjectDescriptor *iod; + if (!movie || !movie->moov) return 0; + if (!movie->moov->iods || !movie->moov->iods->descriptor) return 0xFF; + if (movie->moov->iods->descriptor->tag != GF_ODF_ISOM_IOD_TAG) return 0xFF; + + iod = (GF_IsomInitialObjectDescriptor *)movie->moov->iods->descriptor; + switch (PL_Code) { + case GF_ISOM_PL_AUDIO: return iod->audio_profileAndLevel; + case GF_ISOM_PL_VISUAL: return iod->visual_profileAndLevel; + case GF_ISOM_PL_GRAPHICS: return iod->graphics_profileAndLevel; + case GF_ISOM_PL_SCENE: return iod->scene_profileAndLevel; + case GF_ISOM_PL_OD: return iod->OD_profileAndLevel; + case GF_ISOM_PL_INLINE: return iod->inlineProfileFlag; + case GF_ISOM_PL_MPEGJ: + default: + return 0xFF; + } +} + +GF_EXPORT +GF_Err gf_isom_get_track_matrix(GF_ISOFile *the_file, u32 trackNumber, u32 matrix[9]) +{ + GF_TrackBox *trak = gf_isom_get_track_from_file(the_file, trackNumber); + if (!trak || !trak->Header) return GF_BAD_PARAM; + memcpy(matrix, trak->Header->matrix, sizeof(trak->Header->matrix)); + return GF_OK; +} + +GF_EXPORT +GF_Err gf_isom_get_track_layout_info(GF_ISOFile *movie, u32 trackNumber, u32 *width, u32 *height, s32 *translation_x, s32 *translation_y, s16 *layer) +{ + GF_TrackBox *tk = gf_isom_get_track_from_file(movie, trackNumber); + if (!tk) return GF_BAD_PARAM; + if (width) *width = tk->Header->width>>16; + if (height) *height = tk->Header->height>>16; + if (layer) *layer = tk->Header->layer; + if (translation_x) *translation_x = tk->Header->matrix[6] >> 16; + if (translation_y) *translation_y = tk->Header->matrix[7] >> 16; + return GF_OK; +} + + +/*returns total amount of media bytes in track*/ +u64 gf_isom_get_media_data_size(GF_ISOFile *movie, u32 trackNumber) +{ + u32 i, size; + GF_SampleSizeBox *stsz; + GF_TrackBox *tk = gf_isom_get_track_from_file(movie, trackNumber); + if (!tk) return 0; + stsz = tk->Media->information->sampleTable->SampleSize; + if (stsz->sampleSize) return stsz->sampleSize*stsz->sampleCount; + size = 0; + for (i=0; isampleCount;i++) size += stsz->sizes[i]; + return size; +} + + +GF_EXPORT +void gf_isom_set_default_sync_track(GF_ISOFile *movie, u32 trackNumber) +{ + GF_TrackBox *tk = gf_isom_get_track_from_file(movie, trackNumber); + if (!tk) movie->es_id_default_sync = -1; + else movie->es_id_default_sync = tk->Header->trackID; +} + + +GF_EXPORT +Bool gf_isom_is_single_av(GF_ISOFile *file) +{ + u32 count, i, nb_any, nb_a, nb_v, nb_scene, nb_od, nb_text; + nb_a = nb_v = nb_any = nb_scene = nb_od = nb_text = 0; + + if (!file->moov) return 0; + count = gf_isom_get_track_count(file); + for (i=0; i1) nb_any++; + else nb_scene++; + break; + case GF_ISOM_MEDIA_OD: + if (gf_isom_get_sample_count(file, i+1)>1) nb_any++; + else nb_od++; + break; + case GF_ISOM_MEDIA_TEXT: + case GF_ISOM_MEDIA_SUBT: + nb_text++; + break; + case GF_ISOM_MEDIA_AUDIO: + nb_a++; + break; + case GF_ISOM_MEDIA_VISUAL: + /*discard file with images*/ + if (gf_isom_get_sample_count(file, i+1)==1) nb_any++; + else nb_v++; + break; + default: + nb_any++; + break; + } + } + if (nb_any) return 0; + if ((nb_scene<=1) && (nb_od<=1) && (nb_a<=1) && (nb_v<=1) && (nb_text<=1) ) return 1; + return 0; +} + +GF_EXPORT +Bool gf_isom_is_JPEG2000(GF_ISOFile *mov) +{ + return (mov && mov->is_jp2) ? 1 : 0; +} + +GF_EXPORT +u32 gf_isom_guess_specification(GF_ISOFile *file) +{ + u32 count, i, nb_any, nb_m4s, nb_a, nb_v, nb_scene, nb_od, nb_mp3, nb_aac, nb_m4v, nb_avc, nb_amr, nb_h263, nb_qcelp, nb_evrc, nb_smv, nb_text; + + nb_m4s = nb_a = nb_v = nb_any = nb_scene = nb_od = nb_mp3 = nb_aac = nb_m4v = nb_avc = nb_amr = nb_h263 = nb_qcelp = nb_evrc = nb_smv = nb_text = 0; + + if (file->is_jp2) { + if (file->moov) return GF_4CC('m','j','p','2'); + return GF_4CC('j','p','2',' '); + } + if (!file->moov) { + if (!file->meta || !file->meta->handler) return 0; + return file->meta->handler->handlerType; + } + + count = gf_isom_get_track_count(file); + for (i=0; i1) nb_m4s++; + } else if (mtype==GF_ISOM_MEDIA_OD) { + nb_od++; + /*forces non-isma*/ + if (gf_isom_get_sample_count(file, i+1)>1) nb_m4s++; + } + else if ((mtype==GF_ISOM_MEDIA_TEXT) || (mtype==GF_ISOM_MEDIA_SUBT)) nb_text++; + else if ((mtype==GF_ISOM_MEDIA_AUDIO) || (mtype==GF_ISOM_MEDIA_VISUAL)) { + switch (mstype) { + case GF_ISOM_SUBTYPE_3GP_AMR: + case GF_ISOM_SUBTYPE_3GP_AMR_WB: + nb_amr++; + break; + case GF_ISOM_SUBTYPE_3GP_H263: nb_h263++; break; + case GF_ISOM_SUBTYPE_3GP_EVRC: nb_evrc++; break; + case GF_ISOM_SUBTYPE_3GP_QCELP: nb_qcelp++; break; + case GF_ISOM_SUBTYPE_3GP_SMV: nb_smv++; break; + case GF_ISOM_SUBTYPE_AVC_H264: nb_avc++; break; + case GF_ISOM_SUBTYPE_AVC2_H264: nb_avc++; break; + case GF_ISOM_SUBTYPE_SVC_H264: nb_avc++; break; + case GF_ISOM_SUBTYPE_MPEG4: + case GF_ISOM_SUBTYPE_MPEG4_CRYP: + { + GF_DecoderConfig *dcd = gf_isom_get_decoder_config(file, i+1, 1); + switch (dcd->streamType) { + case GF_STREAM_VISUAL: + if (dcd->objectTypeIndication==GPAC_OTI_VIDEO_MPEG4_PART2) nb_m4v++; + else if (dcd->objectTypeIndication==GPAC_OTI_VIDEO_AVC) nb_avc++; + else nb_v++; + break; + case GF_STREAM_AUDIO: + switch (dcd->objectTypeIndication) { + case GPAC_OTI_AUDIO_AAC_MPEG2_MP: + case GPAC_OTI_AUDIO_AAC_MPEG2_LCP: + case GPAC_OTI_AUDIO_AAC_MPEG2_SSRP: + case GPAC_OTI_AUDIO_AAC_MPEG4: + nb_aac++; + break; + case GPAC_OTI_AUDIO_MPEG2_PART3: + case GPAC_OTI_AUDIO_MPEG1: + nb_mp3++; + break; + case GPAC_OTI_AUDIO_EVRC_VOICE: nb_evrc++; break; + case GPAC_OTI_AUDIO_SMV_VOICE: nb_smv++; break; + case GPAC_OTI_AUDIO_13K_VOICE: nb_qcelp++; break; + default: nb_a++; break; + } + break; + /*SHOULD NEVER HAPPEN - IF SO, BROKEN MPEG4 FILE*/ + default: + nb_any++; + break; + } + gf_odf_desc_del((GF_Descriptor *)dcd); + } + break; + default: + if (mtype==GF_ISOM_MEDIA_VISUAL) nb_v++; + else nb_a++; + break; + } + } else if ((mtype==GF_ISOM_SUBTYPE_MPEG4) || (mtype==GF_ISOM_SUBTYPE_MPEG4_CRYP)) nb_m4s++; + else nb_any++; + } + if (nb_any) return GF_ISOM_BRAND_ISOM; + if (nb_qcelp || nb_evrc || nb_smv) { + /*non std mix of streams*/ + if (nb_m4s || nb_avc || nb_scene || nb_od || nb_mp3 || nb_a || nb_v) return GF_ISOM_BRAND_ISOM; + return GF_ISOM_BRAND_3G2A; + } + /*other a/v/s streams*/ + if (nb_v || nb_a || nb_m4s) return GF_ISOM_BRAND_MP42; + + nb_v = nb_m4v + nb_avc + nb_h263; + nb_a = nb_mp3 + nb_aac + nb_amr; + + /*avc file: whatever has AVC and no systems*/ + if (nb_avc) { + if (!nb_scene && !nb_od) return GF_ISOM_BRAND_AVC1; + return GF_ISOM_BRAND_MP42; + } + /*MP3: ISMA and MPEG4*/ + if (nb_mp3) { + if (!nb_text && (nb_v<=1) && (nb_a<=1) && (nb_scene==1) && (nb_od==1)) + return GF_4CC('I', 'S', 'M', 'A'); + return GF_ISOM_BRAND_MP42; + } + /*MP4*/ + if (nb_scene || nb_od) { + /*issue with AMR and H263 which don't have MPEG mapping: non compliant file*/ + if (nb_amr || nb_h263) return GF_ISOM_BRAND_ISOM; + return GF_ISOM_BRAND_MP42; + } + /*use ISMA (3GP fine too)*/ + if (!nb_amr && !nb_h263 && !nb_text) { + if ((nb_v<=1) && (nb_a<=1)) return GF_4CC('I', 'S', 'M', 'A'); + return GF_ISOM_BRAND_MP42; + } + + if ((nb_v<=1) && (nb_a<=1) && (nb_text<=1)) return nb_text ? GF_ISOM_BRAND_3GP6 : GF_ISOM_BRAND_3GP5; + return GF_ISOM_BRAND_3GG6; +} + +GF_ItemListBox *gf_ismo_locate_box(GF_List *list, u32 boxType, bin128 UUID) +{ + u32 i; + GF_Box *box; + i=0; + while ((box = (GF_Box *)gf_list_enum(list, &i))) { + if (box->type == boxType) { + GF_UUIDBox* box2 = (GF_UUIDBox* )box; + if (boxType != GF_ISOM_BOX_TYPE_UUID) return (GF_ItemListBox *)box; + if (!memcmp(box2->uuid, UUID, 16)) return (GF_ItemListBox *)box; + } + } + return NULL; +} + +/*Apple extensions*/ + + +GF_EXPORT +GF_Err gf_isom_apple_get_tag(GF_ISOFile *mov, u32 tag, const char **data, u32 *data_len) +{ + u32 i; + GF_ListItemBox *info; + GF_ItemListBox *ilst; + GF_MetaBox *meta; + + *data = NULL; + *data_len = 0; + + meta = gf_isom_apple_get_meta_extensions(mov); + if (!meta) return GF_URL_ERROR; + + ilst = gf_ismo_locate_box(meta->other_boxes, GF_ISOM_BOX_TYPE_ILST, NULL); + if (!ilst) return GF_URL_ERROR; + + if (tag==GF_ISOM_ITUNE_PROBE) return GF_OK; + + i=0; + while ( (info=(GF_ListItemBox *) gf_list_enum(ilst->tags, &i))) { + if (info->type==tag) break; + /*special cases*/ + if ((tag==GF_ISOM_ITUNE_GENRE) && (info->type==(u32) GF_ISOM_BOX_TYPE_0xA9GEN)) break; + info = NULL; + } + if (!info || !info->data || !info->data->data) return GF_URL_ERROR; + + if ((tag == GF_ISOM_ITUNE_GENRE) && (info->data->flags == 0)) { + if (info->data->dataSize && (info->data->dataSize>2) && (info->data->dataSize < 5)) { + GF_BitStream* bs = gf_bs_new(info->data->data, info->data->dataSize, GF_BITSTREAM_READ); + *data_len = gf_bs_read_int(bs, info->data->dataSize * 8); + gf_bs_del(bs); + return GF_OK; + } + } +// if (info->data->flags != 0x1) return GF_URL_ERROR; + *data = info->data->data; + *data_len = info->data->dataSize; + if ((tag==GF_ISOM_ITUNE_COVER_ART) && (info->data->flags==14)) *data_len |= (1<<31); + return GF_OK; +} + + + + +GF_EXPORT +GF_Err gf_isom_get_track_switch_group_count(GF_ISOFile *movie, u32 trackNumber, u32 *alternateGroupID, u32 *nb_groups) +{ + GF_UserDataMap *map; + GF_TrackBox *trak; + + trak = gf_isom_get_track_from_file(movie, trackNumber); + if (!trak) return GF_BAD_PARAM; + *alternateGroupID = trak->Header->alternate_group; + *nb_groups = 0; + if (!trak->udta) return GF_OK; + + map = udta_getEntry(trak->udta, GF_ISOM_BOX_TYPE_TSEL, NULL); + if (!map) return GF_OK; + *nb_groups = gf_list_count(map->boxList); + return GF_OK; +} + +GF_EXPORT +const u32 *gf_isom_get_track_switch_parameter(GF_ISOFile *movie, u32 trackNumber, u32 group_index, u32 *switchGroupID, u32 *criteriaListSize) +{ + GF_TrackBox *trak; + GF_UserDataMap *map; + GF_TrackSelectionBox *tsel; + + trak = gf_isom_get_track_from_file(movie, trackNumber); + if (!group_index || !trak || !trak->udta) return NULL; + + map = udta_getEntry(trak->udta, GF_ISOM_BOX_TYPE_TSEL, NULL); + if (!map) return NULL; + tsel = (GF_TrackSelectionBox *) gf_list_get(map->boxList, group_index-1); + *switchGroupID = tsel->switchGroup; + *criteriaListSize = tsel->attributeListCount; + return (const u32 *) tsel->attributeList; +} + + +GF_EXPORT +GF_Err gf_isom_get_timed_meta_data_info(GF_ISOFile *file, u32 track, u32 sampleDescription, Bool *is_xml, const char **mime_or_namespace, const char **content_encoding, const char **schema_loc) +{ + GF_TrackBox *trak; + GF_MetaDataSampleEntryBox *ptr; + trak = gf_isom_get_track_from_file(file, track); + if (!trak || !sampleDescription) return GF_BAD_PARAM; + ptr = (GF_MetaDataSampleEntryBox*)gf_list_get(trak->Media->information->sampleTable->SampleDescription->boxList, sampleDescription-1); + if (!ptr) return GF_BAD_PARAM; + + if (ptr->type==GF_ISOM_BOX_TYPE_METX) { + if (is_xml) *is_xml = 1; + if (schema_loc) *schema_loc = ptr->xml_schema_loc; + } else { + if (schema_loc) *schema_loc = NULL; + } + if (mime_or_namespace) *mime_or_namespace = ptr->mime_type_or_namespace; + if (content_encoding) *content_encoding = ptr->content_encoding; + return GF_OK; +} + +u32 gf_isom_get_next_alternate_group_id(GF_ISOFile *movie) +{ + u32 id = 0; + u32 i=0; + + while (i< gf_isom_get_track_count(movie) ) { + GF_TrackBox *trak = gf_isom_get_track_from_file(movie, i+1); + if (trak->Header->alternate_group > id) + id = trak->Header->alternate_group; + i++; + } + return id+1; +} + + +u32 gf_isom_sample_has_subsamples(GF_ISOFile *movie, u32 track, u32 sampleNumber) +{ + return gf_isom_sample_get_subsample_entry(movie, track, sampleNumber, NULL); +} + +GF_Err gf_isom_sample_get_subsample(GF_ISOFile *movie, u32 track, u32 sampleNumber, u32 subSampleNumber, u32 *size, u8 *priority, u32 *reserved, Bool *discardable) +{ + GF_SubSampleEntry *entry; + GF_SampleEntry *sub_sample; + u32 count = gf_isom_sample_get_subsample_entry(movie, track, sampleNumber, &sub_sample); + if (!size || !priority || !discardable) return GF_BAD_PARAM; + + if (!subSampleNumber || (subSampleNumber>count)) return GF_BAD_PARAM; + entry = (GF_SubSampleEntry *) gf_list_get(sub_sample->SubSamples, subSampleNumber-1); + *size = entry->subsample_size; + *priority = entry->subsample_priority; + *reserved = entry->reserved; + *discardable = entry->discardable ? 1 : 0; + return GF_OK; +} + +GF_Err gf_isom_get_rvc_config(GF_ISOFile *movie, u32 track, u32 sampleDescriptionIndex, u16 *rvc_predefined, char **data, u32 *size, const char **mime) +{ + GF_MPEGVisualSampleEntryBox *entry; + GF_TrackBox *trak; + + if (!rvc_predefined || !data || !size) return GF_BAD_PARAM; + *rvc_predefined = 0; + + trak = gf_isom_get_track_from_file(movie, track); + if (!trak) return GF_BAD_PARAM; + + + entry = (GF_MPEGVisualSampleEntryBox *) gf_list_get(trak->Media->information->sampleTable->SampleDescription->boxList, sampleDescriptionIndex-1); + if (!entry ) return GF_BAD_PARAM; + switch (entry->type) { + case GF_ISOM_BOX_TYPE_MP4V: + case GF_ISOM_BOX_TYPE_AVC1: + case GF_ISOM_BOX_TYPE_AVC2: + case GF_ISOM_BOX_TYPE_SVC1: + case GF_ISOM_BOX_TYPE_ENCV: + break; + default: + return GF_BAD_PARAM; + } + if (!entry->rvcc) return GF_BAD_PARAM; + + *rvc_predefined = entry->rvcc->predefined_rvc_config; + if (entry->rvcc->rvc_meta_idx) { + if (!data || !size) return GF_OK; + return gf_isom_extract_meta_item_mem(movie, 0, track, entry->rvcc->rvc_meta_idx, data, size, mime); + } + return GF_OK; +} + +GF_EXPORT +Bool gf_isom_moov_first(GF_ISOFile *movie) +{ + u32 i; + for (i=0; iTopBoxes); i++) { + GF_Box *b = (GF_Box *) gf_list_get(movie->TopBoxes, i); + if (b->type == GF_ISOM_BOX_TYPE_MOOV) return 1; + if (b->type == GF_ISOM_BOX_TYPE_MDAT) return 0; + } + return 0; +} + +#endif /*GPAC_DISABLE_ISOM*/ diff --git a/src/gpacmp4/isom_store.c b/src/gpacmp4/isom_store.c new file mode 100644 index 00000000..2cf9a77c --- /dev/null +++ b/src/gpacmp4/isom_store.c @@ -0,0 +1,1294 @@ + +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / ISO Media File Format sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ +#include "../disable_warnings.h" +#include + +#if !defined(GPAC_DISABLE_ISOM) && !defined(GPAC_DISABLE_ISOM_WRITE) + +#define GPAC_ISOM_CPRT_NOTICE "IsoMedia File Produced with GPAC "GPAC_FULL_VERSION + +static GF_Err gf_isom_insert_copyright(GF_ISOFile *movie) +{ + u32 i; + GF_Box *a; + GF_FreeSpaceBox *_free; + i=0; + while ((a = (GF_Box *)gf_list_enum(movie->TopBoxes, &i))) { + if (a->type == GF_ISOM_BOX_TYPE_FREE) { + _free = (GF_FreeSpaceBox *)a; + if (_free->dataSize) { + if (!strcmp(_free->data, GPAC_ISOM_CPRT_NOTICE)) return GF_OK; + if (strstr(_free->data, "File Produced with GPAC")) { + gf_free(_free->data); + _free->data = gf_strdup(GPAC_ISOM_CPRT_NOTICE); + _free->dataSize = strlen(_free->data); + return GF_OK; + } + } + } + } + a = gf_isom_box_new(GF_ISOM_BOX_TYPE_FREE); + if (!a) return GF_OUT_OF_MEM; + _free = (GF_FreeSpaceBox *)a; + _free->dataSize = strlen(GPAC_ISOM_CPRT_NOTICE) + 1; + _free->data = gf_strdup(GPAC_ISOM_CPRT_NOTICE); + if (!_free->data) return GF_OUT_OF_MEM; + return gf_list_add(movie->TopBoxes, _free); +} + +typedef struct +{ + /*the curent sample of this track*/ + u32 sampleNumber; + /*timeScale of the media (for interleaving)*/ + u32 timeScale; + /*this is for generic, time-based interleaving. Expressed in Media TimeScale*/ + u32 chunkDur; + u64 DTSprev; + u8 isDone; + u64 prev_offset; + GF_MediaBox *mdia; + /*each writer has a sampleToChunck and ChunkOffset tables + these tables are filled during emulation mode and then will replace the table in the GF_SampleTableBox*/ + GF_SampleToChunkBox *stsc; + /*we don't know if it's a large offset or not*/ + GF_Box *stco; +} TrackWriter; + +typedef struct +{ + char *buffer; + u32 size; + GF_ISOFile *movie; + u32 total_samples, nb_done; +} MovieWriter; + +void CleanWriters(GF_List *writers) +{ + TrackWriter *writer; + while (gf_list_count(writers)) { + writer = (TrackWriter*)gf_list_get(writers, 0); + gf_isom_box_del(writer->stco); + gf_isom_box_del((GF_Box *)writer->stsc); + gf_free(writer); + gf_list_rem(writers, 0); + } +} + +void ResetWriters(GF_List *writers) +{ + u32 i; + TrackWriter *writer; + i=0; + while ((writer = (TrackWriter *)gf_list_enum(writers, &i))) { + writer->isDone = 0; + writer->chunkDur = 0; + writer->DTSprev = 0; + writer->sampleNumber = 1; + gf_isom_box_del((GF_Box *)writer->stsc); + writer->stsc = (GF_SampleToChunkBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_STSC); + if (writer->stco->type == GF_ISOM_BOX_TYPE_STCO) { + gf_free(((GF_ChunkOffsetBox *)writer->stco)->offsets); + ((GF_ChunkOffsetBox *)writer->stco)->offsets = NULL; + ((GF_ChunkOffsetBox *)writer->stco)->nb_entries = 0; + ((GF_ChunkOffsetBox *)writer->stco)->alloc_size = 0; + } else { + gf_free(((GF_ChunkLargeOffsetBox *)writer->stco)->offsets); + ((GF_ChunkLargeOffsetBox *)writer->stco)->offsets = NULL; + ((GF_ChunkLargeOffsetBox *)writer->stco)->nb_entries = 0; + ((GF_ChunkLargeOffsetBox *)writer->stco)->alloc_size = 0; + } + } +} + +GF_Err SetupWriters(MovieWriter *mw, GF_List *writers, u8 interleaving) +{ + u32 i, trackCount; + TrackWriter *writer; + GF_TrackBox *trak; + GF_ISOFile *movie = mw->movie; + + mw->total_samples = mw->nb_done = 0; + if (!movie->moov) return GF_OK; + + trackCount = gf_list_count(movie->moov->trackList); + for (i = 0; i < trackCount; i++) { + trak = gf_isom_get_track(movie->moov, i+1); + + GF_SAFEALLOC(writer, TrackWriter); + if (!writer) goto exit; + writer->sampleNumber = 1; + writer->mdia = trak->Media; + writer->timeScale = trak->Media->mediaHeader->timeScale; + writer->isDone = 0; + writer->DTSprev = 0; + writer->chunkDur = 0; + writer->stsc = (GF_SampleToChunkBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_STSC); + if (trak->Media->information->sampleTable->ChunkOffset->type == GF_ISOM_BOX_TYPE_STCO) { + writer->stco = gf_isom_box_new(GF_ISOM_BOX_TYPE_STCO); + } else { + writer->stco = gf_isom_box_new(GF_ISOM_BOX_TYPE_CO64); + } + /*stops from chunk escape*/ + if (interleaving) writer->mdia->information->sampleTable->MaxSamplePerChunk = 0; + /*for progress, assume only one descIndex*/ + if (Media_IsSelfContained(writer->mdia, 1)) mw->total_samples += trak->Media->information->sampleTable->SampleSize->sampleCount; + /*optimization for interleaving: put audio last (this can be overriden by priorities)*/ + if (movie->storageMode != GF_ISOM_STORE_INTERLEAVED) { + gf_list_add(writers, writer); + } else { + if (writer->mdia->information->InfoHeader && writer->mdia->information->InfoHeader->type == GF_ISOM_BOX_TYPE_SMHD) { + gf_list_add(writers, writer); + } else { + gf_list_insert(writers, writer, 0); + } + } + } + return GF_OK; + +exit: + CleanWriters(writers); + return GF_OUT_OF_MEM; +} + + +static void ShiftMetaOffset(GF_MetaBox *meta, u64 offset) +{ + u32 i, count; + if (!meta->item_locations) return; + + count = gf_list_count(meta->item_locations->location_entries); + for (i=0; iitem_locations->location_entries, i); + if (iloc->data_reference_index) continue; + if (!iloc->base_offset) { + GF_ItemExtentEntry *entry = (GF_ItemExtentEntry *)gf_list_get(iloc->extent_entries, 0); + if (entry && !entry->extent_length && !entry->original_extent_offset && (gf_list_count(iloc->extent_entries)==1) ) + continue; + } + + iloc->base_offset += offset; + } +} + +static GF_Err ShiftOffset(GF_ISOFile *file, GF_List *writers, u64 offset) +{ + u32 i, j, k, l, last; + TrackWriter *writer; + GF_StscEntry *ent; + GF_ChunkOffsetBox *stco; + GF_ChunkLargeOffsetBox *co64; + + if (file->meta) ShiftMetaOffset(file->meta, offset); + if (file->moov && file->moov->meta) ShiftMetaOffset(file->moov->meta, offset); + + i=0; + while ((writer = (TrackWriter *)gf_list_enum(writers, &i))) { + if (writer->mdia->mediaTrack->meta) ShiftMetaOffset(writer->mdia->mediaTrack->meta, offset); + + //we have to proceed entry by entry in case a part of the media is not self-contained... + for (j=0; jstsc->nb_entries; j++) { + ent = &writer->stsc->entries[j]; + if (!Media_IsSelfContained(writer->mdia, ent->sampleDescriptionIndex)) continue; + + //OK, get the chunk(s) number(s) and "shift" its (their) offset(s). + if (writer->stco->type == GF_ISOM_BOX_TYPE_STCO) { + stco = (GF_ChunkOffsetBox *) writer->stco; + //be carefull for the last entry, nextChunk is set to 0 in edit mode... + last = ent->nextChunk ? ent->nextChunk : stco->nb_entries + 1; + for (k = ent->firstChunk; k < last; k++) { + + if (stco->offsets[k-1] + offset > 0xFFFFFFFF) { + //too bad, rewrite the table.... + co64 = (GF_ChunkLargeOffsetBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_CO64); + if (!co64) return GF_OUT_OF_MEM; + co64->nb_entries = stco->nb_entries; + co64->offsets = (u64*)gf_malloc(co64->nb_entries * sizeof(u64)); + if (!co64) { + gf_isom_box_del((GF_Box *)co64); + return GF_OUT_OF_MEM; + } + //duplicate the table + for (l = 0; l < co64->nb_entries; l++) { + co64->offsets[l] = (u64) stco->offsets[l]; + if (l + 1 == k) co64->offsets[l] += offset; + } + //and replace our box + gf_isom_box_del(writer->stco); + writer->stco = (GF_Box *)co64; + } else { + stco->offsets[k-1] += (u32) offset; + } + } + } else { + co64 = (GF_ChunkLargeOffsetBox *) writer->stco; + //be carefull for the last entry ... + last = ent->nextChunk ? ent->nextChunk : co64->nb_entries + 1; + for (k = ent->firstChunk; k < last; k++) { + co64->offsets[k-1] += offset; + } + } + } + } + + return GF_OK; + +} + + +//replace the chunk and offset tables... +static GF_Err WriteMoovAndMeta(GF_ISOFile *movie, GF_List *writers, GF_BitStream *bs) +{ + u32 i; + TrackWriter *writer; + GF_Err e; + GF_Box *stco; + GF_SampleToChunkBox *stsc; + + if (movie->meta) { + //write the moov box... + e = gf_isom_box_size((GF_Box *)movie->meta); + if (e) return e; + e = gf_isom_box_write((GF_Box *)movie->meta, bs); + if (e) return e; + } + + if (movie->moov) { + //switch all our tables + i=0; + while ((writer = (TrackWriter*)gf_list_enum(writers, &i))) { + //don't delete them !!! + stsc = writer->mdia->information->sampleTable->SampleToChunk; + stco = writer->mdia->information->sampleTable->ChunkOffset; + writer->mdia->information->sampleTable->SampleToChunk = writer->stsc; + writer->mdia->information->sampleTable->ChunkOffset = writer->stco; + writer->stco = stco; + writer->stsc = stsc; + } + //write the moov box... + e = gf_isom_box_size((GF_Box *)movie->moov); + if (e) return e; + e = gf_isom_box_write((GF_Box *)movie->moov, bs); + //and re-switch our table. We have to do it that way because it is + //needed when the moov is written first + i=0; + while ((writer = (TrackWriter*)gf_list_enum(writers, &i))) { + //don't delete them !!! + stsc = writer->stsc; + stco = writer->stco; + writer->stsc = writer->mdia->information->sampleTable->SampleToChunk; + writer->stco = writer->mdia->information->sampleTable->ChunkOffset; + writer->mdia->information->sampleTable->SampleToChunk = stsc; + writer->mdia->information->sampleTable->ChunkOffset = stco; + } + if (e) return e; + } + return GF_OK; +} + +//compute the size of the moov as it will be written. +u64 GetMoovAndMetaSize(GF_ISOFile *movie, GF_List *writers) +{ + u32 i; + u64 size; + TrackWriter *writer; + + size = 0; + if (movie->moov) { + gf_isom_box_size((GF_Box *)movie->moov); + size = movie->moov->size; + if (size > 0xFFFFFFFF) size += 8; + + i=0; + while ((writer = (TrackWriter*)gf_list_enum(writers, &i))) { + size -= writer->mdia->information->sampleTable->ChunkOffset->size; + size -= writer->mdia->information->sampleTable->SampleToChunk->size; + gf_isom_box_size((GF_Box *)writer->stsc); + gf_isom_box_size(writer->stco); + size += writer->stsc->size; + size += writer->stco->size; + } + } + if (movie->meta) { + u64 msize; + gf_isom_box_size((GF_Box *)movie->meta); + msize = movie->meta->size; + if (msize > 0xFFFFFFFF) msize += 8; + size += msize; + } + return size; +} + +//Write a sample to the file - this is only called for self-contained media +GF_Err WriteSample(MovieWriter *mw, u32 size, u64 offset, u8 isEdited, GF_BitStream *bs) +{ + GF_DataMap *map; + u32 bytes; + + if (size>mw->size) { + mw->buffer = (char*)gf_realloc(mw->buffer, size); + mw->size = size; + } + + if (!mw->buffer) return GF_OUT_OF_MEM; + + if (isEdited) { + map = mw->movie->editFileMap; + } else { + map = mw->movie->movieFileMap; + } + //get the payload... + bytes = gf_isom_datamap_get_data(map, mw->buffer, size, offset); + if (bytes != size) return GF_IO_ERR; + //write it to our stream... + bytes = gf_bs_write_data(bs, mw->buffer, size); + if (bytes != size) return GF_IO_ERR; + + mw->nb_done++; + gf_set_progress("ISO File Writing", mw->nb_done, mw->total_samples); + return GF_OK; +} + + +GF_Err DoWriteMeta(GF_ISOFile *file, GF_MetaBox *meta, GF_BitStream *bs, Bool Emulation, u64 baseOffset, u64 *mdatSize) +{ + GF_ItemExtentEntry *entry; + u64 maxExtendOffset, maxExtendSize; + u32 i, j, count; + + maxExtendOffset = 0; + maxExtendSize = 0; + *mdatSize = 0; + if (!meta->item_locations) return GF_OK; + + count = gf_list_count(meta->item_locations->location_entries); + for (i=0; iitem_locations->location_entries, i); + /*get item info*/ + GF_ItemInfoEntryBox *iinf = NULL; + j=0; + while ((iinf = (GF_ItemInfoEntryBox *)gf_list_enum(meta->item_infos->item_infos, &j))) { + if (iinf->item_ID==iloc->item_ID) break; + iinf = NULL; + } + + if (!iloc->base_offset && (gf_list_count(iloc->extent_entries)==1)) { + entry = (GF_ItemExtentEntry *)gf_list_get(iloc->extent_entries, 0); + if (!entry->extent_length && !entry->original_extent_offset) { + entry->extent_offset = 0; + continue; + } + } + + it_size = 0; + /*for self contained only*/ + if (!iloc->data_reference_index) { + iloc->base_offset = baseOffset; + + /*new resource*/ + if (iinf->full_path) { + FILE *src=NULL; + + if (!iinf->data_len) { + src = gf_f64_open(iinf->full_path, "rb"); + if (!src) continue; + gf_f64_seek(src, 0, SEEK_END); + it_size = gf_f64_tell(src); + gf_f64_seek(src, 0, SEEK_SET); + } else { + it_size = iinf->data_len; + } + if (maxExtendSizeextent_entries)) { + GF_SAFEALLOC(entry, GF_ItemExtentEntry); + gf_list_add(iloc->extent_entries, entry); + } + entry = (GF_ItemExtentEntry *)gf_list_get(iloc->extent_entries, 0); + entry->extent_offset = 0; + entry->extent_length = it_size; + + /*OK write to mdat*/ + if (!Emulation) { + if (src) { + char cache_data[4096]; + u64 remain = entry->extent_length; + while (remain) { + u32 size_cache = (remain>4096) ? 4096 : (u32) remain; + size_cache = fread(cache_data, sizeof(char), size_cache, src); + gf_bs_write_data(bs, cache_data, size_cache); + remain -= size_cache; + } + } else { + gf_bs_write_data(bs, iinf->full_path, iinf->data_len); + } + } + if (src) fclose(src); + } + else if (gf_list_count(iloc->extent_entries)) { + u32 j; + j=0; + while ((entry = (GF_ItemExtentEntry *)gf_list_enum(iloc->extent_entries, &j))) { + if (j && (maxExtendOffsetextent_offset = baseOffset + it_size; + + it_size += entry->extent_length; + if (maxExtendSizeextent_length) maxExtendSize = entry->extent_length; + + /*Reading from the input file*/ + if (!Emulation) { + char cache_data[4096]; + u64 remain = entry->extent_length; + gf_bs_seek(file->movieFileMap->bs, entry->original_extent_offset + iloc->original_base_offset); + while (remain) { + u32 size_cache = (remain>4096) ? 4096 : (u32) remain; + gf_bs_read_data(file->movieFileMap->bs, cache_data, size_cache); + /*Writing to the output file*/ + gf_bs_write_data(bs, cache_data, size_cache); + remain -= size_cache; + } + } + } + } + baseOffset += it_size; + *mdatSize += it_size; + } else { + /*we MUST have at least one extent for the dref data*/ + if (!gf_list_count(iloc->extent_entries)) { + GF_SAFEALLOC(entry, GF_ItemExtentEntry); + gf_list_add(iloc->extent_entries, entry); + } + entry = (GF_ItemExtentEntry *)gf_list_get(iloc->extent_entries, 0); + entry->extent_offset = 0; + /*0 means full length of referenced file*/ + entry->extent_length = 0; + } + } + /*update offset & size length fields*/ + if (baseOffset>0xFFFFFFFF) meta->item_locations->base_offset_size = 8; + else if (baseOffset) meta->item_locations->base_offset_size = 4; + + if (maxExtendSize>0xFFFFFFFF) meta->item_locations->length_size = 8; + else if (maxExtendSize) meta->item_locations->length_size = 4; + + if (maxExtendOffset>0xFFFFFFFF) meta->item_locations->offset_size = 8; + else if (maxExtendOffset) meta->item_locations->offset_size = 4; + return GF_OK; +} + +//this function writes track by track in the order of tracks inside the moov... +GF_Err DoWrite(MovieWriter *mw, GF_List *writers, GF_BitStream *bs, u8 Emulation, u64 StartOffset) +{ + u32 i; + GF_Err e; + TrackWriter *writer; + u64 offset, sampOffset, predOffset; + u32 chunkNumber, descIndex, sampSize; + u8 isEdited, force; + u64 size, mdatSize = 0; + GF_ISOFile *movie = mw->movie; + + /*write meta content first - WE DON'T support fragmentation of resources in ISOM atm*/ + if (movie->openMode != GF_ISOM_OPEN_WRITE) { + if (movie->meta) { + e = DoWriteMeta(movie, movie->meta, bs, Emulation, StartOffset, &size); + if (e) return e; + mdatSize += size; + StartOffset += size; + } + if (movie->moov && movie->moov->meta) { + e = DoWriteMeta(movie, movie->meta, bs, Emulation, StartOffset, &size); + if (e) return e; + mdatSize += size; + StartOffset += size; + } + i=0; + while ((writer = (TrackWriter*)gf_list_enum(writers, &i))) { + if (writer->mdia->mediaTrack->meta) { + e = DoWriteMeta(movie, movie->meta, bs, Emulation, StartOffset, &size); + if (e) return e; + mdatSize += size; + StartOffset += size; + } + } + } + + offset = StartOffset; + predOffset = 0; + i=0; + while ((writer = (TrackWriter*)gf_list_enum(writers, &i))) { + while (!writer->isDone) { + //To Check: are empty sample tables allowed ??? + if (writer->sampleNumber > writer->mdia->information->sampleTable->SampleSize->sampleCount) { + writer->isDone = 1; + continue; + } + e = stbl_GetSampleInfos(writer->mdia->information->sampleTable, writer->sampleNumber, &sampOffset, &chunkNumber, &descIndex, &isEdited); + if (e) return e; + e = stbl_GetSampleSize(writer->mdia->information->sampleTable->SampleSize, writer->sampleNumber, &sampSize); + if (e) return e; + + //update our chunks. + force = 0; + if (movie->openMode == GF_ISOM_OPEN_WRITE) { + offset = sampOffset; + if (predOffset != offset) + force = 1; + } + //update our global offset... + if (Media_IsSelfContained(writer->mdia, descIndex) ) { + e = stbl_SetChunkAndOffset(writer->mdia->information->sampleTable, writer->sampleNumber, descIndex, writer->stsc, &writer->stco, offset, force); + if (e) return e; + if (movie->openMode == GF_ISOM_OPEN_WRITE) { + predOffset = sampOffset + sampSize; + } else { + offset += sampSize; + mdatSize += sampSize; + } + } else { + if (predOffset != offset) force = 1; + predOffset = sampOffset + sampSize; + //we have a DataRef, so use the offset idicated in sampleToChunk and ChunkOffset tables... + e = stbl_SetChunkAndOffset(writer->mdia->information->sampleTable, writer->sampleNumber, descIndex, writer->stsc, &writer->stco, sampOffset, force); + if (e) return e; + } + //we write the sample if not emulation + if (!Emulation) { + if (Media_IsSelfContained(writer->mdia, descIndex) ) { + e = WriteSample(mw, sampSize, sampOffset, isEdited, bs); + if (e) return e; + } + } + //ok, the track is done + if (writer->sampleNumber == writer->mdia->information->sampleTable->SampleSize->sampleCount) { + writer->isDone = 1; + } else { + writer->sampleNumber ++; + } + } + } + //set the mdatSize... + movie->mdat->dataSize = mdatSize; + return GF_OK; +} + + +//write the file track by track, with moov box before or after the mdat +GF_Err WriteFlat(MovieWriter *mw, u8 moovFirst, GF_BitStream *bs) +{ + GF_Err e; + u32 i; + u64 offset, finalOffset, totSize, begin, firstSize, finalSize; + GF_Box *a; + GF_List *writers = gf_list_new(); + GF_ISOFile *movie = mw->movie; + + begin = totSize = 0; + + //first setup the writers + e = SetupWriters(mw, writers, 0); + if (e) goto exit; + + if (!moovFirst) { + if (movie->openMode == GF_ISOM_OPEN_WRITE) { + begin = 0; + totSize = gf_isom_datamap_get_offset(movie->editFileMap); + /*start boxes have not been written yet, do it*/ + if (!totSize) { + if (movie->is_jp2) { + gf_bs_write_u32(movie->editFileMap->bs, 12); + gf_bs_write_u32(movie->editFileMap->bs, GF_4CC('j','P',' ',' ')); + gf_bs_write_u32(movie->editFileMap->bs, 0x0D0A870A); + totSize += 12; + begin += 12; + } + if (movie->brand) { + e = gf_isom_box_size((GF_Box *)movie->brand); if (e) goto exit; + e = gf_isom_box_write((GF_Box *)movie->brand, movie->editFileMap->bs); if (e) goto exit; + totSize += movie->brand->size; + begin += movie->brand->size; + } + if (movie->pdin) { + e = gf_isom_box_size((GF_Box *)movie->pdin); if (e) goto exit; + e = gf_isom_box_write((GF_Box *)movie->pdin, movie->editFileMap->bs); if (e) goto exit; + totSize += movie->pdin->size; + begin += movie->pdin->size; + } + } else { + if (movie->is_jp2) begin += 12; + if (movie->brand) begin += movie->brand->size; + if (movie->pdin) begin += movie->pdin->size; + } + totSize -= begin; + } else { + if (movie->is_jp2) { + gf_bs_write_u32(bs, 12); + gf_bs_write_u32(bs, GF_4CC('j','P',' ',' ')); + gf_bs_write_u32(bs, 0x0D0A870A); + } + if (movie->brand) { + e = gf_isom_box_size((GF_Box *)movie->brand); + if (e) goto exit; + e = gf_isom_box_write((GF_Box *)movie->brand, bs); + if (e) goto exit; + } + /*then progressive download*/ + if (movie->pdin) { + e = gf_isom_box_size((GF_Box *)movie->pdin); + if (e) goto exit; + e = gf_isom_box_write((GF_Box *)movie->pdin, bs); + if (e) goto exit; + } + } + + //if the moov is at the end, write directly + i=0; + while ((a = (GF_Box*)gf_list_enum(movie->TopBoxes, &i))) { + switch (a->type) { + /*written by hand*/ + case GF_ISOM_BOX_TYPE_MOOV: + case GF_ISOM_BOX_TYPE_META: + case GF_ISOM_BOX_TYPE_FTYP: + case GF_ISOM_BOX_TYPE_PDIN: + break; + case GF_ISOM_BOX_TYPE_MDAT: + //in case we're capturing + if (movie->openMode == GF_ISOM_OPEN_WRITE) { + //emulate a write to recreate our tables (media data already written) + e = DoWrite(mw, writers, bs, 1, begin); + if (e) goto exit; + continue; + } + //to avoid computing the size each time write always 4 + 4 + 8 bytes before + begin = gf_bs_get_position(bs); + gf_bs_write_u64(bs, 0); + gf_bs_write_u64(bs, 0); + e = DoWrite(mw, writers, bs, 0, gf_bs_get_position(bs)); + if (e) goto exit; + totSize = gf_bs_get_position(bs) - begin; + break; + default: + e = gf_isom_box_size(a); + if (e) goto exit; + e = gf_isom_box_write(a, bs); + if (e) goto exit; + break; + } + } + + //OK, write the movie box. + e = WriteMoovAndMeta(movie, writers, bs); + if (e) goto exit; + + /*if data has been written, update mdat size*/ + if (totSize) { + offset = gf_bs_get_position(bs); + e = gf_bs_seek(bs, begin); + if (e) goto exit; + if (totSize > 0xFFFFFFFF) { + gf_bs_write_u32(bs, 1); + } else { + gf_bs_write_u32(bs, (u32) totSize); + } + gf_bs_write_u32(bs, GF_ISOM_BOX_TYPE_MDAT); + if (totSize > 0xFFFFFFFF) gf_bs_write_u64(bs, totSize); + e = gf_bs_seek(bs, offset); + } + movie->mdat->size = totSize; + goto exit; + } + + //nope, we have to write the moov first. The pb is that + //1 - we don't know its size till the mdat is written + //2 - we don't know the ofset at which the mdat will start... + //3 - once the mdat is written, the chunkOffset table can have changed... + + if (movie->is_jp2) { + gf_bs_write_u32(bs, 12); + gf_bs_write_u32(bs, GF_4CC('j','P',' ',' ')); + gf_bs_write_u32(bs, 0x0D0A870A); + } + if (movie->brand) { + e = gf_isom_box_size((GF_Box *)movie->brand); + if (e) goto exit; + e = gf_isom_box_write((GF_Box *)movie->brand, bs); + if (e) goto exit; + } + /*then progressive dnload*/ + if (movie->pdin) { + e = gf_isom_box_size((GF_Box *)movie->pdin); + if (e) goto exit; + e = gf_isom_box_write((GF_Box *)movie->pdin, bs); + if (e) goto exit; + } + //What we will do is first emulate the write from the begining... + //note: this will set the size of the mdat + e = DoWrite(mw, writers, bs, 1, gf_bs_get_position(bs)); + if (e) goto exit; + + firstSize = GetMoovAndMetaSize(movie, writers); + //offset = (firstSize > 0xFFFFFFFF ? firstSize + 8 : firstSize) + 8 + (movie->mdat->dataSize > 0xFFFFFFFF ? 8 : 0); + offset = firstSize + 8 + (movie->mdat->dataSize > 0xFFFFFFFF ? 8 : 0); + e = ShiftOffset(movie, writers, offset); + if (e) goto exit; + //get the size and see if it has changed (eg, we moved to 64 bit offsets) + finalSize = GetMoovAndMetaSize(movie, writers); + if (firstSize != finalSize) { + //we need to remove our offsets + ResetWriters(writers); + //finalOffset = (finalSize > 0xFFFFFFFF ? finalSize + 8 : finalSize) + 8 + (movie->mdat->dataSize > 0xFFFFFFFF ? 8 : 0); + finalOffset = finalSize + 8 + (movie->mdat->dataSize > 0xFFFFFFFF ? 8 : 0); + //OK, now we're sure about the final size. + //we don't need to re-emulate, as the only thing that changed is the offset + //so just shift the offset + e = ShiftOffset(movie, writers, finalOffset - offset); + if (e) goto exit; + } + //now write our stuff + e = WriteMoovAndMeta(movie, writers, bs); + if (e) goto exit; + e = gf_isom_box_size((GF_Box *)movie->mdat); + if (e) goto exit; + e = gf_isom_box_write((GF_Box *)movie->mdat, bs); + if (e) goto exit; + + //we don't need the offset as the moov is already written... + ResetWriters(writers); + e = DoWrite(mw, writers, bs, 0, 0); + if (e) goto exit; + //then the rest + i=0; + while ((a = (GF_Box*)gf_list_enum(movie->TopBoxes, &i))) { + switch (a->type) { + case GF_ISOM_BOX_TYPE_MOOV: + case GF_ISOM_BOX_TYPE_META: + case GF_ISOM_BOX_TYPE_FTYP: + case GF_ISOM_BOX_TYPE_PDIN: + case GF_ISOM_BOX_TYPE_MDAT: + break; + default: + e = gf_isom_box_size(a); + if (e) goto exit; + e = gf_isom_box_write(a, bs); + if (e) goto exit; + } + } + +exit: + CleanWriters(writers); + gf_list_del(writers); + return e; +} + +GF_Err DoFullInterleave(MovieWriter *mw, GF_List *writers, GF_BitStream *bs, u8 Emulation, u64 StartOffset) +{ + + u32 i, tracksDone; + TrackWriter *tmp, *curWriter, *prevWriter; + GF_Err e; + u64 DTS, DTStmp, TStmp; + s64 res; + u32 descIndex, sampSize, chunkNumber; + u16 curGroupID, curTrackPriority; + u8 forceNewChunk, writeGroup, isEdited; + //this is used to emulate the write ... + u64 offset, totSize, sampOffset; + GF_ISOFile *movie = mw->movie; + e = GF_OK; + + + totSize = 0; + curGroupID = 1; + + prevWriter = NULL; + //we emulate a write from this offset... + offset = StartOffset; + writeGroup = 1; + tracksDone = 0; + + + //browse each groups + while (1) { + writeGroup = 1; + + //proceed a group + while (writeGroup) { + //first get the appropriated sample for the min time in this group + curWriter = NULL; + DTStmp = (u64) -1; + TStmp = 0; + curTrackPriority = (u16) -1; + + i=0; + while ((tmp = (TrackWriter*)gf_list_enum(writers, &i))) { + + //is it done writing ? + //is it in our group ?? + if (tmp->isDone || tmp->mdia->information->sampleTable->groupID != curGroupID) continue; + + //OK, get the current sample in this track + stbl_GetSampleDTS(tmp->mdia->information->sampleTable->TimeToSample, tmp->sampleNumber, &DTS); + res = TStmp ? DTStmp * tmp->timeScale - DTS * TStmp : 0; + if (res < 0) continue; + if ((!res) && curTrackPriority <= tmp->mdia->information->sampleTable->trackPriority) continue; + curWriter = tmp; + curTrackPriority = tmp->mdia->information->sampleTable->trackPriority; + DTStmp = DTS; + TStmp = tmp->timeScale; + } + //no sample found, we're done with this group + if (!curWriter) { + //we're done with the group + curTrackPriority = 0; + writeGroup = 0; + continue; + } + //To Check: are empty sample tables allowed ??? + if (curWriter->sampleNumber > curWriter->mdia->information->sampleTable->SampleSize->sampleCount) { + curWriter->isDone = 1; + tracksDone ++; + continue; + } + + e = stbl_GetSampleInfos(curWriter->mdia->information->sampleTable, curWriter->sampleNumber, &sampOffset, &chunkNumber, &descIndex, &isEdited); + if (e) return e; + e = stbl_GetSampleSize(curWriter->mdia->information->sampleTable->SampleSize, curWriter->sampleNumber, &sampSize); + if (e) return e; + + //do we actually write, or do we emulate ? + if (Emulation) { + //are we in the same track ??? If not, force a new chunk when adding this sample + if (curWriter != prevWriter) { + forceNewChunk = 1; + } else { + forceNewChunk = 0; + } + //update our offsets... + if (Media_IsSelfContained(curWriter->mdia, descIndex) ) { + e = stbl_SetChunkAndOffset(curWriter->mdia->information->sampleTable, curWriter->sampleNumber, descIndex, curWriter->stsc, &curWriter->stco, offset, forceNewChunk); + if (e) return e; + offset += sampSize; + totSize += sampSize; + } else { + if (curWriter->prev_offset != sampOffset) forceNewChunk = 1; + curWriter->prev_offset = sampOffset + sampSize; + + //we have a DataRef, so use the offset idicated in sampleToChunk + //and ChunkOffset tables... + e = stbl_SetChunkAndOffset(curWriter->mdia->information->sampleTable, curWriter->sampleNumber, descIndex, curWriter->stsc, &curWriter->stco, sampOffset, 0); + if (e) return e; + } + } else { + //this is no game, we're writing .... + if (Media_IsSelfContained(curWriter->mdia, descIndex) ) { + e = WriteSample(mw, sampSize, sampOffset, isEdited, bs); + if (e) return e; + } + } + //ok, the sample is done + if (curWriter->sampleNumber == curWriter->mdia->information->sampleTable->SampleSize->sampleCount) { + curWriter->isDone = 1; + //one more track done... + tracksDone ++; + } else { + curWriter->sampleNumber ++; + } + prevWriter = curWriter; + } + //if all our track are done, break + if (tracksDone == gf_list_count(writers)) break; + //go to next group + curGroupID ++; + } + movie->mdat->dataSize = totSize; + return GF_OK; +} + +/*uncomment the following to easily test large file generation. This will prepend 4096*1MByte of 0 before the media data*/ +//#define TEST_LARGE_FILES + +GF_Err DoInterleave(MovieWriter *mw, GF_List *writers, GF_BitStream *bs, u8 Emulation, u64 StartOffset, Bool drift_inter) +{ + u32 i, tracksDone; + TrackWriter *tmp, *curWriter; + GF_Err e; + u32 descIndex, sampSize, chunkNumber; + u64 DTS; + u16 curGroupID; + u8 forceNewChunk, writeGroup, isEdited; + //this is used to emulate the write ... + u64 offset, sampOffset, size, mdatSize; + u32 count; + GF_ISOFile *movie = mw->movie; + + mdatSize = 0; + +#ifdef TEST_LARGE_FILES + if (!Emulation) { + char *blank; + u32 count, i; + i = count = 0; + blank = gf_malloc(sizeof(char)*1024*1024); + memset(blank, 0, sizeof(char)*1024*1024); + count = 4096; + memset(blank, 0, sizeof(char)*1024*1024); + while (imeta) { + e = DoWriteMeta(movie, movie->meta, bs, Emulation, StartOffset, &size); + if (e) return e; + mdatSize += size; + StartOffset += (u32) size; + } + if (movie->moov && movie->moov->meta) { + e = DoWriteMeta(movie, movie->moov->meta, bs, Emulation, StartOffset, &size); + if (e) return e; + mdatSize += size; + StartOffset += (u32) size; + } + i=0; + while ((tmp = (TrackWriter*)gf_list_enum(writers, &i))) { + if (tmp->mdia->mediaTrack->meta) { + e = DoWriteMeta(movie, tmp->mdia->mediaTrack->meta, bs, Emulation, StartOffset, &size); + if (e) return e; + mdatSize += size; + StartOffset += (u32) size; + } + } + + + + if (movie->storageMode == GF_ISOM_STORE_TIGHT) + return DoFullInterleave(mw, writers, bs, Emulation, StartOffset); + + e = GF_OK; + + curGroupID = 1; + //we emulate a write from this offset... + offset = StartOffset; + writeGroup = 1; + tracksDone = 0; + +#ifdef TEST_LARGE_FILES + offset += mdatSize; +#endif + + count = gf_list_count(writers); + //browse each groups + while (1) { + /*the max DTS the chunk of the current writer*/ + u64 chunkLastDTS = 0; + /*the timescale related to the max DTS*/ + u32 chunkLastScale = 0; + + writeGroup = 1; + + //proceed a group + while (writeGroup) { + curWriter = NULL; + for (i=0 ; i < count; i++) { + tmp = (TrackWriter*)gf_list_get(writers, i); + + //is it done writing ? + if (tmp->isDone) continue; + + //is it in our group ?? + if (tmp->mdia->information->sampleTable->groupID != curGroupID) continue; + + //write till this chunk is full on this track... + while (1) { + //To Check: are empty sample tables allowed ??? + if (tmp->sampleNumber > tmp->mdia->information->sampleTable->SampleSize->sampleCount) { + tmp->isDone = 1; + tracksDone ++; + break; + } + + //OK, get the current sample in this track + stbl_GetSampleDTS(tmp->mdia->information->sampleTable->TimeToSample, tmp->sampleNumber, &DTS); + + //can this sample fit in our chunk ? + if ( ( (DTS - tmp->DTSprev) + tmp->chunkDur) * movie->moov->mvhd->timeScale > movie->interleavingTime * tmp->timeScale + /*drfit check: reject sample if outside our check window*/ + || (drift_inter && chunkLastDTS && ( ((u64)tmp->DTSprev*chunkLastScale) > ((u64)chunkLastDTS*tmp->timeScale)) ) + ) { + //in case the sample is longer than InterleaveTime + if (!tmp->chunkDur) { + forceNewChunk = 1; + } else { + //this one is full. go to next one (exit the loop) + tmp->chunkDur = 0; + break; + } + } else { + forceNewChunk = tmp->chunkDur ? 0 : 1; + } + //OK, we can write this track + curWriter = tmp; + + //small check for first 2 samples (DTS = 0 :) + if (tmp->sampleNumber == 2 && !tmp->chunkDur) forceNewChunk = 0; + + tmp->chunkDur += (u32) (DTS - tmp->DTSprev); + tmp->DTSprev = DTS; + + e = stbl_GetSampleInfos(curWriter->mdia->information->sampleTable, curWriter->sampleNumber, &sampOffset, &chunkNumber, &descIndex, &isEdited); + if (e) return e; + e = stbl_GetSampleSize(curWriter->mdia->information->sampleTable->SampleSize, curWriter->sampleNumber, &sampSize); + if (e) return e; + + //do we actually write, or do we emulate ? + if (Emulation) { + //update our offsets... + if (Media_IsSelfContained(curWriter->mdia, descIndex) ) { + e = stbl_SetChunkAndOffset(curWriter->mdia->information->sampleTable, curWriter->sampleNumber, descIndex, curWriter->stsc, &curWriter->stco, offset, forceNewChunk); + if (e) return e; + offset += sampSize; + mdatSize += sampSize; + } else { + if (curWriter->prev_offset != sampOffset) forceNewChunk = 1; + curWriter->prev_offset = sampOffset + sampSize; + + //we have a DataRef, so use the offset idicated in sampleToChunk + //and ChunkOffset tables... + e = stbl_SetChunkAndOffset(curWriter->mdia->information->sampleTable, curWriter->sampleNumber, descIndex, curWriter->stsc, &curWriter->stco, sampOffset, forceNewChunk); + if (e) return e; + } + } else { + //this is no game, we're writing .... + if (Media_IsSelfContained(curWriter->mdia, descIndex) ) { + e = WriteSample(mw, sampSize, sampOffset, isEdited, bs); + if (e) return e; + } + } + //ok, the sample is done + if (curWriter->sampleNumber == curWriter->mdia->information->sampleTable->SampleSize->sampleCount) { + curWriter->isDone = 1; + //one more track done... + tracksDone ++; + break; + } else { + curWriter->sampleNumber ++; + } + } + /*record chunk end-time & track timescale for drift-controled interleaving*/ + if (drift_inter && curWriter) { + chunkLastScale = curWriter->timeScale; + chunkLastDTS = curWriter->DTSprev; + /*add one interleave window drift - since the "maxDTS" is the previously written one, we will + have the following cases: + - sample doesn't fit: post-pone and force new chunk + - sample time larger than previous chunk time + interleave: post-pone and force new chunk + - otherwise store and track becomes current reference + + this ensures a proper drift regulation (max DTS diff is less than the interleaving window) + */ + chunkLastDTS += curWriter->timeScale * movie->interleavingTime / movie->moov->mvhd->timeScale; + } + } + //no sample found, we're done with this group + if (!curWriter) { + writeGroup = 0; + continue; + } + } + //if all our track are done, break + if (tracksDone == gf_list_count(writers)) break; + //go to next group + curGroupID ++; + } + if (movie->mdat) movie->mdat->dataSize = mdatSize; + return GF_OK; +} + + +static GF_Err WriteInterleaved(MovieWriter *mw, GF_BitStream *bs, Bool drift_inter) +{ + GF_Err e; + u32 i; + GF_Box *a; + u64 firstSize, finalSize, offset, finalOffset; + GF_List *writers = gf_list_new(); + GF_ISOFile *movie = mw->movie; + + //first setup the writers + e = SetupWriters(mw, writers, 1); + if (e) goto exit; + + + if (movie->is_jp2) { + gf_bs_write_u32(bs, 12); + gf_bs_write_u32(bs, GF_4CC('j','P',' ',' ')); + gf_bs_write_u32(bs, 0x0D0A870A); + } + if (movie->brand) { + e = gf_isom_box_size((GF_Box *)movie->brand); + if (e) goto exit; + e = gf_isom_box_write((GF_Box *)movie->brand, bs); + if (e) goto exit; + } + if (movie->pdin) { + e = gf_isom_box_size((GF_Box *)movie->pdin); + if (e) goto exit; + e = gf_isom_box_write((GF_Box *)movie->pdin, bs); + if (e) goto exit; + } + + e = DoInterleave(mw, writers, bs, 1, gf_bs_get_position(bs), drift_inter); + if (e) goto exit; + + firstSize = GetMoovAndMetaSize(movie, writers); + offset = firstSize; + if (movie->mdat && movie->mdat->dataSize) offset += 8 + (movie->mdat->dataSize > 0xFFFFFFFF ? 8 : 0); + e = ShiftOffset(movie, writers, offset); + if (e) goto exit; + //get the size and see if it has changed (eg, we moved to 64 bit offsets) + finalSize = GetMoovAndMetaSize(movie, writers); + if (firstSize != finalSize) { + //we need to remove our offsets + ResetWriters(writers); + finalOffset = finalSize; + if (movie->mdat->dataSize) finalOffset += 8 + (movie->mdat->dataSize > 0xFFFFFFFF ? 8 : 0); + //OK, now we're sure about the final size -> shift the offsets + //we don't need to re-emulate, as the only thing that changed is the offset + //so just shift the offset + e = ShiftOffset(movie, writers, finalOffset - offset); + if (e) goto exit; + firstSize = GetMoovAndMetaSize(movie, writers); + } + //now write our stuff + e = WriteMoovAndMeta(movie, writers, bs); + if (e) goto exit; + + /*we have 8 extra bytes for large size (not computed in gf_isom_box_size) */ + if (movie->mdat && movie->mdat->dataSize) { + if (movie->mdat->dataSize > 0xFFFFFFFF) movie->mdat->dataSize += 8; + e = gf_isom_box_size((GF_Box *)movie->mdat); + if (e) goto exit; + e = gf_isom_box_write((GF_Box *)movie->mdat, bs); + if (e) goto exit; + } + + //we don't need the offset as we are writing... + ResetWriters(writers); + e = DoInterleave(mw, writers, bs, 0, 0, drift_inter); + if (e) goto exit; + + //then the rest + i=0; + while ((a = (GF_Box*)gf_list_enum(movie->TopBoxes, &i))) { + switch (a->type) { + case GF_ISOM_BOX_TYPE_MOOV: + case GF_ISOM_BOX_TYPE_META: + case GF_ISOM_BOX_TYPE_FTYP: + case GF_ISOM_BOX_TYPE_PDIN: + case GF_ISOM_BOX_TYPE_MDAT: + break; + default: + e = gf_isom_box_size(a); + if (e) goto exit; + e = gf_isom_box_write(a, bs); + if (e) goto exit; + } + } + +exit: + CleanWriters(writers); + gf_list_del(writers); + return e; +} + + +GF_Err WriteToFile(GF_ISOFile *movie) +{ + FILE *stream; + GF_BitStream *bs; + MovieWriter mw; + GF_Err e = GF_OK; + if (!movie) return GF_BAD_PARAM; + + if (movie->openMode == GF_ISOM_OPEN_READ) return GF_BAD_PARAM; + + e = gf_isom_insert_copyright(movie); + if (e) return e; + + memset(&mw, 0, sizeof(mw)); + mw.movie = movie; + + //capture mode: we don't need a new bitstream + if (movie->openMode == GF_ISOM_OPEN_WRITE) { + e = WriteFlat(&mw, 0, movie->editFileMap->bs); + } else { + //OK, we need a new bitstream + stream = gf_f64_open(movie->finalName, "w+b"); + if (!stream) return GF_IO_ERR; + bs = gf_bs_from_file(stream, GF_BITSTREAM_WRITE); + if (!bs) { + fclose(stream); + return GF_OUT_OF_MEM; + } + + switch (movie->storageMode) { + case GF_ISOM_STORE_TIGHT: + case GF_ISOM_STORE_INTERLEAVED: + e = WriteInterleaved(&mw, bs, 0); + break; + case GF_ISOM_STORE_DRIFT_INTERLEAVED: + e = WriteInterleaved(&mw, bs, 1); + break; + case GF_ISOM_STORE_STREAMABLE: + e = WriteFlat(&mw, 1, bs); + break; + default: + e = WriteFlat(&mw, 0, bs); + break; + } + + gf_bs_del(bs); + fclose(stream); + } + if (mw.buffer) gf_free(mw.buffer); + if (mw.nb_done + +#if !defined(GPAC_DISABLE_ISOM) && !defined(GPAC_DISABLE_ISOM_WRITE) + +GF_Err CanAccessMovie(GF_ISOFile *movie, u32 Mode) +{ + if (!movie) return GF_BAD_PARAM; + if (movie->openMode < Mode) return GF_ISOM_INVALID_MODE; + +#ifndef GPAC_DISABLE_ISOM_FRAGMENTS + if (movie->FragmentsFlags & GF_ISOM_FRAG_WRITE_READY) return GF_ISOM_INVALID_MODE; +#endif + return GF_OK; +} + +static GF_Err unpack_track(GF_TrackBox *trak) +{ + GF_Err e = GF_OK; + if (!trak->is_unpacked) { + e = stbl_UnpackOffsets(trak->Media->information->sampleTable); + trak->is_unpacked = 1; + } + return e; +} + + +GF_Err FlushCaptureMode(GF_ISOFile *movie) +{ + GF_Err e; + if (movie->openMode != GF_ISOM_OPEN_WRITE) return GF_OK; + /*make sure nothing was added*/ + if (gf_bs_get_position(movie->editFileMap->bs)) return GF_OK; + + /*add all first boxes*/ + if (movie->brand) { + e = gf_isom_box_size((GF_Box *)movie->brand); + if (e) return e; + e = gf_isom_box_write((GF_Box *)movie->brand, movie->editFileMap->bs); + if (e) return e; + } + if (movie->pdin) { + e = gf_isom_box_size((GF_Box *)movie->pdin); + if (e) return e; + e = gf_isom_box_write((GF_Box *)movie->pdin, movie->editFileMap->bs); + if (e) return e; + } + + /*we have a trick here: the data will be stored on the fly, so the first + thing in the file is the MDAT. As we don't know if we have a large file (>4 GB) or not + do as if we had one and write 16 bytes: 4 (type) + 4 (size) + 8 (largeSize)...*/ + gf_bs_write_int(movie->editFileMap->bs, 0, 128); + return GF_OK; +} + +static GF_Err CheckNoData(GF_ISOFile *movie) +{ + if (movie->openMode != GF_ISOM_OPEN_WRITE) return GF_OK; + if (gf_bs_get_position(movie->editFileMap->bs)) return GF_BAD_PARAM; + return GF_OK; +} + +/************************************************************** + File Writing / Editing +**************************************************************/ +//quick function to add an IOD/OD to the file if not present (iods is optional) +GF_Err AddMovieIOD(GF_MovieBox *moov, u8 isIOD) +{ + GF_Descriptor *od; + GF_ObjectDescriptorBox *iods; + + //do we have an IOD ?? If not, create one. + if (moov->iods) return GF_OK; + + if (isIOD) { + od = gf_odf_desc_new(GF_ODF_ISOM_IOD_TAG); + } else { + od = gf_odf_desc_new(GF_ODF_ISOM_OD_TAG); + } + if (!od) return GF_OUT_OF_MEM; + ((GF_IsomObjectDescriptor *)od)->objectDescriptorID = 1; + + iods = (GF_ObjectDescriptorBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_IODS); + iods->descriptor = od; + return moov_AddBox((GF_Box*)moov, (GF_Box *)iods); +} + +//add a track to the root OD +GF_EXPORT +GF_Err gf_isom_add_track_to_root_od(GF_ISOFile *movie, u32 trackNumber) +{ + GF_Err e; + GF_ES_ID_Inc *inc; + + e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE); + if (e) return e; + gf_isom_insert_moov(movie); + + if (!movie->moov->iods) AddMovieIOD(movie->moov, 0); + + if (gf_isom_is_track_in_root_od(movie, trackNumber) == 1) return GF_OK; + + inc = (GF_ES_ID_Inc *) gf_odf_desc_new(GF_ODF_ESD_INC_TAG); + inc->trackID = gf_isom_get_track_id(movie, trackNumber); + if (!inc->trackID) { + gf_odf_desc_del((GF_Descriptor *)inc); + return movie->LastError; + } + if ( (movie->LastError = gf_isom_add_desc_to_root_od(movie, (GF_Descriptor *)inc) ) ) { + return movie->LastError; + } + gf_odf_desc_del((GF_Descriptor *)inc); + return GF_OK; +} + +//remove the root OD +GF_Err gf_isom_remove_root_od(GF_ISOFile *movie) +{ + GF_Err e; + + e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE); + if (e) return e; + if (!movie->moov || !movie->moov->iods) return GF_OK; + gf_isom_box_del((GF_Box *)movie->moov->iods); + movie->moov->iods = NULL; + return GF_OK; +} + +//remove a track to the root OD +GF_Err gf_isom_remove_track_from_root_od(GF_ISOFile *movie, u32 trackNumber) +{ + GF_List *esds; + GF_ES_ID_Inc *inc; + u32 i; + GF_Err e; + + e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE); + if (e) return e; + if (!movie->moov) return GF_OK; + + if (!gf_isom_is_track_in_root_od(movie, trackNumber)) return GF_OK; + + if (!movie->moov->iods) AddMovieIOD(movie->moov, 0); + + switch (movie->moov->iods->descriptor->tag) { + case GF_ODF_ISOM_IOD_TAG: + esds = ((GF_IsomInitialObjectDescriptor *)movie->moov->iods->descriptor)->ES_ID_IncDescriptors; + break; + case GF_ODF_ISOM_OD_TAG: + esds = ((GF_IsomObjectDescriptor *)movie->moov->iods->descriptor)->ES_ID_IncDescriptors; + break; + default: + return GF_ISOM_INVALID_FILE; + } + + //get the desc + i=0; + while ((inc = (GF_ES_ID_Inc*)gf_list_enum(esds, &i))) { + if (inc->trackID == gf_isom_get_track_id(movie, trackNumber)) { + gf_odf_desc_del((GF_Descriptor *)inc); + gf_list_rem(esds, i-1); + break; + } + } + //we don't remove the iod for P&Ls and other potential info + return GF_OK; +} + +GF_EXPORT +GF_Err gf_isom_set_creation_time(GF_ISOFile *movie, u64 time) +{ + if (!movie || !movie->moov) return GF_BAD_PARAM; + movie->moov->mvhd->creationTime = time; + movie->moov->mvhd->modificationTime = time; + return GF_OK; +} + +GF_EXPORT +GF_Err gf_isom_set_track_creation_time(GF_ISOFile *movie,u32 trackNumber, u64 time) +{ + GF_TrackBox *trak; + trak = gf_isom_get_track_from_file(movie, trackNumber); + if (!trak) return GF_BAD_PARAM; + + trak->Header->creationTime = time; + trak->Header->modificationTime = time; + return GF_OK; +} + + +//sets the enable flag of a track +GF_EXPORT +GF_Err gf_isom_set_track_enabled(GF_ISOFile *movie, u32 trackNumber, u8 enableTrack) +{ + GF_Err e; + GF_TrackBox *trak; + + e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE); + if (e) return e; + + trak = gf_isom_get_track_from_file(movie, trackNumber); + if (!trak) return GF_BAD_PARAM; + + if (enableTrack) { + trak->Header->flags |= 1; + } else { + trak->Header->flags &= ~1; + } + return GF_OK; +} + +GF_Err gf_isom_set_media_language(GF_ISOFile *movie, u32 trackNumber, char *three_char_code) +{ + GF_Err e; + GF_TrackBox *trak; + trak = gf_isom_get_track_from_file(movie, trackNumber); + if (!trak) return GF_BAD_PARAM; + e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE); + if (e) return e; + memcpy(trak->Media->mediaHeader->packedLanguage, three_char_code, sizeof(char)*3); + trak->Media->mediaHeader->modificationTime = gf_isom_get_mp4time(); + return GF_OK; +} + +static void gf_isom_set_root_iod(GF_ISOFile *movie) +{ + GF_IsomInitialObjectDescriptor *iod; + GF_IsomObjectDescriptor *od; + + gf_isom_insert_moov(movie); + if (!movie->moov->iods) { + AddMovieIOD(movie->moov, 1); + return; + } + //if OD, switch to IOD + if (movie->moov->iods->descriptor->tag == GF_ODF_ISOM_IOD_TAG) return; + od = (GF_IsomObjectDescriptor *) movie->moov->iods->descriptor; + iod = (GF_IsomInitialObjectDescriptor*)gf_malloc(sizeof(GF_IsomInitialObjectDescriptor)); + memset(iod, 0, sizeof(GF_IsomInitialObjectDescriptor)); + + iod->ES_ID_IncDescriptors = od->ES_ID_IncDescriptors; + od->ES_ID_IncDescriptors = NULL; + //not used in root OD + iod->ES_ID_RefDescriptors = NULL; + iod->extensionDescriptors = od->extensionDescriptors; + od->extensionDescriptors = NULL; + iod->IPMP_Descriptors = od->IPMP_Descriptors; + od->IPMP_Descriptors = NULL; + iod->objectDescriptorID = od->objectDescriptorID; + iod->OCIDescriptors = od->OCIDescriptors; + od->OCIDescriptors = NULL; + iod->tag = GF_ODF_ISOM_IOD_TAG; + iod->URLString = od->URLString; + od->URLString = NULL; + + gf_odf_desc_del((GF_Descriptor *) od); + movie->moov->iods->descriptor = (GF_Descriptor *)iod; +} + +GF_Err gf_isom_add_desc_to_root_od(GF_ISOFile *movie, GF_Descriptor *theDesc) +{ + GF_Err e; + GF_Descriptor *desc, *dupDesc; + + e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE); + if (e) return e; + gf_isom_insert_moov(movie); + + if (!movie->moov->iods) AddMovieIOD(movie->moov, 0); + if (theDesc->tag==GF_ODF_IPMP_TL_TAG) gf_isom_set_root_iod(movie); + + desc = movie->moov->iods->descriptor; + //the type of desc is handled at the OD/IOD level, we'll be notified + //if the desc is not allowed + switch (desc->tag) { + case GF_ODF_ISOM_IOD_TAG: + case GF_ODF_ISOM_OD_TAG: + //duplicate the desc + e = gf_odf_desc_copy(theDesc, &dupDesc); + if (e) return e; + //add it (MUST BE (I)OD level desc) + movie->LastError = gf_odf_desc_add_desc(desc, dupDesc); + if (movie->LastError) gf_odf_desc_del((GF_Descriptor *)dupDesc); + break; + default: + movie->LastError = GF_ISOM_INVALID_FILE; + break; + } + return movie->LastError; +} + + +GF_Err gf_isom_set_timescale(GF_ISOFile *movie, u32 timeScale) +{ + GF_Err e; + e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE); + if (e) return e; + gf_isom_insert_moov(movie); + + movie->moov->mvhd->timeScale = timeScale; + movie->interleavingTime = timeScale; + return GF_OK; +} + +GF_Err gf_isom_set_pl_indication(GF_ISOFile *movie, u8 PL_Code, u8 ProfileLevel) +{ + GF_IsomInitialObjectDescriptor *iod; + GF_Err e; + + e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE); + if (e) return e; + + gf_isom_set_root_iod(movie); + iod = (GF_IsomInitialObjectDescriptor *)movie->moov->iods->descriptor; + + switch (PL_Code) { + case GF_ISOM_PL_AUDIO: + iod->audio_profileAndLevel = ProfileLevel; + break; + case GF_ISOM_PL_GRAPHICS: + iod->graphics_profileAndLevel = ProfileLevel; + break; + case GF_ISOM_PL_OD: + iod->OD_profileAndLevel = ProfileLevel; + break; + case GF_ISOM_PL_SCENE: + iod->scene_profileAndLevel = ProfileLevel; + break; + case GF_ISOM_PL_VISUAL: + iod->visual_profileAndLevel = ProfileLevel; + break; + case GF_ISOM_PL_INLINE: + iod->inlineProfileFlag = ProfileLevel ? 1 : 0; + break; + } + return GF_OK; +} + + +GF_Err gf_isom_set_root_od_id(GF_ISOFile *movie, u32 OD_ID) +{ + GF_Err e; + e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE); + if (e) return e; + + gf_isom_insert_moov(movie); + if (!movie->moov->iods) AddMovieIOD(movie->moov, 0); + + switch (movie->moov->iods->descriptor->tag) { + case GF_ODF_ISOM_OD_TAG: + ((GF_IsomObjectDescriptor *)movie->moov->iods->descriptor)->objectDescriptorID = OD_ID; + break; + case GF_ODF_ISOM_IOD_TAG: + ((GF_IsomInitialObjectDescriptor *)movie->moov->iods->descriptor)->objectDescriptorID = OD_ID; + break; + default: + return GF_ISOM_INVALID_FILE; + } + return GF_OK; +} + +GF_Err gf_isom_set_root_od_url(GF_ISOFile *movie, char *url_string) +{ + GF_Err e; + e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE); + if (e) return e; + gf_isom_insert_moov(movie); + + if (!movie->moov->iods) AddMovieIOD(movie->moov, 0); + switch (movie->moov->iods->descriptor->tag) { + case GF_ODF_ISOM_OD_TAG: + if (((GF_IsomObjectDescriptor *)movie->moov->iods->descriptor)->URLString) gf_free(((GF_IsomObjectDescriptor *)movie->moov->iods->descriptor)->URLString); + ((GF_IsomObjectDescriptor *)movie->moov->iods->descriptor)->URLString = url_string ? gf_strdup(url_string) : NULL; + break; + case GF_ODF_ISOM_IOD_TAG: + if (((GF_IsomInitialObjectDescriptor *)movie->moov->iods->descriptor)->URLString) gf_free(((GF_IsomInitialObjectDescriptor *)movie->moov->iods->descriptor)->URLString); + ((GF_IsomInitialObjectDescriptor *)movie->moov->iods->descriptor)->URLString = url_string ? gf_strdup(url_string) : NULL; + break; + default: + return GF_ISOM_INVALID_FILE; + } + return GF_OK; +} + + + +//creates a new Track. If trackID = 0, the trackID is chosen by the API +//returns the track number or 0 if error +GF_EXPORT +u32 gf_isom_new_track(GF_ISOFile *movie, u32 trakID, u32 MediaType, u32 TimeScale) +{ + GF_Err e; + u64 now; + u8 isHint; + GF_TrackBox *trak; + GF_TrackHeaderBox *tkhd; + GF_MediaBox *mdia; + + e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE); + if (e) { + gf_isom_set_last_error(movie, e); + return 0; + } + gf_isom_insert_moov(movie); + + + isHint = 0; + //we're creating a hint track... it's the same, but mode HAS TO BE EDIT + if (MediaType == GF_ISOM_MEDIA_HINT) { +// if (movie->openMode != GF_ISOM_OPEN_EDIT) return 0; + isHint = 1; + } + + mdia = NULL; + tkhd = NULL; + trak = NULL; + if (trakID) { + //check if we are in ES_ID boundaries + if (!isHint && (trakID > 0xFFFF)) { + gf_isom_set_last_error(movie, GF_BAD_PARAM); + return 0; + } + //here we should look for available IDs ... + if (!RequestTrack(movie->moov, trakID)) return 0; + } else { + trakID = movie->moov->mvhd->nextTrackID; + if (!trakID) trakID = 1; + /*ESIDs are on 16 bits*/ + if (! isHint && (trakID > 0xFFFF)) trakID = 1; + + while (1) { + if (RequestTrack(movie->moov, trakID)) break; + trakID += 1; + if (trakID == 0xFFFFFFFF) break; + } + if (trakID == 0xFFFFFFFF) { + gf_isom_set_last_error(movie, GF_BAD_PARAM); + return 0; + } + if (! isHint && (trakID > 0xFFFF)) { + gf_isom_set_last_error(movie, GF_BAD_PARAM); + return 0; + } + } + + //OK, now create a track... + trak = (GF_TrackBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_TRAK); + if (!trak) { + gf_isom_set_last_error(movie, GF_OUT_OF_MEM); + return 0; + } + tkhd = (GF_TrackHeaderBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_TKHD); + if (!tkhd) { + gf_isom_set_last_error(movie, GF_OUT_OF_MEM); + gf_isom_box_del((GF_Box *)trak); + return 0; + } + now = gf_isom_get_mp4time(); + tkhd->creationTime = now; + tkhd->modificationTime = now; + //OK, set up the media trak + e = NewMedia(&mdia, MediaType, TimeScale); + if (e) { + gf_isom_box_del((GF_Box *)mdia); + gf_isom_box_del((GF_Box *)trak); + gf_isom_box_del((GF_Box *)tkhd); + return 0; + } + //OK, add this media to our track + mdia->mediaTrack = trak; + + e = trak_AddBox((GF_Box*)trak, (GF_Box *) tkhd); if (e) goto err_exit; + e = trak_AddBox((GF_Box*)trak, (GF_Box *) mdia); if (e) goto err_exit; + tkhd->trackID = trakID; + + + //some default properties for Audio, Visual or private tracks + switch (MediaType) { + case GF_ISOM_MEDIA_VISUAL: + case GF_ISOM_MEDIA_SCENE: + case GF_ISOM_MEDIA_TEXT: + case GF_ISOM_MEDIA_SUBT: + /*320-240 pix in 16.16*/ + tkhd->width = 0x01400000; + tkhd->height = 0x00F00000; + break; + case GF_ISOM_MEDIA_AUDIO: + tkhd->volume = 0x0100; + break; + } + + mdia->mediaHeader->creationTime = mdia->mediaHeader->modificationTime = now; + trak->Header->creationTime = trak->Header->modificationTime = now; + + //OK, add our trak + e = moov_AddBox((GF_Box*)movie->moov, (GF_Box *)trak); if (e) goto err_exit; + //set the new ID available + if (trakID+1> movie->moov->mvhd->nextTrackID) + movie->moov->mvhd->nextTrackID = trakID+1; + + //and return our track number + return gf_isom_get_track_by_id(movie, trakID); + +err_exit: + if (tkhd) gf_isom_box_del((GF_Box *)tkhd); + if (trak) gf_isom_box_del((GF_Box *)trak); + if (mdia) gf_isom_box_del((GF_Box *)mdia); + return 0; +} + + +//Create a new StreamDescription in the file. The URL and URN are used to describe external media +GF_EXPORT +GF_Err gf_isom_new_mpeg4_description(GF_ISOFile *movie, + u32 trackNumber, + GF_ESD *esd, + char *URLname, + char *URNname, + u32 *outDescriptionIndex) +{ + GF_TrackBox *trak; + GF_Err e; + u32 dataRefIndex; + GF_ESD *new_esd; + + e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE); + if (e) return e; + + trak = gf_isom_get_track_from_file(movie, trackNumber); + if (!trak || !trak->Media || + !esd || !esd->decoderConfig || + !esd->slConfig) return GF_BAD_PARAM; + + //get or create the data ref + e = Media_FindDataRef(trak->Media->information->dataInformation->dref, URLname, URNname, &dataRefIndex); + if (e) return e; + if (!dataRefIndex) { + e = Media_CreateDataRef(trak->Media->information->dataInformation->dref, URLname, URNname, &dataRefIndex); + if (e) return e; + } + //duplicate our desc + e = gf_odf_desc_copy((GF_Descriptor *)esd, (GF_Descriptor **)&new_esd); + if (e) return e;; + trak->Media->mediaHeader->modificationTime = gf_isom_get_mp4time(); + e = Track_SetStreamDescriptor(trak, 0, dataRefIndex, new_esd, outDescriptionIndex); + if (e) { + gf_odf_desc_del((GF_Descriptor *)new_esd); + return e; + } + if (new_esd->URLString) { + + } + return e; +} + +//Add samples to a track. Use streamDescriptionIndex to specify the desired stream (if several) +GF_EXPORT +GF_Err gf_isom_add_sample(GF_ISOFile *movie, u32 trackNumber, u32 StreamDescriptionIndex, GF_ISOSample *sample) +{ + GF_Err e; + GF_TrackBox *trak; + GF_SampleEntryBox *entry; + u32 dataRefIndex; + u64 data_offset; + u32 descIndex; + GF_DataEntryURLBox *Dentry; + + e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE); + if (e) return e; + + trak = gf_isom_get_track_from_file(movie, trackNumber); + if (!trak) return GF_BAD_PARAM; + + e = FlushCaptureMode(movie); + if (e) return e; + + e = unpack_track(trak); + if (e) return e; + + //OK, add the sample + //1- Get the streamDescriptionIndex and dataRefIndex + //not specified, get the latest used... + descIndex = StreamDescriptionIndex; + if (!StreamDescriptionIndex) { + descIndex = trak->Media->information->sampleTable->currentEntryIndex; + } + e = Media_GetSampleDesc(trak->Media, descIndex, &entry, &dataRefIndex); + if (e) return e; + if (!entry || !dataRefIndex) return GF_BAD_PARAM; + //set the current to this one + trak->Media->information->sampleTable->currentEntryIndex = descIndex; + + + //get this dataRef and return false if not self contained + Dentry = (GF_DataEntryURLBox*)gf_list_get(trak->Media->information->dataInformation->dref->boxList, dataRefIndex - 1); + if (!Dentry || Dentry->flags != 1) return GF_BAD_PARAM; + + //Open our data map. We are adding stuff, so use EDIT + e = gf_isom_datamap_open(trak->Media, dataRefIndex, 1); + if (e) return e; + + //Get the offset... + data_offset = gf_isom_datamap_get_offset(trak->Media->information->dataHandler); + + /*rewrite OD frame*/ + if (trak->Media->handler->handlerType == GF_ISOM_MEDIA_OD) { + GF_ISOSample *od_sample = NULL; + e = Media_ParseODFrame(trak->Media, sample, &od_sample); + if (!e) e = Media_AddSample(trak->Media, data_offset, od_sample, descIndex, 0); + if (!e) e = gf_isom_datamap_add_data(trak->Media->information->dataHandler, od_sample->data, od_sample->dataLength); + if (od_sample) gf_isom_sample_del(&od_sample); + } else { + e = Media_AddSample(trak->Media, data_offset, sample, descIndex, 0); + if (!e) e = gf_isom_datamap_add_data(trak->Media->information->dataHandler, sample->data, sample->dataLength); + } + if (e) return e; + + trak->Media->mediaHeader->modificationTime = gf_isom_get_mp4time(); + return SetTrackDuration(trak); +} + +GF_Err gf_isom_add_sample_shadow(GF_ISOFile *movie, u32 trackNumber, GF_ISOSample *sample) +{ + GF_Err e; + GF_TrackBox *trak; + GF_ISOSample *prev; + GF_SampleEntryBox *entry; + u32 dataRefIndex; + u64 data_offset; + u32 descIndex; + u32 sampleNum, prevSampleNum; + GF_DataEntryURLBox *Dentry; + Bool offset_times = 0; + + e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE); + if (e) return e; + + trak = gf_isom_get_track_from_file(movie, trackNumber); + if (!trak || !sample) return GF_BAD_PARAM; + + e = FlushCaptureMode(movie); + if (e) return e; + + e = unpack_track(trak); + if (e) return e; + + e = findEntryForTime(trak->Media->information->sampleTable, sample->DTS, 0, &sampleNum, &prevSampleNum); + if (e) return e; + /*we need the EXACT match*/ + if (!sampleNum) return GF_BAD_PARAM; + + prev = gf_isom_get_sample_info(movie, trackNumber, sampleNum, &descIndex, NULL); + if (!prev) return gf_isom_last_error(movie); + /*for conformance*/ + if (sample->DTS==prev->DTS) offset_times = 1; + gf_isom_sample_del(&prev); + + e = Media_GetSampleDesc(trak->Media, descIndex, &entry, &dataRefIndex); + if (e) return e; + if (!entry || !dataRefIndex) return GF_BAD_PARAM; + trak->Media->information->sampleTable->currentEntryIndex = descIndex; + + //get this dataRef and return false if not self contained + Dentry = (GF_DataEntryURLBox*)gf_list_get(trak->Media->information->dataInformation->dref->boxList, dataRefIndex - 1); + if (!Dentry || Dentry->flags != 1) return GF_BAD_PARAM; + + //Open our data map. We are adding stuff, so use EDIT + e = gf_isom_datamap_open(trak->Media, dataRefIndex, 1); + if (e) return e; + + data_offset = gf_isom_datamap_get_offset(trak->Media->information->dataHandler); + if (offset_times) sample->DTS += 1; + + /*REWRITE ANY OD STUFF*/ + if (trak->Media->handler->handlerType == GF_ISOM_MEDIA_OD) { + GF_ISOSample *od_sample = NULL; + e = Media_ParseODFrame(trak->Media, sample, &od_sample); + if (!e) e = Media_AddSample(trak->Media, data_offset, od_sample, descIndex, sampleNum); + if (!e) e = gf_isom_datamap_add_data(trak->Media->information->dataHandler, od_sample->data, od_sample->dataLength); + if (od_sample) gf_isom_sample_del(&od_sample); + } else { + e = Media_AddSample(trak->Media, data_offset, sample, descIndex, sampleNum); + if (!e) e = gf_isom_datamap_add_data(trak->Media->information->dataHandler, sample->data, sample->dataLength); + } + if (e) return e; + if (offset_times) sample->DTS -= 1; + + //OK, update duration + e = Media_SetDuration(trak); + if (e) return e; + trak->Media->mediaHeader->modificationTime = gf_isom_get_mp4time(); + return SetTrackDuration(trak); +} + +GF_Err gf_isom_set_sample_rap(GF_ISOFile *movie, u32 trackNumber) +{ + GF_SampleTableBox *stbl; + GF_Err e; + GF_TrackBox *trak; + e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE); + if (e) return e; + + trak = gf_isom_get_track_from_file(movie, trackNumber); + if (!trak) return GF_BAD_PARAM; + stbl = trak->Media->information->sampleTable; + if (!stbl->SyncSample) stbl->SyncSample = (GF_SyncSampleBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_STSS); + return stbl_AddRAP(stbl->SyncSample, stbl->SampleSize->sampleCount); + +} + +GF_Err gf_isom_append_sample_data(GF_ISOFile *movie, u32 trackNumber, char *data, u32 data_size) +{ + GF_Err e; + GF_TrackBox *trak; + GF_SampleEntryBox *entry; + u32 dataRefIndex; + u32 descIndex; + GF_DataEntryURLBox *Dentry; + + if (!data_size) return GF_OK; + e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE); + if (e) return e; + + trak = gf_isom_get_track_from_file(movie, trackNumber); + if (!trak) return GF_BAD_PARAM; + + if (trak->Media->handler->handlerType == GF_ISOM_MEDIA_OD) return GF_BAD_PARAM; + + //OK, add the sample + descIndex = trak->Media->information->sampleTable->currentEntryIndex; + + e = Media_GetSampleDesc(trak->Media, descIndex, &entry, &dataRefIndex); + if (e) return e; + if (!entry || !dataRefIndex) return GF_BAD_PARAM; + + //get this dataRef and return false if not self contained + Dentry = (GF_DataEntryURLBox*)gf_list_get(trak->Media->information->dataInformation->dref->boxList, dataRefIndex - 1); + if (!Dentry || Dentry->flags != 1) return GF_BAD_PARAM; + + //Open our data map. We are adding stuff, so use EDIT + e = gf_isom_datamap_open(trak->Media, dataRefIndex, 1); + if (e) return e; + + //add the media data + e = gf_isom_datamap_add_data(trak->Media->information->dataHandler, data, data_size); + if (e) return e; + //update data size + return stbl_SampleSizeAppend(trak->Media->information->sampleTable->SampleSize, data_size); +} + + +//Add sample reference to a track. The SampleOffset is the offset of the data in the referenced file +//you must have created a StreamDescription with URL or URN specifying your referenced file +//the data offset specifies the begining of the chunk +//Use streamDescriptionIndex to specify the desired stream (if several) +GF_Err gf_isom_add_sample_reference(GF_ISOFile *movie, u32 trackNumber, u32 StreamDescriptionIndex, GF_ISOSample *sample, u64 dataOffset) +{ + GF_TrackBox *trak; + GF_SampleEntryBox *entry; + u32 dataRefIndex; + u32 descIndex; + GF_DataEntryURLBox *Dentry; + GF_Err e; + + e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE); + if (e) return e; + + trak = gf_isom_get_track_from_file(movie, trackNumber); + if (!trak) return GF_BAD_PARAM; + + e = unpack_track(trak); + if (e) return e; + + //OD is not allowed as a data ref + if (trak->Media->handler->handlerType == GF_ISOM_MEDIA_OD) { + return GF_BAD_PARAM; + } + //OK, add the sample + //1- Get the streamDescriptionIndex and dataRefIndex + //not specified, get the latest used... + descIndex = StreamDescriptionIndex; + if (!StreamDescriptionIndex) { + descIndex = trak->Media->information->sampleTable->currentEntryIndex; + } + e = Media_GetSampleDesc(trak->Media, descIndex, &entry, &dataRefIndex); + if (e) return e; + if (!entry || !dataRefIndex) return GF_BAD_PARAM; + //set the current to this one + trak->Media->information->sampleTable->currentEntryIndex = descIndex; + + + //get this dataRef and return false if self contained + Dentry =(GF_DataEntryURLBox*) gf_list_get(trak->Media->information->dataInformation->dref->boxList, dataRefIndex - 1); + if (Dentry->flags == 1) return GF_BAD_PARAM; + + //add the meta data + e = Media_AddSample(trak->Media, dataOffset, sample, descIndex, 0); + if (e) return e; + + trak->Media->mediaHeader->modificationTime = gf_isom_get_mp4time(); + //OK, update duration + e = Media_SetDuration(trak); + if (e) return e; + return SetTrackDuration(trak); + +} + +//set the duration of the last media sample. If not set, the duration of the last sample is the +//duration of the previous one if any, or 1000 (default value). +GF_Err gf_isom_set_last_sample_duration(GF_ISOFile *movie, u32 trackNumber, u32 duration) +{ + GF_TrackBox *trak; + GF_SttsEntry *ent; + GF_TimeToSampleBox *stts; + u64 mdur; + GF_Err e; + + e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE); + if (e) return e; + + trak = gf_isom_get_track_from_file(movie, trackNumber); + if (!trak) return GF_BAD_PARAM; + + mdur = trak->Media->mediaHeader->duration; + stts = trak->Media->information->sampleTable->TimeToSample; + if (!stts->nb_entries) return GF_BAD_PARAM; + //get the last entry + ent = (GF_SttsEntry*) &stts->entries[stts->nb_entries-1]; + + mdur -= ent->sampleDelta; + if (duration) { + mdur += duration; + //we only have one sample + if (ent->sampleCount == 1) { + ent->sampleDelta = duration; + } else { + if (ent->sampleDelta == duration) return GF_OK; + ent->sampleCount -= 1; + + if (stts->nb_entries==stts->alloc_size) { + stts->alloc_size++; + stts->entries = (GF_SttsEntry *) gf_realloc(stts->entries, sizeof(GF_SttsEntry)*stts->alloc_size); + if (!stts->entries) return GF_OUT_OF_MEM; + } + stts->entries[stts->nb_entries].sampleCount = 1; + stts->entries[stts->nb_entries].sampleDelta = duration; + stts->nb_entries++; + //and update the write cache + stts->w_currentSampleNum = trak->Media->information->sampleTable->SampleSize->sampleCount; + } + } + trak->Media->mediaHeader->modificationTime = gf_isom_get_mp4time(); + trak->Media->mediaHeader->duration = mdur; + return SetTrackDuration(trak); +} + +//update a sample data in the media. Note that the sample MUST exists +GF_Err gf_isom_update_sample(GF_ISOFile *movie, u32 trackNumber, u32 sampleNumber, GF_ISOSample *sample, Bool data_only) +{ + GF_Err e; + GF_TrackBox *trak; + + e = CanAccessMovie(movie, GF_ISOM_OPEN_EDIT); + if (e) return e; + + trak = gf_isom_get_track_from_file(movie, trackNumber); + if (!trak) return GF_BAD_PARAM; + + e = unpack_track(trak); + if (e) return e; + + //block for hint tracks + if (trak->Media->handler->handlerType == GF_ISOM_MEDIA_HINT) return GF_BAD_PARAM; + + //REWRITE ANY OD STUFF + if (trak->Media->handler->handlerType == GF_ISOM_MEDIA_OD) { + GF_ISOSample *od_sample = NULL; + e = Media_ParseODFrame(trak->Media, sample, &od_sample); + if (!e) e = Media_UpdateSample(trak->Media, sampleNumber, od_sample, data_only); + if (od_sample) gf_isom_sample_del(&od_sample); + } else { + e = Media_UpdateSample(trak->Media, sampleNumber, sample, data_only); + } + if (e) return e; + trak->Media->mediaHeader->modificationTime = gf_isom_get_mp4time(); + return GF_OK; +} + +//update a sample data in the media. Note that the sample MUST exists, +//that sample->data MUST be NULL and sample->dataLength must be NON NULL; +GF_Err gf_isom_update_sample_reference(GF_ISOFile *movie, u32 trackNumber, u32 sampleNumber, GF_ISOSample *sample, u64 data_offset) +{ + GF_Err e; + GF_TrackBox *trak; + + e = CanAccessMovie(movie, GF_ISOM_OPEN_EDIT); + if (e) return e; + + trak = gf_isom_get_track_from_file(movie, trackNumber); + if (!trak) return GF_BAD_PARAM; + + //block for hint tracks + if (trak->Media->handler->handlerType == GF_ISOM_MEDIA_HINT) return GF_BAD_PARAM; + + if (!sampleNumber || !sample) return GF_BAD_PARAM; + + e = unpack_track(trak); + if (e) return e; + + //OD is not allowed as a data ref + if (trak->Media->handler->handlerType == GF_ISOM_MEDIA_OD) { + return GF_BAD_PARAM; + } + //OK, update it + e = Media_UpdateSampleReference(trak->Media, sampleNumber, sample, data_offset); + if (e) return e; + + trak->Media->mediaHeader->modificationTime = gf_isom_get_mp4time(); + return GF_OK; +} + + +//Remove a given sample +GF_Err gf_isom_remove_sample(GF_ISOFile *movie, u32 trackNumber, u32 sampleNumber) +{ + GF_Err e; + GF_TrackBox *trak; + + e = CanAccessMovie(movie, GF_ISOM_OPEN_EDIT); + if (e) return e; + + trak = gf_isom_get_track_from_file(movie, trackNumber); + if (!trak || !sampleNumber || (sampleNumber > trak->Media->information->sampleTable->SampleSize->sampleCount) ) + return GF_BAD_PARAM; + + //block for hint tracks + if (trak->Media->handler->handlerType == GF_ISOM_MEDIA_HINT) return GF_BAD_PARAM; + + e = unpack_track(trak); + if (e) return e; + + //remove DTS + e = stbl_RemoveDTS(trak->Media->information->sampleTable, sampleNumber, trak->Media->mediaHeader->timeScale); + if (e) return e; + //remove CTS if any + if (trak->Media->information->sampleTable->CompositionOffset) { + e = stbl_RemoveCTS(trak->Media->information->sampleTable, sampleNumber); + if (e) return e; + } + //remove size + e = stbl_RemoveSize(trak->Media->information->sampleTable->SampleSize, sampleNumber); + if (e) return e; + //remove sampleToChunk and chunk + e = stbl_RemoveChunk(trak->Media->information->sampleTable, sampleNumber); + if (e) return e; + //remove sync + if (trak->Media->information->sampleTable->SyncSample) { + e = stbl_RemoveRAP(trak->Media->information->sampleTable, sampleNumber); + if (e) return e; + } + //remove sample dep + if (trak->Media->information->sampleTable->SampleDep) { + e = stbl_RemoveRedundant(trak->Media->information->sampleTable, sampleNumber); + if (e) return e; + } + //remove shadow + if (trak->Media->information->sampleTable->ShadowSync) { + e = stbl_RemoveShadow(trak->Media->information->sampleTable->ShadowSync, sampleNumber); + if (e) return e; + } + //remove padding + e = stbl_RemovePaddingBits(trak->Media->information->sampleTable, sampleNumber); + if (e) return e; + + return SetTrackDuration(trak); +} + + +GF_Err gf_isom_set_final_name(GF_ISOFile *movie, char *filename) +{ + GF_Err e; + if (!movie ) return GF_BAD_PARAM; + + //if mode is not OPEN_EDIT file was created under the right name + e = CanAccessMovie(movie, GF_ISOM_OPEN_EDIT); + if (e) return e; + + if (filename) { + //we don't allow file overwriting + if ( (movie->openMode == GF_ISOM_OPEN_EDIT) + && movie->fileName && !strcmp(filename, movie->fileName)) + return GF_BAD_PARAM; + if (movie->finalName) gf_free(movie->finalName); + movie->finalName = gf_strdup(filename); + if (!movie->finalName) return GF_OUT_OF_MEM; + } + return GF_OK; +} + +//Add a system descriptor to the ESD of a stream(EDIT or WRITE mode only) +GF_Err gf_isom_add_desc_to_description(GF_ISOFile *movie, u32 trackNumber, u32 StreamDescriptionIndex, GF_Descriptor *theDesc) +{ + GF_IPIPtr *ipiD; + GF_Err e; + u16 tmpRef; + GF_TrackBox *trak; + GF_Descriptor *desc; + GF_ESD *esd; + GF_TrackReferenceBox *tref; + GF_TrackReferenceTypeBox *dpnd; + + e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE); + if (e) return e; + + trak = gf_isom_get_track_from_file(movie, trackNumber); + if (!trak) return GF_BAD_PARAM; + + /*GETS NATIVE DESCRIPTOR ONLY*/ + e = Media_GetESD(trak->Media, StreamDescriptionIndex, &esd, 1); + if (e) return e; + + //duplicate the desc + e = gf_odf_desc_copy(theDesc, &desc); + if (e) return e; + + //and add it to the ESD EXCEPT IPI PTR (we need to translate from ES_ID to TrackID!!! + trak->Media->mediaHeader->modificationTime = gf_isom_get_mp4time(); + switch (desc->tag) { + case GF_ODF_IPI_PTR_TAG: + goto insertIPI; + + default: + return gf_odf_desc_add_desc((GF_Descriptor *)esd, desc); + } + + +insertIPI: + if (esd->ipiPtr) { + gf_odf_desc_del((GF_Descriptor *) esd->ipiPtr); + esd->ipiPtr = NULL; + } + + ipiD = (GF_IPIPtr *) desc; + //find a tref + if (!trak->References) { + tref = (GF_TrackReferenceBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_TREF); + e = trak_AddBox((GF_Box*)trak, (GF_Box *)tref); + if (e) return e; + } + tref = trak->References; + + e = Track_FindRef(trak, GF_ISOM_REF_IPI, &dpnd); + if (e) return e; + if (!dpnd) { + tmpRef = 0; + dpnd = (GF_TrackReferenceTypeBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_REFT); + dpnd->reference_type = GF_ISOM_BOX_TYPE_IPIR; + e = tref_AddBox((GF_Box*)tref, (GF_Box *) dpnd); + if (e) return e; + e = reftype_AddRefTrack(dpnd, ipiD->IPI_ES_Id, &tmpRef); + if (e) return e; + //and replace the tag and value... + ipiD->IPI_ES_Id = tmpRef; + ipiD->tag = GF_ODF_ISOM_IPI_PTR_TAG; + } else { + //Watch out! ONLY ONE IPI dependancy is allowed per stream + dpnd->trackIDCount = 1; + dpnd->trackIDs[0] = ipiD->IPI_ES_Id; + //and replace the tag and value... + ipiD->IPI_ES_Id = 1; + ipiD->tag = GF_ODF_ISOM_IPI_PTR_TAG; + } + //and add the desc to the esd... + return gf_odf_desc_add_desc((GF_Descriptor *)esd, desc); +} + + +//use carefully. Very usefull when you made a lot of changes (IPMP, IPI, OCI, ...) +//THIS WILL REPLACE THE WHOLE DESCRIPTOR ... +GF_Err gf_isom_change_mpeg4_description(GF_ISOFile *movie, u32 trackNumber, u32 StreamDescriptionIndex, GF_ESD *newESD) +{ + GF_Err e; + GF_ESD *esd; + GF_TrackBox *trak; + GF_SampleEntryBox *entry; + GF_SampleDescriptionBox *stsd; + + e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE); + if (e) return e; + + trak = gf_isom_get_track_from_file(movie, trackNumber); + if (!trak) return GF_BAD_PARAM; + + stsd = trak->Media->information->sampleTable->SampleDescription; + if (!stsd) return movie->LastError = GF_ISOM_INVALID_FILE; + + if (!StreamDescriptionIndex || StreamDescriptionIndex > gf_list_count(stsd->boxList)) { + return movie->LastError = GF_BAD_PARAM; + } + entry = (GF_SampleEntryBox *)gf_list_get(stsd->boxList, StreamDescriptionIndex - 1); + //no support for generic sample entries (eg, no MPEG4 descriptor) + if (entry == NULL) return GF_BAD_PARAM; + + trak->Media->mediaHeader->modificationTime = gf_isom_get_mp4time(); + //duplicate our desc + e = gf_odf_desc_copy((GF_Descriptor *)newESD, (GF_Descriptor **)&esd); + if (e) return e; + return Track_SetStreamDescriptor(trak, StreamDescriptionIndex, entry->dataReferenceIndex, esd, NULL); +} + +GF_Err gf_isom_set_visual_info(GF_ISOFile *movie, u32 trackNumber, u32 StreamDescriptionIndex, u32 Width, u32 Height) +{ + GF_Err e; + GF_TrackBox *trak; + GF_SampleEntryBox *entry; + GF_SampleDescriptionBox *stsd; + e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE); + if (e) return e; + + trak = gf_isom_get_track_from_file(movie, trackNumber); + if (!trak) return GF_BAD_PARAM; + + stsd = trak->Media->information->sampleTable->SampleDescription; + if (!stsd) { + return movie->LastError = GF_ISOM_INVALID_FILE; + } + if (!StreamDescriptionIndex || StreamDescriptionIndex > gf_list_count(stsd->boxList)) { + return movie->LastError = GF_BAD_PARAM; + } + entry = (GF_SampleEntryBox *)gf_list_get(stsd->boxList, StreamDescriptionIndex - 1); + //no support for generic sample entries (eg, no MPEG4 descriptor) + if (entry == NULL) return GF_BAD_PARAM; + trak->Media->mediaHeader->modificationTime = gf_isom_get_mp4time(); + + //valid for MPEG visual, JPG and 3GPP H263 + switch (entry->type) { + case GF_ISOM_BOX_TYPE_MP4V: + case GF_ISOM_SUBTYPE_3GP_H263: + case GF_ISOM_BOX_TYPE_AVC1: + case GF_ISOM_BOX_TYPE_AVC2: + case GF_ISOM_BOX_TYPE_SVC1: + ((GF_VisualSampleEntryBox*)entry)->Width = Width; + ((GF_VisualSampleEntryBox*)entry)->Height = Height; + trak->Header->width = Width<<16; + trak->Header->height = Height<<16; + return GF_OK; + /*check BIFS*/ + default: + if (trak->Media->handler->handlerType==GF_ISOM_MEDIA_SCENE) { + trak->Header->width = Width<<16; + trak->Header->height = Height<<16; + return GF_OK; + } + return GF_BAD_PARAM; + } +} + +GF_Err gf_isom_set_pixel_aspect_ratio(GF_ISOFile *movie, u32 trackNumber, u32 StreamDescriptionIndex, u32 hSpacing, u32 vSpacing) +{ + GF_Err e; + GF_TrackBox *trak; + GF_SampleEntryBox *entry; + GF_VisualSampleEntryBox*vent; + GF_SampleDescriptionBox *stsd; + e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE); + if (e) return e; + + trak = gf_isom_get_track_from_file(movie, trackNumber); + if (!trak) return GF_BAD_PARAM; + + stsd = trak->Media->information->sampleTable->SampleDescription; + if (!stsd) return movie->LastError = GF_ISOM_INVALID_FILE; + if (!StreamDescriptionIndex || StreamDescriptionIndex > gf_list_count(stsd->boxList)) { + return movie->LastError = GF_BAD_PARAM; + } + entry = (GF_SampleEntryBox *)gf_list_get(stsd->boxList, StreamDescriptionIndex - 1); + //no support for generic sample entries (eg, no MPEG4 descriptor) + if (entry == NULL) return GF_BAD_PARAM; + trak->Media->mediaHeader->modificationTime = gf_isom_get_mp4time(); + switch (entry->type) { + case GF_ISOM_BOX_TYPE_MP4V: + case GF_ISOM_SUBTYPE_3GP_H263: + case GF_ISOM_BOX_TYPE_AVC1: + case GF_ISOM_BOX_TYPE_AVC2: + case GF_ISOM_BOX_TYPE_SVC1: + break; + default: + return GF_BAD_PARAM; + } + + vent = (GF_VisualSampleEntryBox*)entry; + if (!hSpacing || !vSpacing) { + if (vent->pasp) gf_isom_box_del((GF_Box*)vent->pasp); + vent->pasp = NULL; + return GF_OK; + } + if (!vent->pasp) vent->pasp = (GF_PixelAspectRatioBox*)gf_isom_box_new(GF_ISOM_BOX_TYPE_PASP); + vent->pasp->hSpacing = hSpacing; + vent->pasp->vSpacing = vSpacing; + return GF_OK; +} + +GF_Err gf_isom_set_audio_info(GF_ISOFile *movie, u32 trackNumber, u32 StreamDescriptionIndex, u32 sampleRate, u32 nbChannels, u8 bitsPerSample) +{ + GF_Err e; + GF_TrackBox *trak; + GF_SampleEntryBox *entry; + GF_SampleDescriptionBox *stsd; + e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE); + if (e) return e; + + trak = gf_isom_get_track_from_file(movie, trackNumber); + if (!trak) return GF_BAD_PARAM; + + stsd = trak->Media->information->sampleTable->SampleDescription; + if (!stsd) { + return movie->LastError = GF_ISOM_INVALID_FILE; + } + if (!StreamDescriptionIndex || StreamDescriptionIndex > gf_list_count(stsd->boxList)) { + return movie->LastError = GF_BAD_PARAM; + } + entry = (GF_SampleEntryBox *)gf_list_get(stsd->boxList, StreamDescriptionIndex - 1); + //no support for generic sample entries (eg, no MPEG4 descriptor) + if (entry == NULL) return GF_BAD_PARAM; + trak->Media->mediaHeader->modificationTime = gf_isom_get_mp4time(); + + switch (entry->type) { + case GF_ISOM_BOX_TYPE_MP4A: + case GF_ISOM_SUBTYPE_3GP_AMR: + case GF_ISOM_SUBTYPE_3GP_AMR_WB: + case GF_ISOM_SUBTYPE_3GP_EVRC: + case GF_ISOM_SUBTYPE_3GP_QCELP: + case GF_ISOM_SUBTYPE_3GP_SMV: + ((GF_AudioSampleEntryBox*)entry)->samplerate_hi = sampleRate; + ((GF_AudioSampleEntryBox*)entry)->samplerate_lo = 0; + ((GF_AudioSampleEntryBox*)entry)->channel_count = nbChannels; + ((GF_AudioSampleEntryBox*)entry)->bitspersample = bitsPerSample; + return GF_OK; + /*check BIFS*/ + default: + return GF_BAD_PARAM; + } +} + +//set the storage mode of a file (FLAT, STREAMABLE, INTERLEAVED) +GF_Err gf_isom_set_storage_mode(GF_ISOFile *movie, u8 storageMode) +{ + GF_Err e; + e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE); + if (e) return e; + + switch (storageMode) { + case GF_ISOM_STORE_FLAT: + case GF_ISOM_STORE_STREAMABLE: + case GF_ISOM_STORE_INTERLEAVED: + case GF_ISOM_STORE_DRIFT_INTERLEAVED: + case GF_ISOM_STORE_TIGHT: + movie->storageMode = storageMode; + return GF_OK; + default: + return GF_BAD_PARAM; + } +} + + +//update or insert a new edit segment in the track time line. Edits are used to modify +//the media normal timing. EditTime and EditDuration are expressed in Movie TimeScale +//If a segment with EditTime already exists, IT IS ERASED +GF_Err gf_isom_set_edit_segment(GF_ISOFile *movie, u32 trackNumber, u64 EditTime, u64 EditDuration, u64 MediaTime, u8 EditMode) +{ + GF_TrackBox *trak; + GF_EditBox *edts; + GF_EditListBox *elst; + GF_EdtsEntry *ent, *newEnt; + u32 i; + GF_Err e; + u64 startTime; + + e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE); + if (e) return e; + + trak = gf_isom_get_track_from_file(movie, trackNumber); + if (!trak) return GF_BAD_PARAM; + + edts = trak->editBox; + if (! edts) { + edts = (GF_EditBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_EDTS); + if (!edts) return GF_OUT_OF_MEM; + trak_AddBox((GF_Box*)trak, (GF_Box *)edts); + } + elst = edts->editList; + if (!elst) { + elst = (GF_EditListBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_ELST); + if (!elst) return GF_OUT_OF_MEM; + edts_AddBox((GF_Box*)edts, (GF_Box *)elst); + } + + startTime = 0; + ent = NULL; + //get the prev entry to this startTime if any + i=0; + while ((ent = (GF_EdtsEntry *)gf_list_enum(elst->entryList, &i))) { + if ( (startTime <= EditTime) && (startTime + ent->segmentDuration > EditTime) ) + goto found; + startTime += ent->segmentDuration; + } + + //not found, add a new entry and adjust the prev one if any + if (!ent) { + newEnt = CreateEditEntry(EditDuration, MediaTime, EditMode); + if (!newEnt) return GF_OUT_OF_MEM; + gf_list_add(elst->entryList, newEnt); + return SetTrackDuration(trak); + } + + startTime -= ent->segmentDuration; + +found: + + //if same time, we erase the current one... + if (startTime == EditTime) { + ent->segmentDuration = EditDuration; + switch (EditMode) { + case GF_ISOM_EDIT_EMPTY: + ent->mediaRate = 1; + ent->mediaTime = -1; + break; + case GF_ISOM_EDIT_DWELL: + ent->mediaRate = 0; + ent->mediaTime = MediaTime; + break; + default: + ent->mediaRate = 1; + ent->mediaTime = MediaTime; + break; + } + return SetTrackDuration(trak); + } + + //adjust so that the prev ent leads to EntryTime + //Note: we don't change the next one as it is unknown to us in + //a lot of case (the author's changes) + ent->segmentDuration = EditTime - startTime; + newEnt = CreateEditEntry(EditDuration, MediaTime, EditMode); + if (!newEnt) return GF_OUT_OF_MEM; + //is it the last entry ??? + if (i >= gf_list_count(elst->entryList) - 1) { + //add the new entry at the end + gf_list_add(elst->entryList, newEnt); + return SetTrackDuration(trak); + } else { + //insert after the current entry (which is i) + gf_list_insert(elst->entryList, newEnt, i+1); + return SetTrackDuration(trak); + } +} + +//remove the edit segments for the whole track +GF_Err gf_isom_remove_edit_segments(GF_ISOFile *movie, u32 trackNumber) +{ + GF_Err e; + GF_TrackBox *trak; + GF_EdtsEntry *ent; + trak = gf_isom_get_track_from_file(movie, trackNumber); + if (!trak) return GF_BAD_PARAM; + + e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE); + if (e) return e; + + if (!trak->editBox || !trak->editBox->editList) return GF_OK; + + while (gf_list_count(trak->editBox->editList->entryList)) { + ent = (GF_EdtsEntry*)gf_list_get(trak->editBox->editList->entryList, 0); + gf_free(ent); + e = gf_list_rem(trak->editBox->editList->entryList, 0); + if (e) return e; + } + //then delete the GF_EditBox... + gf_isom_box_del((GF_Box *)trak->editBox); + trak->editBox = NULL; + return SetTrackDuration(trak); +} + + +//remove the edit segments for the whole track +GF_Err gf_isom_remove_edit_segment(GF_ISOFile *movie, u32 trackNumber, u32 seg_index) +{ + GF_Err e; + GF_TrackBox *trak; + GF_EdtsEntry *ent, *next_ent; + trak = gf_isom_get_track_from_file(movie, trackNumber); + if (!trak || !seg_index) return GF_BAD_PARAM; + + e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE); + if (e) return e; + + if (!trak->editBox || !trak->editBox->editList) return GF_OK; + if (gf_list_count(trak->editBox->editList->entryList)<=1) return gf_isom_remove_edit_segments(movie, trackNumber); + + ent = (GF_EdtsEntry*) gf_list_get(trak->editBox->editList->entryList, seg_index-1); + gf_list_rem(trak->editBox->editList->entryList, seg_index-1); + next_ent = (GF_EdtsEntry *)gf_list_get(trak->editBox->editList->entryList, seg_index-1); + if (next_ent) next_ent->segmentDuration += ent->segmentDuration; + gf_free(ent); + return SetTrackDuration(trak); +} + + +GF_Err gf_isom_append_edit_segment(GF_ISOFile *movie, u32 trackNumber, u64 EditDuration, u64 MediaTime, u8 EditMode) +{ + GF_Err e; + GF_TrackBox *trak; + GF_EdtsEntry *ent; + trak = gf_isom_get_track_from_file(movie, trackNumber); + if (!trak) return GF_BAD_PARAM; + e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE); + if (e) return e; + + if (!trak->editBox) { + GF_EditBox *edts = (GF_EditBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_EDTS); + if (!edts) return GF_OUT_OF_MEM; + trak_AddBox((GF_Box*)trak, (GF_Box *)edts); + } + if (!trak->editBox->editList) { + GF_EditListBox *elst = (GF_EditListBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_ELST); + if (!elst) return GF_OUT_OF_MEM; + edts_AddBox((GF_Box*)trak->editBox, (GF_Box *)elst); + } + ent = (GF_EdtsEntry *)gf_malloc(sizeof(GF_EdtsEntry)); + if (!ent) return GF_OUT_OF_MEM; + + ent->segmentDuration = EditDuration; + switch (EditMode) { + case GF_ISOM_EDIT_EMPTY: + ent->mediaRate = 1; + ent->mediaTime = -1; + break; + case GF_ISOM_EDIT_DWELL: + ent->mediaRate = 0; + ent->mediaTime = MediaTime; + break; + default: + ent->mediaRate = 1; + ent->mediaTime = MediaTime; + break; + } + gf_list_add(trak->editBox->editList->entryList, ent); + return SetTrackDuration(trak); +} + +GF_Err gf_isom_modify_edit_segment(GF_ISOFile *movie, u32 trackNumber, u32 seg_index, u64 EditDuration, u64 MediaTime, u8 EditMode) +{ + GF_Err e; + GF_TrackBox *trak; + GF_EdtsEntry *ent; + trak = gf_isom_get_track_from_file(movie, trackNumber); + if (!trak || !seg_index) return GF_BAD_PARAM; + e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE); + if (e) return e; + + if (!trak->editBox || !trak->editBox->editList) return GF_OK; + if (gf_list_count(trak->editBox->editList->entryList)editBox->editList->entryList, seg_index-1); + + ent->segmentDuration = EditDuration; + switch (EditMode) { + case GF_ISOM_EDIT_EMPTY: + ent->mediaRate = 1; + ent->mediaTime = -1; + break; + case GF_ISOM_EDIT_DWELL: + ent->mediaRate = 0; + ent->mediaTime = MediaTime; + break; + default: + ent->mediaRate = 1; + ent->mediaTime = MediaTime; + break; + } + return SetTrackDuration(trak); +} + +//removes the desired track +GF_Err gf_isom_remove_track(GF_ISOFile *movie, u32 trackNumber) +{ + GF_Err e; + GF_TrackBox *the_trak, *trak; + GF_TrackReferenceTypeBox *tref; + u32 i, j, k, *newRefs, descIndex; + u8 found; + GF_ISOSample *samp; + the_trak = gf_isom_get_track_from_file(movie, trackNumber); + if (!the_trak) return GF_BAD_PARAM; + + e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE); + if (e) return e; + + if (movie->moov->iods && movie->moov->iods->descriptor) { + GF_Descriptor *desc; + GF_ES_ID_Inc *inc; + GF_List *ESDs; + desc = movie->moov->iods->descriptor; + if (desc->tag == GF_ODF_ISOM_IOD_TAG) { + ESDs = ((GF_IsomInitialObjectDescriptor *)desc)->ES_ID_IncDescriptors; + } else if (desc->tag == GF_ODF_ISOM_OD_TAG) { + ESDs = ((GF_IsomObjectDescriptor *)desc)->ES_ID_IncDescriptors; + } else { + return GF_ISOM_INVALID_FILE; + } + + //remove the track ref from the root OD if any + i=0; + while ((inc = (GF_ES_ID_Inc *)gf_list_enum(ESDs, &i))) { + if (inc->trackID == the_trak->Header->trackID) { + gf_odf_desc_del((GF_Descriptor *)inc); + i--; + gf_list_rem(ESDs, i); + } + } + } + + //remove the track from the movie + gf_list_del_item(movie->moov->trackList, the_trak); + + //rewrite any OD tracks + i=0; + while ((trak = (GF_TrackBox *)gf_list_enum(movie->moov->trackList, &i))) { + if (trak->Media->handler->handlerType != GF_ISOM_MEDIA_OD) continue; + //this is an OD track... + j = gf_isom_get_sample_count(movie, i); + for (k=0; k < j; k++) { + //getting the sample will remove the references to the deleted track in the output OD frame + samp = gf_isom_get_sample(movie, i, k+1, &descIndex); + if (!samp) break; + //so let's update with the new OD frame ! If the sample is empty, remove it + if (!samp->dataLength) { + e = gf_isom_remove_sample(movie, i, k+1); + if (e) return e; + } else { + e = gf_isom_update_sample(movie, i, k+1, samp, 1); + if (e) return e; + } + //and don't forget to delete the sample + gf_isom_sample_del(&samp); + } + } + + //remove the track ref from any "tref" box in all tracks (except the one to delete ;) + i=0; + while ((trak = (GF_TrackBox *)gf_list_enum(movie->moov->trackList, &i))) { + if (trak == the_trak) continue; + if (! trak->References || ! gf_list_count(trak->References->boxList)) continue; + + j=0; + while ((tref = (GF_TrackReferenceTypeBox *)gf_list_enum(trak->References->boxList, &j))) { + found = 0; + for (k=0; ktrackIDCount; k++) { + if (tref->trackIDs[k] == the_trak->Header->trackID) found++; + } + if (!found) continue; + //no more refs, remove this ref_type + if (found == tref->trackIDCount) { + gf_isom_box_del((GF_Box *)tref); + j--; + gf_list_rem(trak->References->boxList, j); + } else { + newRefs = (u32*)gf_malloc(sizeof(u32) * (tref->trackIDCount - found)); + found = 0; + for (k = 0; k < tref->trackIDCount; k++) { + if (tref->trackIDs[k] != the_trak->Header->trackID) { + newRefs[k-found] = tref->trackIDs[k]; + } else { + found++; + } + } + gf_free(tref->trackIDs); + tref->trackIDs = newRefs; + tref->trackIDCount -= found; + } + } + //a little opt: remove the ref box if empty... + if (! gf_list_count(trak->References->boxList)) { + gf_isom_box_del((GF_Box *)trak->References); + trak->References = NULL; + } + } + + //delete the track + gf_isom_box_del((GF_Box *)the_trak); + + /*update next track ID*/ + movie->moov->mvhd->nextTrackID = 0; + i=0; + while ((trak = (GF_TrackBox *)gf_list_enum(movie->moov->trackList, &i))) { + if (trak->Header->trackID>movie->moov->mvhd->nextTrackID) + movie->moov->mvhd->nextTrackID = trak->Header->trackID; + } + return GF_OK; +} + + +GF_Err gf_isom_set_copyright(GF_ISOFile *movie, const char *threeCharCode, char *notice) +{ + GF_Err e; + GF_CopyrightBox *ptr; + GF_UserDataMap *map; + u32 count, i; + + e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE); + if (e) return e; + + if (!notice || !threeCharCode) return GF_BAD_PARAM; + + gf_isom_insert_moov(movie); + + if (!movie->moov->udta) { + e = moov_AddBox((GF_Box*)movie->moov, gf_isom_box_new(GF_ISOM_BOX_TYPE_UDTA)); + if (e) return e; + } + map = udta_getEntry(movie->moov->udta, GF_ISOM_BOX_TYPE_CPRT, NULL); + + if (map) { + //try to find one in our language... + count = gf_list_count(map->boxList); + for (i=0; iboxList, i); + if (!strcmp(threeCharCode, (const char *) ptr->packedLanguageCode)) { + gf_free(ptr->notice); + ptr->notice = (char*)gf_malloc(sizeof(char) * (strlen(notice) + 1)); + strcpy(ptr->notice, notice); + return GF_OK; + } + } + } + //nope, create one + ptr = (GF_CopyrightBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_CPRT); + + memcpy(ptr->packedLanguageCode, threeCharCode, 4); + ptr->notice = (char*)gf_malloc(sizeof(char) * (strlen(notice)+1)); + strcpy(ptr->notice, notice); + return udta_AddBox(movie->moov->udta, (GF_Box *) ptr); +} + +GF_Err gf_isom_add_chapter(GF_ISOFile *movie, u32 trackNumber, u64 timestamp, char *name) +{ + GF_Err e; + GF_ChapterListBox *ptr; + u32 i, count; + GF_ChapterEntry *ce; + GF_UserDataBox *udta; + GF_UserDataMap *map; + + e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE); + if (e) return e; + + gf_isom_insert_moov(movie); + + if (trackNumber) { + GF_TrackBox *trak = gf_isom_get_track_from_file(movie, trackNumber); + if (!trak) return GF_BAD_PARAM; + if (!trak->udta) { + e = trak_AddBox((GF_Box*)trak, gf_isom_box_new(GF_ISOM_BOX_TYPE_UDTA)); + if (e) return e; + } + udta = trak->udta; + } else { + if (!movie->moov->udta) { + e = moov_AddBox((GF_Box*)movie->moov, gf_isom_box_new(GF_ISOM_BOX_TYPE_UDTA)); + if (e) return e; + } + udta = movie->moov->udta; + } + + ptr = NULL; + map = udta_getEntry(udta, GF_ISOM_BOX_TYPE_CHPL, NULL); + if (!map) { + ptr = (GF_ChapterListBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_CHPL); + e = udta_AddBox(udta, (GF_Box *) ptr); + if (e) return e; + } else { + ptr = (GF_ChapterListBox*)gf_list_get(map->boxList, 0); + } + /*this may happen if original MP4 is not properly formatted*/ + if (!ptr) { + ptr = (GF_ChapterListBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_CHPL); + gf_list_add(map->boxList, ptr); + } + + GF_SAFEALLOC(ce, GF_ChapterEntry); + ce->start_time = timestamp * 10000L; + ce->name = name ? gf_strdup(name) : NULL; + + /*insert in order*/ + count = gf_list_count(ptr->list); + for (i=0; ilist, i); + if (ace->start_time == ce->start_time) { + if (ace->name) gf_free(ace->name); + ace->name = ce->name; + gf_free(ce); + return GF_OK; + } + if (ace->start_time >= ce->start_time) + return gf_list_insert(ptr->list, ce, i); + } + return gf_list_add(ptr->list, ce); +} + + +GF_Err gf_isom_remove_chapter(GF_ISOFile *movie, u32 trackNumber, u32 index) +{ + GF_Err e; + GF_ChapterListBox *ptr; + GF_ChapterEntry *ce; + GF_UserDataBox *udta; + GF_UserDataMap *map; + + e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE); + if (e) return e; + gf_isom_insert_moov(movie); + + if (trackNumber) { + GF_TrackBox *trak = gf_isom_get_track_from_file(movie, trackNumber); + if (!trak) return GF_BAD_PARAM; + if (!trak->udta) { + e = trak_AddBox((GF_Box*)trak, gf_isom_box_new(GF_ISOM_BOX_TYPE_UDTA)); + if (e) return e; + } + udta = trak->udta; + } else { + if (!movie->moov->udta) { + e = moov_AddBox((GF_Box*)movie->moov, gf_isom_box_new(GF_ISOM_BOX_TYPE_UDTA)); + if (e) return e; + } + udta = movie->moov->udta; + } + + map = udta_getEntry(udta, GF_ISOM_BOX_TYPE_CHPL, NULL); + if (!map) return GF_OK; + ptr = (GF_ChapterListBox*)gf_list_get(map->boxList, 0); + if (!ptr) return GF_OK; + + if (index) { + ce = (GF_ChapterEntry *)gf_list_get(ptr->list, index-1); + if (!ce) return GF_BAD_PARAM; + if (ce->name) gf_free(ce->name); + gf_free(ce); + gf_list_rem(ptr->list, index-1); + } else { + while (gf_list_count(ptr->list)) { + ce = (GF_ChapterEntry *)gf_list_get(ptr->list, 0); + if (ce->name) gf_free(ce->name); + gf_free(ce); + gf_list_rem(ptr->list, 0); + } + } + if (!gf_list_count(ptr->list)) { + gf_list_del_item(udta->recordList, map); + gf_isom_box_array_del(map->boxList); + gf_free(map); + } + return GF_OK; +} + +GF_Err gf_isom_remove_copyright(GF_ISOFile *movie, u32 index) +{ + GF_Err e; + GF_CopyrightBox *ptr; + GF_UserDataMap *map; + u32 count; + + e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE); + if (e) return e; + gf_isom_insert_moov(movie); + + if (!index) return GF_BAD_PARAM; + if (!movie->moov->udta) return GF_OK; + + map = udta_getEntry(movie->moov->udta, GF_ISOM_BOX_TYPE_CPRT, NULL); + if (!map) return GF_OK; + + count = gf_list_count(map->boxList); + if (index>count) return GF_BAD_PARAM; + + ptr = (GF_CopyrightBox*)gf_list_get(map->boxList, index-1); + if (ptr) { + gf_list_rem(map->boxList, index-1); + if (ptr->notice) gf_free(ptr->notice); + gf_free(ptr); + } + /*last copyright, remove*/ + if (!gf_list_count(map->boxList)) { + gf_list_del_item(movie->moov->udta->recordList, map); + gf_list_del(map->boxList); + gf_free(map); + } + return GF_OK; +} + + + +GF_Err gf_isom_set_watermark(GF_ISOFile *movie, bin128 UUID, u8* data, u32 length) +{ + GF_Err e; + GF_UnknownUUIDBox *ptr; + GF_UserDataMap *map; + + e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE); + if (e) return e; + + gf_isom_insert_moov(movie); + if (!movie->moov->udta) { + e = moov_AddBox((GF_Box*)movie->moov, gf_isom_box_new(GF_ISOM_BOX_TYPE_UDTA)); + if (e) return e; + } + + map = udta_getEntry(movie->moov->udta, GF_ISOM_BOX_TYPE_UUID, (bin128 *) & UUID); + if (map) { + ptr = (GF_UnknownUUIDBox *)gf_list_get(map->boxList, 0); + if (ptr) { + gf_free(ptr->data); + ptr->data = (char*)gf_malloc(length); + memcpy(ptr->data, data, length); + ptr->dataSize = length; + return GF_OK; + } + } + //nope, create one + ptr = (GF_UnknownUUIDBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_UUID); + memcpy(ptr->uuid, UUID, 16); + ptr->data = (char*)gf_malloc(length); + memcpy(ptr->data, data, length); + ptr->dataSize = length; + return udta_AddBox(movie->moov->udta, (GF_Box *) ptr); +} + +//set the interleaving time of media data (INTERLEAVED mode only) +//InterleaveTime is in MovieTimeScale +GF_Err gf_isom_set_interleave_time(GF_ISOFile *movie, u32 InterleaveTime) +{ + GF_Err e; + e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE); + if (e) return e; + + if (!InterleaveTime || !movie->moov) return GF_OK; + movie->interleavingTime = InterleaveTime; + return GF_OK; +} + +u32 gf_isom_get_interleave_time(GF_ISOFile *movie) +{ + return movie ? movie->interleavingTime : 0; +} + +//set the storage mode of a file (FLAT, STREAMABLE, INTERLEAVED) +u8 gf_isom_get_storage_mode(GF_ISOFile *movie) +{ + return movie ? movie->storageMode : 0; +} + + + + +//use a compact track version for sample size. This is not usually recommended +//except for speech codecs where the track has a lot of small samples +//compaction is done automatically while writing based on the track's sample sizes +GF_Err gf_isom_use_compact_size(GF_ISOFile *movie, u32 trackNumber, u8 CompactionOn) +{ + GF_TrackBox *trak; + u32 i, size; + GF_SampleSizeBox *stsz; + GF_Err e; + + e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE); + if (e) return e; + + trak = gf_isom_get_track_from_file(movie, trackNumber); + if (!trak) return GF_BAD_PARAM; + + if (!trak->Media || !trak->Media->information + || !trak->Media->information->sampleTable || !trak->Media->information->sampleTable->SampleSize) + return GF_ISOM_INVALID_FILE; + + stsz = trak->Media->information->sampleTable->SampleSize; + + //switch to regular table + if (!CompactionOn) { + if (stsz->type == GF_ISOM_BOX_TYPE_STSZ) return GF_OK; + stsz->type = GF_ISOM_BOX_TYPE_STSZ; + //invalidate the sampleSize and recompute it + stsz->sampleSize = 0; + if (!stsz->sampleCount) return GF_OK; + //if the table is empty we can only assume the track is empty (no size indication) + if (!stsz->sizes) return GF_OK; + size = stsz->sizes[0]; + //check whether the sizes are all the same or not + for (i=1; isampleCount; i++) { + if (size != stsz->sizes[i]) { + size = 0; + break; + } + } + if (size) { + gf_free(stsz->sizes); + stsz->sizes = NULL; + stsz->sampleSize = size; + } + return GF_OK; + } + + //switch to compact table + if (stsz->type == GF_ISOM_BOX_TYPE_STZ2) return GF_OK; + //fill the table. Although it seems weird , this is needed in case of edition + //after the function is called. NOte however than we force regular table + //at write time if all samples are of same size + if (stsz->sampleSize) { + //this is a weird table indeed ;) + if (stsz->sizes) gf_free(stsz->sizes); + stsz->sizes = (u32*) gf_malloc(sizeof(u32)*stsz->sampleCount); + memset(stsz->sizes, stsz->sampleSize, sizeof(u32)); + } + //set the SampleSize to 0 while the file is open + stsz->sampleSize = 0; + stsz->type = GF_ISOM_BOX_TYPE_STZ2; + return GF_OK; +} + + + +GF_Err gf_isom_set_brand_info(GF_ISOFile *movie, u32 MajorBrand, u32 MinorVersion) +{ + u32 i, *p; + GF_Err e; + + if (!MajorBrand) return GF_BAD_PARAM; + + if (! (movie->FragmentsFlags & GF_ISOM_FRAG_WRITE_READY)) { + e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE); + if (e) return e; + + e = CheckNoData(movie); + if (e) return e; + } + + if (!movie->brand) { + movie->brand = (GF_FileTypeBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_FTYP); + gf_list_add(movie->TopBoxes, movie->brand); + } + + movie->brand->majorBrand = MajorBrand; + movie->brand->minorVersion = MinorVersion; + + if (!movie->brand->altBrand) { + movie->brand->altBrand = (u32*)gf_malloc(sizeof(u32)); + movie->brand->altBrand[0] = MajorBrand; + movie->brand->altCount = 1; + return GF_OK; + } + + //if brand already present don't change anything + for (i=0; ibrand->altCount; i++) { + if (movie->brand->altBrand[i] == MajorBrand) return GF_OK; + } + p = (u32*)gf_malloc(sizeof(u32)*(movie->brand->altCount + 1)); + if (!p) return GF_OUT_OF_MEM; + memcpy(p, movie->brand->altBrand, sizeof(u32)*movie->brand->altCount); + p[movie->brand->altCount] = MajorBrand; + movie->brand->altCount += 1; + gf_free(movie->brand->altBrand); + movie->brand->altBrand = p; + return GF_OK; +} + +GF_Err gf_isom_modify_alternate_brand(GF_ISOFile *movie, u32 Brand, u8 AddIt) +{ + u32 i, k, *p; + GF_Err e; + + if (!Brand) return GF_BAD_PARAM; + + if (! (movie->FragmentsFlags & GF_ISOM_FRAG_WRITE_READY)) { + e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE); + if (e) return e; + + e = CheckNoData(movie); + if (e) return e; + } + + if (!movie->brand && AddIt) { + movie->brand = (GF_FileTypeBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_FTYP); + gf_list_add(movie->TopBoxes, movie->brand); + } + + //do not mofify major one + if (!AddIt && movie->brand->majorBrand == Brand) return GF_OK; + + if (!AddIt && movie->brand->altCount == 1) { + //fixes it in case + movie->brand->altBrand[0] = movie->brand->majorBrand; + return GF_OK; + } + //check for the brand + for (i=0; ibrand->altCount; i++) { + if (movie->brand->altBrand[i] == Brand) goto found; + } + //Not found + if (!AddIt) return GF_OK; + //add it + p = (u32*)gf_malloc(sizeof(u32)*(movie->brand->altCount + 1)); + if (!p) return GF_OUT_OF_MEM; + memcpy(p, movie->brand->altBrand, sizeof(u32)*movie->brand->altCount); + p[movie->brand->altCount] = Brand; + movie->brand->altCount += 1; + gf_free(movie->brand->altBrand); + movie->brand->altBrand = p; + return GF_OK; + +found: + + //found + if (AddIt) return GF_OK; + assert(movie->brand->altCount>1); + + //remove it + p = (u32*)gf_malloc(sizeof(u32)*(movie->brand->altCount - 1)); + if (!p) return GF_OUT_OF_MEM; + k = 0; + for (i=0; ibrand->altCount; i++) { + if (movie->brand->altBrand[i] == Brand) continue; + else { + p[k] = movie->brand->altBrand[i]; + k++; + } + } + movie->brand->altCount -= 1; + gf_free(movie->brand->altBrand); + movie->brand->altBrand = p; + return GF_OK; +} + + +GF_Err gf_isom_reset_alt_brands(GF_ISOFile *movie) +{ + u32 *p; + GF_Err e; + + if (! (movie->FragmentsFlags & GF_ISOM_FRAG_WRITE_READY)) { + e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE); + if (e) return e; + + e = CheckNoData(movie); + if (e) return e; + } + + if (!movie->brand) { + movie->brand = (GF_FileTypeBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_FTYP); + gf_list_add(movie->TopBoxes, movie->brand); + } + p = (u32*)gf_malloc(sizeof(u32)); + if (!p) return GF_OUT_OF_MEM; + p[0] = movie->brand->majorBrand; + movie->brand->altCount = 1; + gf_free(movie->brand->altBrand); + movie->brand->altBrand = p; + return GF_OK; +} + +GF_Err gf_isom_set_sample_padding_bits(GF_ISOFile *movie, u32 trackNumber, u32 sampleNumber, u8 NbBits) +{ + GF_TrackBox *trak; + GF_Err e; + + e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE); + if (e) return e; + + trak = gf_isom_get_track_from_file(movie, trackNumber); + if (!trak || NbBits > 7) return GF_BAD_PARAM; + + //set Padding info + return stbl_SetPaddingBits(trak->Media->information->sampleTable, sampleNumber, NbBits); +} + + +GF_Err gf_isom_remove_user_data_item(GF_ISOFile *movie, u32 trackNumber, u32 UserDataType, bin128 UUID, u32 UserDataIndex) +{ + GF_UserDataMap *map; + GF_Box *a; + u32 i; + bin128 t; + GF_Err e; + GF_TrackBox *trak; + GF_UserDataBox *udta; + + e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE); + if (e) return e; + + if (UserDataType == GF_ISOM_BOX_TYPE_UUID) UserDataType = 0; + memset(t, 1, 16); + + if (trackNumber) { + trak = gf_isom_get_track_from_file(movie, trackNumber); + if (!trak) return GF_BAD_PARAM; + udta = trak->udta; + } else { + udta = movie->moov->udta; + } + if (!udta) return GF_BAD_PARAM; + if (!UserDataIndex) return GF_BAD_PARAM; + + i=0; + while ((map = (GF_UserDataMap*)gf_list_enum(udta->recordList, &i))) { + if ((map->boxType == GF_ISOM_BOX_TYPE_UUID) && !memcmp(map->uuid, UUID, 16)) goto found; + else if (map->boxType == UserDataType) goto found; + } + //not found + return GF_OK; + +found: + + if (UserDataIndex > gf_list_count(map->boxList) ) return GF_BAD_PARAM; + //delete the box + a = (GF_Box*)gf_list_get(map->boxList, UserDataIndex-1); + + gf_list_rem(map->boxList, UserDataIndex-1); + gf_isom_box_del(a); + + //remove the map if empty + if (!gf_list_count(map->boxList)) { + gf_list_rem(udta->recordList, i-1); + gf_isom_box_array_del(map->boxList); + gf_free(map); + } + //but we keep the UDTA no matter what + return GF_OK; +} + +GF_Err gf_isom_remove_user_data(GF_ISOFile *movie, u32 trackNumber, u32 UserDataType, bin128 UUID) +{ + GF_UserDataMap *map; + u32 i; + GF_Err e; + bin128 t; + GF_TrackBox *trak; + GF_UserDataBox *udta; + + e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE); + if (e) return e; + + if (UserDataType == GF_ISOM_BOX_TYPE_UUID) UserDataType = 0; + memset(t, 1, 16); + + if (trackNumber) { + trak = gf_isom_get_track_from_file(movie, trackNumber); + if (!trak) return GF_BAD_PARAM; + udta = trak->udta; + } else { + udta = movie->moov->udta; + } + if (!udta) return GF_BAD_PARAM; + + i=0; + while ((map = (GF_UserDataMap*)gf_list_enum(udta->recordList, &i))) { + if ((map->boxType == GF_ISOM_BOX_TYPE_UUID) && !memcmp(map->uuid, UUID, 16)) goto found; + else if (map->boxType == UserDataType) goto found; + } + //not found + return GF_OK; + +found: + + gf_list_rem(udta->recordList, i-1); + gf_isom_box_array_del(map->boxList); + gf_free(map); + + //but we keep the UDTA no matter what + return GF_OK; +} + +GF_Err gf_isom_add_user_data(GF_ISOFile *movie, u32 trackNumber, u32 UserDataType, bin128 UUID, char *data, u32 DataLength) +{ + GF_UnknownBox *a; + GF_Err e; + GF_TrackBox *trak; + GF_UserDataBox *udta; + + e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE); + if (e) return e; + + if (UserDataType == GF_ISOM_BOX_TYPE_UUID) UserDataType = 0; + + if (trackNumber) { + trak = gf_isom_get_track_from_file(movie, trackNumber); + if (!trak) return GF_BAD_PARAM; + if (!trak->udta) trak_AddBox((GF_Box*)trak, gf_isom_box_new(GF_ISOM_BOX_TYPE_UDTA)); + udta = trak->udta; + } else { + if (!movie->moov->udta) moov_AddBox((GF_Box*)movie->moov, gf_isom_box_new(GF_ISOM_BOX_TYPE_UDTA)); + udta = movie->moov->udta; + } + if (!udta) return GF_OUT_OF_MEM; + + //create a default box + if (UserDataType) { + a = (GF_UnknownBox *) gf_isom_box_new(UserDataType); + } else { + a = (GF_UnknownBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_UUID); + memcpy( ((GF_UUIDBox*)a)->uuid, UUID, 16); + } + + if (DataLength) { + a->data = (char*)gf_malloc(sizeof(char)*DataLength); + memcpy(a->data, data, DataLength); + a->dataSize = DataLength; + } + return udta_AddBox(udta, (GF_Box *) a); +} + + +GF_Err gf_isom_add_sample_fragment(GF_ISOFile *movie, u32 trackNumber, u32 sampleNumber, u16 FragmentSize) +{ + GF_Err e; + GF_TrackBox *trak; + + e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE); + if (e) return e; + + trak = gf_isom_get_track_from_file(movie, trackNumber); + if (!trak || !sampleNumber || !FragmentSize) return GF_BAD_PARAM; + + //set Padding info + return stbl_AddSampleFragment(trak->Media->information->sampleTable, sampleNumber, FragmentSize); +} + + +GF_Err gf_isom_remove_sample_fragment(GF_ISOFile *movie, u32 trackNumber, u32 sampleNumber) +{ + GF_TrackBox *trak; + GF_Err e; + + e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE); + if (e) return e; + + trak = gf_isom_get_track_from_file(movie, trackNumber); + if (!trak) return GF_BAD_PARAM; + return stbl_RemoveSampleFragments(trak->Media->information->sampleTable, sampleNumber); +} + +GF_Err gf_isom_remove_sample_fragments(GF_ISOFile *movie, u32 trackNumber) +{ + GF_TrackBox *trak; + GF_Err e; + + e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE); + if (e) return e; + + trak = gf_isom_get_track_from_file(movie, trackNumber); + if (!trak) return GF_BAD_PARAM; + + if (trak->Media->information->sampleTable->Fragments) { + gf_isom_box_del((GF_Box *)trak->Media->information->sampleTable->Fragments); + trak->Media->information->sampleTable->Fragments = NULL; + } + return GF_OK; +} + +GF_Err gf_isom_clone_pl_indications(GF_ISOFile *orig, GF_ISOFile *dest) +{ + GF_IsomInitialObjectDescriptor *iod_d; + if (!orig || !dest) return GF_BAD_PARAM; + if (!orig->moov->iods || !orig->moov->iods->descriptor) return GF_OK; + if (orig->moov->iods->descriptor->tag != GF_ODF_ISOM_IOD_TAG) return GF_OK; + + AddMovieIOD(dest->moov, 1); + gf_odf_desc_del((GF_Descriptor *)dest->moov->iods->descriptor); + gf_odf_desc_copy((GF_Descriptor *)orig->moov->iods->descriptor, (GF_Descriptor **)&dest->moov->iods->descriptor); + iod_d = (GF_IsomInitialObjectDescriptor *) dest->moov->iods->descriptor; + while (gf_list_count(iod_d->ES_ID_IncDescriptors)) { + GF_Descriptor *d = (GF_Descriptor *)gf_list_get(iod_d->ES_ID_IncDescriptors, 0); + gf_list_rem(iod_d->ES_ID_IncDescriptors, 0); + gf_odf_desc_del(d); + } + while (gf_list_count(iod_d->ES_ID_RefDescriptors)) { + GF_Descriptor *d = (GF_Descriptor *)gf_list_get(iod_d->ES_ID_RefDescriptors, 0); + gf_list_rem(iod_d->ES_ID_RefDescriptors, 0); + gf_odf_desc_del(d); + } + return GF_OK; +} + +static GF_Err clone_box(GF_Box *src, GF_Box **dst) +{ + GF_Err e; + char *data; + u32 data_size; + GF_BitStream *bs; + + if (*dst) { + gf_isom_box_del(*dst); + *dst=NULL; + } + bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); + if (!bs) return GF_OUT_OF_MEM; + e = gf_isom_box_size( (GF_Box *) src); + if (!e) e = gf_isom_box_write((GF_Box *) src, bs); + gf_bs_get_content(bs, &data, &data_size); + gf_bs_del(bs); + if (e) return e; + bs = gf_bs_new(data, data_size, GF_BITSTREAM_READ); + if (!bs) return GF_OUT_OF_MEM; + e = gf_isom_parse_box(dst, bs); + gf_bs_del(bs); + gf_free(data); + return e; +} +GF_Err gf_isom_clone_movie(GF_ISOFile *orig_file, GF_ISOFile *dest_file, Bool clone_tracks, Bool keep_hint_tracks) +{ + GF_Err e; + + e = CanAccessMovie(dest_file, GF_ISOM_OPEN_WRITE); + if (e) return e; + + if (orig_file->brand) { + gf_list_del_item(dest_file->TopBoxes, dest_file->brand); + gf_isom_box_del((GF_Box *)dest_file->brand); + dest_file->brand = NULL; + clone_box((GF_Box *)orig_file->brand, (GF_Box **)&dest_file->brand); + if (dest_file->brand) gf_list_add(dest_file->TopBoxes, dest_file->brand); + } + + if (orig_file->meta) { + gf_list_del_item(dest_file->TopBoxes, dest_file->meta); + gf_isom_box_del((GF_Box *)dest_file->meta); + dest_file->meta = NULL; + /*fixme - check imports*/ + clone_box((GF_Box *)orig_file->meta, (GF_Box **)dest_file->meta); + if (dest_file->meta) gf_list_add(dest_file->TopBoxes, dest_file->meta); + } + if (orig_file->moov) { + u32 i, dstTrack; + GF_Box *iods; + GF_List *tracks = gf_list_new(); + GF_List *old_tracks = orig_file->moov->trackList; + orig_file->moov->trackList = tracks; + iods = (GF_Box*)orig_file->moov->iods; + orig_file->moov->iods = NULL; + clone_box((GF_Box *)orig_file->moov, (GF_Box **)&dest_file->moov); + orig_file->moov->trackList = old_tracks; + gf_list_del(tracks); + orig_file->moov->iods = (GF_ObjectDescriptorBox*)iods; + if (clone_tracks) { + for (i=0; imoov->trackList); i++) { + GF_TrackBox *trak = (GF_TrackBox *) gf_list_get( orig_file->moov->trackList, i); + if (!trak) continue; + if (keep_hint_tracks || (trak->Media->handler->handlerType != GF_ISOM_MEDIA_HINT)) { + e = gf_isom_clone_track(orig_file, i+1, dest_file, 1, &dstTrack); + if (e) return e; + } + } + if (iods) + clone_box((GF_Box *)orig_file->moov->iods, (GF_Box **)dest_file->moov->iods); + } else { + dest_file->moov->mvhd->nextTrackID = 1; + gf_isom_clone_pl_indications(orig_file, dest_file); + } + dest_file->moov->mov = dest_file; + } + return GF_OK; +} + + + +GF_Err gf_isom_clone_track(GF_ISOFile *orig_file, u32 orig_track, GF_ISOFile *dest_file, Bool keep_data_ref, u32 *dest_track) +{ + GF_TrackBox *trak, *new_tk; + GF_BitStream *bs; + char *data; + u32 data_size; + Double ts_scale; + GF_Err e; + GF_SampleEntryBox *entry; + GF_SampleTableBox *stbl, *stbl_temp; + + e = CanAccessMovie(dest_file, GF_ISOM_OPEN_WRITE); + if (e) return e; + gf_isom_insert_moov(dest_file); + + /*get orig sample desc and clone it*/ + trak = gf_isom_get_track_from_file(orig_file, orig_track); + if (!trak || !trak->Media) return GF_BAD_PARAM; + + stbl = trak->Media->information->sampleTable; + stbl_temp = (GF_SampleTableBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_STBL); + stbl_temp->SampleDescription = stbl->SampleDescription; + trak->Media->information->sampleTable = stbl_temp; + + bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); + + gf_isom_box_size( (GF_Box *) trak); + gf_isom_box_write((GF_Box *) trak, bs); + gf_bs_get_content(bs, &data, &data_size); + gf_bs_del(bs); + bs = gf_bs_new(data, data_size, GF_BITSTREAM_READ); + e = gf_isom_parse_box((GF_Box **) &new_tk, bs); + gf_bs_del(bs); + gf_free(data); + trak->Media->information->sampleTable = stbl; + + stbl_temp->SampleDescription = NULL; + gf_isom_box_del((GF_Box *)stbl_temp); + if (e) return e; + + /*create default boxes*/ + stbl = new_tk->Media->information->sampleTable; + stbl->ChunkOffset = gf_isom_box_new(GF_ISOM_BOX_TYPE_STCO); + stbl->SampleSize = (GF_SampleSizeBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_STSZ); + stbl->SampleToChunk = (GF_SampleToChunkBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_STSC); + stbl->TimeToSample = (GF_TimeToSampleBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_STTS); + + /*check trackID validity before adding track*/ + if (gf_isom_get_track_by_id(dest_file, new_tk->Header->trackID)) { + u32 ID = 1; + while (1) { + if (RequestTrack(dest_file->moov, ID)) break; + ID += 1; + if (ID == 0xFFFFFFFF) break; + } + new_tk->Header->trackID = ID; + } + + moov_AddBox((GF_Box*)dest_file->moov, (GF_Box *)new_tk); + + /*rewrite edit list segmentDuration to new movie timescale*/ + ts_scale = dest_file->moov->mvhd->timeScale; + ts_scale /= orig_file->moov->mvhd->timeScale; + new_tk->Header->duration = (u64) (s64) ((s64) new_tk->Header->duration * ts_scale); + if (new_tk->editBox && new_tk->editBox->editList) { + u32 i, count = gf_list_count(new_tk->editBox->editList->entryList); + for (i=0; ieditBox->editList->entryList, i); + ent->segmentDuration = (u64) (s64) ((s64) ent->segmentDuration * ts_scale); + } + } + + /*reset data ref*/ + if (!keep_data_ref) { + gf_isom_box_array_del(new_tk->Media->information->dataInformation->dref->boxList); + new_tk->Media->information->dataInformation->dref->boxList = gf_list_new(); + /*update data ref*/ + entry = (GF_SampleEntryBox*)gf_list_get(new_tk->Media->information->sampleTable->SampleDescription->boxList, 0); + if (entry) { + u32 dref; + Media_CreateDataRef(new_tk->Media->information->dataInformation->dref, NULL, NULL, &dref); + entry->dataReferenceIndex = dref; + } + } + + *dest_track = gf_list_count(dest_file->moov->trackList); + + if (dest_file->moov->mvhd->nextTrackID<= new_tk->Header->trackID) + dest_file->moov->mvhd->nextTrackID = new_tk->Header->trackID+1; + + return GF_OK; +} + + +GF_Err gf_isom_clone_sample_description(GF_ISOFile *the_file, u32 trackNumber, GF_ISOFile *orig_file, u32 orig_track, u32 orig_desc_index, char *URLname, char *URNname, u32 *outDescriptionIndex) +{ + GF_TrackBox *trak; + GF_BitStream *bs; + char *data; + u32 data_size; + GF_Box *entry; + GF_Err e; + u32 dataRefIndex; + + e = CanAccessMovie(the_file, GF_ISOM_OPEN_WRITE); + if (e) return e; + + /*get orig sample desc and clone it*/ + trak = gf_isom_get_track_from_file(orig_file, orig_track); + if (!trak || !trak->Media) return GF_BAD_PARAM; + + entry = (GF_Box*)gf_list_get(trak->Media->information->sampleTable->SampleDescription->boxList, orig_desc_index-1); + if (!entry) return GF_BAD_PARAM; + + bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); + + gf_isom_box_size(entry); + gf_isom_box_write(entry, bs); + gf_bs_get_content(bs, &data, &data_size); + gf_bs_del(bs); + bs = gf_bs_new(data, data_size, GF_BITSTREAM_READ); + e = gf_isom_parse_box(&entry, bs); + gf_bs_del(bs); + gf_free(data); + if (e) return e; + + /*get new track and insert clone*/ + trak = gf_isom_get_track_from_file(the_file, trackNumber); + if (!trak || !trak->Media) goto exit; + + /*get or create the data ref*/ + e = Media_FindDataRef(trak->Media->information->dataInformation->dref, URLname, URNname, &dataRefIndex); + if (e) goto exit; + if (!dataRefIndex) { + e = Media_CreateDataRef(trak->Media->information->dataInformation->dref, URLname, URNname, &dataRefIndex); + if (e) goto exit; + } + trak->Media->mediaHeader->modificationTime = gf_isom_get_mp4time(); + /*overwrite dref*/ + ((GF_SampleEntryBox *)entry)->dataReferenceIndex = dataRefIndex; + e = gf_list_add(trak->Media->information->sampleTable->SampleDescription->boxList, entry); + *outDescriptionIndex = gf_list_count(trak->Media->information->sampleTable->SampleDescription->boxList); + + /*also clone track w/h info*/ + if (gf_isom_get_media_type(the_file, trackNumber) == GF_ISOM_MEDIA_VISUAL) { + gf_isom_set_visual_info(the_file, trackNumber, (*outDescriptionIndex), ((GF_VisualSampleEntryBox*)entry)->Width, ((GF_VisualSampleEntryBox*)entry)->Height); + } + return e; + +exit: + gf_isom_box_del(entry); + return e; +} + + +GF_Err gf_isom_new_generic_sample_description(GF_ISOFile *movie, u32 trackNumber, char *URLname, char *URNname, GF_GenericSampleDescription *udesc, u32 *outDescriptionIndex) +{ + GF_TrackBox *trak; + GF_Err e; + u32 dataRefIndex; + + e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE); + if (e) return e; + + trak = gf_isom_get_track_from_file(movie, trackNumber); + if (!trak || !trak->Media || !udesc) return GF_BAD_PARAM; + + //get or create the data ref + e = Media_FindDataRef(trak->Media->information->dataInformation->dref, URLname, URNname, &dataRefIndex); + if (e) return e; + if (!dataRefIndex) { + e = Media_CreateDataRef(trak->Media->information->dataInformation->dref, URLname, URNname, &dataRefIndex); + if (e) return e; + } + trak->Media->mediaHeader->modificationTime = gf_isom_get_mp4time(); + + if (trak->Media->handler->handlerType==GF_ISOM_MEDIA_VISUAL) { + GF_GenericVisualSampleEntryBox *entry; + //create a new entry + entry = (GF_GenericVisualSampleEntryBox*) gf_isom_box_new(GF_ISOM_BOX_TYPE_GNRV); + if (!entry) return GF_OUT_OF_MEM; + + if (!udesc->codec_tag) { + entry->EntryType = GF_ISOM_BOX_TYPE_UUID; + memcpy(entry->uuid, udesc->UUID, sizeof(bin128)); + } else { + entry->EntryType = udesc->codec_tag; + } + entry->dataReferenceIndex = dataRefIndex; + entry->vendor = udesc->vendor_code; + entry->version = udesc->version; + entry->revision = udesc->revision; + entry->temporal_quality = udesc->temporal_quality; + entry->spacial_quality = udesc->spacial_quality; + entry->Width = udesc->width; + entry->Height = udesc->height; + strcpy(entry->compressor_name, udesc->compressor_name); + entry->color_table_index = -1; + entry->frames_per_sample = 1; + entry->horiz_res = udesc->h_res ? udesc->h_res : 0x00480000; + entry->vert_res = udesc->v_res ? udesc->v_res : 0x00480000; + entry->bit_depth = udesc->depth ? udesc->depth : 0x18; + if (udesc->extension_buf && udesc->extension_buf_size) { + entry->data = (char*)gf_malloc(sizeof(char) * udesc->extension_buf_size); + if (!entry->data) { + gf_isom_box_del((GF_Box *) entry); + return GF_OUT_OF_MEM; + } + memcpy(entry->data, udesc->extension_buf, udesc->extension_buf_size); + entry->data_size = udesc->extension_buf_size; + } + e = gf_list_add(trak->Media->information->sampleTable->SampleDescription->boxList, entry); + } + else if (trak->Media->handler->handlerType==GF_ISOM_MEDIA_AUDIO) { + GF_GenericAudioSampleEntryBox *gena; + //create a new entry + gena = (GF_GenericAudioSampleEntryBox*) gf_isom_box_new(GF_ISOM_BOX_TYPE_GNRA); + if (!gena) return GF_OUT_OF_MEM; + + if (!udesc->codec_tag) { + gena->EntryType = GF_ISOM_BOX_TYPE_UUID; + memcpy(gena->uuid, udesc->UUID, sizeof(bin128)); + } else { + gena->EntryType = udesc->codec_tag; + } + gena->dataReferenceIndex = dataRefIndex; + gena->vendor = udesc->vendor_code; + gena->version = udesc->version; + gena->revision = udesc->revision; + gena->bitspersample = udesc->bits_per_sample ? udesc->bits_per_sample : 16; + gena->channel_count = udesc->nb_channels ? udesc->nb_channels : 2; + gena->samplerate_hi = udesc->samplerate>>16; + gena->samplerate_lo = udesc->samplerate & 0xFF; + + if (udesc->extension_buf && udesc->extension_buf_size) { + gena->data = (char*)gf_malloc(sizeof(char) * udesc->extension_buf_size); + if (!gena->data) { + gf_isom_box_del((GF_Box *) gena); + return GF_OUT_OF_MEM; + } + memcpy(gena->data, udesc->extension_buf, udesc->extension_buf_size); + gena->data_size = udesc->extension_buf_size; + } + e = gf_list_add(trak->Media->information->sampleTable->SampleDescription->boxList, gena); + } + else { + GF_GenericSampleEntryBox *genm; + //create a new entry + genm = (GF_GenericSampleEntryBox*) gf_isom_box_new(GF_ISOM_BOX_TYPE_GNRM); + if (!genm) return GF_OUT_OF_MEM; + + if (!udesc->codec_tag) { + genm->EntryType = GF_ISOM_BOX_TYPE_UUID; + memcpy(genm->uuid, udesc->UUID, sizeof(bin128)); + } else { + genm->EntryType = udesc->codec_tag; + } + genm->dataReferenceIndex = dataRefIndex; + if (udesc->extension_buf && udesc->extension_buf_size) { + genm->data = (char*)gf_malloc(sizeof(char) * udesc->extension_buf_size); + if (!genm->data) { + gf_isom_box_del((GF_Box *) genm); + return GF_OUT_OF_MEM; + } + memcpy(genm->data, udesc->extension_buf, udesc->extension_buf_size); + genm->data_size = udesc->extension_buf_size; + } + e = gf_list_add(trak->Media->information->sampleTable->SampleDescription->boxList, genm); + } + *outDescriptionIndex = gf_list_count(trak->Media->information->sampleTable->SampleDescription->boxList); + return e; +} + +//use carefully. Very usefull when you made a lot of changes (IPMP, IPI, OCI, ...) +//THIS WILL REPLACE THE WHOLE DESCRIPTOR ... +GF_Err gf_isom_change_generic_sample_description(GF_ISOFile *movie, u32 trackNumber, u32 StreamDescriptionIndex, GF_GenericSampleDescription *udesc) +{ + GF_TrackBox *trak; + GF_Err e; + GF_GenericVisualSampleEntryBox *entry; + + e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE); + if (e) return e; + + trak = gf_isom_get_track_from_file(movie, trackNumber); + if (!trak || !trak->Media || !StreamDescriptionIndex) return GF_BAD_PARAM; + + entry = (GF_GenericVisualSampleEntryBox *)gf_list_get(trak->Media->information->sampleTable->SampleDescription->boxList, StreamDescriptionIndex-1); + if (!entry) return GF_BAD_PARAM; + if (entry->type == GF_ISOM_BOX_TYPE_GNRV) { + entry->vendor = udesc->vendor_code; + entry->version = udesc->version; + entry->revision = udesc->revision; + entry->temporal_quality = udesc->temporal_quality; + entry->spacial_quality = udesc->spacial_quality; + entry->Width = udesc->width; + entry->Height = udesc->height; + strcpy(entry->compressor_name, udesc->compressor_name); + entry->color_table_index = -1; + entry->frames_per_sample = 1; + entry->horiz_res = udesc->h_res ? udesc->h_res : 0x00480000; + entry->vert_res = udesc->v_res ? udesc->v_res : 0x00480000; + entry->bit_depth = udesc->depth ? udesc->depth : 0x18; + if (entry->data) gf_free(entry->data); + entry->data = NULL; + entry->data_size = 0; + if (udesc->extension_buf && udesc->extension_buf_size) { + entry->data = (char*)gf_malloc(sizeof(char) * udesc->extension_buf_size); + if (!entry->data) { + gf_isom_box_del((GF_Box *) entry); + return GF_OUT_OF_MEM; + } + memcpy(entry->data, udesc->extension_buf, udesc->extension_buf_size); + entry->data_size = udesc->extension_buf_size; + } + return GF_OK; + } else if (entry->type == GF_ISOM_BOX_TYPE_GNRA) { + GF_GenericAudioSampleEntryBox *gena = (GF_GenericAudioSampleEntryBox *)entry; + gena->vendor = udesc->vendor_code; + gena->version = udesc->version; + gena->revision = udesc->revision; + gena->bitspersample = udesc->bits_per_sample ? udesc->bits_per_sample : 16; + gena->channel_count = udesc->nb_channels ? udesc->nb_channels : 2; + gena->samplerate_hi = udesc->samplerate>>16; + gena->samplerate_lo = udesc->samplerate & 0xFF; + if (gena->data) gf_free(gena->data); + gena->data = NULL; + gena->data_size = 0; + + if (udesc->extension_buf && udesc->extension_buf_size) { + gena->data = (char*)gf_malloc(sizeof(char) * udesc->extension_buf_size); + if (!gena->data) { + gf_isom_box_del((GF_Box *) gena); + return GF_OUT_OF_MEM; + } + memcpy(gena->data, udesc->extension_buf, udesc->extension_buf_size); + gena->data_size = udesc->extension_buf_size; + } + return GF_OK; + } else if (entry->type == GF_ISOM_BOX_TYPE_GNRM) { + GF_GenericSampleEntryBox *genm = (GF_GenericSampleEntryBox *)entry; + if (genm->data) gf_free(genm->data); + genm->data = NULL; + genm->data_size = 0; + + if (udesc->extension_buf && udesc->extension_buf_size) { + genm->data = (char*)gf_malloc(sizeof(char) * udesc->extension_buf_size); + if (!genm->data) { + gf_isom_box_del((GF_Box *) genm); + return GF_OUT_OF_MEM; + } + memcpy(genm->data, udesc->extension_buf, udesc->extension_buf_size); + genm->data_size = udesc->extension_buf_size; + } + return GF_OK; + } + return GF_BAD_PARAM; +} + +/*removes given stream description*/ +GF_Err gf_isom_remove_sample_description(GF_ISOFile *movie, u32 trackNumber, u32 streamDescIndex) +{ + GF_TrackBox *trak; + GF_Err e; + GF_Box *entry; + + e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE); + if (e) return e; + trak = gf_isom_get_track_from_file(movie, trackNumber); + if (!trak || !trak->Media || !streamDescIndex) return GF_BAD_PARAM; + entry = (GF_Box*)gf_list_get(trak->Media->information->sampleTable->SampleDescription->boxList, streamDescIndex-1); + if (!entry) return GF_BAD_PARAM; + gf_list_rem(trak->Media->information->sampleTable->SampleDescription->boxList, streamDescIndex-1); + gf_isom_box_del(entry); + return GF_OK; +} + + +//sets a track reference +GF_Err gf_isom_set_track_reference(GF_ISOFile *the_file, u32 trackNumber, u32 referenceType, u32 ReferencedTrackID) +{ + GF_Err e; + GF_TrackBox *trak; + GF_TrackReferenceBox *tref; + GF_TrackReferenceTypeBox *dpnd; + + trak = gf_isom_get_track_from_file(the_file, trackNumber); + if (!trak) return GF_BAD_PARAM; + + //no tref, create one + tref = trak->References; + if (!tref) { + tref = (GF_TrackReferenceBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_TREF); + e = trak_AddBox((GF_Box*)trak, (GF_Box *) tref); + if (e) return e; + } + //find a ref of the given type + e = Track_FindRef(trak, referenceType, &dpnd); + if (!dpnd) { + dpnd = (GF_TrackReferenceTypeBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_REFT); + dpnd->reference_type = referenceType; + e = tref_AddBox((GF_Box*)tref, (GF_Box *)dpnd); + if (e) return e; + } + //add the ref + return reftype_AddRefTrack(dpnd, ReferencedTrackID, NULL); +} + +//removes a track reference +GF_Err gf_isom_remove_track_reference(GF_ISOFile *the_file, u32 trackNumber, u32 referenceType, u32 ReferenceIndex) +{ + GF_Err e; + GF_TrackBox *trak; + GF_TrackReferenceBox *tref; + GF_TrackReferenceTypeBox *dpnd, *tmp; + u32 i, k, *newIDs; + trak = gf_isom_get_track_from_file(the_file, trackNumber); + if (!trak || !ReferenceIndex) return GF_BAD_PARAM; + + //no tref, nothing to remove + tref = trak->References; + if (!tref) return GF_OK; + //find a ref of the given type otherwise return + e = Track_FindRef(trak, referenceType, &dpnd); + if (e || !dpnd) return GF_OK; + //remove the ref + if (ReferenceIndex > dpnd->trackIDCount) return GF_BAD_PARAM; + //last one + if (dpnd->trackIDCount==1) { + i=0; + while ((tmp = (GF_TrackReferenceTypeBox *)gf_list_enum(tref->boxList, &i))) { + if (tmp==dpnd) { + gf_list_rem(tref->boxList, i-1); + gf_isom_box_del((GF_Box *) dpnd); + return GF_OK; + } + } + } + k = 0; + newIDs = (u32*)gf_malloc(sizeof(u32)*(dpnd->trackIDCount-1)); + for (i=0; itrackIDCount; i++) { + if (i+1 != ReferenceIndex) { + newIDs[k] = dpnd->trackIDs[i]; + k++; + } + } + gf_free(dpnd->trackIDs); + dpnd->trackIDCount -= 1; + dpnd->trackIDs = newIDs; + return GF_OK; +} + + +//changes track ID +GF_Err gf_isom_set_track_id(GF_ISOFile *movie, u32 trackNumber, u32 trackID) +{ + GF_TrackReferenceTypeBox *ref; + GF_TrackBox *trak, *a_trak; + u32 i, j, k; + + trak = gf_isom_get_track_from_file(movie, trackNumber); + if (trak && (trak->Header->trackID==trackID)) return GF_OK; + a_trak = gf_isom_get_track_from_id(movie->moov, trackID); + if (!movie || !trak || a_trak) return GF_BAD_PARAM; + + if (movie->moov->mvhd->nextTrackID<=trackID) + movie->moov->mvhd->nextTrackID = trackID; + + /*rewrite all dependencies*/ + i=0; + while ((a_trak = (GF_TrackBox*)gf_list_enum(movie->moov->trackList, &i))) { + if (!a_trak->References) continue; + j=0; + while ((ref = (GF_TrackReferenceTypeBox *)gf_list_enum(a_trak->References->boxList, &j))) { + for (k=0; ktrackIDCount; k++) { + if (ref->trackIDs[k]==trak->Header->trackID) { + ref->trackIDs[k] = trackID; + break; + } + } + } + } + + /*and update IOD if any*/ + if (movie->moov->iods && movie->moov->iods->descriptor) { + GF_ES_ID_Inc *inc; + GF_IsomObjectDescriptor *od = (GF_IsomObjectDescriptor *)movie->moov->iods->descriptor; + u32 i = 0; + while ((inc = (GF_ES_ID_Inc*)gf_list_enum(od->ES_ID_IncDescriptors, &i))) { + if (inc->trackID==trak->Header->trackID) inc->trackID = trackID; + } + } + trak->Header->trackID = trackID; + return GF_OK; +} + + +GF_EXPORT +GF_Err gf_isom_modify_cts_offset(GF_ISOFile *the_file, u32 trackNumber, u32 sample_number, u32 offset) +{ + GF_TrackBox *trak = gf_isom_get_track_from_file(the_file, trackNumber); + if (!trak) return GF_BAD_PARAM; + if (!trak->Media->information->sampleTable->CompositionOffset) return GF_BAD_PARAM; + if (!trak->Media->information->sampleTable->CompositionOffset->unpack_mode) return GF_BAD_PARAM; + /*we're in unpack mode: one entry per sample*/ + trak->Media->information->sampleTable->CompositionOffset->entries[sample_number - 1].decodingOffset = offset; + return GF_OK; +} + +GF_Err gf_isom_remove_cts_info(GF_ISOFile *the_file, u32 trackNumber) +{ + GF_TrackBox *trak = gf_isom_get_track_from_file(the_file, trackNumber); + if (!trak) return GF_BAD_PARAM; + if (!trak->Media->information->sampleTable->CompositionOffset) return GF_OK; + + gf_isom_box_del((GF_Box *)trak->Media->information->sampleTable->CompositionOffset); + trak->Media->information->sampleTable->CompositionOffset = NULL; + return GF_OK; +} + +GF_Err gf_isom_set_cts_packing(GF_ISOFile *the_file, u32 trackNumber, Bool unpack) +{ + GF_Err e; + GF_Err stbl_repackCTS(GF_CompositionOffsetBox *ctts); + GF_Err stbl_unpackCTS(GF_SampleTableBox *stbl); + + GF_TrackBox *trak = gf_isom_get_track_from_file(the_file, trackNumber); + if (!trak) return GF_BAD_PARAM; + if (unpack) { + if (!trak->Media->information->sampleTable->CompositionOffset) trak->Media->information->sampleTable->CompositionOffset = (GF_CompositionOffsetBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_CTTS); + e = stbl_unpackCTS(trak->Media->information->sampleTable); + } else { + if (!trak->Media->information->sampleTable->CompositionOffset) return GF_OK; + e = stbl_repackCTS(trak->Media->information->sampleTable->CompositionOffset); + } + if (e) return e; + return SetTrackDuration(trak); +} + +GF_EXPORT +GF_Err gf_isom_set_track_matrix(GF_ISOFile *the_file, u32 trackNumber, u32 matrix[9]) +{ + GF_TrackBox *trak = gf_isom_get_track_from_file(the_file, trackNumber); + if (!trak || !trak->Header) return GF_BAD_PARAM; + memcpy(trak->Header->matrix, matrix, sizeof(trak->Header->matrix)); + return GF_OK; +} + +GF_Err gf_isom_set_track_layout_info(GF_ISOFile *the_file, u32 trackNumber, u32 width, u32 height, s32 translation_x, s32 translation_y, s16 layer) +{ + GF_TrackBox *trak = gf_isom_get_track_from_file(the_file, trackNumber); + if (!trak || !trak->Header) return GF_BAD_PARAM; + trak->Header->width = width; + trak->Header->height = height; + trak->Header->matrix[6] = translation_x; + trak->Header->matrix[7] = translation_y; + trak->Header->layer = layer; + return GF_OK; +} + +GF_Err gf_isom_set_track_name(GF_ISOFile *the_file, u32 trackNumber, char *name) +{ + GF_TrackBox *trak = gf_isom_get_track_from_file(the_file, trackNumber); + if (!trak) return GF_BAD_PARAM; + if (trak->name) gf_free(trak->name); + trak->name = NULL; + if (name) trak->name = gf_strdup(name); + return GF_OK; +} +const char *gf_isom_get_track_name(GF_ISOFile *the_file, u32 trackNumber) +{ + GF_TrackBox *trak = gf_isom_get_track_from_file(the_file, trackNumber); + if (!trak) return NULL; + return trak->name; +} + + +GF_Err gf_isom_store_movie_config(GF_ISOFile *movie, Bool remove_all) +{ + u32 i, count, len; + char *data; + GF_BitStream *bs; + bin128 binID; + if (movie == NULL) return GF_BAD_PARAM; + + gf_isom_remove_user_data(movie, 0, GF_4CC('G','P','A','C'), binID); + count = gf_isom_get_track_count(movie); + for (i=0; istorageMode); + gf_bs_write_u32(bs, movie->interleavingTime); + gf_bs_get_content(bs, &data, &len); + gf_bs_del(bs); + gf_isom_add_user_data(movie, 0, GF_4CC('G','P','A','C'), binID, data, len); + gf_free(data); + /*update tracks: interleaving group/priority and track edit name*/ + for (i=0; iMedia->information->sampleTable->groupID); + gf_bs_write_u32(bs, trak->Media->information->sampleTable->trackPriority); + len = trak->name ? strlen(trak->name) : 0; + gf_bs_write_u32(bs, len); + for (j=0; jname[j]); + gf_bs_get_content(bs, &data, &len); + gf_bs_del(bs); + gf_isom_add_user_data(movie, i+1, GF_4CC('G','P','A','C'), binID, data, len); + gf_free(data); + } + return GF_OK; +} + + +GF_Err gf_isom_load_movie_config(GF_ISOFile *movie) +{ + u32 i, count, len; + char *data; + GF_BitStream *bs; + Bool found_cfg; + bin128 binID; + if (movie == NULL) return GF_BAD_PARAM; + + found_cfg = 0; + /*restore movie*/ + count = gf_isom_get_user_data_count(movie, 0, GF_4CC('G','P','A','C'), binID); + for (i=0; istorageMode = gf_bs_read_u8(bs); + movie->interleavingTime = gf_bs_read_u32(bs); + gf_bs_del(bs); + gf_free(data); + found_cfg = 1; + break; + } + + for (i=0; iMedia->information->sampleTable->groupID = gf_bs_read_u32(bs); + trak->Media->information->sampleTable->trackPriority = gf_bs_read_u32(bs); + len = gf_bs_read_u32(bs); + if (len) { + u32 k; + trak->name = (char*)gf_malloc(sizeof(char)*(len+1)); + for (k=0;kname[k] = gf_bs_read_u8(bs); + trak->name[k] = 0; + } + gf_bs_del(bs); + gf_free(data); + found_cfg = 1; + break; + } + } + return found_cfg ? GF_OK : GF_NOT_SUPPORTED; +} + +GF_Err gf_isom_set_media_timescale(GF_ISOFile *the_file, u32 trackNumber, u32 newTS) +{ + Double scale; + GF_TrackBox *trak; + trak = gf_isom_get_track_from_file(the_file, trackNumber); + if (!trak || !trak->Media | !trak->Media->mediaHeader) return GF_BAD_PARAM; + if (trak->Media->mediaHeader->timeScale==newTS) return GF_OK; + + scale = newTS; + scale /= trak->Media->mediaHeader->timeScale; + trak->Media->mediaHeader->timeScale = newTS; + if (trak->editBox) { + GF_EdtsEntry *ent; + u32 i=0; + while ((ent = (GF_EdtsEntry*)gf_list_enum(trak->editBox->editList->entryList, &i))) { + ent->mediaTime = (u32) (scale*ent->mediaTime); + } + } + return SetTrackDuration(trak); +} + + + +Bool gf_isom_is_same_sample_description(GF_ISOFile *f1, u32 tk1, GF_ISOFile *f2, u32 tk2) +{ + u32 i, count; + GF_TrackBox *trak1, *trak2; + GF_BitStream *bs; + char *data1, *data2; + u32 data1_size, data2_size; + GF_ESD *esd1, *esd2; + GF_Box *a; + Bool ret, need_memcmp; + + /*get orig sample desc and clone it*/ + trak1 = gf_isom_get_track_from_file(f1, tk1); + if (!trak1 || !trak1->Media) return 0; + trak2 = gf_isom_get_track_from_file(f2, tk2); + if (!trak2 || !trak2->Media) return 0; + + if (trak1->Media->handler->handlerType != trak2->Media->handler->handlerType) return 0; + count = gf_list_count(trak1->Media->information->sampleTable->SampleDescription->boxList); + if (count != gf_list_count(trak2->Media->information->sampleTable->SampleDescription->boxList)) return 0; + + need_memcmp = 1; + for (i=0; iMedia->information->sampleTable->SampleDescription->boxList, i); + GF_Box *ent2 = (GF_Box *)gf_list_get(trak2->Media->information->sampleTable->SampleDescription->boxList, i); + if (ent1->type != ent2->type) return 0; + + switch (ent1->type) { + /*for MPEG-4 streams, only compare decSpecInfo (bitrate may not be the same but that's not an issue)*/ + case GF_ISOM_BOX_TYPE_MP4S: + case GF_ISOM_BOX_TYPE_MP4A: + case GF_ISOM_BOX_TYPE_MP4V: + case GF_ISOM_BOX_TYPE_ENCA: + case GF_ISOM_BOX_TYPE_ENCV: + case GF_ISOM_BOX_TYPE_ENCS: + Media_GetESD(trak1->Media, i+1, &esd1, 1); + Media_GetESD(trak2->Media, i+1, &esd2, 1); + if (!esd1 || !esd2) continue; + need_memcmp = 0; + if (esd1->decoderConfig->streamType != esd2->decoderConfig->streamType) return 0; + if (esd1->decoderConfig->objectTypeIndication != esd2->decoderConfig->objectTypeIndication) return 0; + if (!esd1->decoderConfig->decoderSpecificInfo && esd2->decoderConfig->decoderSpecificInfo) return 0; + if (esd1->decoderConfig->decoderSpecificInfo && !esd2->decoderConfig->decoderSpecificInfo) return 0; + if (!esd1->decoderConfig->decoderSpecificInfo || !esd2->decoderConfig->decoderSpecificInfo) continue; + if (memcmp(esd1->decoderConfig->decoderSpecificInfo->data, esd2->decoderConfig->decoderSpecificInfo->data, sizeof(char)*esd1->decoderConfig->decoderSpecificInfo->dataLength)!=0) return 0; + break; + case GF_ISOM_BOX_TYPE_AVC1: + case GF_ISOM_BOX_TYPE_AVC2: + case GF_ISOM_BOX_TYPE_SVC1: + { + GF_MPEGVisualSampleEntryBox *avc1 = (GF_MPEGVisualSampleEntryBox *)ent1; + GF_MPEGVisualSampleEntryBox *avc2 = (GF_MPEGVisualSampleEntryBox *)ent2; + data1 = data2 = NULL; + + bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); + a = (GF_Box *)avc1->avc_config; + gf_isom_box_size(a); + gf_isom_box_write(a, bs); + gf_bs_get_content(bs, &data1, &data1_size); + gf_bs_del(bs); + + bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); + a = (GF_Box *)avc2->avc_config; + gf_isom_box_size(a); + gf_isom_box_write(a, bs); + gf_bs_get_content(bs, &data2, &data2_size); + gf_bs_del(bs); + + ret = 0; + if (data1_size == data2_size) { + ret = (memcmp(data1, data2, sizeof(char)*data1_size)==0) ? 1 : 0; + } + gf_free(data1); + gf_free(data2); + return ret; + } + break; + case GF_ISOM_BOX_TYPE_LSR1: + { + GF_LASeRSampleEntryBox *lsr1 = (GF_LASeRSampleEntryBox *)ent1; + GF_LASeRSampleEntryBox *lsr2 = (GF_LASeRSampleEntryBox *)ent2; + if (lsr1->lsr_config && lsr2->lsr_config + && lsr1->lsr_config->hdr && lsr2->lsr_config->hdr + && (lsr1->lsr_config->hdr_size==lsr2->lsr_config->hdr_size) + && !memcmp(lsr1->lsr_config->hdr, lsr2->lsr_config->hdr, lsr2->lsr_config->hdr_size) + ) { + return 1; + } + return 0; + } + break; + } + } + if (!need_memcmp) return 1; + ret = 0; + + data1 = data2 = NULL; + + bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); + a = (GF_Box *)trak1->Media->information->sampleTable->SampleDescription; + gf_isom_box_size(a); + gf_isom_box_write(a, bs); + gf_bs_get_content(bs, &data1, &data1_size); + gf_bs_del(bs); + + bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); + a = (GF_Box *)trak2->Media->information->sampleTable->SampleDescription; + gf_isom_box_size(a); + gf_isom_box_write(a, bs); + gf_bs_get_content(bs, &data2, &data2_size); + gf_bs_del(bs); + + if (data1_size == data2_size) { + ret = (memcmp(data1, data2, sizeof(char)*data1_size)==0) ? 1 : 0; + } + gf_free(data1); + gf_free(data2); + return ret; +} + +u64 gf_isom_estimate_size(GF_ISOFile *movie) +{ + GF_Err e; + GF_Box *a; + u32 i, count; + u64 mdat_size; + if (!movie) return 0; + + mdat_size = 0; + count = gf_list_count(movie->moov->trackList); + for (i=0; i 0xFFFFFFFF) mdat_size += 8; + } + + i=0; + while ((a = (GF_Box*)gf_list_enum(movie->TopBoxes, &i))) { + e = gf_isom_box_size(a); + assert (e == GF_OK); + mdat_size += a->size; + } + return mdat_size; +} + + +//set shadowing on/off +GF_Err gf_isom_remove_sync_shadows(GF_ISOFile *movie, u32 trackNumber) +{ + GF_TrackBox *trak; + GF_SampleTableBox *stbl; + + if (movie->openMode == GF_ISOM_OPEN_READ) return GF_ISOM_INVALID_MODE; + trak = gf_isom_get_track_from_file(movie, trackNumber); + if (!trak) return GF_BAD_PARAM; + + stbl = trak->Media->information->sampleTable; + if (stbl->ShadowSync) { + gf_isom_box_del((GF_Box *) stbl->ShadowSync); + stbl->ShadowSync = NULL; + } + return GF_OK; +} + +//fill the sync shadow table +GF_Err gf_isom_set_sync_shadow(GF_ISOFile *movie, u32 trackNumber, u32 sampleNumber, u32 syncSample) +{ + GF_TrackBox *trak; + GF_SampleTableBox *stbl; + u8 isRAP; + GF_Err e; + + if (movie->openMode == GF_ISOM_OPEN_READ) return GF_ISOM_INVALID_MODE; + trak = gf_isom_get_track_from_file(movie, trackNumber); + if (!trak || !sampleNumber || !syncSample) return GF_BAD_PARAM; + + stbl = trak->Media->information->sampleTable; + if (!stbl->ShadowSync) stbl->ShadowSync = (GF_ShadowSyncBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_STSH); + + //if no sync, skip + if (!stbl->SyncSample) return GF_OK; + //else set the sync shadow. + //if the sample is sync, ignore + e = stbl_GetSampleRAP(stbl->SyncSample, sampleNumber, &isRAP, NULL, NULL); + if (e) return e; + if (isRAP) return GF_OK; + //if the shadowing sample is not sync, error + e = stbl_GetSampleRAP(stbl->SyncSample, syncSample, &isRAP, NULL, NULL); + if (e) return e; + if (!isRAP) return GF_BAD_PARAM; + + return stbl_SetSyncShadow(stbl->ShadowSync, sampleNumber, syncSample); +} + +//set the GroupID of a track (only used for interleaving) +GF_Err gf_isom_set_track_group(GF_ISOFile *movie, u32 trackNumber, u32 GroupID) +{ + GF_TrackBox *trak; + + if (movie->openMode != GF_ISOM_OPEN_EDIT) return GF_ISOM_INVALID_MODE; + trak = gf_isom_get_track_from_file(movie, trackNumber); + if (!trak || !GroupID) return GF_BAD_PARAM; + + trak->Media->information->sampleTable->groupID = GroupID; + return GF_OK; +} + + +//set the Priority of a track within a Group (only used for tight interleaving) +//Priority ranges from 1 to 9 +GF_Err gf_isom_set_track_priority_in_group(GF_ISOFile *movie, u32 trackNumber, u32 Priority) +{ + GF_TrackBox *trak; + + if (movie->openMode != GF_ISOM_OPEN_EDIT) return GF_ISOM_INVALID_MODE; + trak = gf_isom_get_track_from_file(movie, trackNumber); + if (!trak || !Priority) return GF_BAD_PARAM; + + trak->Media->information->sampleTable->trackPriority = Priority > 255 ? 255 : Priority; + return GF_OK; +} + +//set the max SamplesPerChunk (for file optimization) +GF_Err gf_isom_set_max_samples_per_chunk(GF_ISOFile *movie, u32 trackNumber, u32 maxSamplesPerChunk) +{ + GF_TrackBox *trak; + + if (movie->openMode == GF_ISOM_OPEN_READ) return GF_ISOM_INVALID_MODE; + trak = gf_isom_get_track_from_file(movie, trackNumber); + if (!trak || !maxSamplesPerChunk) return GF_BAD_PARAM; + + trak->Media->information->sampleTable->MaxSamplePerChunk = maxSamplesPerChunk; + return GF_OK; +} + + +GF_Err gf_isom_set_extraction_slc(GF_ISOFile *the_file, u32 trackNumber, u32 StreamDescriptionIndex, GF_SLConfig *slConfig) +{ + GF_TrackBox *trak; + GF_SampleEntryBox *entry; + GF_Err e; + GF_SLConfig **slc; + + trak = gf_isom_get_track_from_file(the_file, trackNumber); + if (!trak) return GF_BAD_PARAM; + + e = Media_GetSampleDesc(trak->Media, StreamDescriptionIndex, &entry, NULL); + if (e) return e; + + //we must be sure we are not using a remote ESD + switch (entry->type) { + case GF_ISOM_BOX_TYPE_MP4S: + if (((GF_MPEGSampleEntryBox *)entry)->esd->desc->slConfig->predefined != SLPredef_MP4) return GF_BAD_PARAM; + slc = & ((GF_MPEGSampleEntryBox *)entry)->slc; + break; + case GF_ISOM_BOX_TYPE_MP4A: + if (((GF_MPEGAudioSampleEntryBox *)entry)->esd->desc->slConfig->predefined != SLPredef_MP4) return GF_BAD_PARAM; + slc = & ((GF_MPEGAudioSampleEntryBox *)entry)->slc; + break; + case GF_ISOM_BOX_TYPE_MP4V: + if (((GF_MPEGVisualSampleEntryBox *)entry)->esd->desc->slConfig->predefined != SLPredef_MP4) return GF_BAD_PARAM; + slc = & ((GF_MPEGVisualSampleEntryBox *)entry)->slc; + break; + default: + return GF_BAD_PARAM; + } + + if (*slc) { + gf_odf_desc_del((GF_Descriptor *)*slc); + *slc = NULL; + } + if (!slConfig) return GF_OK; + //finally duplicate the SL + return gf_odf_desc_copy((GF_Descriptor *) slConfig, (GF_Descriptor **) slc); +} + + +GF_Err gf_isom_get_extraction_slc(GF_ISOFile *the_file, u32 trackNumber, u32 StreamDescriptionIndex, GF_SLConfig **slConfig) +{ + GF_TrackBox *trak; + GF_SampleEntryBox *entry; + GF_Err e; + GF_SLConfig *slc; + + trak = gf_isom_get_track_from_file(the_file, trackNumber); + if (!trak) return GF_BAD_PARAM; + + e = Media_GetSampleDesc(trak->Media, StreamDescriptionIndex, &entry, NULL); + if (e) return e; + + //we must be sure we are not using a remote ESD + slc = NULL; + *slConfig = NULL; + switch (entry->type) { + case GF_ISOM_BOX_TYPE_MP4S: + if (((GF_MPEGSampleEntryBox *)entry)->esd->desc->slConfig->predefined != SLPredef_MP4) return GF_BAD_PARAM; + slc = ((GF_MPEGSampleEntryBox *)entry)->slc; + break; + case GF_ISOM_BOX_TYPE_MP4A: + if (((GF_MPEGAudioSampleEntryBox *)entry)->esd->desc->slConfig->predefined != SLPredef_MP4) return GF_BAD_PARAM; + slc = ((GF_MPEGAudioSampleEntryBox *)entry)->slc; + break; + case GF_ISOM_BOX_TYPE_MP4V: + if (((GF_MPEGVisualSampleEntryBox *)entry)->esd->desc->slConfig->predefined != SLPredef_MP4) return GF_BAD_PARAM; + slc = ((GF_MPEGVisualSampleEntryBox *)entry)->slc; + break; + default: + return GF_BAD_PARAM; + } + + if (!slc) return GF_OK; + //finally duplicate the SL + return gf_odf_desc_copy((GF_Descriptor *) slc, (GF_Descriptor **) slConfig); +} + + +u32 gf_isom_get_track_group(GF_ISOFile *the_file, u32 trackNumber) +{ + GF_TrackBox *trak; + trak = gf_isom_get_track_from_file(the_file, trackNumber); + if (!trak) return 0; + return trak->Media->information->sampleTable->groupID; +} + + +u32 gf_isom_get_track_priority_in_group(GF_ISOFile *the_file, u32 trackNumber) +{ + GF_TrackBox *trak; + trak = gf_isom_get_track_from_file(the_file, trackNumber); + if (!trak) return 0; + return trak->Media->information->sampleTable->trackPriority; +} + + +GF_Err gf_isom_make_interleave(GF_ISOFile *file, Double TimeInSec) +{ + GF_Err e; + if (gf_isom_get_mode(file) < GF_ISOM_OPEN_EDIT) return GF_BAD_PARAM; + e = gf_isom_set_storage_mode(file, GF_ISOM_STORE_INTERLEAVED); + if (e) return e; + return gf_isom_set_interleave_time(file, (u32) (TimeInSec * gf_isom_get_timescale(file))); +} + + +GF_Err gf_isom_set_handler_name(GF_ISOFile *the_file, u32 trackNumber, const char *nameUTF8) +{ + GF_TrackBox *trak; + trak = gf_isom_get_track_from_file(the_file, trackNumber); + if (!trak) return GF_BAD_PARAM; + if (trak->Media->handler->nameUTF8) gf_free(trak->Media->handler->nameUTF8); + trak->Media->handler->nameUTF8 = NULL; + + if (!nameUTF8) return GF_OK; + + if (!strnicmp(nameUTF8, "file://", 7)) { + u8 BOM[4]; + FILE *f = gf_f64_open(nameUTF8+7, "rb"); + u64 size; + if (!f) return GF_URL_ERROR; + gf_f64_seek(f, 0, SEEK_END); + size = gf_f64_tell(f); + gf_f64_seek(f, 0, SEEK_SET); + if (3!=fread(BOM, sizeof(char), 3, f)){ + fclose(f); + return GF_CORRUPTED_DATA; + } + /*skip BOM if any*/ + if ((BOM[0]==0xEF) && (BOM[1]==0xBB) && (BOM[2]==0xBF)) size -= 3; + else if ((BOM[0]==0xEF) || (BOM[0]==0xFF)) { + fclose(f); + return GF_BAD_PARAM; + } + else gf_f64_seek(f, 0, SEEK_SET); + trak->Media->handler->nameUTF8 = (char*)gf_malloc(sizeof(char)*(size_t)(size+1)); + size = fread(trak->Media->handler->nameUTF8, sizeof(char), (size_t)size, f); + trak->Media->handler->nameUTF8[size] = 0; + fclose(f); + } else { + u32 i, j, len; + char szOrig[1024], szLine[1024]; + strcpy(szOrig, nameUTF8); + j=0; + len = strlen(szOrig); + for (i=0; i> 6) & 0x3 ); + j++; + szOrig[i] &= 0xbf; + } + /*UTF8 2 bytes char */ + else if ( (szOrig[i] & 0xe0) == 0xc0) { + szLine[j] = szOrig[i]; i++; j++; + } + /*UTF8 3 bytes char */ + else if ( (szOrig[i] & 0xf0) == 0xe0) { + szLine[j] = szOrig[i]; i++; j++; + szLine[j] = szOrig[i]; i++; j++; + } + /*UTF8 4 bytes char */ + else if ( (szOrig[i] & 0xf8) == 0xf0) { + szLine[j] = szOrig[i]; i++; j++; + szLine[j] = szOrig[i]; i++; j++; + szLine[j] = szOrig[i]; i++; j++; + } + } + szLine[j] = szOrig[i]; + j++; + } + szLine[j] = 0; + trak->Media->handler->nameUTF8 = gf_strdup(szLine); + } + return GF_OK; +} + +GF_Err gf_isom_clone_root_od(GF_ISOFile *input, GF_ISOFile *output) +{ + GF_List *esds; + GF_Err e; + u32 i; + GF_Descriptor *desc; + + e = gf_isom_remove_root_od(output); + if (e) return e; + if (!input->moov || !input->moov->iods || !input->moov->iods->descriptor) return GF_OK; + gf_isom_insert_moov(output); + e = AddMovieIOD(output->moov, 0); + if (e) return e; + if (output->moov->iods->descriptor) gf_odf_desc_del(output->moov->iods->descriptor); + output->moov->iods->descriptor = NULL; + gf_odf_desc_copy(input->moov->iods->descriptor, &output->moov->iods->descriptor); + + switch (output->moov->iods->descriptor->tag) { + case GF_ODF_ISOM_IOD_TAG: + esds = ((GF_IsomInitialObjectDescriptor *)output->moov->iods->descriptor)->ES_ID_IncDescriptors; + break; + case GF_ODF_ISOM_OD_TAG: + esds = ((GF_IsomObjectDescriptor *)output->moov->iods->descriptor)->ES_ID_IncDescriptors; + break; + default: + return GF_ISOM_INVALID_FILE; + } + + //get the desc + i=0; + while ((desc = (GF_Descriptor*)gf_list_enum(esds, &i))) { + gf_odf_desc_del(desc); + gf_list_rem(esds, i-1); + } + return GF_OK; +} + +GF_EXPORT +GF_Err gf_isom_set_media_type(GF_ISOFile *movie, u32 trackNumber, u32 new_type) +{ + GF_TrackBox *trak = gf_isom_get_track_from_file(movie, trackNumber); + if (!trak || !new_type) return GF_BAD_PARAM; + trak->Media->handler->handlerType = new_type; + return GF_OK; +} + +GF_EXPORT +GF_Err gf_isom_set_media_subtype(GF_ISOFile *movie, u32 trackNumber, u32 sampleDescriptionIndex, u32 new_type) +{ + GF_SampleEntryBox*entry; + GF_TrackBox *trak = gf_isom_get_track_from_file(movie, trackNumber); + if (!trak || !sampleDescriptionIndex || !new_type) return GF_BAD_PARAM; + + entry = (GF_SampleEntryBox*)gf_list_get(trak->Media->information->sampleTable->SampleDescription->boxList, sampleDescriptionIndex - 1); + if (!entry) return GF_BAD_PARAM; + entry->type = new_type; + return GF_OK; +} + + +GF_EXPORT +GF_Err gf_isom_set_JPEG2000(GF_ISOFile *mov, Bool set_on) +{ + if (!mov) return GF_BAD_PARAM; + mov->is_jp2 = set_on; + return GF_OK; +} + +GF_Err gf_isom_remove_uuid(GF_ISOFile *movie, u32 trackNumber, bin128 UUID) +{ + u32 i, count; + GF_List *list; + + if (trackNumber==(u32) -1) { + if (!movie) return GF_BAD_PARAM; + list = movie->TopBoxes; + } else if (trackNumber) { + GF_TrackBox *trak = gf_isom_get_track_from_file(movie, trackNumber); + if (!trak) return GF_BAD_PARAM; + list = trak->boxes; + } else { + if (!movie) return GF_BAD_PARAM; + list = movie->moov->boxes; + } + + count = gf_list_count(list); + for (i=0; itype != GF_ISOM_BOX_TYPE_UUID) continue; + if (memcmp(UUID, uuid->uuid, sizeof(bin128))) continue; + gf_list_rem(list, i); + i--; + count--; + gf_isom_box_del((GF_Box*)uuid); + } + return GF_OK; +} + + +GF_Err gf_isom_add_uuid(GF_ISOFile *movie, u32 trackNumber, bin128 UUID, char *data, u32 data_size) +{ + GF_List *list; + GF_UnknownUUIDBox *uuid; + + if (!data_size || !data) return GF_OK; + + if (trackNumber==(u32) -1) { + if (!movie) return GF_BAD_PARAM; + list = movie->TopBoxes; + } else if (trackNumber) { + GF_TrackBox *trak = gf_isom_get_track_from_file(movie, trackNumber); + if (!trak) return GF_BAD_PARAM; + list = trak->boxes; + } else { + if (!movie) return GF_BAD_PARAM; + list = movie->moov->boxes; + } + + GF_SAFEALLOC(uuid, GF_UnknownUUIDBox); + uuid->type = GF_ISOM_BOX_TYPE_UUID; + memcpy(uuid->uuid, UUID, sizeof(bin128)); + uuid->dataSize = data_size; + uuid->data = (char*)gf_malloc(sizeof(char)*data_size); + memcpy(uuid->data, data, sizeof(char)*data_size); + gf_list_add(list, uuid); + return GF_OK; +} + +/*Apple extensions*/ + +GF_Err gf_isom_apple_set_tag(GF_ISOFile *mov, u32 tag, const char *data, u32 data_len) +{ + GF_BitStream *bs; + GF_Err e; + GF_ItemListBox *ilst; + GF_MetaBox *meta; + GF_ListItemBox *info; + u32 btype, i; + + + e = CanAccessMovie(mov, GF_ISOM_OPEN_WRITE); + if (e) return e; + + meta = gf_isom_apple_create_meta_extensions(mov); + if (!meta) return GF_BAD_PARAM; + + ilst = gf_ismo_locate_box(meta->other_boxes, GF_ISOM_BOX_TYPE_ILST, NULL); + if (!ilst) return GF_NOT_SUPPORTED; + + if (tag==GF_ISOM_ITUNE_GENRE) { + btype = data ? GF_ISOM_BOX_TYPE_0xA9GEN : GF_ISOM_BOX_TYPE_GNRE; + } else { + btype = tag; + } + /*remove tag*/ + i = 0; + while ((info = (GF_ListItemBox *) gf_list_enum(ilst->tags, &i))) { + if (info->type==btype) { + gf_list_rem(ilst->tags, i-1); + gf_isom_box_del((GF_Box *) info); + break; + } + } + + if (data != NULL) { + info = (GF_ListItemBox *)gf_isom_box_new(btype); + if (info == NULL) return GF_OUT_OF_MEM; + switch (btype) { + case GF_ISOM_BOX_TYPE_TRKN: + case GF_ISOM_BOX_TYPE_DISK: + case GF_ISOM_BOX_TYPE_GNRE: + info->data->flags = 0x0; + break; + case GF_ISOM_BOX_TYPE_PGAP: + case GF_ISOM_BOX_TYPE_CPIL: + info->data->flags = 0x15; + break; + default: + info->data->flags = 0x1; + break; + } + if (tag==GF_ISOM_ITUNE_COVER_ART) { + if (data_len & 0x80000000) { + data_len = (data_len & 0x7FFFFFFF); + info->data->flags = 14; + } else { + info->data->flags = 13; + } + } + info->data->dataSize = data_len; + info->data->data = (char*)gf_malloc(sizeof(char)*data_len); + memcpy(info->data->data , data, sizeof(char)*data_len); + } + else if (data_len && (tag==GF_ISOM_ITUNE_GENRE)) { + info = (GF_ListItemBox *)gf_isom_box_new(btype); + if (info == NULL) return GF_OUT_OF_MEM; + bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); + gf_bs_write_u16(bs, data_len); + gf_bs_get_content(bs, & info->data->data, &info->data->dataSize); + info->data->flags = 0x0; + gf_bs_del(bs); + } else if (data_len && (tag==GF_ISOM_ITUNE_COMPILATION)) { + info = (GF_ListItemBox *)gf_isom_box_new(btype); + if (info == NULL) return GF_OUT_OF_MEM; + info->data->data = (char*)gf_malloc(sizeof(char)); + info->data->data[0] = 1; + info->data->dataSize = 1; + info->data->flags = 21; + } else if (data_len && (tag==GF_ISOM_ITUNE_TEMPO)) { + info = (GF_ListItemBox *)gf_isom_box_new(btype); + if (info == NULL) return GF_OUT_OF_MEM; + bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); + gf_bs_write_u16(bs, data_len); + gf_bs_get_content(bs, &info->data->data, &info->data->dataSize); + info->data->flags = 0x15; + gf_bs_del(bs); + } + return gf_list_add(ilst->tags, info); +} + +GF_EXPORT +GF_Err gf_isom_set_alternate_group_id(GF_ISOFile *movie, u32 trackNumber, u32 groupId) +{ + GF_TrackBox *trak = gf_isom_get_track_from_file(movie, trackNumber); + if (!trak) return GF_BAD_PARAM; + trak->Header->alternate_group = groupId; + return GF_OK; +} + + +GF_EXPORT +GF_Err gf_isom_set_track_switch_parameter(GF_ISOFile *movie, u32 trackNumber, u32 trackRefGroup, Bool is_switch_group, u32 *switchGroupID, u32 *criteriaList, u32 criteriaListCount) +{ + GF_TrackSelectionBox *tsel; + GF_TrackBox *trak; + GF_UserDataMap *map; + GF_Err e; + u32 alternateGroupID = 0; + u32 next_switch_group_id = 0; + + trak = gf_isom_get_track_from_file(movie, trackNumber); + if (!trak || !switchGroupID) return GF_BAD_PARAM; + + + if (trackRefGroup) { + GF_TrackBox *trak_ref = gf_isom_get_track_from_file(movie, trackRefGroup); + if (trak_ref != trak) { + if (!trak_ref || !trak_ref->Header->alternate_group) return GF_BAD_PARAM; + alternateGroupID = trak_ref->Header->alternate_group; + } else { + alternateGroupID = trak->Header->alternate_group; + } + } + if (!alternateGroupID) { + /*there is a function for this ....*/ + if (trak->Header->alternate_group) return GF_BAD_PARAM; + alternateGroupID = gf_isom_get_next_alternate_group_id(movie); + } + + if (is_switch_group) { + u32 i=0; + while (i< gf_isom_get_track_count(movie) ) { + //locate first available ID + GF_TrackBox *a_trak = gf_isom_get_track_from_file(movie, i+1); + + if (a_trak->udta) { + u32 j, count; + map = udta_getEntry(a_trak->udta, GF_ISOM_BOX_TYPE_TSEL, NULL); + if (map) { + count = gf_list_count(map->boxList); + for (j=0; jboxList, j); + + if (*switchGroupID) { + if (tsel->switchGroup==next_switch_group_id) { + if (a_trak->Header->alternate_group != alternateGroupID) return GF_BAD_PARAM; + } + } else { + if (tsel->switchGroup && (tsel->switchGroup>=next_switch_group_id) ) + next_switch_group_id = tsel->switchGroup; + } + } + } + + } + i++; + } + if (! *switchGroupID) *switchGroupID = next_switch_group_id+1; + } + + + trak->Header->alternate_group = alternateGroupID; + + tsel = NULL; + if (*switchGroupID) { + if (!trak->udta) { + e = trak_AddBox((GF_Box*)trak, gf_isom_box_new(GF_ISOM_BOX_TYPE_UDTA)); + if (e) return e; + } + + map = udta_getEntry(trak->udta, GF_ISOM_BOX_TYPE_TSEL, NULL); + + /*locate tsel box with no switch group*/ + if (map) { + u32 j, count = gf_list_count(map->boxList); + for (j=0; jboxList, j); + if (tsel->switchGroup == *switchGroupID) break; + tsel = NULL; + } + } + if (!tsel) { + tsel = (GF_TrackSelectionBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_TSEL); + e = udta_AddBox(trak->udta, (GF_Box *) tsel); + if (e) return e; + } + + tsel->switchGroup = *switchGroupID; + tsel->attributeListCount = criteriaListCount; + if (tsel->attributeList) gf_free(tsel->attributeList); + tsel->attributeList = (u32 *) gf_malloc(sizeof(u32)*criteriaListCount); + memcpy(tsel->attributeList, criteriaList, sizeof(u32)*criteriaListCount); + } + return GF_OK; +} + +void reset_tsel_box(GF_TrackBox *trak) +{ + GF_UserDataMap *map; + trak->Header->alternate_group = 0; + map = udta_getEntry(trak->udta, GF_ISOM_BOX_TYPE_TSEL, NULL); + if (map) { + gf_list_del_item(trak->udta->recordList, map); + gf_isom_box_array_del(map->boxList); + gf_free(map); + } + +} + +GF_EXPORT +GF_Err gf_isom_reset_track_switch_parameter(GF_ISOFile *movie, u32 trackNumber, Bool reset_all_group) +{ + GF_TrackBox *trak; + u32 alternateGroupID = 0; + + trak = gf_isom_get_track_from_file(movie, trackNumber); + if (!trak) return GF_BAD_PARAM; + if (!trak->Header->alternate_group) return GF_OK; + + alternateGroupID = trak->Header->alternate_group; + if (reset_all_group) { + u32 i=0; + while (i< gf_isom_get_track_count(movie) ) { + //locate first available ID + GF_TrackBox *a_trak = gf_isom_get_track_from_file(movie, i+1); + if (a_trak->Header->alternate_group == alternateGroupID) reset_tsel_box(a_trak); + i++; + } + } else { + reset_tsel_box(trak); + } + return GF_OK; +} + + +GF_EXPORT +GF_Err gf_isom_reset_switch_parameters(GF_ISOFile *movie) +{ + u32 i=0; + while (i< gf_isom_get_track_count(movie) ) { + //locate first available ID + GF_TrackBox *a_trak = gf_isom_get_track_from_file(movie, i+1); + reset_tsel_box(a_trak); + i++; + } + return GF_OK; +} + +GF_Err gf_isom_timed_meta_data_config_new(GF_ISOFile *movie, u32 trackNumber, Bool is_xml, char *mime_or_namespace, char *content_encoding, char *schema_loc, char *URLname, char *URNname, u32 *outDescriptionIndex) +{ + GF_TrackBox *trak; + GF_Err e; + u32 dataRefIndex; + GF_MetaDataSampleEntryBox *metad; + + e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE); + if (e) return e; + + trak = gf_isom_get_track_from_file(movie, trackNumber); + if (!trak || !trak->Media || !mime_or_namespace) + return GF_BAD_PARAM; + + if (trak->Media->handler->handlerType==GF_ISOM_MEDIA_META) + return GF_BAD_PARAM; + + //get or create the data ref + e = Media_FindDataRef(trak->Media->information->dataInformation->dref, URLname, URNname, &dataRefIndex); + if (e) return e; + if (!dataRefIndex) { + e = Media_CreateDataRef(trak->Media->information->dataInformation->dref, URLname, URNname, &dataRefIndex); + if (e) return e; + } + trak->Media->mediaHeader->modificationTime = gf_isom_get_mp4time(); + + metad = (GF_MetaDataSampleEntryBox*) gf_isom_box_new(is_xml ? GF_ISOM_BOX_TYPE_METX : GF_ISOM_BOX_TYPE_METT); + if (!metad) return GF_OUT_OF_MEM; + + metad->dataReferenceIndex = dataRefIndex; + metad->mime_type_or_namespace = gf_strdup(mime_or_namespace); + if (content_encoding) metad->content_encoding = gf_strdup(content_encoding); + if (schema_loc) metad->xml_schema_loc = gf_strdup(schema_loc); + + e = gf_list_add(trak->Media->information->sampleTable->SampleDescription->boxList, metad); + if (outDescriptionIndex) *outDescriptionIndex = gf_list_count(trak->Media->information->sampleTable->SampleDescription->boxList); + return e; +} + + +GF_Err gf_isom_add_subsample(GF_ISOFile *movie, u32 track, u32 sampleNumber, u32 subSampleSize, u8 priority, u32 reserved, Bool discardable) +{ + GF_SubSampleInformationBox *sub_samples; + GF_TrackBox *trak; + GF_Err e; + + e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE); + if (e) return e; + + trak = gf_isom_get_track_from_file(movie, track); + if (!trak || !trak->Media || !trak->Media->information->sampleTable) + return GF_BAD_PARAM; + + if (!trak->Media->information->sampleTable->SubSamples) { + trak->Media->information->sampleTable->SubSamples = (GF_SubSampleInformationBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_SUBS); + sub_samples = trak->Media->information->sampleTable->SubSamples; + sub_samples->version = (subSampleSize>0xFFFF) ? 1 : 0; + } else { + sub_samples = trak->Media->information->sampleTable->SubSamples; + } + return gf_isom_add_subsample_info(sub_samples, sampleNumber, subSampleSize, priority, reserved, discardable); +} + + +GF_Err gf_isom_set_rvc_config(GF_ISOFile *movie, u32 track, u32 sampleDescriptionIndex, u16 rvc_predefined, char *mime, char *data, u32 size) +{ + GF_MPEGVisualSampleEntryBox *entry; + GF_Err e; + GF_TrackBox *trak; + + e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE); + if (e) return e; + + trak = gf_isom_get_track_from_file(movie, track); + if (!trak) return GF_BAD_PARAM; + + + entry = (GF_MPEGVisualSampleEntryBox *) gf_list_get(trak->Media->information->sampleTable->SampleDescription->boxList, sampleDescriptionIndex-1); + if (!entry ) return GF_BAD_PARAM; + switch (entry->type) { + case GF_ISOM_BOX_TYPE_MP4V: + case GF_ISOM_BOX_TYPE_AVC1: + case GF_ISOM_BOX_TYPE_AVC2: + case GF_ISOM_BOX_TYPE_SVC1: + case GF_ISOM_BOX_TYPE_ENCV: + break; + default: + return GF_BAD_PARAM; + } + + if (entry->rvcc && entry->rvcc->rvc_meta_idx) { + gf_isom_remove_meta_item(movie, 0, track, entry->rvcc->rvc_meta_idx); + entry->rvcc->rvc_meta_idx = 0; + } + + if (!entry->rvcc) { + entry->rvcc = (GF_RVCConfigurationBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_RVCC); + } + entry->rvcc->predefined_rvc_config = rvc_predefined; + if (!rvc_predefined) { + e = gf_isom_set_meta_type(movie, 0, track, GF_4CC('r','v','c','i')); + if (e) return e; + gf_isom_modify_alternate_brand(movie, GF_ISOM_BRAND_ISO2, 1); + e = gf_isom_add_meta_item_memory(movie, 0, track, "rvcconfig.xml", mime, NULL, data, size); + if (e) return e; + entry->rvcc->rvc_meta_idx = gf_isom_get_meta_item_count(movie, 0, track); + } + return GF_OK; +} + + +#endif /*!defined(GPAC_DISABLE_ISOM) && !defined(GPAC_DISABLE_ISOM_WRITE)*/ + + diff --git a/src/gpacmp4/list.c b/src/gpacmp4/list.c new file mode 100644 index 00000000..6052765c --- /dev/null +++ b/src/gpacmp4/list.c @@ -0,0 +1,732 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / common tools sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include + +/* GF_List modes, ONLY ONE CAN BE DEFINED + + linked-list + #define GF_LIST_LINKED + + double navigation linked-list + #define GF_LIST_DOUBLE_LINKED + + single step memory array + #define GF_LIST_ARRAY + + multi-step memory array withou gf_realloc on remove, using the GF_LIST_REALLOC macro + GF_LIST_ARRAY_GROW +*/ + +/*after some tuning, this seems to be the fastest mode on WINCE*/ +#ifdef _WIN32_WCE +#define GF_LIST_LINKED +#else +#define GF_LIST_ARRAY_GROW +#endif + +#define GF_LIST_REALLOC(a) (a = a ? (3*a/2) : 10) +//#define GF_LIST_REALLOC(a) (a++) + + +#if defined(GF_LIST_LINKED) + +typedef struct tagIS +{ + struct tagIS *next; + void *data; +} ItemSlot; + +struct _tag_array +{ + struct tagIS *head; + struct tagIS *tail; + u32 entryCount; + s32 foundEntryNumber; + struct tagIS *foundEntry; +}; + + +GF_EXPORT +GF_List * gf_list_new() +{ + GF_List *nlist = (GF_List *) gf_malloc(sizeof(GF_List)); + if (! nlist) return NULL; + nlist->head = nlist->foundEntry = NULL; + nlist->tail = NULL; + nlist->foundEntryNumber = -1; + nlist->entryCount = 0; + return nlist; +} + +GF_EXPORT +void gf_list_del(GF_List *ptr) +{ + if (!ptr) return; + while (ptr->entryCount) gf_list_rem(ptr, 0); + gf_free(ptr); +} + +GF_EXPORT +void gf_list_reset(GF_List *ptr) +{ + while (ptr && ptr->entryCount) gf_list_rem(ptr, 0); +} + +GF_EXPORT +GF_Err gf_list_add(GF_List *ptr, void* item) +{ + ItemSlot *entry; + if (! ptr) return GF_BAD_PARAM; + entry = (ItemSlot *) gf_malloc(sizeof(ItemSlot)); + if (!entry) return GF_OUT_OF_MEM; + entry->data = item; + entry->next = NULL; + if (! ptr->head) { + ptr->head = entry; + ptr->entryCount = 1; + } else { + ptr->entryCount += 1; + ptr->tail->next = entry; + } + ptr->tail = entry; + ptr->foundEntryNumber = ptr->entryCount - 1; + ptr->foundEntry = entry; + return GF_OK; +} + +GF_EXPORT +u32 gf_list_count(const GF_List *ptr) +{ + if (! ptr) return 0; + return ptr->entryCount; +} + +GF_EXPORT +void *gf_list_get(GF_List *ptr, u32 itemNumber) +{ + ItemSlot *entry; + u32 i; + + if (!ptr || (itemNumber >= ptr->entryCount) ) return NULL; + + if (!ptr->foundEntry || (itemNumber < (u32) ptr->foundEntryNumber) ) { + ptr->foundEntryNumber = 0; + ptr->foundEntry = ptr->head; + } + entry = ptr->foundEntry; + for (i = ptr->foundEntryNumber; i < itemNumber; i++ ) { + entry = entry->next; + } + ptr->foundEntryNumber = itemNumber; + ptr->foundEntry = entry; + return (void *) entry->data; +} + +GF_EXPORT +void *gf_list_last(GF_List *ptr) +{ + ItemSlot *entry; + if (!ptr || !ptr->entryCount) return NULL; + entry = ptr->head; + while (entry->next) entry = entry->next; + return entry->data; +} + +GF_EXPORT +GF_Err gf_list_rem(GF_List *ptr, u32 itemNumber) +{ + ItemSlot *tmp, *tmp2; + u32 i; + + /* !! if head is null (empty list)*/ + if ( (! ptr) || (! ptr->head) || (ptr->head && !ptr->entryCount) || (itemNumber >= ptr->entryCount) ) + return GF_BAD_PARAM; + + /*we delete the head*/ + if (! itemNumber) { + tmp = ptr->head; + ptr->head = ptr->head->next; + ptr->entryCount --; + ptr->foundEntry = ptr->head; + ptr->foundEntryNumber = 0; + gf_free(tmp); + /*that was the last entry, reset the tail*/ + if (!ptr->entryCount) { + ptr->tail = ptr->head = ptr->foundEntry = NULL; + ptr->foundEntryNumber = -1; + } + return GF_OK; + } + + tmp = ptr->head; + i = 0; + while (i < itemNumber - 1) { + tmp = tmp->next; + i++; + } + tmp2 = tmp->next; + tmp->next = tmp2->next; + /*if we deleted the last entry, update the tail !!!*/ + if (! tmp->next || (ptr->tail == tmp2) ) { + ptr->tail = tmp; + tmp->next = NULL; + } + + gf_free(tmp2); + ptr->entryCount --; + ptr->foundEntry = ptr->head; + ptr->foundEntryNumber = 0; + + return GF_OK; +} + +GF_EXPORT +GF_Err gf_list_rem_last(GF_List *ptr) +{ + return gf_list_rem(ptr, ptr->entryCount-1); +} + +GF_EXPORT +GF_Err gf_list_insert(GF_List *ptr, void *item, u32 position) +{ + u32 i; + ItemSlot *tmp, *tmp2; + + if (!ptr || !item) return GF_BAD_PARAM; + /*if last entry or first of an empty array...*/ + if (position >= ptr->entryCount) return gf_list_add(ptr, item); + + tmp2 = (ItemSlot *) gf_malloc(sizeof(ItemSlot)); + tmp2->data = item; + tmp2->next = NULL; + /*special case for the head*/ + if (position == 0) { + tmp2->next = ptr->head; + ptr->head = tmp2; + ptr->entryCount ++; + ptr->foundEntry = tmp2; + ptr->foundEntryNumber = 0; + return GF_OK; + } + tmp = ptr->head; + for (i = 1; i < position; i++) { + tmp = tmp->next; + if (!tmp) + break; + } + tmp2->next = tmp->next; + tmp->next = tmp2; + ptr->entryCount ++; + ptr->foundEntry = tmp2; + ptr->foundEntryNumber = i; + return GF_OK; +} + +#elif defined(GF_LIST_DOUBLE_LINKED) + + +typedef struct tagIS +{ + struct tagIS *next; + struct tagIS *prev; + void *data; +} ItemSlot; + +struct _tag_array +{ + struct tagIS *head; + struct tagIS *tail; + u32 entryCount; + s32 foundEntryNumber; + struct tagIS *foundEntry; +}; + + +GF_EXPORT +GF_List * gf_list_new() +{ + GF_List *nlist = (GF_List *) gf_malloc(sizeof(GF_List)); + if (! nlist) return NULL; + nlist->head = nlist->foundEntry = NULL; + nlist->tail = NULL; + nlist->foundEntryNumber = -1; + nlist->entryCount = 0; + return nlist; +} + +GF_EXPORT +void gf_list_del(GF_List *ptr) +{ + if (!ptr) return; + while (ptr->entryCount) { + gf_list_rem(ptr, 0); + } + gf_free(ptr); +} + +GF_EXPORT +void gf_list_reset(GF_List *ptr) +{ + while (ptr && ptr->entryCount) gf_list_rem(ptr, 0); +} + +GF_EXPORT +GF_Err gf_list_add(GF_List *ptr, void* item) +{ + ItemSlot *entry; + if (! ptr) return GF_BAD_PARAM; + entry = (ItemSlot *) gf_malloc(sizeof(ItemSlot)); + if (!entry) return GF_OUT_OF_MEM; + entry->data = item; + entry->next = entry->prev = NULL; + + if (! ptr->head) { + ptr->head = entry; + ptr->entryCount = 1; + } else { + ptr->entryCount += 1; + entry->prev = ptr->tail; + ptr->tail->next = entry; + } + ptr->tail = entry; + ptr->foundEntryNumber = ptr->entryCount - 1; + ptr->foundEntry = entry; + return GF_OK; +} + + +GF_EXPORT +u32 gf_list_count(GF_List *ptr) +{ + if (! ptr) return 0; + return ptr->entryCount; +} + +GF_EXPORT +void *gf_list_get(GF_List *ptr, u32 itemNumber) +{ + ItemSlot *entry; + u32 i; + + if (!ptr || !ptr->head || (itemNumber >= ptr->entryCount) ) return NULL; + + if (!itemNumber) { + ptr->foundEntry = ptr->head; + ptr->foundEntryNumber = 0; + return ptr->head->data; + } + + entry = ptr->foundEntry; + if ( itemNumber < (u32) ptr->foundEntryNumber ) { + for (i = ptr->foundEntryNumber; i > itemNumber; i-- ) { + entry = entry->prev; + } + } else { + for (i = ptr->foundEntryNumber; i < itemNumber; i++ ) { + entry = entry->next; + } + } + ptr->foundEntryNumber = itemNumber; + ptr->foundEntry = entry; + return (void *) entry->data; +} + +GF_EXPORT +void *gf_list_last(GF_List *ptr) +{ + if(!ptr || !ptr->tail) return NULL; + return ptr->tail->data; +} + +GF_EXPORT +GF_Err gf_list_rem(GF_List *ptr, u32 itemNumber) +{ + ItemSlot *tmp; + u32 i; + + /* !! if head is null (empty list)*/ + if ( (! ptr) || (! ptr->head) || (ptr->head && !ptr->entryCount) || (itemNumber >= ptr->entryCount) ) + return GF_BAD_PARAM; + + /*we delete the head*/ + if (! itemNumber) { + tmp = ptr->head; + ptr->head = ptr->head->next; + + ptr->entryCount --; + ptr->foundEntry = ptr->head; + ptr->foundEntryNumber = 0; + gf_free(tmp); + + /*that was the last entry, reset the tail*/ + if (!ptr->entryCount) { + ptr->tail = ptr->head = ptr->foundEntry = NULL; + ptr->foundEntryNumber = -1; + } else { + ptr->head->prev = NULL; + } + return GF_OK; + } + else if (itemNumber==ptr->entryCount-1) { + tmp = ptr->tail; + ptr->tail = tmp->prev; + ptr->tail->next = NULL; + ptr->entryCount--; + if (ptr->foundEntry==tmp) { + ptr->foundEntry = ptr->tail; + ptr->foundEntryNumber = ptr->entryCount-1; + } + gf_free(tmp); + return GF_OK; + } + + tmp = ptr->foundEntry; + if ( itemNumber < (u32) ptr->foundEntryNumber ) { + for (i = ptr->foundEntryNumber; i > itemNumber; i-- ) { + tmp = tmp->prev; + } + } else { + for (i = ptr->foundEntryNumber; i < itemNumber; i++ ) { + tmp = tmp->next; + } + } + tmp->prev->next = tmp->next; + tmp->next->prev = tmp->prev; + if (tmp==ptr->foundEntry) ptr->foundEntry = tmp->next; + gf_free(tmp); + ptr->entryCount--; + return GF_OK; +} + +GF_EXPORT +GF_Err gf_list_rem_last(GF_List *ptr) +{ + return gf_list_rem(ptr, ptr->entryCount-1); +} + +GF_EXPORT +GF_Err gf_list_insert(GF_List *ptr, void *item, u32 position) +{ + u32 i; + ItemSlot *tmp, *tmp2; + + if (!ptr || !item) return GF_BAD_PARAM; + /*if last entry or first of an empty array...*/ + if (position >= ptr->entryCount) return gf_list_add(ptr, item); + tmp2 = (ItemSlot *) gf_malloc(sizeof(ItemSlot)); + tmp2->data = item; + tmp2->next = tmp2->prev = NULL; + /*special case for the head*/ + if (position == 0) { + ptr->head->prev = tmp2; + tmp2->next = ptr->head; + ptr->head = tmp2; + ptr->entryCount ++; + ptr->foundEntry = tmp2; + ptr->foundEntryNumber = 0; + return GF_OK; + } + + tmp = ptr->foundEntry; + if ( position < (u32) ptr->foundEntryNumber ) { + for (i = ptr->foundEntryNumber; i >= position; i-- ) { + tmp = tmp->prev; + } + tmp = tmp->prev; + } else { + for (i = ptr->foundEntryNumber; i < position; i++ ) { + tmp = tmp->next; + } + } + tmp2->next = tmp->next; + tmp2->next->prev = tmp2; + tmp2->prev = tmp; + tmp2->prev->next = tmp2; + ptr->entryCount ++; + ptr->foundEntry = tmp2; + ptr->foundEntryNumber = position; + return GF_OK; +} + +#elif defined(GF_LIST_ARRAY) + +struct _tag_array +{ + void **slots; + u32 entryCount; +}; + + +GF_EXPORT +GF_List * gf_list_new() +{ + GF_List *nlist = (GF_List *) gf_malloc(sizeof(GF_List)); + if (! nlist) return NULL; + nlist->slots = NULL; + nlist->entryCount = 0; + return nlist; +} + +GF_EXPORT +void gf_list_del(GF_List *ptr) +{ + if (!ptr) return; + gf_free(ptr->slots); + gf_free(ptr); +} + +GF_EXPORT +GF_Err gf_list_add(GF_List *ptr, void* item) +{ + if (! ptr) return GF_BAD_PARAM; + + ptr->entryCount ++; + ptr->slots = (void **) gf_realloc(ptr->slots, ptr->entryCount*sizeof(void*)); + if (!ptr->slots) { + ptr->entryCount = 0; + return GF_OUT_OF_MEM; + } + ptr->slots[ptr->entryCount-1] = item; + return GF_OK; +} + +GF_EXPORT +u32 gf_list_count(GF_List *ptr) +{ + return ptr ? ptr->entryCount : 0; +} + +GF_EXPORT +void *gf_list_get(GF_List *ptr, u32 itemNumber) +{ + if(!ptr || (itemNumber >= ptr->entryCount)) return NULL; + return ptr->slots[itemNumber]; +} + +GF_EXPORT +void *gf_list_last(GF_List *ptr) +{ + if(!ptr || !ptr->entryCount) return NULL; + return ptr->slots[ptr->entryCount-1]; +} + + +/*WARNING: itemNumber is from 0 to entryCount - 1*/ +GF_EXPORT +GF_Err gf_list_rem(GF_List *ptr, u32 itemNumber) +{ + u32 i; + if ( !ptr || !ptr->slots || !ptr->entryCount) return GF_BAD_PARAM; + i = ptr->entryCount - itemNumber - 1; + if (i) memmove(&ptr->slots[itemNumber], & ptr->slots[itemNumber +1], sizeof(void *)*i); + ptr->slots[ptr->entryCount-1] = NULL; + ptr->entryCount -= 1; + ptr->slots = (void **) gf_realloc(ptr->slots, sizeof(void*)*ptr->entryCount); + return GF_OK; +} + +GF_EXPORT +GF_Err gf_list_rem_last(GF_List *ptr) +{ + if ( !ptr || !ptr->slots || !ptr->entryCount) return GF_BAD_PARAM; + ptr->entryCount -= 1; + ptr->slots = (void **) gf_realloc(ptr->slots, sizeof(void*)*ptr->entryCount); + return GF_OK; +} + + +/*WARNING: position is from 0 to entryCount - 1*/ +GF_EXPORT +GF_Err gf_list_insert(GF_List *ptr, void *item, u32 position) +{ + u32 i; + if (!ptr || !item) return GF_BAD_PARAM; + /*if last entry or first of an empty array...*/ + if (position >= ptr->entryCount) return gf_list_add(ptr, item); + ptr->slots = (void **) gf_realloc(ptr->slots, (ptr->entryCount+1)*sizeof(void*)); + i = ptr->entryCount - position; + memmove(&ptr->slots[position + 1], &ptr->slots[position], sizeof(void *)*i); + ptr->entryCount++; + ptr->slots[position] = item; + return GF_OK; +} + +GF_EXPORT +void gf_list_reset(GF_List *ptr) +{ + if (ptr) { + ptr->entryCount = 0; + gf_free(ptr->slots); + ptr->slots = NULL; + } +} + +#else /*GF_LIST_ARRAY_GROW*/ + + +struct _tag_array +{ + void **slots; + u32 entryCount; + u32 allocSize; +}; + +GF_EXPORT +GF_List * gf_list_new() +{ + GF_List *nlist; + + nlist = (GF_List *) gf_malloc(sizeof(GF_List)); + if (! nlist) return NULL; + + nlist->slots = NULL; + nlist->entryCount = 0; + nlist->allocSize = 0; + return nlist; +} + +GF_EXPORT +void gf_list_del(GF_List *ptr) +{ + if (!ptr) return; + gf_free(ptr->slots); + gf_free(ptr); +} + +static void realloc_chain(GF_List *ptr) +{ + GF_LIST_REALLOC(ptr->allocSize); + ptr->slots = (void **) gf_realloc(ptr->slots, ptr->allocSize*sizeof(void*)); +} + +GF_EXPORT +GF_Err gf_list_add(GF_List *ptr, void* item) +{ + if (! ptr) + return GF_BAD_PARAM; + if (ptr->allocSize==ptr->entryCount) realloc_chain(ptr); + if (!ptr->slots) + return GF_OUT_OF_MEM; + + ptr->slots[ptr->entryCount] = item; + ptr->entryCount ++; + return GF_OK; +} + +GF_EXPORT +u32 gf_list_count(const GF_List *ptr) +{ + if (!ptr) return 0; + return ptr->entryCount; +} + +GF_EXPORT +void *gf_list_get(GF_List *ptr, u32 itemNumber) +{ + if(!ptr || (itemNumber >= ptr->entryCount)) return NULL; + return ptr->slots[itemNumber]; +} + +GF_EXPORT +void *gf_list_last(GF_List *ptr) +{ + if(!ptr || !ptr->entryCount) return NULL; + return ptr->slots[ptr->entryCount-1]; +} + + +/*WARNING: itemNumber is from 0 to entryCount - 1*/ +GF_EXPORT +GF_Err gf_list_rem(GF_List *ptr, u32 itemNumber) +{ + u32 i; + if ( !ptr || !ptr->slots || !ptr->entryCount) return GF_BAD_PARAM; + i = ptr->entryCount - itemNumber - 1; + if (i) memmove(&ptr->slots[itemNumber], & ptr->slots[itemNumber +1], sizeof(void *)*i); + ptr->slots[ptr->entryCount-1] = NULL; + ptr->entryCount -= 1; + return GF_OK; +} + +GF_EXPORT +GF_Err gf_list_rem_last(GF_List *ptr) +{ + if ( !ptr || !ptr->slots || !ptr->entryCount) return GF_BAD_PARAM; + ptr->slots[ptr->entryCount-1] = NULL; + ptr->entryCount -= 1; + return GF_OK; +} + +/*WARNING: position is from 0 to entryCount - 1*/ +GF_EXPORT +GF_Err gf_list_insert(GF_List *ptr, void *item, u32 position) +{ + u32 i; + if (!ptr || !item) return GF_BAD_PARAM; + /*if last entry or first of an empty array...*/ + if (position >= ptr->entryCount) return gf_list_add(ptr, item); + if (ptr->allocSize==ptr->entryCount) realloc_chain(ptr); + + i = ptr->entryCount - position; + memmove(&ptr->slots[position + 1], &ptr->slots[position], sizeof(void *)*i); + ptr->entryCount++; + ptr->slots[position] = item; + return GF_OK; +} + +GF_EXPORT +void gf_list_reset(GF_List *ptr) +{ + if (ptr) ptr->entryCount = 0; +} + +#endif + +GF_EXPORT +s32 gf_list_find(GF_List *ptr, void *item) +{ + u32 i, count; + count = gf_list_count(ptr); + for (i=0; i=0) gf_list_rem(ptr, (u32) i); + return i; +} + +GF_EXPORT +void *gf_list_enum(GF_List *ptr, u32 *pos) +{ + void *res = gf_list_get(ptr, *pos); + (*pos)++; + return res; +} diff --git a/src/gpacmp4/media.c b/src/gpacmp4/media.c new file mode 100644 index 00000000..599d68f9 --- /dev/null +++ b/src/gpacmp4/media.c @@ -0,0 +1,833 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / ISO Media File Format sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ +#include "../disable_warnings.h" +#include +#include + +#ifndef GPAC_DISABLE_ISOM + +GF_Err Media_GetSampleDesc(GF_MediaBox *mdia, u32 SampleDescIndex, GF_SampleEntryBox **out_entry, u32 *dataRefIndex) +{ + GF_SampleDescriptionBox *stsd; + GF_SampleEntryBox *entry = NULL; + + if (!mdia) return GF_ISOM_INVALID_FILE; + + stsd = mdia->information->sampleTable->SampleDescription; + if (!stsd) return GF_ISOM_INVALID_FILE; + if (!SampleDescIndex || (SampleDescIndex > gf_list_count(stsd->boxList)) ) return GF_BAD_PARAM; + + entry = (GF_SampleEntryBox*)gf_list_get(stsd->boxList, SampleDescIndex - 1); + if (!entry) return GF_ISOM_INVALID_FILE; + + if (out_entry) *out_entry = entry; + if (dataRefIndex) *dataRefIndex = entry->dataReferenceIndex; + return GF_OK; +} + +GF_Err Media_GetSampleDescIndex(GF_MediaBox *mdia, u64 DTS, u32 *sampleDescIndex) +{ + GF_Err e; + u32 sampleNumber, prevSampleNumber, num; + u64 offset; + u8 isEdited; + if (sampleDescIndex == NULL) return GF_BAD_PARAM; + + //find the sample for this time + e = findEntryForTime(mdia->information->sampleTable, (u32) DTS, 0, &sampleNumber, &prevSampleNumber); + if (e) return e; + + if (!sampleNumber && !prevSampleNumber) { + //we have to assume the track was created to be used... If we have a sampleDesc, OK + if (gf_list_count(mdia->information->sampleTable->SampleDescription->boxList)) { + (*sampleDescIndex) = 1; + return GF_OK; + } + return GF_BAD_PARAM; + } + return stbl_GetSampleInfos(mdia->information->sampleTable, ( sampleNumber ? sampleNumber : prevSampleNumber), &offset, &num, sampleDescIndex, &isEdited); +} + +static GF_Err gf_isom_get_3gpp_audio_esd(GF_SampleTableBox *stbl, GF_GenericAudioSampleEntryBox *entry, GF_ESD **out_esd) +{ + GF_BitStream *bs; + char szName[80]; + + (*out_esd) = gf_odf_desc_esd_new(2); + (*out_esd)->decoderConfig->streamType = GF_STREAM_AUDIO; + /*official mapping to MPEG-4*/ + switch (entry->type) { + case GF_ISOM_SUBTYPE_3GP_EVRC: + (*out_esd)->decoderConfig->objectTypeIndication = GPAC_OTI_AUDIO_EVRC_VOICE; + return GF_OK; + case GF_ISOM_SUBTYPE_3GP_QCELP: + { + u32 block_size, sample_rate, sample_size, i; + GF_SttsEntry *ent; + /*only map CBR*/ + sample_size = stbl->SampleSize->sampleSize; + (*out_esd)->decoderConfig->objectTypeIndication = GPAC_OTI_AUDIO_13K_VOICE; + bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); + gf_bs_write_data(bs, "QLCMfmt ", 8); + gf_bs_write_u32_le(bs, 150);/*fmt chunk size*/ + gf_bs_write_u8(bs, 1); + gf_bs_write_u8(bs, 0); + /*QCELP GUID*/ + gf_bs_write_data(bs, "\x41\x6D\x7F\x5E\x15\xB1\xD0\x11\xBA\x91\x00\x80\x5F\xB4\xB9\x7E", 16); + gf_bs_write_u16_le(bs, 1); + memset(szName, 0, 80); + strcpy(szName, "QCELP-13K(GPAC-emulated)"); + gf_bs_write_data(bs, szName, 80); + ent = &stbl->TimeToSample->entries[0]; + sample_rate = entry->samplerate_hi; + block_size = ent ? ent->sampleDelta : 160; + gf_bs_write_u16_le(bs, 8*sample_size*sample_rate/block_size); + gf_bs_write_u16_le(bs, sample_size); + gf_bs_write_u16_le(bs, block_size); + gf_bs_write_u16_le(bs, sample_rate); + gf_bs_write_u16_le(bs, entry->bitspersample); + gf_bs_write_u32_le(bs, sample_size ? 0 : 7); + /**/ + for (i=0; i<7; i++) { + static const u32 qcelp_r2s [] = {0, 1, 1, 4, 2, 8, 3, 17, 4, 35, 5, 8, 14, 1}; + if (sample_size) { + gf_bs_write_u16(bs, 0); + } else { + gf_bs_write_u8(bs, qcelp_r2s[2*i+1]); + gf_bs_write_u8(bs, qcelp_r2s[2*i]); + } + } + gf_bs_write_u16(bs, 0); + memset(szName, 0, 80); + gf_bs_write_data(bs, szName, 20);/*reserved*/ + gf_bs_get_content(bs, & (*out_esd)->decoderConfig->decoderSpecificInfo->data, & (*out_esd)->decoderConfig->decoderSpecificInfo->dataLength); + gf_bs_del(bs); + } + return GF_OK; + case GF_ISOM_SUBTYPE_3GP_SMV: + (*out_esd)->decoderConfig->objectTypeIndication = GPAC_OTI_AUDIO_SMV_VOICE; + return GF_OK; + default: + break; + } + /*this is a user-reserved used in gpac - we need a std OTI for AMR/AMRWB*/ + (*out_esd)->decoderConfig->objectTypeIndication = GPAC_OTI_MEDIA_GENERIC; + bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); + gf_bs_write_u32(bs, entry->type); + gf_bs_write_u16(bs, entry->samplerate_hi); + gf_bs_write_u16(bs, (entry->type == GF_ISOM_SUBTYPE_3GP_AMR) ? 160 : 320); + gf_bs_write_u8(bs, entry->channel_count); + gf_bs_write_u8(bs, entry->bitspersample); + gf_bs_write_u8(bs, 0); + gf_bs_get_content(bs, & (*out_esd)->decoderConfig->decoderSpecificInfo->data, & (*out_esd)->decoderConfig->decoderSpecificInfo->dataLength); + gf_bs_del(bs); + return GF_OK; +} + +GF_Err Media_GetESD(GF_MediaBox *mdia, u32 sampleDescIndex, GF_ESD **out_esd, Bool true_desc_only) +{ + GF_ESD *esd; + GF_MPEGSampleEntryBox *entry = NULL; + GF_ESDBox *ESDa; + GF_SampleDescriptionBox *stsd = mdia->information->sampleTable->SampleDescription; + + *out_esd = NULL; + if (!stsd || !stsd->boxList || !sampleDescIndex || (sampleDescIndex > gf_list_count(stsd->boxList)) ) + return GF_BAD_PARAM; + + esd = NULL; + entry = (GF_MPEGSampleEntryBox*)gf_list_get(stsd->boxList, sampleDescIndex - 1); + if (! entry) return GF_ISOM_INVALID_MEDIA; + + *out_esd = NULL; + ESDa = NULL; + switch (entry->type) { + case GF_ISOM_BOX_TYPE_MP4V: + case GF_ISOM_BOX_TYPE_ENCV: + ESDa = ((GF_MPEGVisualSampleEntryBox*)entry)->esd; + if (ESDa) esd = (GF_ESD *) ESDa->desc; + /*avc1 encrypted*/ + else esd = ((GF_MPEGVisualSampleEntryBox*) entry)->emul_esd; + break; + case GF_ISOM_BOX_TYPE_AVC1: + case GF_ISOM_BOX_TYPE_AVC2: + case GF_ISOM_BOX_TYPE_SVC1: + esd = ((GF_MPEGVisualSampleEntryBox*) entry)->emul_esd; + break; + case GF_ISOM_BOX_TYPE_MP4A: + case GF_ISOM_BOX_TYPE_ENCA: + ESDa = ((GF_MPEGAudioSampleEntryBox*)entry)->esd; + if (ESDa) esd = (GF_ESD *) ESDa->desc; + break; + case GF_ISOM_BOX_TYPE_MP4S: + case GF_ISOM_BOX_TYPE_ENCS: + ESDa = entry->esd; + if (ESDa) esd = (GF_ESD *) ESDa->desc; + break; + case GF_ISOM_BOX_TYPE_TX3G: + case GF_ISOM_BOX_TYPE_TEXT: + if (!true_desc_only && mdia->mediaTrack->moov->mov->convert_streaming_text) { + GF_Err e = gf_isom_get_ttxt_esd(mdia, out_esd); + if (e) return e; + break; + } + else return GF_ISOM_INVALID_MEDIA; + + case GF_ISOM_SUBTYPE_3GP_AMR: + case GF_ISOM_SUBTYPE_3GP_AMR_WB: + case GF_ISOM_SUBTYPE_3GP_EVRC: + case GF_ISOM_SUBTYPE_3GP_QCELP: + case GF_ISOM_SUBTYPE_3GP_SMV: + if (!true_desc_only) { + GF_Err e = gf_isom_get_3gpp_audio_esd(mdia->information->sampleTable, (GF_GenericAudioSampleEntryBox*)entry, out_esd); + if (e) return e; + break; + } else return GF_ISOM_INVALID_MEDIA; + + case GF_ISOM_SUBTYPE_3GP_H263: + if (true_desc_only) { + return GF_ISOM_INVALID_MEDIA; + } else { + GF_BitStream *bs; + esd = gf_odf_desc_esd_new(2); + *out_esd = esd; + esd->decoderConfig->streamType = GF_STREAM_VISUAL; + esd->decoderConfig->objectTypeIndication = GPAC_OTI_MEDIA_GENERIC; + bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); + gf_bs_write_u32(bs, entry->type); + gf_bs_write_u16(bs, ((GF_MPEGVisualSampleEntryBox*)entry)->Width); + gf_bs_write_u16(bs, ((GF_MPEGVisualSampleEntryBox*)entry)->Height); + gf_bs_get_content(bs, & esd->decoderConfig->decoderSpecificInfo->data, & esd->decoderConfig->decoderSpecificInfo->dataLength); + gf_bs_del(bs); + break; + } + + case GF_ISOM_SUBTYPE_LSR1: + if (true_desc_only) { + return GF_ISOM_INVALID_MEDIA; + } else { + GF_LASeRSampleEntryBox*ptr = (GF_LASeRSampleEntryBox*)entry; + esd = gf_odf_desc_esd_new(2); + *out_esd = esd; + esd->decoderConfig->streamType = GF_STREAM_SCENE; + esd->decoderConfig->objectTypeIndication = GPAC_OTI_SCENE_LASER; + esd->decoderConfig->decoderSpecificInfo->dataLength = ptr->lsr_config->hdr_size; + esd->decoderConfig->decoderSpecificInfo->data = (char *) gf_malloc(sizeof(char)*ptr->lsr_config->hdr_size); + memcpy(esd->decoderConfig->decoderSpecificInfo->data, ptr->lsr_config->hdr, sizeof(char)*ptr->lsr_config->hdr_size); + break; + } + + default: return GF_ISOM_INVALID_MEDIA; + } + + if (true_desc_only) { + if (!esd) return GF_ISOM_INVALID_MEDIA; + *out_esd = esd; + return GF_OK; + } else { + if (!esd && !*out_esd) return GF_ISOM_INVALID_MEDIA; + if (*out_esd == NULL) gf_odf_desc_copy((GF_Descriptor *)esd, (GF_Descriptor **)out_esd); + } + return GF_OK; +} + +Bool Media_IsSampleSyncShadow(GF_ShadowSyncBox *stsh, u32 sampleNumber) +{ + u32 i; + GF_StshEntry *ent; + if (!stsh) return 0; + i=0; + while ((ent = (GF_StshEntry*)gf_list_enum(stsh->entries, &i))) { + if ((u32) ent->syncSampleNumber == sampleNumber) return 1; + else if ((u32) ent->syncSampleNumber > sampleNumber) return 0; + } + return 0; +} + +GF_Err Media_GetSample(GF_MediaBox *mdia, u32 sampleNumber, GF_ISOSample **samp, u32 *sIDX, Bool no_data, u64 *out_offset) +{ + GF_Err e; + u32 bytesRead; + u32 dataRefIndex, chunkNumber; + u64 offset, new_size; + u8 isEdited; + GF_SampleEntryBox *entry; + + + if (!mdia || !mdia->information->sampleTable) return GF_BAD_PARAM; + + //OK, here we go.... + if (sampleNumber > mdia->information->sampleTable->SampleSize->sampleCount) return GF_BAD_PARAM; + + //get the DTS + e = stbl_GetSampleDTS(mdia->information->sampleTable->TimeToSample, sampleNumber, &(*samp)->DTS); + if (e) return e; + //the CTS offset + if (mdia->information->sampleTable->CompositionOffset) { + e = stbl_GetSampleCTS(mdia->information->sampleTable->CompositionOffset , sampleNumber, &(*samp)->CTS_Offset); + if (e) return e; + } else { + (*samp)->CTS_Offset = 0; + } + //the size + e = stbl_GetSampleSize(mdia->information->sampleTable->SampleSize, sampleNumber, &(*samp)->dataLength); + if (e) return e; + //the RAP + if (mdia->information->sampleTable->SyncSample) { + e = stbl_GetSampleRAP(mdia->information->sampleTable->SyncSample, sampleNumber, &(*samp)->IsRAP, NULL, NULL); + if (e) return e; + } else { + //if no SyncSample, all samples are sync (cf spec) + (*samp)->IsRAP = 1; + } + /*overwrite sync sample with sample dep if any*/ + if (mdia->information->sampleTable->SampleDep) { + u32 dependsOn, dependedOn, redundant; + e = stbl_GetSampleDepType(mdia->information->sampleTable->SampleDep, sampleNumber, &dependsOn, &dependedOn, &redundant); + if (!e) { + if (dependsOn==1) (*samp)->IsRAP = 0; + else if (dependsOn==2) (*samp)->IsRAP = 1; + /*if not depended upon and redundant, mark as carousel sample*/ + if ((dependedOn==2) && (redundant==1)) (*samp)->IsRAP = 2; + /*TODO FIXME - we must enhance the IsRAP semantics to carry disposable info ... */ + } + } + /*get sync shadow*/ + if (Media_IsSampleSyncShadow(mdia->information->sampleTable->ShadowSync, sampleNumber)) (*samp)->IsRAP = 2; + + //the data info + if (!sIDX && !no_data) return GF_BAD_PARAM; + if (!sIDX && !out_offset) return GF_OK; + + (*sIDX) = 0; + e = stbl_GetSampleInfos(mdia->information->sampleTable, sampleNumber, &offset, &chunkNumber, sIDX, &isEdited); + if (e) return e; + + //then get the DataRef + e = Media_GetSampleDesc(mdia, *sIDX, &entry, &dataRefIndex); + if (e) return e; + + // Open the data handler - check our mode, don't reopen in read only if this is + //the same entry. In other modes we have no choice because the main data map is + //divided into the original and the edition files + if (mdia->mediaTrack->moov->mov->openMode == GF_ISOM_OPEN_READ) { + //same as last call in read mode + if (!mdia->information->dataHandler || (mdia->information->dataEntryIndex != dataRefIndex)) { + e = gf_isom_datamap_open(mdia, dataRefIndex, isEdited); + if (e) return e; + } + } else { + e = gf_isom_datamap_open(mdia, dataRefIndex, isEdited); + if (e) return e; + } + + if (out_offset) *out_offset = offset; + if (no_data) return GF_OK; + + /*and finally get the data, include padding if needed*/ + (*samp)->data = (char *) gf_malloc(sizeof(char) * ( (*samp)->dataLength + mdia->mediaTrack->padding_bytes) ); + if (mdia->mediaTrack->padding_bytes) + memset((*samp)->data + (*samp)->dataLength, 0, sizeof(char) * mdia->mediaTrack->padding_bytes); + + //check if we can get the sample (make sure we have enougth data...) + new_size = gf_bs_get_size(mdia->information->dataHandler->bs); + if (offset + (*samp)->dataLength > new_size) { + //always refresh the size to avoid wrong info on http/ftp + new_size = gf_bs_get_refreshed_size(mdia->information->dataHandler->bs); + if (offset + (*samp)->dataLength > new_size) { + mdia->BytesMissing = offset + (*samp)->dataLength - new_size; + return GF_ISOM_INCOMPLETE_FILE; + } + } + + bytesRead = gf_isom_datamap_get_data(mdia->information->dataHandler, (*samp)->data, (*samp)->dataLength, offset); + //if bytesRead != sampleSize, we have an IO err + if (bytesRead < (*samp)->dataLength) { + return GF_IO_ERR; + } + mdia->BytesMissing = 0; + //finally rewrite the sample if this is an OD Access Unit + if (mdia->handler->handlerType == GF_ISOM_MEDIA_OD) { + e = Media_RewriteODFrame(mdia, *samp); + if (e) return e; + } + else if (mdia->mediaTrack->moov->mov->convert_streaming_text + && ((mdia->handler->handlerType == GF_ISOM_MEDIA_TEXT) || (mdia->handler->handlerType == GF_ISOM_MEDIA_SUBT)) + ) { + u64 dur; + if (sampleNumber == mdia->information->sampleTable->SampleSize->sampleCount) { + dur = mdia->mediaHeader->duration - (*samp)->DTS; + } else { + stbl_GetSampleDTS(mdia->information->sampleTable->TimeToSample, sampleNumber+1, &dur); + dur -= (*samp)->DTS; + } + e = gf_isom_rewrite_text_sample(*samp, *sIDX, (u32) dur); + if (e) return e; + } + return GF_OK; +} + + + +GF_Err Media_CheckDataEntry(GF_MediaBox *mdia, u32 dataEntryIndex) +{ + + GF_DataEntryURLBox *entry; + GF_DataMap *map; + GF_Err e; + if (!mdia || !dataEntryIndex || dataEntryIndex > gf_list_count(mdia->information->dataInformation->dref->boxList)) return GF_BAD_PARAM; + + entry = (GF_DataEntryURLBox*)gf_list_get(mdia->information->dataInformation->dref->boxList, dataEntryIndex - 1); + if (!entry) return GF_ISOM_INVALID_FILE; + if (entry->flags == 1) return GF_OK; + + //ok, not self contained, let's go for it... + //we don't know what's a URN yet + if (entry->type == GF_ISOM_BOX_TYPE_URN) return GF_NOT_SUPPORTED; + if (mdia->mediaTrack->moov->mov->openMode == GF_ISOM_OPEN_WRITE) { + e = gf_isom_datamap_new(entry->location, NULL, GF_ISOM_DATA_MAP_READ, &map); + } else { + e = gf_isom_datamap_new(entry->location, mdia->mediaTrack->moov->mov->fileName, GF_ISOM_DATA_MAP_READ, &map); + } + if (e) return e; + gf_isom_datamap_del(map); + return GF_OK; +} + + +Bool Media_IsSelfContained(GF_MediaBox *mdia, u32 StreamDescIndex) +{ + u32 drefIndex=0; + GF_FullBox *a; + GF_SampleEntryBox *se = NULL; + + Media_GetSampleDesc(mdia, StreamDescIndex, &se, &drefIndex); + if (!drefIndex) return 0; + a = (GF_FullBox*)gf_list_get(mdia->information->dataInformation->dref->boxList, drefIndex - 1); + if (a->flags & 1) return 1; + /*QT specific*/ + if (a->type == GF_4CC('a', 'l', 'i', 's')) return 1; + return 0; +} + + + +//look for a sync sample from a given point in media time +GF_Err Media_FindSyncSample(GF_SampleTableBox *stbl, u32 searchFromSample, u32 *sampleNumber, u8 mode) +{ + u8 isRAP; + u32 next, prev; + if (!stbl || !stbl->SyncSample) return GF_BAD_PARAM; + + //set to current sample if we don't find a RAP + *sampleNumber = searchFromSample; + + //this is not the exact sample, but the prev move to next sample if enough samples.... + if ( (mode == GF_ISOM_SEARCH_SYNC_FORWARD) && (searchFromSample == stbl->SampleSize->sampleCount) ) { + return GF_OK; + } + if ( (mode == GF_ISOM_SEARCH_SYNC_BACKWARD) && !searchFromSample) { + *sampleNumber = 1; + return GF_OK; + } + //get the entry + stbl_GetSampleRAP(stbl->SyncSample, searchFromSample, &isRAP, &prev, &next); + if (isRAP) { + (*sampleNumber) = searchFromSample; + return GF_OK; + } + //nothing yet, go for next time... + if (mode == GF_ISOM_SEARCH_SYNC_FORWARD) { + if (next) *sampleNumber = next; + } else { + if (prev) *sampleNumber = prev; + } + return GF_OK; +} + +//create a DataReference if not existing (only for WRITE-edit mode) +GF_Err Media_FindDataRef(GF_DataReferenceBox *dref, char *URLname, char *URNname, u32 *dataRefIndex) +{ + u32 i; + GF_DataEntryURLBox *entry; + + if (!dref) return GF_BAD_PARAM; + *dataRefIndex = 0; + i=0; + while ((entry = (GF_DataEntryURLBox*)gf_list_enum(dref->boxList, &i))) { + if (entry->type == GF_ISOM_BOX_TYPE_URL) { + //self-contained case + if (entry->flags == 1) { + //if nothing specified, get the dataRef + if (!URLname && !URNname) { + *dataRefIndex = i; + return GF_OK; + } + } else { + //OK, check if we have URL + if (URLname && !strcmp(URLname, entry->location)) { + *dataRefIndex = i; + return GF_OK; + } + } + } else { + //this is a URN one, only check the URN name (URL optional) + if (URNname && !strcmp(URNname, ((GF_DataEntryURNBox *)entry)->nameURN)) { + *dataRefIndex = i; + return GF_OK; + } + } + } + return GF_OK; +} + +//Get the total media duration based on the TimeToSample table +GF_Err Media_SetDuration(GF_TrackBox *trak) +{ + GF_ESD *esd; + u64 DTS; + GF_SttsEntry *ent; + u32 nbSamp = trak->Media->information->sampleTable->SampleSize->sampleCount; + + //we need to check how many samples we have. + // == 1 -> last sample duration == default duration + // > 1 -> last sample duration == prev sample duration + switch (nbSamp) { + case 0: + trak->Media->mediaHeader->duration = 0; + if (Track_IsMPEG4Stream(trak->Media->handler->handlerType)) { + Media_GetESD(trak->Media, 1, &esd, 1); + if (esd && esd->URLString) trak->Media->mediaHeader->duration = (u64) -1; + } + return GF_OK; + +// case 1: +// trak->Media->mediaHeader->duration = trak->Media->mediaHeader->timeScale; +// return GF_OK; + + default: + //we assume a constant frame rate for the media and assume the last sample + //will be hold the same time as the prev one + stbl_GetSampleDTS(trak->Media->information->sampleTable->TimeToSample, nbSamp, &DTS); + ent = &trak->Media->information->sampleTable->TimeToSample->entries[trak->Media->information->sampleTable->TimeToSample->nb_entries-1]; + trak->Media->mediaHeader->duration = DTS; + +#if 1 + trak->Media->mediaHeader->duration += ent->sampleDelta; +#else + if (!ent) { + u64 DTSprev; + stbl_GetSampleDTS(trak->Media->information->sampleTable->TimeToSample, nbSamp-1, &DTSprev); + trak->Media->mediaHeader->duration += (DTS - DTSprev); + } else { +#ifndef GPAC_DISABLE_ISOM_WRITE + if (trak->moov->mov->editFileMap && trak->Media->information->sampleTable->CompositionOffset) { + u32 count, i; + u64 max_ts; + GF_DttsEntry *cts_ent; + GF_CompositionOffsetBox *ctts = trak->Media->information->sampleTable->CompositionOffset; + if (ctts->w_LastSampleNumber==nbSamp) { + count = gf_list_count(ctts->entryList); + max_ts = trak->Media->mediaHeader->duration; + while (count) { + count -= 1; + cts_ent = gf_list_get(ctts->entryList, count); + if (nbSampsampleCount) break; + + for (i=0; isampleCount; i++) { + stbl_GetSampleDTS(trak->Media->information->sampleTable->TimeToSample, nbSamp-i, &DTS); + if ((s32) cts_ent->decodingOffset < 0) max_ts = DTS; + else max_ts = DTS + cts_ent->decodingOffset; + if (max_ts>=trak->Media->mediaHeader->duration) { + trak->Media->mediaHeader->duration = max_ts; + } else { + break; + } + } + if (max_tsMedia->mediaHeader->duration) { + break; + } + nbSamp-=cts_ent->sampleCount; + } + } + } +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + trak->Media->mediaHeader->duration += ent->sampleDelta; + } +#endif + return GF_OK; + } +} + + + + +#ifndef GPAC_DISABLE_ISOM_WRITE + + +GF_Err Media_CreateDataRef(GF_DataReferenceBox *dref, char *URLname, char *URNname, u32 *dataRefIndex) +{ + GF_Err e; + GF_DataEntryURLBox *entry; + + GF_Err dref_AddDataEntry(GF_DataReferenceBox *ptr, GF_Box *entry); + + if (!URLname && !URNname) { + //THIS IS SELF CONTAIN, create a regular entry if needed + entry = (GF_DataEntryURLBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_URL); + entry->location = NULL; + entry->flags = 0; + entry->flags |= 1; + e = dref_AddDataEntry(dref, (GF_Box *)entry); + if (e) return e; + *dataRefIndex = gf_list_count(dref->boxList); + return GF_OK; + } else if (!URNname && URLname) { + //THIS IS URL + entry = (GF_DataEntryURLBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_URL); + entry->flags = 0; + entry->location = (char*)gf_malloc(strlen(URLname)+1); + if (! entry->location) { + gf_isom_box_del((GF_Box *)entry); + return GF_OUT_OF_MEM; + } + strcpy(entry->location, URLname); + e = dref_AddDataEntry(dref, (GF_Box *)entry); + if (e) return e; + *dataRefIndex = gf_list_count(dref->boxList); + return GF_OK; + } else { + //THIS IS URN + entry = (GF_DataEntryURLBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_URN); + ((GF_DataEntryURNBox *)entry)->flags = 0; + ((GF_DataEntryURNBox *)entry)->nameURN = (char*)gf_malloc(strlen(URNname)+1); + if (! ((GF_DataEntryURNBox *)entry)->nameURN) { + gf_isom_box_del((GF_Box *)entry); + return GF_OUT_OF_MEM; + } + strcpy(((GF_DataEntryURNBox *)entry)->nameURN, URNname); + //check for URL + if (URLname) { + ((GF_DataEntryURNBox *)entry)->location = (char*)gf_malloc(strlen(URLname)+1); + if (! ((GF_DataEntryURNBox *)entry)->location) { + gf_isom_box_del((GF_Box *)entry); + return GF_OUT_OF_MEM; + } + strcpy(((GF_DataEntryURNBox *)entry)->location, URLname); + } + e = dref_AddDataEntry(dref, (GF_Box *)entry); + if (e) return e; + *dataRefIndex = gf_list_count(dref->boxList); + return GF_OK; + } + return GF_OK; +} + + +GF_Err Media_AddSample(GF_MediaBox *mdia, u64 data_offset, GF_ISOSample *sample, u32 StreamDescIndex, u32 syncShadowNumber) +{ + GF_Err e; + GF_SampleTableBox *stbl; + u32 sampleNumber, i; + if (!mdia || !sample) return GF_BAD_PARAM; + + stbl = mdia->information->sampleTable; + + //get a valid sampleNumber for this new guy + e = stbl_AddDTS(stbl, sample->DTS, &sampleNumber, mdia->mediaHeader->timeScale); + if (e) return e; + + //add size + e = stbl_AddSize(stbl->SampleSize, sampleNumber, sample->dataLength); + if (e) return e; + + //adds CTS offset + if (sample->CTS_Offset) { + //if we don't have a CTS table, add it... + if (!stbl->CompositionOffset) stbl->CompositionOffset = (GF_CompositionOffsetBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_CTTS); + //then add our CTS (the prev samples with no CTS offset will be automatically added... + e = stbl_AddCTS(stbl, sampleNumber, sample->CTS_Offset); + if (e) return e; + } else if (stbl->CompositionOffset) { + e = stbl_AddCTS(stbl, sampleNumber, sample->CTS_Offset); + if (e) return e; + } + + //The first non sync sample we see must create a syncTable + if (sample->IsRAP) { + //insert it only if we have a sync table + if (stbl->SyncSample) { + e = stbl_AddRAP(stbl->SyncSample, sampleNumber); + if (e) return e; + } + } else { + //non-sync sample. Create a SyncSample table if needed + if (!stbl->SyncSample) { + stbl->SyncSample = (GF_SyncSampleBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_STSS); + //all the prev samples are sync + for (i=0; iSampleSize->sampleCount; i++) { + if (i+1 != sampleNumber) { + e = stbl_AddRAP(stbl->SyncSample, i+1); + if (e) return e; + } + } + } + } + if (sample->IsRAP==2) { + e = stbl_AddRedundant(stbl, sampleNumber); + if (e) return e; + } + + //and update the chunks + e = stbl_AddChunkOffset(mdia, sampleNumber, StreamDescIndex, data_offset); + if (e) return e; + + if (!syncShadowNumber) return GF_OK; + if (!stbl->ShadowSync) stbl->ShadowSync = (GF_ShadowSyncBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_STSH); + return stbl_AddShadow(mdia->information->sampleTable->ShadowSync, sampleNumber, syncShadowNumber); +} + + +GF_Err UpdateSample(GF_MediaBox *mdia, u32 sampleNumber, u32 size, u32 CTS, u64 offset, u8 isRap) +{ + u32 i; + GF_SampleTableBox *stbl = mdia->information->sampleTable; + + //set size, offset, RAP, CTS ... + stbl_SetSampleSize(stbl->SampleSize, sampleNumber, size); + stbl_SetChunkOffset(mdia, sampleNumber, offset); + + //do we have a CTS? + if (stbl->CompositionOffset) { + stbl_SetSampleCTS(stbl, sampleNumber, CTS); + } else { + //do we need one ?? + if (CTS) { + stbl->CompositionOffset = (GF_CompositionOffsetBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_CTTS); + stbl_AddCTS(stbl, sampleNumber, CTS); + } + } + //do we have a sync ??? + if (stbl->SyncSample) { + stbl_SetSampleRAP(stbl->SyncSample, sampleNumber, isRap); + } else { + //do we need one + if (! isRap) { + stbl->SyncSample = (GF_SyncSampleBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_STSS); + //what a pain: all the sample we had have to be sync ... + for (i=0; iSampleSize->sampleCount; i++) { + if (i+1 != sampleNumber) stbl_AddRAP(stbl->SyncSample, i+1); + } + } + } + if (isRap==2) { + stbl_SetRedundant(stbl, sampleNumber); + } + return GF_OK; +} + +GF_Err Media_UpdateSample(GF_MediaBox *mdia, u32 sampleNumber, GF_ISOSample *sample, Bool data_only) +{ + GF_Err e; + u32 drefIndex, chunkNum, descIndex; + u64 newOffset, DTS; + u8 isEdited; + GF_DataEntryURLBox *Dentry; + GF_SampleTableBox *stbl; + + GF_Err stbl_AddBox(GF_SampleTableBox *ptr, GF_Box *a); + + if (!mdia || !sample || !sampleNumber || !mdia->mediaTrack->moov->mov->editFileMap) + return GF_BAD_PARAM; + + stbl = mdia->information->sampleTable; + + if (!data_only) { + //check we have the sampe dts + e = stbl_GetSampleDTS(stbl->TimeToSample, sampleNumber, &DTS); + if (e) return e; + if (DTS != sample->DTS) return GF_BAD_PARAM; + } + + //get our infos + stbl_GetSampleInfos(stbl, sampleNumber, &newOffset, &chunkNum, &descIndex, &isEdited); + + //then check the data ref + e = Media_GetSampleDesc(mdia, descIndex, NULL, &drefIndex); + if (e) return e; + Dentry = (GF_DataEntryURLBox*)gf_list_get(mdia->information->dataInformation->dref->boxList, drefIndex - 1); + if (!Dentry) return GF_ISOM_INVALID_FILE; + + if (Dentry->flags != 1) return GF_BAD_PARAM; + + //MEDIA DATA EDIT: write this new sample to the edit temp file + newOffset = gf_isom_datamap_get_offset(mdia->mediaTrack->moov->mov->editFileMap); + e = gf_isom_datamap_add_data(mdia->mediaTrack->moov->mov->editFileMap, sample->data, sample->dataLength); + if (e) return e; + + if (data_only) { + stbl_SetSampleSize(stbl->SampleSize, sampleNumber, sample->dataLength); + return stbl_SetChunkOffset(mdia, sampleNumber, newOffset); + } + return UpdateSample(mdia, sampleNumber, sample->dataLength, sample->CTS_Offset, newOffset, sample->IsRAP); +} + +GF_Err Media_UpdateSampleReference(GF_MediaBox *mdia, u32 sampleNumber, GF_ISOSample *sample, u64 data_offset) +{ + GF_Err e; + u32 drefIndex, chunkNum, descIndex; + u64 off, DTS; + u8 isEdited; + GF_DataEntryURLBox *Dentry; + GF_SampleTableBox *stbl; + GF_Err stbl_AddBox(GF_SampleTableBox *ptr, GF_Box *a); + + if (!mdia) return GF_BAD_PARAM; + stbl = mdia->information->sampleTable; + + //check we have the sampe dts + e = stbl_GetSampleDTS(stbl->TimeToSample, sampleNumber, &DTS); + if (e) return e; + if (DTS != sample->DTS) return GF_BAD_PARAM; + + //get our infos + stbl_GetSampleInfos(stbl, sampleNumber, &off, &chunkNum, &descIndex, &isEdited); + + //then check the data ref + e = Media_GetSampleDesc(mdia, descIndex, NULL, &drefIndex); + if (e) return e; + Dentry = (GF_DataEntryURLBox*)gf_list_get(mdia->information->dataInformation->dref->boxList, drefIndex - 1); + if (!Dentry) return GF_ISOM_INVALID_FILE; + + //we only modify self-contained data + if (Dentry->flags == 1) return GF_ISOM_INVALID_MODE; + + //and we don't modify the media data + return UpdateSample(mdia, sampleNumber, sample->dataLength, sample->CTS_Offset, data_offset, sample->IsRAP); +} + + +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + +#endif /*GPAC_DISABLE_ISOM*/ diff --git a/src/gpacmp4/media_odf.c b/src/gpacmp4/media_odf.c new file mode 100644 index 00000000..2c0f5547 --- /dev/null +++ b/src/gpacmp4/media_odf.c @@ -0,0 +1,520 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / ISO Media File Format sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include + +#ifndef GPAC_DISABLE_ISOM + +// Rewrite the good dependancies when an OD AU is extracted from the file +GF_Err Media_RewriteODFrame(GF_MediaBox *mdia, GF_ISOSample *sample) +{ + GF_Err e; + GF_ODCodec *ODdecode; + GF_ODCodec *ODencode; + GF_ODCom *com; + + //the commands we proceed + GF_ESDUpdate *esdU, *esdU2; + GF_ESDRemove *esdR, *esdR2; + GF_ODUpdate *odU, *odU2; + + //the desc they contain + GF_ObjectDescriptor *od; + GF_IsomObjectDescriptor *isom_od; + GF_ESD *esd; + GF_ES_ID_Ref *ref; + GF_Descriptor *desc; + GF_TrackReferenceTypeBox *mpod; + u32 i, j, skipped; + + if (!mdia || !sample || !sample->data || !sample->dataLength) return GF_BAD_PARAM; + + mpod = NULL; + e = Track_FindRef(mdia->mediaTrack, GF_ISOM_BOX_TYPE_MPOD, &mpod); + if (e) return e; + //no references, nothing to do... + if (!mpod) return GF_OK; + + ODdecode = gf_odf_codec_new(); + if (!ODdecode) return GF_OUT_OF_MEM; + ODencode = gf_odf_codec_new(); + if (!ODencode) { + gf_odf_codec_del(ODdecode); + return GF_OUT_OF_MEM; + } + e = gf_odf_codec_set_au(ODdecode, sample->data, sample->dataLength); + if (e) goto err_exit; + e = gf_odf_codec_decode(ODdecode); + if (e) goto err_exit; + + while (1) { + com = gf_odf_codec_get_com(ODdecode); + if (!com) break; + + //we only need to rewrite commands with ESDs inside: ESDUpdate and ODUpdate + switch (com->tag) { + case GF_ODF_OD_UPDATE_TAG: + odU = (GF_ODUpdate *) com; + odU2 = (GF_ODUpdate *) gf_odf_com_new(GF_ODF_OD_UPDATE_TAG); + + i=0; + while ((desc = (GF_Descriptor*)gf_list_enum(odU->objectDescriptors, &i))) { + switch (desc->tag) { + case GF_ODF_OD_TAG: + case GF_ODF_ISOM_OD_TAG: + //IOD can be used in OD streams + case GF_ODF_ISOM_IOD_TAG: + break; + default: + return GF_ISOM_INVALID_FILE; + } + e = gf_odf_desc_copy(desc, (GF_Descriptor **)&isom_od); + if (e) goto err_exit; + + //create our OD... + if (desc->tag == GF_ODF_ISOM_IOD_TAG) { + od = (GF_ObjectDescriptor *) gf_malloc(sizeof(GF_InitialObjectDescriptor)); + } else { + od = (GF_ObjectDescriptor *) gf_malloc(sizeof(GF_ObjectDescriptor)); + } + if (!od) { + e = GF_OUT_OF_MEM; + goto err_exit; + } + od->ESDescriptors = gf_list_new(); + //and duplicate... + od->objectDescriptorID = isom_od->objectDescriptorID; + od->tag = GF_ODF_OD_TAG; + od->URLString = isom_od->URLString; + isom_od->URLString = NULL; + od->extensionDescriptors = isom_od->extensionDescriptors; + isom_od->extensionDescriptors = NULL; + od->IPMP_Descriptors = isom_od->IPMP_Descriptors; + isom_od->IPMP_Descriptors = NULL; + od->OCIDescriptors = isom_od->OCIDescriptors; + isom_od->OCIDescriptors = NULL; + + //init as IOD + if (isom_od->tag == GF_ODF_ISOM_IOD_TAG) { + ((GF_InitialObjectDescriptor *)od)->audio_profileAndLevel = ((GF_IsomInitialObjectDescriptor *)isom_od)->audio_profileAndLevel; + ((GF_InitialObjectDescriptor *)od)->inlineProfileFlag = ((GF_IsomInitialObjectDescriptor *)isom_od)->inlineProfileFlag; + ((GF_InitialObjectDescriptor *)od)->graphics_profileAndLevel = ((GF_IsomInitialObjectDescriptor *)isom_od)->graphics_profileAndLevel; + ((GF_InitialObjectDescriptor *)od)->OD_profileAndLevel = ((GF_IsomInitialObjectDescriptor *)isom_od)->OD_profileAndLevel; + ((GF_InitialObjectDescriptor *)od)->scene_profileAndLevel = ((GF_IsomInitialObjectDescriptor *)isom_od)->scene_profileAndLevel; + ((GF_InitialObjectDescriptor *)od)->visual_profileAndLevel = ((GF_IsomInitialObjectDescriptor *)isom_od)->visual_profileAndLevel; + ((GF_InitialObjectDescriptor *)od)->IPMPToolList = ((GF_IsomInitialObjectDescriptor *)isom_od)->IPMPToolList; + ((GF_IsomInitialObjectDescriptor *)isom_od)->IPMPToolList = NULL; + } + + //then rewrite the ESDesc + j=0; + while ((ref = (GF_ES_ID_Ref*)gf_list_enum(isom_od->ES_ID_RefDescriptors, &j))){ + //if the ref index is not valid, skip this desc... + if (!mpod->trackIDs || gf_isom_get_track_from_id(mdia->mediaTrack->moov, mpod->trackIDs[ref->trackRef - 1]) == NULL) continue; + //OK, get the esd + e = GetESDForTime(mdia->mediaTrack->moov, mpod->trackIDs[ref->trackRef - 1], sample->DTS, &esd); + if (!e) e = gf_odf_desc_add_desc((GF_Descriptor *) od, (GF_Descriptor *) esd); + if (e) { + gf_odf_desc_del((GF_Descriptor *)od); + gf_odf_com_del((GF_ODCom **)&odU2); + gf_odf_desc_del((GF_Descriptor *)isom_od); + gf_odf_com_del((GF_ODCom **)&odU); + goto err_exit; + } + + } + //delete our desc + gf_odf_desc_del((GF_Descriptor *)isom_od); + gf_list_add(odU2->objectDescriptors, od); + } + //clean a bit + gf_odf_com_del((GF_ODCom **)&odU); + gf_odf_codec_add_com(ODencode, (GF_ODCom *)odU2); + break; + + case GF_ODF_ESD_UPDATE_TAG: + esdU = (GF_ESDUpdate *) com; + esdU2 = (GF_ESDUpdate *) gf_odf_com_new(GF_ODF_ESD_UPDATE_TAG); + esdU2->ODID = esdU->ODID; + i=0; + while ((ref = (GF_ES_ID_Ref*)gf_list_enum(esdU->ESDescriptors, &i))) { + //if the ref index is not valid, skip this desc... + if (gf_isom_get_track_from_id(mdia->mediaTrack->moov, mpod->trackIDs[ref->trackRef - 1]) == NULL) continue; + //OK, get the esd + e = GetESDForTime(mdia->mediaTrack->moov, mpod->trackIDs[ref->trackRef - 1], sample->DTS, &esd); + if (e) goto err_exit; + gf_list_add(esdU2->ESDescriptors, esd); + } + gf_odf_com_del((GF_ODCom **)&esdU); + gf_odf_codec_add_com(ODencode, (GF_ODCom *)esdU2); + break; + + //brand new case: the ESRemove follows the same principle according to the spec... + case GF_ODF_ESD_REMOVE_REF_TAG: + //both commands have the same structure, only the tags change + esdR = (GF_ESDRemove *) com; + esdR2 = (GF_ESDRemove *) gf_odf_com_new(GF_ODF_ESD_REMOVE_TAG); + esdR2->ODID = esdR->ODID; + esdR2->NbESDs = esdR->NbESDs; + //alloc our stuff + esdR2->ES_ID = (unsigned short*)gf_malloc(sizeof(u32) * esdR->NbESDs); + if (!esdR2->ES_ID) { + e = GF_OUT_OF_MEM; + goto err_exit; + } + skipped = 0; + //get the ES_ID in the mpod indicated in the ES_ID[] + for (i = 0; i < esdR->NbESDs; i++) { + //if the ref index is not valid, remove this desc... + if (gf_isom_get_track_from_id(mdia->mediaTrack->moov, mpod->trackIDs[esdR->ES_ID[i] - 1]) == NULL) { + skipped ++; + } else { + //the command in the file has the ref index of the trackID in the mpod + esdR2->ES_ID[i - skipped] = mpod->trackIDs[esdR->ES_ID[i] - 1]; + } + } + //gf_realloc... + if (skipped && (skipped != esdR2->NbESDs) ) { + esdR2->NbESDs -= skipped; + esdR2->ES_ID = (unsigned short*)gf_realloc(esdR2->ES_ID, sizeof(u32) * esdR2->NbESDs); + } + gf_odf_com_del((GF_ODCom **)&esdR); + gf_odf_codec_add_com(ODencode, (GF_ODCom *)esdR2); + break; + + default: + e = gf_odf_codec_add_com(ODencode, com); + if (e) goto err_exit; + } + } + //encode our new AU + e = gf_odf_codec_encode(ODencode, 1); + if (e) goto err_exit; + + //and set the buffer in the sample + gf_free(sample->data); + sample->data = NULL; + sample->dataLength = 0; + e = gf_odf_codec_get_au(ODencode, &sample->data, &sample->dataLength); + +err_exit: + gf_odf_codec_del(ODdecode); + gf_odf_codec_del(ODencode); + return e; +} + + +// Update the dependancies when an OD AU is inserted in the file +GF_Err Media_ParseODFrame(GF_MediaBox *mdia, GF_ISOSample *sample, GF_ISOSample **od_samp) +{ + GF_TrackReferenceBox *tref; + GF_TrackReferenceTypeBox *mpod; + GF_Err e; + GF_ODCom *com; + GF_ODCodec *ODencode; + GF_ODCodec *ODdecode; + u32 i, j; + //the commands we proceed + GF_ESDUpdate *esdU, *esdU2; + GF_ESDRemove *esdR, *esdR2; + GF_ODUpdate *odU, *odU2; + + //the desc they contain + GF_ObjectDescriptor *od; + GF_IsomObjectDescriptor *isom_od; + GF_ESD *esd; + GF_ES_ID_Ref *ref; + GF_Descriptor *desc; + + *od_samp = NULL; + if (!mdia || !sample || !sample->data || !sample->dataLength) return GF_BAD_PARAM; + + //First find the references, and create them if none + tref = mdia->mediaTrack->References; + if (!tref) { + tref = (GF_TrackReferenceBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_TREF); + e = trak_AddBox((GF_Box*)mdia->mediaTrack, (GF_Box *) tref); + if (e) return e; + } + //then find the OD reference, and create it if none + e = Track_FindRef(mdia->mediaTrack, GF_ISOM_BOX_TYPE_MPOD, &mpod); + if (e) return e; + if (!mpod) { + mpod = (GF_TrackReferenceTypeBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_REFT); + mpod->reference_type = GF_ISOM_BOX_TYPE_MPOD; + e = tref_AddBox((GF_Box*)tref, (GF_Box *)mpod); + if (e) return e; + } + + //OK, create our codecs + ODencode = gf_odf_codec_new(); + if (!ODencode) return GF_OUT_OF_MEM; + ODdecode = gf_odf_codec_new(); + if (!ODdecode) return GF_OUT_OF_MEM; + + e = gf_odf_codec_set_au(ODdecode, sample->data, sample->dataLength); + if (e) goto err_exit; + e = gf_odf_codec_decode(ODdecode); + if (e) goto err_exit; + + while (1) { + com = gf_odf_codec_get_com(ODdecode); + if (!com) break; + + //check our commands + switch (com->tag) { + //Rewrite OD Update + case GF_ODF_OD_UPDATE_TAG: + //duplicate our command + odU = (GF_ODUpdate *) com; + odU2 = (GF_ODUpdate *) gf_odf_com_new(GF_ODF_OD_UPDATE_TAG); + + i=0; + while ((desc = (GF_Descriptor*)gf_list_enum(odU->objectDescriptors, &i))) { + //both OD and IODs are accepted + switch (desc->tag) { + case GF_ODF_OD_TAG: + case GF_ODF_IOD_TAG: + break; + default: + e = GF_ODF_INVALID_DESCRIPTOR; + goto err_exit; + } + //get the esd + e = gf_odf_desc_copy(desc, (GF_Descriptor **)&od); + if (e) goto err_exit; + if (desc->tag == GF_ODF_OD_TAG) { + isom_od = (GF_IsomObjectDescriptor *) gf_malloc(sizeof(GF_IsomObjectDescriptor)); + isom_od->tag = GF_ODF_ISOM_OD_TAG; + } else { + isom_od = (GF_IsomObjectDescriptor *) gf_malloc(sizeof(GF_IsomInitialObjectDescriptor)); + isom_od->tag = GF_ODF_ISOM_IOD_TAG; + //copy PL + ((GF_IsomInitialObjectDescriptor *)isom_od)->inlineProfileFlag = ((GF_InitialObjectDescriptor *)od)->inlineProfileFlag; + ((GF_IsomInitialObjectDescriptor *)isom_od)->graphics_profileAndLevel = ((GF_InitialObjectDescriptor *)od)->graphics_profileAndLevel; + ((GF_IsomInitialObjectDescriptor *)isom_od)->audio_profileAndLevel = ((GF_InitialObjectDescriptor *)od)->audio_profileAndLevel; + ((GF_IsomInitialObjectDescriptor *)isom_od)->OD_profileAndLevel = ((GF_InitialObjectDescriptor *)od)->OD_profileAndLevel; + ((GF_IsomInitialObjectDescriptor *)isom_od)->scene_profileAndLevel = ((GF_InitialObjectDescriptor *)od)->scene_profileAndLevel; + ((GF_IsomInitialObjectDescriptor *)isom_od)->visual_profileAndLevel = ((GF_InitialObjectDescriptor *)od)->visual_profileAndLevel; + ((GF_IsomInitialObjectDescriptor *)isom_od)->IPMPToolList = ((GF_InitialObjectDescriptor *)od)->IPMPToolList; + ((GF_InitialObjectDescriptor *)od)->IPMPToolList = NULL; + } + //in OD stream only ref desc are accepted + isom_od->ES_ID_RefDescriptors = gf_list_new(); + isom_od->ES_ID_IncDescriptors = NULL; + + //TO DO: check that a given sampleDescription exists + isom_od->extensionDescriptors = od->extensionDescriptors; + od->extensionDescriptors = NULL; + isom_od->IPMP_Descriptors = od->IPMP_Descriptors; + od->IPMP_Descriptors = NULL; + isom_od->OCIDescriptors = od->OCIDescriptors; + od->OCIDescriptors = NULL; + isom_od->URLString = od->URLString; + od->URLString = NULL; + isom_od->objectDescriptorID = od->objectDescriptorID; + + j=0; + while ((esd = (GF_ESD*)gf_list_enum(od->ESDescriptors, &j))) { + ref = (GF_ES_ID_Ref *) gf_odf_desc_new(GF_ODF_ESD_REF_TAG); + //1 to 1 mapping trackID and ESID. Add this track to MPOD + //if track does not exist, this will be remove while reading the OD stream + e = reftype_AddRefTrack(mpod, esd->ESID, &ref->trackRef); + e = gf_odf_desc_add_desc((GF_Descriptor *)isom_od, (GF_Descriptor *)ref); + if (e) goto err_exit; + } + //delete our desc + gf_odf_desc_del((GF_Descriptor *)od); + //and add the new one to our command + gf_list_add(odU2->objectDescriptors, isom_od); + } + //delete the command + gf_odf_com_del((GF_ODCom **)&odU); + //and add the new one to the codec + gf_odf_codec_add_com(ODencode, (GF_ODCom *)odU2); + break; + + //Rewrite ESD Update + case GF_ODF_ESD_UPDATE_TAG: + esdU = (GF_ESDUpdate *) com; + esdU2 = (GF_ESDUpdate *) gf_odf_com_new(GF_ODF_ESD_UPDATE_TAG); + esdU2->ODID = esdU->ODID; + i=0; + while ((esd = (GF_ESD*)gf_list_enum(esdU->ESDescriptors, &i))) { + ref = (GF_ES_ID_Ref *) gf_odf_desc_new(GF_ODF_ESD_REF_TAG); + //1 to 1 mapping trackID and ESID + e = reftype_AddRefTrack(mpod, esd->ESID, &ref->trackRef); + e = gf_list_add(esdU2->ESDescriptors, ref); + if (e) goto err_exit; + } + gf_odf_com_del((GF_ODCom **)&esdU); + gf_odf_codec_add_com(ODencode, (GF_ODCom *)esdU2); + break; + + //Brand new case: the ESRemove has to be rewritten too according to the specs... + case GF_ODF_ESD_REMOVE_TAG: + esdR = (GF_ESDRemove *) com; + esdR2 = (GF_ESDRemove *) gf_odf_com_new(GF_ODF_ESD_REMOVE_TAG); + //change the tag for the file format + esdR2->tag = GF_ODF_ESD_REMOVE_REF_TAG; + esdR2->ODID = esdR->ODID; + esdR2->NbESDs = esdR->NbESDs; + if (esdR->NbESDs) { + //alloc our stuff + esdR2->ES_ID = (unsigned short*)gf_malloc(sizeof(u32) * esdR->NbESDs); + if (!esdR2->ES_ID) { + e = GF_OUT_OF_MEM; + goto err_exit; + } + for (i = 0; i < esdR->NbESDs; i++) { + //1 to 1 mapping trackID and ESID + e = reftype_AddRefTrack(mpod, esdR->ES_ID[i], &esdR2->ES_ID[i]); + if (e) goto err_exit; + } + } + gf_odf_com_del(&com); + gf_odf_codec_add_com(ODencode, (GF_ODCom *)esdR2); + break; + + //Add the command as is + default: + e = gf_odf_codec_add_com(ODencode, com); + if (e) goto err_exit; + } + } + + //encode our new AU + e = gf_odf_codec_encode(ODencode, 1); + if (e) goto err_exit; + + //and set the buffer in the sample + *od_samp = gf_isom_sample_new(); + (*od_samp)->CTS_Offset = sample->CTS_Offset; + (*od_samp)->DTS = sample->DTS; + (*od_samp)->IsRAP = sample->IsRAP; + e = gf_odf_codec_get_au(ODencode, & (*od_samp)->data, & (*od_samp)->dataLength); + if (e) { + gf_isom_sample_del(od_samp); + *od_samp = NULL; + } + +err_exit: + + gf_odf_codec_del(ODencode); + gf_odf_codec_del(ODdecode); + return e; +} + + + +// Rewrite the good dependancies when an OD AU is extracted from the file +static u32 Media_FindOD_ID(GF_MediaBox *mdia, GF_ISOSample *sample, u32 track_id) +{ + GF_Err e; + GF_ODCodec *ODdecode; + GF_ODCom *com; + u32 the_od_id; + GF_ODUpdate *odU; + GF_ESD *esd; + GF_Descriptor *desc; + GF_TrackReferenceTypeBox *mpod; + u32 i, j; + + if (!mdia || !sample || !sample->data || !sample->dataLength) return 0; + + mpod = NULL; + e = Track_FindRef(mdia->mediaTrack, GF_ISOM_BOX_TYPE_MPOD, &mpod); + if (e) return 0; + //no references, nothing to do... + if (!mpod) return 0; + + the_od_id = 0; + + ODdecode = gf_odf_codec_new(); + if (!ODdecode) return 0; + e = gf_odf_codec_set_au(ODdecode, sample->data, sample->dataLength); + if (e) goto err_exit; + e = gf_odf_codec_decode(ODdecode); + if (e) goto err_exit; + + while (1) { + GF_List *esd_list = NULL; + com = gf_odf_codec_get_com(ODdecode); + if (!com) break; + if (com->tag != GF_ODF_OD_UPDATE_TAG) continue; + odU = (GF_ODUpdate *) com; + + i=0; + while ((desc = (GF_Descriptor*)gf_list_enum(odU->objectDescriptors, &i))) { + switch (desc->tag) { + case GF_ODF_OD_TAG: + case GF_ODF_IOD_TAG: + esd_list = ((GF_ObjectDescriptor *)desc)->ESDescriptors; break; + default: + continue; + } + j=0; + while ((esd = (GF_ESD*)gf_list_enum( esd_list, &j))){ + if (esd->ESID==track_id) { + the_od_id = ((GF_IsomObjectDescriptor*)desc)->objectDescriptorID; + break; + } + } + if (the_od_id) break; + } + gf_odf_com_del((GF_ODCom **)&odU); + if (the_od_id) break; + } + +err_exit: + gf_odf_codec_del(ODdecode); + if (e) return 0; + return the_od_id; +} + + +GF_EXPORT +u32 gf_isom_find_od_for_track(GF_ISOFile *file, u32 track) +{ + u32 i, j, di, the_od_id; + GF_TrackBox *od_tk; + GF_TrackBox *tk = gf_isom_get_track_from_file(file, track); + if (!tk) return 0; + + the_od_id = 0; + i=0; + while ( (od_tk = (GF_TrackBox*)gf_list_enum(file->moov->trackList, &i))) { + if (od_tk->Media->handler->handlerType != GF_ISOM_MEDIA_OD) continue; + + for (j=0; jMedia->information->sampleTable->SampleSize->sampleCount; j++) { + GF_ISOSample *samp = gf_isom_get_sample(file, i, j+1, &di); + the_od_id = Media_FindOD_ID(od_tk->Media, samp, tk->Header->trackID); + gf_isom_sample_del(&samp); + if (the_od_id) return the_od_id; + } + } + return 0; +} + +#endif /*GPAC_DISABLE_ISOM*/ diff --git a/src/gpacmp4/meta.c b/src/gpacmp4/meta.c new file mode 100644 index 00000000..66ea61f7 --- /dev/null +++ b/src/gpacmp4/meta.c @@ -0,0 +1,674 @@ +/* + * GPAC Multimedia Framework + * + * Authors: Cyril Concolato - Jean le Feuvre + * Copyright (c) 2005-200X ENST + * All rights reserved + * + * This file is part of GPAC / ISO Media File Format sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include "../disable_warnings.h" +#include + +#ifndef GPAC_DISABLE_ISOM + +GF_MetaBox *gf_isom_get_meta(GF_ISOFile *file, Bool root_meta, u32 track_num) +{ + GF_TrackBox *tk; + if (!file) return NULL; + if (root_meta) return file->meta; + if (!track_num) return file->moov ? file->moov->meta : NULL; + + tk = (GF_TrackBox*)gf_list_get(file->moov->trackList, track_num-1); + return tk ? tk->meta : NULL; +} + +GF_EXPORT +u32 gf_isom_get_meta_type(GF_ISOFile *file, Bool root_meta, u32 track_num) +{ + GF_MetaBox *meta = gf_isom_get_meta(file, root_meta, track_num); + if (!meta) return 0; + if (!meta->handler) return 0; + return meta->handler->handlerType; +} + +GF_EXPORT +u32 gf_isom_has_meta_xml(GF_ISOFile *file, Bool root_meta, u32 track_num) +{ + u32 i, count; + GF_MetaBox *meta = gf_isom_get_meta(file, root_meta, track_num); + if (!meta) return 0; + + count = gf_list_count(meta->other_boxes); + for (i=0; iother_boxes, i); + if (a->type == GF_ISOM_BOX_TYPE_XML) return 1; + if (a->type == GF_ISOM_BOX_TYPE_BXML) return 2; + } + return 0; +} + +GF_Err gf_isom_extract_meta_xml(GF_ISOFile *file, Bool root_meta, u32 track_num, char *outName, Bool *is_binary) +{ + u32 i, count; + FILE *didfile; + GF_XMLBox *xml = NULL; + GF_MetaBox *meta = gf_isom_get_meta(file, root_meta, track_num); + if (!meta) return GF_BAD_PARAM; + + /*Find XMLBox*/ + count = gf_list_count(meta->other_boxes); + for (i = 0; i other_boxes, i); + if ((a->type == GF_ISOM_BOX_TYPE_XML) || (a->type == GF_ISOM_BOX_TYPE_BXML) ) { + xml = (GF_XMLBox *)a; + break; + } + } + if (!xml || !xml->xml || !xml->xml_length) return GF_BAD_PARAM; + + didfile = gf_f64_open(outName, "wt"); + if (!didfile) return GF_IO_ERR; + fwrite(xml->xml, xml->xml_length, 1, didfile); + fclose(didfile); + + if (is_binary) *is_binary = (xml->type==GF_ISOM_BOX_TYPE_BXML) ? 1 : 0; + return GF_OK; +} + +GF_XMLBox *gf_isom_get_meta_xml(GF_ISOFile *file, Bool root_meta, u32 track_num, Bool *is_binary) +{ + u32 i, count; + GF_MetaBox *meta = gf_isom_get_meta(file, root_meta, track_num); + if (!meta) return NULL; + + /*Find XMLBox*/ + count = gf_list_count(meta->other_boxes); + for (i = 0; i other_boxes, i); + if (a->type == GF_ISOM_BOX_TYPE_XML) { + *is_binary = 0; + return (GF_XMLBox *)a; + } else if (a->type == GF_ISOM_BOX_TYPE_BXML) { + *is_binary = 1; + return (GF_XMLBox *)a; + } + } + return NULL; +} + + + +GF_EXPORT +u32 gf_isom_get_meta_item_count(GF_ISOFile *file, Bool root_meta, u32 track_num) +{ + GF_MetaBox *meta = gf_isom_get_meta(file, root_meta, track_num); + if (!meta || !meta->item_infos || !meta->item_locations) return 0; + return gf_list_count(meta->item_infos->item_infos); +} + +GF_EXPORT +GF_Err gf_isom_get_meta_item_info(GF_ISOFile *file, Bool root_meta, u32 track_num, u32 item_num, + u32 *itemID, u32 *protection_idx, Bool *is_self_reference, + const char **item_name, const char **item_mime_type, const char **item_encoding, + const char **item_url, const char **item_urn) +{ + GF_ItemInfoEntryBox *iinf; + u32 i, count; + GF_MetaBox *meta = gf_isom_get_meta(file, root_meta, track_num); + if (!meta || !meta->item_infos || !meta->item_locations) return GF_BAD_PARAM; + + iinf = (GF_ItemInfoEntryBox *)gf_list_get(meta->item_infos->item_infos, item_num-1); + if (!iinf) return GF_BAD_PARAM; + + if (itemID) (*itemID) = iinf->item_ID; + if (protection_idx) (*protection_idx) = iinf->item_protection_index; + if (item_name) (*item_name) = iinf->item_name; + if (item_mime_type) (*item_mime_type) = iinf->content_type; + if (item_encoding) (*item_encoding) = iinf->content_encoding; + if (is_self_reference) *is_self_reference = 0; + + if (item_url) (*item_url) = NULL; + if (item_urn) (*item_urn) = NULL; + + count = gf_list_count(meta->item_locations->location_entries); + for (i=0; iitem_locations->location_entries, i); + if (iloc->item_ID==iinf->item_ID) { + if (iloc->data_reference_index) { + GF_Box *a = (GF_Box *)gf_list_get(meta->file_locations->dref->boxList, iloc->data_reference_index-1); + if (a->type==GF_ISOM_BOX_TYPE_URL) { + if (item_url) (*item_url) = ((GF_DataEntryURLBox*)a)->location; + } else if (a->type==GF_ISOM_BOX_TYPE_URN) { + if (item_url) (*item_url) = ((GF_DataEntryURNBox*)a)->location; + if (item_urn) (*item_urn) = ((GF_DataEntryURNBox*)a)->nameURN; + } + break; + } else if (is_self_reference && !iloc->base_offset) { + GF_ItemExtentEntry *entry = (GF_ItemExtentEntry *)gf_list_get(iloc->extent_entries, 0); + if (!entry->extent_length +#ifndef GPAC_DISABLE_ISOM_WRITE + && !entry->original_extent_offset +#endif + ) + *is_self_reference = 1; + } + } + } + return GF_OK; +} + +GF_EXPORT +u32 gf_isom_get_meta_item_by_id(GF_ISOFile *file, Bool root_meta, u32 track_num, u32 item_ID) +{ + GF_ItemInfoEntryBox *iinf; + u32 i, count; + GF_MetaBox *meta = gf_isom_get_meta(file, root_meta, track_num); + if (!meta || !meta->item_infos || !meta->item_locations) return 0; + count = gf_list_count(meta->item_infos->item_infos); + for (i=0; iitem_infos->item_infos, i); + if (iinf->item_ID==item_ID) return i+1; + } + return 0; +} + +GF_Err gf_isom_extract_meta_item_extended(GF_ISOFile *file, Bool root_meta, u32 track_num, u32 item_id, const char *dump_file_name, char **out_data, u32 *out_size, const char **out_mime ) +{ + GF_BitStream *item_bs; + char szPath[1024]; + GF_ItemExtentEntry *extent_entry; + FILE *resource = NULL; + u32 i, count; + GF_ItemLocationEntry *location_entry; + u32 item_num; + char *item_name = NULL; + + GF_MetaBox *meta = gf_isom_get_meta(file, root_meta, track_num); + if (!meta || !meta->item_infos || !meta->item_locations) return GF_BAD_PARAM; + + if (out_mime) *out_mime = NULL; + + item_num = gf_isom_get_meta_item_by_id(file, root_meta, track_num, item_id); + if (item_num) { + GF_ItemInfoEntryBox *item_entry = (GF_ItemInfoEntryBox *)gf_list_get(meta->item_infos->item_infos, item_num-1); + item_name = item_entry->item_name; + if (out_mime) *out_mime = item_entry->content_type; + } + + location_entry = NULL; + count = gf_list_count(meta->item_locations->location_entries); + for (i=0; iitem_locations->location_entries, i); + if (location_entry->item_ID == item_id) break; + location_entry = NULL; + } + + if (!location_entry) return GF_BAD_PARAM; + /*FIXME*/ + if (location_entry->data_reference_index) { + char *item_url = NULL, *item_urn = NULL; + GF_Box *a = (GF_Box *)gf_list_get(meta->file_locations->dref->boxList, location_entry->data_reference_index-1); + if (a->type==GF_ISOM_BOX_TYPE_URL) { + item_url = ((GF_DataEntryURLBox*)a)->location; + } else if (a->type==GF_ISOM_BOX_TYPE_URN) { + item_url = ((GF_DataEntryURNBox*)a)->location; + item_urn = ((GF_DataEntryURNBox*)a)->nameURN; + } + GF_LOG(GF_LOG_INFO, GF_LOG_CONTAINER, ("[IsoMedia] Item already outside the ISO file at URL: %s, URN: %s\n", (item_url?item_url:"N/A"), (item_urn?item_urn:"N/A") )); + return GF_OK; + } + + /*don't extract self-reference item*/ + count = gf_list_count(location_entry->extent_entries); + if (!location_entry->base_offset && (count==1)) { + extent_entry = (GF_ItemExtentEntry *)gf_list_get(location_entry->extent_entries, 0); + if (!extent_entry->extent_length +#ifndef GPAC_DISABLE_ISOM_WRITE + && !extent_entry->original_extent_offset +#endif + ) return GF_BAD_PARAM; + } + + item_bs = NULL; + + if (out_data) { + item_bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); + } else if (dump_file_name) { + strcpy(szPath, dump_file_name); + resource = gf_f64_open(szPath, "wb"); + item_bs = gf_bs_from_file(resource, GF_BITSTREAM_WRITE); + } else { + if (item_name) strcpy(szPath, item_name); + else sprintf(szPath, "item_id%02d", item_id); + resource = gf_f64_open(szPath, "wb"); + item_bs = gf_bs_from_file(resource, GF_BITSTREAM_WRITE); + } + + for (i=0; iextent_entries, i); + gf_bs_seek(file->movieFileMap->bs, location_entry->base_offset + extent_entry->extent_offset); + + remain = extent_entry->extent_length; + while (remain) { + u32 cache_size = (remain>4096) ? 4096 : (u32) remain; + gf_bs_read_data(file->movieFileMap->bs, buf_cache, cache_size); + gf_bs_write_data(item_bs, buf_cache, cache_size); + remain -= cache_size; + } + } + if (out_data) { + gf_bs_get_content(item_bs, out_data, out_size); + } + if (resource) { + fclose(resource); + } + gf_bs_del(item_bs); + return GF_OK; +} + +GF_Err gf_isom_extract_meta_item(GF_ISOFile *file, Bool root_meta, u32 track_num, u32 item_id, const char *dump_file_name) +{ + return gf_isom_extract_meta_item_extended(file, root_meta, track_num, item_id, dump_file_name, NULL, NULL, NULL); +} + +GF_Err gf_isom_extract_meta_item_mem(GF_ISOFile *file, Bool root_meta, u32 track_num, u32 item_id, char **out_data, u32 *out_size, const char **out_mime) +{ + return gf_isom_extract_meta_item_extended(file, root_meta, track_num, item_id, NULL, out_data, out_size, out_mime); +} + +u32 gf_isom_get_meta_primary_item_id(GF_ISOFile *file, Bool root_meta, u32 track_num) +{ + GF_MetaBox *meta = gf_isom_get_meta(file, root_meta, track_num); + if (!meta || !meta->primary_resource) return 0; + return meta->primary_resource->item_ID; +} + + +#ifndef GPAC_DISABLE_ISOM_WRITE + + +GF_Err gf_isom_set_meta_type(GF_ISOFile *file, Bool root_meta, u32 track_num, u32 metaType) +{ + char szName[20]; + GF_MetaBox *meta; + + GF_Err e = CanAccessMovie(file, GF_ISOM_OPEN_WRITE); + if (e) return e; + + meta = gf_isom_get_meta(file, root_meta, track_num); + if (!meta) { + if (!metaType) return GF_OK; + meta = (GF_MetaBox *) meta_New(); + if (root_meta) { + file->meta = meta; + gf_list_add(file->TopBoxes, meta); + } else { + gf_isom_insert_moov(file); + if (!track_num) { + file->moov->meta = meta; + } else { + GF_TrackBox *tk = (GF_TrackBox *)gf_list_get(file->moov->trackList, track_num-1); + if (!tk) { + gf_isom_box_del((GF_Box *)meta); + return GF_BAD_PARAM; + } + tk->meta = meta; + } + } + } else if (!metaType) { + if (root_meta) { + gf_list_del_item(file->TopBoxes, meta); + gf_isom_box_del((GF_Box *)file->meta); + file->meta = NULL; + } else if (file->moov) { + if (!track_num) { + gf_isom_box_del((GF_Box *)file->moov->meta); + file->moov->meta = NULL; + } else { + GF_TrackBox *tk = (GF_TrackBox *)gf_list_get(file->moov->trackList, track_num-1); + if (!tk) return GF_BAD_PARAM; + gf_isom_box_del((GF_Box *)tk->meta); + tk->meta = NULL; + } + } + return GF_OK; + } + + if (!meta->handler) + meta->handler = (GF_HandlerBox *)hdlr_New(); + + if (meta->handler->nameUTF8) gf_free(meta->handler->nameUTF8); + meta->handler->handlerType = metaType; + sprintf(szName, "GPAC %s Handler", gf_4cc_to_str(metaType)); + meta->handler->nameUTF8 = gf_strdup(szName); + return GF_OK; +} + +GF_Err gf_isom_remove_meta_xml(GF_ISOFile *file, Bool root_meta, u32 track_num) +{ + u32 i; + GF_Box *a; + GF_MetaBox *meta = gf_isom_get_meta(file, root_meta, track_num); + if (!meta) return GF_BAD_PARAM; + i=0; + while ((a = (GF_Box*)gf_list_enum(meta->other_boxes, &i))) { + switch (a->type) { + case GF_ISOM_BOX_TYPE_XML: + case GF_ISOM_BOX_TYPE_BXML: + gf_list_rem(meta->other_boxes, i-1); + gf_isom_box_del(a); + return GF_OK; + } + } + return GF_OK; +} + +GF_Err gf_isom_set_meta_xml(GF_ISOFile *file, Bool root_meta, u32 track_num, char *XMLFileName, Bool IsBinaryXML) +{ + GF_Err e; + FILE *xmlfile; + GF_XMLBox *xml; + GF_MetaBox *meta; + + e = CanAccessMovie(file, GF_ISOM_OPEN_WRITE); + if (e) return e; + + meta = gf_isom_get_meta(file, root_meta, track_num); + if (!meta) return GF_BAD_PARAM; + + e = gf_isom_remove_meta_xml(file, root_meta, track_num); + if (e) return e; + + xml = (GF_XMLBox *)xml_New(); + if (!xml) return GF_OUT_OF_MEM; + gf_list_add(meta->other_boxes, xml); + if (IsBinaryXML) xml->type = GF_ISOM_BOX_TYPE_BXML; + + + /*assume 32bit max size = 4Go should be sufficient for a DID!!*/ + xmlfile = gf_f64_open(XMLFileName, "rb"); + if (!xmlfile) return GF_URL_ERROR; + gf_f64_seek(xmlfile, 0, SEEK_END); + assert(gf_f64_tell(xmlfile) < 1<<31); + xml->xml_length = (u32) gf_f64_tell(xmlfile); + gf_f64_seek(xmlfile, 0, SEEK_SET); + xml->xml = (char*)gf_malloc(sizeof(unsigned char)*xml->xml_length); + xml->xml_length = fread(xml->xml, 1, sizeof(unsigned char)*xml->xml_length, xmlfile); + if (ferror(xmlfile)) { + gf_free(xml->xml); + xml->xml = NULL; + return GF_BAD_PARAM; + } + fclose(xmlfile); + return GF_OK; +} + +GF_Err gf_isom_set_meta_xml_memory(GF_ISOFile *file, Bool root_meta, u32 track_num, unsigned char *data, u32 data_size, Bool IsBinaryXML) +{ + GF_Err e; + GF_XMLBox *xml; + GF_MetaBox *meta; + + e = CanAccessMovie(file, GF_ISOM_OPEN_WRITE); + if (e) return e; + + meta = gf_isom_get_meta(file, root_meta, track_num); + if (!meta) return GF_BAD_PARAM; + + e = gf_isom_remove_meta_xml(file, root_meta, track_num); + if (e) return e; + + xml = (GF_XMLBox *)xml_New(); + if (!xml) return GF_OUT_OF_MEM; + gf_list_add(meta->other_boxes, xml); + if (IsBinaryXML) xml->type = GF_ISOM_BOX_TYPE_BXML; + + + /*assume 32bit max size = 4Go should be sufficient for a DID!!*/ + xml->xml_length = data_size; + xml->xml = (char*)gf_malloc(sizeof(unsigned char)*xml->xml_length); + memcpy(xml->xml, data, sizeof(unsigned char)*xml->xml_length); + return GF_OK; +} + +GF_Err gf_isom_add_meta_item_extended(GF_ISOFile *file, Bool root_meta, u32 track_num, Bool self_reference, char *resource_path, const char *item_name, const char *mime_type, const char *content_encoding, const char *URL, const char *URN, char *data, u32 data_len) +{ + GF_Err e; + GF_ItemLocationEntry *location_entry; + GF_ItemInfoEntryBox *infe; + GF_MetaBox *meta; + u32 lastItemID = 0; + + if (!self_reference && !item_name && !resource_path) return GF_BAD_PARAM; + e = CanAccessMovie(file, GF_ISOM_OPEN_WRITE); + if (e) return e; + meta = gf_isom_get_meta(file, root_meta, track_num); + if (!meta) { + GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("Trying to add item, but missing meta box")); + return GF_BAD_PARAM; + } + + e = FlushCaptureMode(file); + if (e) return e; + + /*check file exists */ + if (!URN && !URL && !self_reference && !data) { + FILE *src = gf_f64_open(resource_path, "rb"); + if (!src) return GF_URL_ERROR; + fclose(src); + } + + if (meta->item_infos) { + u32 i; + u32 item_count = gf_list_count(meta->item_infos->item_infos); + for (i = 0; i < item_count; i++) { + GF_ItemInfoEntryBox *e= (GF_ItemInfoEntryBox *)gf_list_get(meta->item_infos->item_infos, i); + if (e->item_ID > lastItemID) lastItemID = e->item_ID; + } + } + + infe = (GF_ItemInfoEntryBox *)infe_New(); + infe->item_ID = ++lastItemID; + + /*get relative name*/ + if (item_name) { + infe->item_name = gf_strdup(item_name); + } else if (resource_path) { + if (strrchr(resource_path, GF_PATH_SEPARATOR)) { + infe->item_name = gf_strdup(strrchr(resource_path, GF_PATH_SEPARATOR) + 1); + } else { + infe->item_name = gf_strdup(resource_path); + } + } + + if (mime_type) { + infe->content_type = gf_strdup(mime_type); + } else { + infe->content_type = gf_strdup("application/octet-stream"); + } + if (content_encoding) infe->content_encoding = gf_strdup(content_encoding); + + /*Creation of the ItemLocation */ + location_entry = (GF_ItemLocationEntry*)gf_malloc(sizeof(GF_ItemLocationEntry)); + if (!location_entry) { + gf_isom_box_del((GF_Box *)infe); + return GF_OUT_OF_MEM; + } + memset(location_entry, 0, sizeof(GF_ItemLocationEntry)); + location_entry->extent_entries = gf_list_new(); + + /*Creates an mdat if it does not exist*/ + if (!file->mdat) { + file->mdat = (GF_MediaDataBox *)mdat_New(); + gf_list_add(file->TopBoxes, file->mdat); + } + + /*Creation an ItemLocation Box if it does not exist*/ + if (!meta->item_locations) meta->item_locations = (GF_ItemLocationBox *)iloc_New(); + gf_list_add(meta->item_locations->location_entries, location_entry); + location_entry->item_ID = lastItemID; + + if (!meta->item_infos) meta->item_infos = (GF_ItemInfoBox *) iinf_New(); + e = gf_list_add(meta->item_infos->item_infos, infe); + if (e) return e; + + /*0: the current file*/ + location_entry->data_reference_index = 0; + if (self_reference) { + GF_ItemExtentEntry *entry; + GF_SAFEALLOC(entry, GF_ItemExtentEntry); + gf_list_add(location_entry->extent_entries, entry); + if (!infe->item_name) infe->item_name = gf_strdup(""); + return GF_OK; + } + + /*file not copied, just referenced*/ + if (URL || URN) { + u32 dataRefIndex; + if (!meta->file_locations) meta->file_locations = (GF_DataInformationBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_DINF); + if (!meta->file_locations->dref) meta->file_locations->dref = (GF_DataReferenceBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_DREF); + e = Media_FindDataRef(meta->file_locations->dref, (char *) URL, (char *) URN, &dataRefIndex); + if (e) return e; + if (!dataRefIndex) { + e = Media_CreateDataRef(meta->file_locations->dref, (char *) URL, (char *) URN, &dataRefIndex); + if (e) return e; + } + location_entry->data_reference_index = dataRefIndex; + } + + /*capture mode, write to disk*/ + if ((file->openMode == GF_ISOM_OPEN_WRITE) && !location_entry->data_reference_index) { + FILE *src; + GF_ItemExtentEntry *entry; + GF_SAFEALLOC(entry, GF_ItemExtentEntry); + + location_entry->base_offset = gf_bs_get_position(file->editFileMap->bs); + + /*update base offset size*/ + if (location_entry->base_offset>0xFFFFFFFF) meta->item_locations->base_offset_size = 8; + else if (location_entry->base_offset && !meta->item_locations->base_offset_size) meta->item_locations->base_offset_size = 4; + + entry->extent_length = 0; + entry->extent_offset = 0; + gf_list_add(location_entry->extent_entries, entry); + + if (data) { + gf_bs_write_data(file->editFileMap->bs, data, data_len); + /*update length size*/ + if (entry->extent_length>0xFFFFFFFF) meta->item_locations->length_size = 8; + else if (entry->extent_length && !meta->item_locations->length_size) meta->item_locations->length_size = 4; + } else if (resource_path) { + src = gf_f64_open(resource_path, "rb"); + if (src) { + char cache_data[4096]; + u64 remain; + gf_f64_seek(src, 0, SEEK_END); + entry->extent_length = gf_f64_tell(src); + gf_f64_seek(src, 0, SEEK_SET); + + remain = entry->extent_length; + while (remain) { + u32 size_cache = (remain>4096) ? 4096 : (u32) remain; + size_cache = fread(cache_data, 1, size_cache, src); + gf_bs_write_data(file->editFileMap->bs, cache_data, size_cache); + remain -= size_cache; + } + fclose(src); + + /*update length size*/ + if (entry->extent_length>0xFFFFFFFF) meta->item_locations->length_size = 8; + else if (entry->extent_length && !meta->item_locations->length_size) meta->item_locations->length_size = 4; + } + } + } + /*store full path for info*/ + else if (!location_entry->data_reference_index) { + if (data) { + infe->full_path = (char *) gf_malloc(sizeof(char) * data_len); + memcpy(infe->full_path, data, sizeof(char) * data_len); + infe->data_len = data_len; + } else { + infe->full_path = gf_strdup(resource_path); + infe->data_len = 0; + } + } + return GF_OK; +} + +GF_Err gf_isom_add_meta_item(GF_ISOFile *file, Bool root_meta, u32 track_num, Bool self_reference, char *resource_path, const char *item_name, const char *mime_type, const char *content_encoding, const char *URL, const char *URN) +{ + return gf_isom_add_meta_item_extended(file, root_meta, track_num, self_reference, resource_path, item_name, mime_type, content_encoding, URL, URN, NULL, 0); +} + +GF_Err gf_isom_add_meta_item_memory(GF_ISOFile *file, Bool root_meta, u32 track_num, const char *item_name, const char *mime_type, const char *content_encoding, char *data, u32 data_len) +{ + return gf_isom_add_meta_item_extended(file, root_meta, track_num, 0, NULL, item_name, mime_type, content_encoding, NULL, NULL, data, data_len); +} + +GF_Err gf_isom_remove_meta_item(GF_ISOFile *file, Bool root_meta, u32 track_num, u32 item_id) +{ + GF_ItemInfoEntryBox *iinf; + u32 i, count; + GF_MetaBox *meta = gf_isom_get_meta(file, root_meta, track_num); + u32 item_num; + if (!meta || !meta->item_infos || !meta->item_locations) return GF_BAD_PARAM; + + item_num = gf_isom_get_meta_item_by_id(file, root_meta, track_num, item_id); + if (!item_num) return GF_BAD_PARAM; + iinf = (GF_ItemInfoEntryBox *)gf_list_get(meta->item_infos->item_infos, item_num-1); + gf_list_rem(meta->item_infos->item_infos, item_num-1); + + count = gf_list_count(meta->item_locations->location_entries); + for (i=0; iitem_locations->location_entries, i); + if (iloc->item_ID==iinf->item_ID) { + /*FIXME: remove data ref...*/ + if (iloc->data_reference_index) { } + + gf_list_rem(meta->item_locations->location_entries, i); + gf_isom_box_del((GF_Box *)iloc); + break; + } + } + gf_isom_box_del((GF_Box *)iinf); + return GF_OK; +} + +GF_Err gf_isom_set_meta_primary_item(GF_ISOFile *file, Bool root_meta, u32 track_num, u32 item_id) +{ + GF_MetaBox *meta = gf_isom_get_meta(file, root_meta, track_num); + if (!meta || !meta->item_infos || !meta->item_locations) return GF_BAD_PARAM; + /*either one or the other*/ + if (gf_isom_has_meta_xml(file, root_meta, track_num)) return GF_BAD_PARAM; + + if (meta->primary_resource) gf_isom_box_del((GF_Box*)meta->primary_resource); + meta->primary_resource = (GF_PrimaryItemBox*) gf_isom_box_new(GF_ISOM_BOX_TYPE_PITM); + meta->primary_resource->item_ID = item_id; + return GF_OK; +} + + +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + + + +#endif /*GPAC_DISABLE_ISOM*/ diff --git a/src/gpacmp4/movie_fragments.c b/src/gpacmp4/movie_fragments.c new file mode 100644 index 00000000..a3e268d8 --- /dev/null +++ b/src/gpacmp4/movie_fragments.c @@ -0,0 +1,1465 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / ISO Media File Format sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include + +#ifndef GPAC_DISABLE_ISOM + +#ifndef GPAC_DISABLE_ISOM_FRAGMENTS + +GF_TrackExtendsBox *GetTrex(GF_MovieBox *moov, u32 TrackID) +{ + u32 i; + GF_TrackExtendsBox *trex; + i=0; + while ((trex = (GF_TrackExtendsBox *)gf_list_enum(moov->mvex->TrackExList, &i))) { + if (trex->trackID == TrackID) return trex; + } + return NULL; +} + + +#ifndef GPAC_DISABLE_ISOM_WRITE + +GF_TrackFragmentBox *GetTraf(GF_ISOFile *mov, u32 TrackID) +{ + u32 i; + GF_TrackFragmentBox *traf; + if (!mov->moof) return NULL; + + //reverse browse the TRAFs, as there may be more than one per track ... + for (i=gf_list_count(mov->moof->TrackList); i>0; i--) { + traf = (GF_TrackFragmentBox *)gf_list_get(mov->moof->TrackList, i-1); + if (traf->tfhd->trackID == TrackID) return traf; + } + return NULL; +} + +GF_Err gf_isom_finalize_for_fragment(GF_ISOFile *movie, Bool use_segments) +{ + GF_Err e; + u32 i; + Bool store_file = 1; + GF_TrackExtendsBox *trex; + if (!movie || !movie->moov) return GF_BAD_PARAM; + + if (movie->openMode==GF_ISOM_OPEN_CAT_FRAGMENTS) { + /*from now on we are in write mode*/ + movie->openMode = GF_ISOM_OPEN_WRITE; + store_file = 0; + movie->append_segment = 1; + } else { + movie->NextMoofNumber = 1; + } + + //this is only allowed in write mode + if (movie->openMode != GF_ISOM_OPEN_WRITE) return GF_ISOM_INVALID_MODE; + + if (movie->FragmentsFlags & GF_ISOM_FRAG_WRITE_READY) return GF_OK; + movie->FragmentsFlags = 0; + + if (store_file) { + /*"dash" brand: this is a DASH Initialization Segment*/ + gf_isom_modify_alternate_brand(movie, GF_4CC('d','a','s','h'), 1); + //update durations + gf_isom_get_duration(movie); + + //write movie + e = WriteToFile(movie); + if (e) return e; + } + + //make sure we do have all we need. If not this is not an error, just consider + //the file closed + if (!movie->moov->mvex || !gf_list_count(movie->moov->mvex->TrackExList)) return GF_OK; + + i=0; + while ((trex = (GF_TrackExtendsBox *)gf_list_enum(movie->moov->mvex->TrackExList, &i))) { + if (!trex->trackID || !gf_isom_get_track_from_id(movie->moov, trex->trackID)) return GF_IO_ERR; + //we could also check all our data refs are local but we'll do that at run time + //in order to allow a mix of both (remote refs in MOOV and local in MVEX) + + //one thing that MUST be done is OD cross-dependancies. The movie fragment spec + //is broken here, since it cannot allow dynamic insertion of new ESD and their + //dependancies + } + + //ok we are fine - note the data map is created at the begining + if (i) movie->FragmentsFlags |= GF_ISOM_FRAG_WRITE_READY; + + if (use_segments) { + movie->use_segments = 1; + movie->moof_list = gf_list_new(); + } + + /*set brands for segment*/ + + /*"msdh": it's a media segment */ + gf_isom_set_brand_info(movie, GF_4CC('m','s','d','h'), 0); + /*remove all brands */ + gf_isom_reset_alt_brands(movie); + /* + msdh: it's a media segment + sims: it's a media segment with an SSIX + msix: it's a media segment with an index + lmsg: it's the last media segment + */ + + return GF_OK; +} + + +GF_Err gf_isom_setup_track_fragment(GF_ISOFile *movie, u32 TrackID, + u32 DefaultSampleDescriptionIndex, + u32 DefaultSampleDuration, + u32 DefaultSampleSize, + u8 DefaultSampleIsSync, + u8 DefaultSamplePadding, + u16 DefaultDegradationPriority) +{ + GF_MovieExtendsBox *mvex; + GF_TrackExtendsBox *trex; + GF_TrackBox *trak; + + if (!movie || !movie->moov) return GF_BAD_PARAM; + //this is only allowed in write mode + if (movie->openMode != GF_ISOM_OPEN_WRITE) return GF_ISOM_INVALID_MODE; + //and only at setup + if (movie->FragmentsFlags & GF_ISOM_FRAG_WRITE_READY) return GF_BAD_PARAM; + + + trak = gf_isom_get_track_from_id(movie->moov, TrackID); + if (!trak) return GF_BAD_PARAM; + + //create MVEX if needed + if (!movie->moov->mvex) { + mvex = (GF_MovieExtendsBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_MVEX); + moov_AddBox((GF_Box*)movie->moov, (GF_Box *) mvex); + } else { + mvex = movie->moov->mvex; + } + + trex = GetTrex(movie->moov, TrackID); + if (!trex) { + trex = (GF_TrackExtendsBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_TREX); + trex->trackID = TrackID; + mvex_AddBox((GF_Box*)mvex, (GF_Box *) trex); + } + trex->track = trak; + trex->def_sample_desc_index = DefaultSampleDescriptionIndex; + trex->def_sample_duration = DefaultSampleDuration; + trex->def_sample_size = DefaultSampleSize; + trex->def_sample_flags = GF_ISOM_FORMAT_FRAG_FLAGS(DefaultSamplePadding, DefaultSampleIsSync, DefaultDegradationPriority); + + return GF_OK; +} + + +u32 GetNumUsedValues(GF_TrackFragmentBox *traf, u32 value, u32 index) +{ + u32 i, j, NumValue = 0; + GF_TrackFragmentRunBox *trun; + GF_TrunEntry *ent; + + i=0; + while ((trun = (GF_TrackFragmentRunBox *)gf_list_enum(traf->TrackRuns, &i))) { + j=0; + while ((ent = (GF_TrunEntry *)gf_list_enum(trun->entries, &j))) { + switch (index) { + case 1: + if (value == ent->Duration) NumValue ++; + break; + case 2: + if (value == ent->size) NumValue ++; + break; + case 3: + if (value == ent->flags) NumValue ++; + break; + } + } + } + return NumValue; +} + + +void ComputeFragmentDefaults(GF_TrackFragmentBox *traf) +{ + u32 i, j, MaxNum, DefValue, ret; + GF_TrackFragmentRunBox *trun; + GF_TrunEntry *ent; + + //Duration default + MaxNum = DefValue = 0; + i=0; + while ((trun = (GF_TrackFragmentRunBox *)gf_list_enum(traf->TrackRuns, &i))) { + j=0; + while ((ent = (GF_TrunEntry *)gf_list_enum(trun->entries, &j))) { + ret = GetNumUsedValues(traf, ent->Duration, 1); + if (ret>MaxNum) { + //at least 2 duration, specify for all + if (MaxNum) { + DefValue = 0; + goto escape_duration; + } + MaxNum = ret; + DefValue = ent->Duration; + } + } + } +escape_duration: + //store if # + if (DefValue && (DefValue != traf->trex->def_sample_duration)) { + traf->tfhd->def_sample_duration = DefValue; + } + + //Size default + MaxNum = DefValue = 0; + i=0; + while ((trun = (GF_TrackFragmentRunBox *)gf_list_enum(traf->TrackRuns, &i))) { + j=0; + while ((ent = (GF_TrunEntry*)gf_list_enum(trun->entries, &j))) { + ret = GetNumUsedValues(traf, ent->size, 2); + if (ret>MaxNum || (ret==1)) { + //at least 2 sizes so we must specify all sizes + if (MaxNum) { + DefValue = 0; + goto escape_size; + } + MaxNum = ret; + DefValue = ent->size; + } + } + } + +escape_size: + //store if # + if (DefValue && (DefValue != traf->trex->def_sample_size)) { + traf->tfhd->def_sample_size = DefValue; + } + + //Flags default + MaxNum = DefValue = 0; + i=0; + while ((trun = (GF_TrackFragmentRunBox *)gf_list_enum(traf->TrackRuns, &i))) { + j=0; + while ((ent = (GF_TrunEntry*)gf_list_enum(trun->entries, &j))) { + ret = GetNumUsedValues(traf, ent->flags, 3); + if (ret>MaxNum) { + MaxNum = ret; + DefValue = ent->flags; + } + } + } + //store if # + if (DefValue && (DefValue != traf->trex->def_sample_flags)) { + traf->tfhd->def_sample_flags = DefValue; + } +} + + +GF_Err gf_isom_set_fragment_option(GF_ISOFile *movie, u32 TrackID, u32 Code, u32 Param) +{ + GF_TrackFragmentBox *traf; + if (!movie || !movie->moov) return GF_BAD_PARAM; + //this is only allowed in write mode + if (movie->openMode != GF_ISOM_OPEN_WRITE) return GF_ISOM_INVALID_MODE; + + traf = GetTraf(movie, TrackID); + if (!traf) return GF_BAD_PARAM; + switch (Code) { + case GF_ISOM_TRAF_EMPTY: + traf->tfhd->EmptyDuration = Param; + break; + case GF_ISOM_TRAF_RANDOM_ACCESS: + traf->tfhd->IFrameSwitching = Param; + break; + case GF_ISOM_TRAF_DATA_CACHE: + //don't cache only one sample ... + traf->DataCache = Param > 1 ? Param : 0; + break; + } + return GF_OK; +} + +//#define USE_BASE_DATA_OFFSET + +void update_trun_offsets(GF_ISOFile *movie, s32 offset) +{ +#ifndef USE_BASE_DATA_OFFSET + GF_TrackFragmentRunBox *trun; + u32 i, j; + GF_TrackFragmentBox *traf; + i=0; + while ((traf = (GF_TrackFragmentBox *) gf_list_enum(movie->moof->TrackList, &i))) { + /*remove base data*/ + traf->tfhd->base_data_offset = 0; + j=0; + while ((trun = (GF_TrackFragmentRunBox *) gf_list_enum(traf->TrackRuns, &j))) { + if (j==1) { + trun->data_offset += offset; + } else { + trun->data_offset = 0; + } + } + } +#endif +} + +u32 UpdateRuns(GF_ISOFile *movie, GF_TrackFragmentBox *traf) +{ + u32 sampleCount, i, j, RunSize, UseDefaultSize, RunDur, UseDefaultDur, RunFlags, NeedFlags, UseDefaultFlag, UseCTS, count; + GF_TrackFragmentRunBox *trun; + GF_TrunEntry *ent, *first_ent; + + sampleCount = 0; + +#ifndef USE_BASE_DATA_OFFSET + if (movie->use_segments) { + traf->tfhd->flags = 0; + } else +#endif + { + traf->tfhd->flags = GF_ISOM_TRAF_BASE_OFFSET; + } + + //empty runs + if (traf->tfhd->EmptyDuration) { + while (gf_list_count(traf->TrackRuns)) { + trun = (GF_TrackFragmentRunBox *)gf_list_get(traf->TrackRuns, 0); + gf_list_rem(traf->TrackRuns, 0); + gf_isom_box_del((GF_Box *)trun); + } + traf->tfhd->flags = GF_ISOM_TRAF_DUR_EMPTY; + if (traf->tfhd->EmptyDuration != traf->trex->def_sample_duration) { + traf->tfhd->def_sample_duration = traf->tfhd->EmptyDuration; + traf->tfhd->flags |= GF_ISOM_TRAF_SAMPLE_DUR; + } + return 0; + } + + + UseDefaultSize = 0; + UseDefaultDur = 0; + UseDefaultFlag = 0; + + i=0; + while ((trun = (GF_TrackFragmentRunBox *)gf_list_enum(traf->TrackRuns, &i))) { + RunSize = 0; + RunDur = 0; + RunFlags = 0; + UseCTS = 0; + NeedFlags = 0; + + first_ent = NULL; + //process all samples in run + count = gf_list_count(trun->entries); + for (j=0; jentries, j); + if (!j) { + first_ent = ent; + RunSize = ent->size; + RunDur = ent->Duration; + } + //we may have one entry only ... + if (j || (count==1)) { + //flags are only after first entry + if (j==1 || (count==1) ) RunFlags = ent->flags; + + if (ent->size != RunSize) RunSize = 0; + if (ent->Duration != RunDur) RunDur = 0; + if (j && (RunFlags != ent->flags)) NeedFlags = 1; + } + if (ent->CTS_Offset) UseCTS = 1; + } + //empty list + if (!first_ent) { + i--; + gf_list_rem(traf->TrackRuns, i); + continue; + } + trun->sample_count = gf_list_count(trun->entries); + trun->flags = 0; + + //size checking + //constant size, check if this is from current fragment default or global default + if (RunSize && (traf->trex->def_sample_size == RunSize)) { + if (!UseDefaultSize) UseDefaultSize = 2; + else if (UseDefaultSize==1) RunSize = 0; + } else if (RunSize && (traf->tfhd->def_sample_size == RunSize)) { + if (!UseDefaultSize) UseDefaultSize = 1; + else if (UseDefaultSize==2) RunSize = 0; + } + //we could check for single entry runs and set the default size in the tfhd but + //that's no bit saving... + else { + RunSize=0; + } + + if (!RunSize) trun->flags |= GF_ISOM_TRUN_SIZE; + + //duration checking + if (RunDur && (traf->trex->def_sample_duration == RunDur)) { + if (!UseDefaultDur) UseDefaultDur = 2; + else if (UseDefaultDur==1) RunDur = 0; + } else if (RunDur && (traf->tfhd->def_sample_duration == RunDur)) { + if (!UseDefaultDur) UseDefaultDur = 1; + else if (UseDefaultDur==2) RunDur = 0; + } + if (!RunDur) trun->flags |= GF_ISOM_TRUN_DURATION; + + //flag checking + if (!NeedFlags) { + if (RunFlags == traf->trex->def_sample_flags) { + if (!UseDefaultFlag) UseDefaultFlag = 2; + else if (UseDefaultFlag==1) NeedFlags = 1; + } else if (RunFlags == traf->tfhd->def_sample_flags) { + if (!UseDefaultFlag) UseDefaultFlag = 1; + else if(UseDefaultFlag==2) NeedFlags = 1; + } + } + if (NeedFlags) { + //one flags entry per sample only + trun->flags |= GF_ISOM_TRUN_FLAGS; + } else { + //indicated in global setup + if (first_ent->flags == traf->trex->def_sample_flags) { + if (!UseDefaultFlag) UseDefaultFlag = 2; + else if (UseDefaultFlag==1) trun->flags |= GF_ISOM_TRUN_FIRST_FLAG; + } + //indicated in local setup + else if (first_ent->flags == traf->tfhd->def_sample_flags) { + if (!UseDefaultFlag) UseDefaultFlag = 1; + else if (UseDefaultFlag==2) trun->flags |= GF_ISOM_TRUN_FIRST_FLAG; + } + //explicit store + else { + trun->flags |= GF_ISOM_TRUN_FIRST_FLAG; + } + } + + //CTS flag + if (UseCTS) trun->flags |= GF_ISOM_TRUN_CTS_OFFSET; + + //run data offset if the offset indicated is 0 (first sample in this MDAT) don't + //indicate it + if (trun->data_offset) trun->flags |= GF_ISOM_TRUN_DATA_OFFSET; + + sampleCount += trun->sample_count; + } + + //last update TRAF flags + if (UseDefaultSize==1) traf->tfhd->flags |= GF_ISOM_TRAF_SAMPLE_SIZE; + if (UseDefaultDur==1) traf->tfhd->flags |= GF_ISOM_TRAF_SAMPLE_DUR; + if (UseDefaultFlag==1) traf->tfhd->flags |= GF_ISOM_TRAF_SAMPLE_FLAGS; + if (traf->tfhd->sample_desc_index && traf->tfhd->sample_desc_index != traf->trex->def_sample_desc_index) traf->tfhd->flags |= GF_ISOM_TRAF_SAMPLE_DESC; + + + return sampleCount; +} + +Bool moof_get_rap_time_offset(GF_MovieFragmentBox *moof, u32 refTrackID, u32 *rap_delta) +{ + u32 i, j, delta; + GF_TrunEntry *ent; + GF_TrackFragmentBox *traf; + GF_TrackFragmentRunBox *trun; + *rap_delta = 0; + for (i=0; iTrackList); i++) { + traf = (GF_TrackFragmentBox *) gf_list_get(moof->TrackList, i); + if (traf->tfhd->trackID==refTrackID) break; + traf=NULL; + } + if (!traf) return 0; + + delta = 0; + i=0; + while ((trun = (GF_TrackFragmentRunBox *) gf_list_enum(traf->TrackRuns, &i))) { + if (trun->flags & GF_ISOM_TRUN_FIRST_FLAG) { + if (GF_ISOM_GET_FRAG_SYNC(trun->flags)) { + ent = (GF_TrunEntry *) gf_list_get(trun->entries, 0); + *rap_delta = delta + ent->CTS_Offset; + return 1; + } + } + j=0; + while ((ent = (GF_TrunEntry *) gf_list_enum(trun->entries, &j))) { + if (GF_ISOM_GET_FRAG_SYNC(ent->flags)) { + *rap_delta = delta + ent->CTS_Offset; + return 1; + } + delta += ent->Duration; + } + } + return 0; +} + +u32 moof_get_duration(GF_MovieFragmentBox *moof, u32 refTrackID) +{ + u32 i, j, duration; + GF_TrunEntry *ent; + GF_TrackFragmentBox *traf = NULL; + GF_TrackFragmentRunBox *trun; + for (i=0; iTrackList); i++) { + traf = (GF_TrackFragmentBox *) gf_list_get(moof->TrackList, i); + if (traf->tfhd->trackID==refTrackID) break; + traf=NULL; + } + if (!traf) return 0; + + duration = 0; + i=0; + while ((trun = (GF_TrackFragmentRunBox *) gf_list_enum(traf->TrackRuns, &i))) { + j=0; + while ((ent = (GF_TrunEntry *) gf_list_enum(trun->entries, &j))) { + duration += ent->Duration; + } + } + return duration; +} + +u32 moof_get_earliest_cts(GF_MovieFragmentBox *moof, u32 refTrackID) +{ + u32 i, j, cts, duration; + GF_TrunEntry *ent; + GF_TrackFragmentBox *traf=NULL; + GF_TrackFragmentRunBox *trun; + for (i=0; iTrackList); i++) { + traf = (GF_TrackFragmentBox *) gf_list_get(moof->TrackList, i); + if (traf->tfhd->trackID==refTrackID) break; + traf=NULL; + } + if (!traf) return 0; + + duration = 0; + cts = 0xFFFFFFFF; + i=0; + while ((trun = (GF_TrackFragmentRunBox *) gf_list_enum(traf->TrackRuns, &i))) { + j=0; + while ((ent = (GF_TrunEntry *) gf_list_enum(trun->entries, &j))) { + if (duration + ent->CTS_Offset < cts) + cts = duration + ent->CTS_Offset; + duration += ent->Duration; + } + } + return cts; +} + +GF_Err StoreFragment(GF_ISOFile *movie, Bool load_mdat_only, s32 data_offset_diff, u32 *moof_size) +{ + GF_Err e; + u64 moof_start; + u32 size, i, s_count, mdat_size; + s32 offset; + char *buffer; + GF_TrackFragmentBox *traf; + GF_TrackFragmentRunBox *trun; + GF_BitStream *bs; + if (!movie->moof) return GF_OK; + + bs = movie->editFileMap->bs; + if (!movie->moof_first) load_mdat_only = 0; + mdat_size = 0; + //1 - flush all caches + i=0; + while ((traf = (GF_TrackFragmentBox*)gf_list_enum(movie->moof->TrackList, &i))) { + if (!traf->DataCache) continue; + s_count = gf_list_count(traf->TrackRuns); + if (!s_count) continue; + trun = (GF_TrackFragmentRunBox *)gf_list_get(traf->TrackRuns, s_count-1); + if (!trun->cache || !trun->sample_count) continue; + + //update offset + trun->data_offset = (u32) (gf_bs_get_position(bs) - movie->moof->fragment_offset - 8); + //write cache + gf_bs_get_content(trun->cache, &buffer, &size); + gf_bs_write_data(bs, buffer, size); + gf_bs_del(trun->cache); + gf_free(buffer); + trun->cache = NULL; + traf->DataCache=0; + } + + if (load_mdat_only) { + u64 pos = gf_bs_get_position(bs); + //we assume we never write large MDATs in fragment mode which should always be true + movie->moof->mdat_size = (u32) (pos - movie->moof->fragment_offset); + movie->moof->mdat = (char *) gf_malloc(sizeof(char) * movie->moof->mdat_size); + if (!movie->moof->mdat) return GF_OUT_OF_MEM; + gf_bs_seek(bs, movie->segment_start); + /*write mdat size*/ + gf_bs_write_u32(bs, (u32) movie->moof->mdat_size); + gf_bs_seek(bs, movie->segment_start); + gf_bs_read_data(bs, movie->moof->mdat, movie->moof->mdat_size); + gf_bs_seek(bs, movie->segment_start); + gf_bs_truncate(bs); + return GF_OK; + } + + + moof_start = gf_bs_get_position(bs); + //2- update MOOF MDAT header + if (!movie->moof->mdat) { + gf_bs_seek(bs, movie->moof->fragment_offset); + //we assume we never write large MDATs in fragment mode which should always be true + mdat_size = (u32) (moof_start - movie->moof->fragment_offset); + gf_bs_write_u32(bs, (u32) mdat_size); + gf_bs_write_u32(bs, GF_ISOM_BOX_TYPE_MDAT); + gf_bs_seek(bs, moof_start); + } + + /*estimate moof size and shift trun offsets*/ +#ifndef USE_BASE_DATA_OFFSET + offset = 0; + if (movie->use_segments) { + e = gf_isom_box_size((GF_Box *) movie->moof); + offset = (s32) movie->moof->size; + /*mdat size & type*/ + offset += 8; + update_trun_offsets(movie, offset); + } +#endif + + //3- clean our traf's + i=0; + while ((traf = (GF_TrackFragmentBox*) gf_list_enum(movie->moof->TrackList, &i))) { + //compute default settings for the TRAF + ComputeFragmentDefaults(traf); + //updates all trun and set all flags, INCLUDING TRAF FLAGS (durations, ...) + s_count = UpdateRuns(movie, traf); + //empty fragment destroy it + if (!traf->tfhd->EmptyDuration && !s_count) { + i--; + gf_list_rem(movie->moof->TrackList, i); + gf_isom_box_del((GF_Box *) traf); + continue; + } + } + + buffer = NULL; + /*rewind bitstream and load mdat in memory */ + if (movie->moof_first && !movie->moof->mdat) { + buffer = (char *) gf_malloc(sizeof(char)*mdat_size); + gf_bs_seek(bs, movie->moof->fragment_offset); + gf_bs_read_data(bs, buffer, mdat_size); + /*back to mdat start and erase with moov*/ + gf_bs_seek(bs, movie->moof->fragment_offset); + gf_bs_truncate(bs); + } + + //4- Write moof + e = gf_isom_box_size((GF_Box *) movie->moof); + if (e) return e; + /*moof first, update traf headers - THIS WILL IMPACT THE MOOF SIZE IF WE + DECIDE NOT TO USE THE DATA-OFFSET FLAG*/ + if (movie->moof_first +#ifndef USE_BASE_DATA_OFFSET + && !movie->use_segments +#endif + ) { + i=0; + while ((traf = (GF_TrackFragmentBox*)gf_list_enum(movie->moof->TrackList, &i))) { + /*offset increases by moof size*/ + traf->tfhd->base_data_offset += movie->moof->size; + traf->tfhd->base_data_offset += data_offset_diff; + } + } +#ifndef USE_BASE_DATA_OFFSET + else if (movie->use_segments) { + if (offset != (movie->moof->size+8)) { + offset = (s32) (movie->moof->size + 8 - offset); + update_trun_offsets(movie, offset); + e = gf_isom_box_size((GF_Box *) movie->moof); + } + } +#endif + + e = gf_isom_box_write((GF_Box *) movie->moof, bs); + if (e) return e; + + //rewrite mdat after moof + if (movie->moof->mdat) { + gf_bs_write_data(bs, movie->moof->mdat, movie->moof->mdat_size); + gf_free(movie->moof->mdat); + movie->moof->mdat = NULL; + } else if (buffer) { + gf_bs_write_data(bs, buffer, mdat_size); + gf_free(buffer); + } + + if (moof_size) *moof_size = (u32) movie->moof->size; + + if (!movie->use_segments) { + gf_isom_box_del((GF_Box *) movie->moof); + movie->moof = NULL; + } + return GF_OK; +} + +static GF_Err sidx_rewrite(GF_SegmentIndexBox *sidx, GF_BitStream *bs, u64 start_pos) +{ + GF_Err e; + u64 pos = gf_bs_get_position(bs); + /*write sidx*/ + gf_bs_seek(bs, start_pos); + e = gf_isom_box_write((GF_Box *) sidx, bs); + gf_bs_seek(bs, pos); + return e; +} + + +GF_Err gf_isom_close_segment(GF_ISOFile *movie, s32 frags_per_sidx, u32 referenceTrackID, u64 ref_track_decode_time, Bool daisy_chain_sidx, Bool last_segment) +{ + GF_SegmentIndexBox *sidx=NULL; + GF_SegmentIndexBox *prev_sidx=NULL; + GF_SegmentIndexBox *root_sidx=NULL; + u64 sidx_start, sidx_end; + Bool first_sidx = 0; + Bool no_sidx = 0; + u32 count, nb_subsegs=0, idx, cur_dur, sidx_dur, sidx_idx; + u64 last_top_box_pos, root_prev_offset, local_sidx_start, local_sidx_end; + GF_TrackBox *trak = NULL; + GF_Err e; + sidx_start = sidx_end = 0; + //and only at setup + if (!movie || !(movie->FragmentsFlags & GF_ISOM_FRAG_WRITE_READY) ) return GF_BAD_PARAM; + if (movie->openMode != GF_ISOM_OPEN_WRITE) return GF_ISOM_INVALID_MODE; + + count = gf_list_count(movie->moov->mvex->TrackExList); + if (!count) return GF_BAD_PARAM; + + count = gf_list_count(movie->moof_list); + if (!count) return GF_OK; + /*store fragment*/ + if (movie->moof) { + e = StoreFragment(movie, 1, 0, NULL); + if (e) return e; + } + gf_bs_seek(movie->editFileMap->bs, movie->segment_start); + gf_bs_truncate(movie->editFileMap->bs); + + if (referenceTrackID) { + trak = gf_isom_get_track_from_id(movie->moov, referenceTrackID); + + /*modify brands STYP*/ + + /*"msix" brand: this is a DASH Initialization Segment*/ + gf_isom_modify_alternate_brand(movie, GF_4CC('m','s','i','x'), 1); + if (last_segment) { + /*"lmsg" brand: this is the last DASH Segment*/ + gf_isom_modify_alternate_brand(movie, GF_4CC('l','m','s','g'), 1); + } + } + /*write STYP*/ + movie->brand->type = GF_ISOM_BOX_TYPE_STYP; + e = gf_isom_box_size((GF_Box *) movie->brand); + if (e) return e; + e = gf_isom_box_write((GF_Box *) movie->brand, movie->editFileMap->bs); + if (e) return e; + + if (frags_per_sidx < 0) { + referenceTrackID = 0; + frags_per_sidx = 0; + no_sidx = 1; + } + + /*prepare SIDX: we write a blank SIDX box with the right number of entries, and will rewrite it later on*/ + if (referenceTrackID) { + Bool is_root_sidx=0; + sidx = (GF_SegmentIndexBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_SIDX); + sidx->reference_ID = referenceTrackID; + sidx->timescale = trak->Media->mediaHeader->timeScale; + /*we don't write anything between sidx and following moov*/ + sidx->first_offset = 0; + sidx->earliest_presentation_time = ref_track_decode_time; + + /*we consider that each fragment is a subsegment - this could be controled by another parameter*/ + if (!frags_per_sidx) { + nb_subsegs = 1; + frags_per_sidx = count; + } else { + nb_subsegs = count/frags_per_sidx; + if (nb_subsegs*frags_per_sidxnb_refs = frags_per_sidx; + daisy_chain_sidx = 0; + } + /*daisy-chain SIDX, reference all fragments plus next */ + else if (daisy_chain_sidx) { + sidx->nb_refs = frags_per_sidx + 1; + /*we will have to adjust earliest cpresentation time*/ + first_sidx = 1; + } + /*root SIDX referencing all subsegments*/ + else { + sidx->nb_refs = nb_subsegs; + is_root_sidx = 1; + } + sidx->refs = (GF_SIDXReference *) gf_malloc(sizeof(GF_SIDXReference)*sidx->nb_refs); + memset(sidx->refs, 0, sizeof(GF_SIDXReference)*sidx->nb_refs); + + /*remember start of sidx*/ + sidx_start = gf_bs_get_position(movie->editFileMap->bs); + + e = gf_isom_box_size((GF_Box *) sidx); + if (e) return e; + e = gf_isom_box_write((GF_Box *) sidx, movie->editFileMap->bs); + if (e) return e; + + sidx_end = gf_bs_get_position(movie->editFileMap->bs); + + count = idx = 0; + + if (is_root_sidx) { + root_sidx = sidx; + sidx = NULL; + } + } + + + sidx_idx = 0; + sidx_dur = 0; + last_top_box_pos = root_prev_offset = sidx_end; + local_sidx_start = local_sidx_end = 0; + + /*cumulated segments duration since start of the sidx */ + cur_dur = 0; + e = GF_OK; + while (gf_list_count(movie->moof_list)) { + s32 offset_diff; + u32 moof_size; + u32 dur; + + movie->moof = (GF_MovieFragmentBox *) gf_list_get(movie->moof_list, 0); + gf_list_rem(movie->moof_list, 0); + + if (!root_sidx && sidx && first_sidx) { + first_sidx = 0; + sidx->earliest_presentation_time = ref_track_decode_time + moof_get_earliest_cts(movie->moof, referenceTrackID); + } + + /*hierarchical or daisy-chain SIDXs*/ + if (!no_sidx && !sidx && (root_sidx || daisy_chain_sidx) ) { + sidx = (GF_SegmentIndexBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_SIDX); + sidx->reference_ID = referenceTrackID; + sidx->timescale = trak->Media->mediaHeader->timeScale; + sidx->earliest_presentation_time = ref_track_decode_time + sidx_dur + moof_get_earliest_cts(movie->moof, referenceTrackID); + + /*we don't write anything between sidx and following moov*/ + sidx->first_offset = 0; + sidx->nb_refs = frags_per_sidx; + if (daisy_chain_sidx && (nb_subsegs>1)) { + sidx->nb_refs += 1; + } + sidx->refs = (GF_SIDXReference *) gf_malloc(sizeof(GF_SIDXReference)*sidx->nb_refs); + memset(sidx->refs, 0, sizeof(GF_SIDXReference)*sidx->nb_refs); + + if (root_sidx) + root_sidx->refs[sidx_idx].reference_type = 1; + + /*remember start of sidx*/ + local_sidx_start = gf_bs_get_position(movie->editFileMap->bs); + + /*write it*/ + e = gf_isom_box_size((GF_Box *) sidx); + if (e) return e; + e = gf_isom_box_write((GF_Box *) sidx, movie->editFileMap->bs); + if (e) return e; + + local_sidx_end = gf_bs_get_position(movie->editFileMap->bs); + + /*adjust prev offset*/ + last_top_box_pos = local_sidx_end; + } + + offset_diff = (s32) (gf_bs_get_position(movie->editFileMap->bs) - movie->moof->fragment_offset); + movie->moof->fragment_offset = gf_bs_get_position(movie->editFileMap->bs); + + if (!e) { + e = StoreFragment(movie, 0, offset_diff, &moof_size); + + if (sidx) { + /*we refer to next moof*/ + sidx->refs[idx].reference_type = 0; + sidx->refs[idx].contains_RAP = moof_get_rap_time_offset(movie->moof, referenceTrackID, & sidx->refs[idx].RAP_delta_time); + if (sidx->refs[idx].contains_RAP) { + sidx->refs[idx].RAP_delta_time += cur_dur; + + if (root_sidx && !root_sidx->refs[sidx_idx].contains_RAP) { + root_sidx->refs[sidx_idx].contains_RAP = 1; + root_sidx->refs[sidx_idx].RAP_delta_time = sidx->refs[idx].RAP_delta_time + sidx_dur; + } + else if (prev_sidx && !prev_sidx->refs[prev_sidx->nb_refs - 1].contains_RAP) { + prev_sidx->refs[prev_sidx->nb_refs - 1].contains_RAP = 1; + prev_sidx->refs[prev_sidx->nb_refs - 1].RAP_delta_time = sidx->refs[idx].RAP_delta_time; + } + + } + + dur = moof_get_duration(movie->moof, referenceTrackID); + sidx->refs[idx].subsegment_duration += dur; + cur_dur += dur; + /*reference size is end of the moof we just wrote minus last_box_pos*/ + sidx->refs[idx].reference_size = (u32) ( gf_bs_get_position(movie->editFileMap->bs) - last_top_box_pos) ; + last_top_box_pos = gf_bs_get_position(movie->editFileMap->bs); + idx++; + + count++; + /*switching to next SIDX*/ + if (count==frags_per_sidx) { + + if (root_sidx) { + root_sidx->refs[sidx_idx].reference_size = (u32) (gf_bs_get_position(movie->editFileMap->bs) - local_sidx_start); + root_sidx->refs[sidx_idx].subsegment_duration = cur_dur; + if (!sidx_idx) { + root_sidx->earliest_presentation_time = sidx->earliest_presentation_time; + } + + sidx_rewrite(sidx, movie->editFileMap->bs, local_sidx_start); + gf_isom_box_del((GF_Box*)sidx); + sidx = NULL; + } else if (daisy_chain_sidx) { + if (prev_sidx) { + if (prev_sidx->refs[prev_sidx->nb_refs - 1].contains_RAP) { + prev_sidx->refs[prev_sidx->nb_refs - 1].contains_RAP = 1; + prev_sidx->refs[prev_sidx->nb_refs - 1].RAP_delta_time += sidx_dur; + } + prev_sidx->refs[prev_sidx->nb_refs - 1].subsegment_duration = sidx_dur; + prev_sidx->refs[prev_sidx->nb_refs - 1].reference_size = (u32) (gf_bs_get_position(movie->editFileMap->bs) - local_sidx_start); + prev_sidx->refs[prev_sidx->nb_refs - 1].reference_type = 1; + sidx_rewrite(prev_sidx, movie->editFileMap->bs, sidx_start); + gf_isom_box_del((GF_Box*)prev_sidx); + + sidx_start = local_sidx_start; + sidx_end = local_sidx_end; + } + nb_subsegs--; + prev_sidx = sidx; + sidx = NULL; + } + sidx_dur += cur_dur; + cur_dur = 0; + count = 0; + idx=0; + sidx_idx++; + } + } + } + gf_isom_box_del((GF_Box *) movie->moof); + movie->moof = NULL; + } + + if (sidx) { + sidx_rewrite(sidx, movie->editFileMap->bs, sidx_start); + gf_isom_box_del((GF_Box*)sidx); + } + if (prev_sidx) { + sidx_rewrite(prev_sidx, movie->editFileMap->bs, sidx_start); + gf_isom_box_del((GF_Box*)prev_sidx); + } + if (root_sidx) { + sidx_rewrite(root_sidx, movie->editFileMap->bs, sidx_start); + gf_isom_box_del((GF_Box*)root_sidx); + } + + if (movie->append_segment) { + char bloc[1024]; + u32 seg_size = (u32) gf_bs_get_size(movie->editFileMap->bs); + gf_bs_seek(movie->editFileMap->bs, 0); + while (seg_size) { + u32 size = gf_bs_read_data(movie->editFileMap->bs, bloc, (seg_size>1024) ? 1024 : seg_size); + gf_bs_write_data(movie->movieFileMap->bs, bloc, size); + seg_size -= size; + } + gf_isom_datamap_del(movie->editFileMap); + movie->editFileMap = gf_isom_fdm_new_temp(NULL); + } + + return e; +} + +GF_Err gf_isom_close_fragments(GF_ISOFile *movie) +{ + if (movie->use_segments) { + return gf_isom_close_segment(movie, 0, 0, 0, 0, 1); + } else { + return StoreFragment(movie, 0, 0, NULL); + } +} + +GF_Err gf_isom_start_segment(GF_ISOFile *movie, char *SegName) +{ + GF_Err e; + //and only at setup + if (!movie || !(movie->FragmentsFlags & GF_ISOM_FRAG_WRITE_READY) ) return GF_BAD_PARAM; + if (movie->openMode != GF_ISOM_OPEN_WRITE) return GF_ISOM_INVALID_MODE; + + if (gf_list_count(movie->moof_list)) + return GF_BAD_PARAM; + + movie->append_segment = 0; + /*update segment file*/ + if (SegName) { + gf_isom_datamap_del(movie->editFileMap); + e = gf_isom_datamap_new(SegName, NULL, GF_ISOM_DATA_MAP_WRITE, & movie->editFileMap); + movie->segment_start = 0; + return e; + } + assert(gf_list_count(movie->moof_list) == 0); + movie->segment_start = gf_bs_get_position(movie->editFileMap->bs); + /*if movieFileMap is not null, we are concatenating segments to the original move, force a copy*/ + if (movie->movieFileMap) + movie->append_segment = 1; + return GF_OK; +} + + +GF_Err gf_isom_start_fragment(GF_ISOFile *movie, Bool moof_first) +{ + u32 i, count; + GF_TrackExtendsBox *trex; + GF_TrackFragmentBox *traf; + GF_Err e; + //and only at setup + if (!movie || !(movie->FragmentsFlags & GF_ISOM_FRAG_WRITE_READY) ) + return GF_BAD_PARAM; + if (movie->openMode != GF_ISOM_OPEN_WRITE) return GF_ISOM_INVALID_MODE; + + count = gf_list_count(movie->moov->mvex->TrackExList); + if (!count) + return GF_BAD_PARAM; + + /*always force cached mode when writing movie segments*/ + if (movie->use_segments) moof_first = 1; + movie->moof_first = moof_first; + + //store existing fragment + if (movie->moof) { + e = StoreFragment(movie, movie->use_segments ? 1 : 0, 0, NULL); + if (e) return e; + } + + /* Inserting free data for testing HTTP streaming */ +#if 0 + if (0) { + GF_FreeSpaceBox *fb = (GF_FreeSpaceBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_FREE); + fb->dataSize = free_data_insert_size; + //gf_list_add(movie->TopBoxes, fb); + e = gf_isom_box_size((GF_Box *) fb); + if (e) return e; + e = gf_isom_box_write((GF_Box *) fb, movie->editFileMap->bs); + if (e) return e; + gf_isom_box_del((GF_Box *)fb); + } +#endif + + //create new fragment + movie->moof = (GF_MovieFragmentBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_MOOF); + movie->moof->mfhd = (GF_MovieFragmentHeaderBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_MFHD); + movie->moof->mfhd->sequence_number = movie->NextMoofNumber; + movie->NextMoofNumber ++; + if (movie->use_segments) + gf_list_add(movie->moof_list, movie->moof); + + + /*remember segment offset*/ + movie->moof->fragment_offset = gf_bs_get_position(movie->editFileMap->bs); + /*prepare MDAT*/ + gf_bs_write_u32(movie->editFileMap->bs, 0); + gf_bs_write_u32(movie->editFileMap->bs, GF_ISOM_BOX_TYPE_MDAT); + + //we create a TRAF for each setup track, unused ones will be removed at store time + for (i=0; imoov->mvex->TrackExList, i); + traf = (GF_TrackFragmentBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_TRAF); + traf->trex = trex; + traf->tfhd = (GF_TrackFragmentHeaderBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_TFHD); + traf->tfhd->trackID = trex->trackID; + //add 8 bytes (MDAT size+type) to avoid the data_offset in the first trun + traf->tfhd->base_data_offset = movie->moof->fragment_offset + 8; + gf_list_add(movie->moof->TrackList, traf); + } + return GF_OK; +} + +u32 GetRunSize(GF_TrackFragmentRunBox *trun) +{ + u32 i, size; + GF_TrunEntry *ent; + size = 0; + i=0; + while ((ent = (GF_TrunEntry*)gf_list_enum(trun->entries, &i))) { + size += ent->size; + } + return size; +} + + +GF_Err gf_isom_fragment_add_sample(GF_ISOFile *movie, u32 TrackID, GF_ISOSample *sample, u32 DescIndex, + u32 Duration, + u8 PaddingBits, u16 DegradationPriority) +{ + u32 count, buffer_size; + char *buffer; + u64 pos; + GF_ISOSample *od_sample = NULL; + GF_TrunEntry *ent; + GF_TrackFragmentBox *traf, *traf_2; + GF_TrackFragmentRunBox *trun; + if (!movie->moof || !(movie->FragmentsFlags & GF_ISOM_FRAG_WRITE_READY) || !sample) + return GF_BAD_PARAM; + + traf = GetTraf(movie, TrackID); + if (!traf) + return GF_BAD_PARAM; + + if (!traf->tfhd->sample_desc_index) traf->tfhd->sample_desc_index = DescIndex ? DescIndex : traf->trex->def_sample_desc_index; + + pos = gf_bs_get_position(movie->editFileMap->bs); + + + //WARNING: we change stream description, create a new TRAF + if ( DescIndex && (traf->tfhd->sample_desc_index != DescIndex)) { + //if we're caching flush the current run + if (traf->DataCache) { + count = gf_list_count(traf->TrackRuns); + if (count) { + trun = (GF_TrackFragmentRunBox *)gf_list_get(traf->TrackRuns, count-1); + trun->data_offset = (u32) (pos - movie->moof->fragment_offset - 8); + gf_bs_get_content(trun->cache, &buffer, &buffer_size); + gf_bs_write_data(movie->editFileMap->bs, buffer, buffer_size); + gf_bs_del(trun->cache); + trun->cache = NULL; + gf_free(buffer); + } + } + traf_2 = (GF_TrackFragmentBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_TRAF); + traf_2->trex = traf->trex; + traf_2->tfhd = (GF_TrackFragmentHeaderBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_TFHD); + traf_2->tfhd->trackID = traf->tfhd->trackID; + //keep the same offset + traf_2->tfhd->base_data_offset = movie->moof->fragment_offset + 8; + gf_list_add(movie->moof->TrackList, traf_2); + + //duplicate infos + traf_2->tfhd->IFrameSwitching = traf->tfhd->IFrameSwitching; + traf_2->DataCache = traf->DataCache; + traf_2->tfhd->sample_desc_index = DescIndex; + + //switch them ... + traf = traf_2; + } + + + pos = gf_bs_get_position(movie->editFileMap->bs); + //add TRUN entry + count = gf_list_count(traf->TrackRuns); + if (count) { + trun = (GF_TrackFragmentRunBox *)gf_list_get(traf->TrackRuns, count-1); + //check data offset when no caching as trun entries shall ALWAYS be contiguous samples + if (!traf->DataCache && (movie->moof->fragment_offset + 8 + trun->data_offset + GetRunSize(trun) != pos) ) + count = 0; + + //check I-frame detection + if (traf->tfhd->IFrameSwitching && sample->IsRAP) + count = 0; + + if (traf->DataCache && (traf->DataCache==trun->sample_count) ) + count = 0; + + //if data cache is on and we're changing TRUN, store the cache and update data offset + if (!count && traf->DataCache) { + trun->data_offset = (u32) (pos - movie->moof->fragment_offset - 8); + gf_bs_get_content(trun->cache, &buffer, &buffer_size); + gf_bs_write_data(movie->editFileMap->bs, buffer, buffer_size); + gf_bs_del(trun->cache); + trun->cache = NULL; + gf_free(buffer); + } + } + + //new run + if (!count) { + trun = (GF_TrackFragmentRunBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_TRUN); + //store data offset (we have the 8 btyes offset of the MDAT) + trun->data_offset = (u32) (pos - movie->moof->fragment_offset - 8); + gf_list_add(traf->TrackRuns, trun); + + //if we use data caching, create a bitstream + if (traf->DataCache) + trun->cache = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); + } + + GF_SAFEALLOC(ent, GF_TrunEntry); + ent->CTS_Offset = sample->CTS_Offset; + ent->Duration = Duration; + ent->size = sample->dataLength; + ent->flags = GF_ISOM_FORMAT_FRAG_FLAGS(PaddingBits, sample->IsRAP, DegradationPriority); + gf_list_add(trun->entries, ent); + + trun->sample_count += 1; + + //rewrite OD frames + if (traf->trex->track->Media->handler->handlerType == GF_ISOM_MEDIA_OD) { + //this may fail if depandancies are not well done ... + Media_ParseODFrame(traf->trex->track->Media, sample, &od_sample); + sample = od_sample; + } + + //finally write the data + if (!traf->DataCache) { + gf_bs_write_data(movie->editFileMap->bs, sample->data, sample->dataLength); + } else if (trun->cache) { + gf_bs_write_data(trun->cache, sample->data, sample->dataLength); + } else { + return GF_BAD_PARAM; + } + if (od_sample) gf_isom_sample_del(&od_sample); + return GF_OK; +} + + + +GF_Err gf_isom_fragment_append_data(GF_ISOFile *movie, u32 TrackID, char *data, u32 data_size, u8 PaddingBits) +{ + u32 count; + u8 rap; + u16 degp; + GF_TrunEntry *ent; + GF_TrackFragmentBox *traf; + GF_TrackFragmentRunBox *trun; + if (!movie->moof || !(movie->FragmentsFlags & GF_ISOM_FRAG_WRITE_READY) ) return GF_BAD_PARAM; + + traf = GetTraf(movie, TrackID); + if (!traf || !traf->tfhd->sample_desc_index) return GF_BAD_PARAM; + + //add TRUN entry + count = gf_list_count(traf->TrackRuns); + if (!count) return GF_BAD_PARAM; + + trun = (GF_TrackFragmentRunBox *)gf_list_get(traf->TrackRuns, count-1); + count = gf_list_count(trun->entries); + if (!count) return GF_BAD_PARAM; + ent = (GF_TrunEntry *)gf_list_get(trun->entries, count-1); + ent->size += data_size; + + rap = GF_ISOM_GET_FRAG_SYNC(ent->flags); + degp = GF_ISOM_GET_FRAG_DEG(ent->flags); + ent->flags = GF_ISOM_FORMAT_FRAG_FLAGS(PaddingBits, rap, degp); + + //finally write the data + if (!traf->DataCache) { + gf_bs_write_data(movie->editFileMap->bs, data, data_size); + } else if (trun->cache) { + gf_bs_write_data(trun->cache, data, data_size); + } else { + return GF_BAD_PARAM; + } + return GF_OK; +} + +GF_Err gf_isom_fragment_add_subsample(GF_ISOFile *movie, u32 TrackID, u32 subSampleSize, u8 priority, u32 reserved, Bool discardable) +{ + u32 i, count, last_sample; + GF_TrackFragmentBox *traf; + if (!movie->moof || !(movie->FragmentsFlags & GF_ISOM_FRAG_WRITE_READY) ) return GF_BAD_PARAM; + + traf = GetTraf(movie, TrackID); + if (!traf || !traf->tfhd->sample_desc_index) return GF_BAD_PARAM; + + /*compute last sample number in traf*/ + last_sample = 0; + count = gf_list_count(traf->TrackRuns); + for (i=0; iTrackRuns, i); + last_sample += trun->sample_count; + } + + if (!traf->subs) { + traf->subs = (GF_SubSampleInformationBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_SUBS); + traf->subs->version = (subSampleSize>0xFFFF) ? 1 : 0; + } + return gf_isom_add_subsample_info(traf->subs, last_sample, subSampleSize, priority, reserved, discardable); +} + +GF_Err gf_isom_fragment_copy_subsample(GF_ISOFile *dest, u32 TrackID, GF_ISOFile *orig, u32 track, u32 sampleNumber) +{ + u32 i, count, last_sample; + GF_SampleEntry *sub_sample; + GF_Err e; + GF_TrackFragmentBox *traf; + if (!dest->moof || !(dest->FragmentsFlags & GF_ISOM_FRAG_WRITE_READY) ) return GF_BAD_PARAM; + + if (! gf_isom_sample_get_subsample_entry(orig, track, sampleNumber, &sub_sample)) return GF_OK; + + traf = GetTraf(dest, TrackID); + if (!traf || !traf->tfhd->sample_desc_index) return GF_BAD_PARAM; + + /*compute last sample number in traf*/ + last_sample = 0; + count = gf_list_count(traf->TrackRuns); + for (i=0; iTrackRuns, i); + last_sample += trun->sample_count; + } + + /*create subsample if needed*/ + if (!traf->subs) { + traf->subs = (GF_SubSampleInformationBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_SUBS); + traf->subs->version = 0; + } + + + count = gf_list_count(sub_sample->SubSamples); + for (i=0; iSubSamples, i); + e = gf_isom_add_subsample_info(traf->subs, last_sample, entry->subsample_size, entry->subsample_priority, entry->reserved, entry->discardable); + if (e) return e; + } + return GF_OK; +} + + +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + +GF_EXPORT +u32 gf_isom_is_track_fragmented(GF_ISOFile *movie, u32 TrackID) +{ + if (!movie || !movie->moov || !movie->moov->mvex) return 0; + return (GetTrex(movie->moov, TrackID) != NULL) ? 1 : 0; +} + +GF_EXPORT +u32 gf_isom_is_fragmented(GF_ISOFile *movie) +{ + if (!movie || !movie->moov) return 0; + /* By default if the Moov has an mvex, the file is fragmented */ + if (movie->moov->mvex) return 1; + /* deprecated old code */ + /*only check moof number (in read mode, mvex can be deleted on the fly)*/ + //return movie->NextMoofNumber ? 1 : 0; + return 0; +} + + +GF_Err gf_isom_set_traf_base_media_decode_time(GF_ISOFile *movie, u32 TrackID, u64 decode_time) +{ + GF_TrackFragmentBox *traf; + if (!movie || !movie->moof || !(movie->FragmentsFlags & GF_ISOM_FRAG_WRITE_READY) ) return GF_BAD_PARAM; + + traf = GetTraf(movie, TrackID); + if (!traf) return GF_BAD_PARAM; + + if (!traf->tfdt) { + traf->tfdt = (GF_TFBaseMediaDecodeTimeBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_TFDT); + if (!traf->tfdt) return GF_OUT_OF_MEM; + } + traf->tfdt->baseMediaDecodeTime = decode_time; + return GF_OK; +} + +#else + +GF_Err gf_isom_finalize_for_fragment(GF_ISOFile *the_file) +{ + return GF_NOT_SUPPORTED; +} + +GF_Err gf_isom_setup_track_fragment(GF_ISOFile *the_file, u32 TrackID, + u32 DefaultSampleDescriptionIndex, + u32 DefaultSampleDuration, + u32 DefaultSampleSize, + u8 DefaultSampleIsSync, + u8 DefaultSamplePadding, + u16 DefaultDegradationPriority) +{ + return GF_NOT_SUPPORTED; +} + +GF_Err gf_isom_set_fragment_option(GF_ISOFile *the_file, u32 TrackID, u32 Code, u32 Param) +{ + return GF_NOT_SUPPORTED; +} + +GF_Err gf_isom_start_fragment(GF_ISOFile *the_file, u32 free_data_insert_size) +{ + return GF_NOT_SUPPORTED; +} + +GF_Err gf_isom_fragment_add_sample(GF_ISOFile *the_file, u32 TrackID, GF_ISOSample *sample, u32 DescIndex, + u32 Duration, + u8 PaddingBits, u16 DegradationPriority) +{ + return GF_NOT_SUPPORTED; +} + + +GF_EXPORT +u32 gf_isom_is_track_fragmented(GF_ISOFile *the_file, u32 TrackID) +{ + return 0; +} + +GF_EXPORT +u32 gf_isom_is_fragmented(GF_ISOFile *the_file) +{ + return 0; +} + +GF_Err gf_isom_fragment_add_subsample(GF_ISOFile *movie, u32 TrackID, u32 subSampleSize, u8 priority, u32 reserved, Bool discardable) +{ + return GF_NOT_SUPPORTED; +} + +GF_Err gf_isom_fragment_copy_subsample(GF_ISOFile *dest, u32 TrackID, GF_ISOFile *orig, u32 track, u32 sampleNumber) +{ + return GF_NOT_SUPPORTED; +} + +GF_Err gf_isom_set_traf_base_media_decode_time(GF_ISOFile *movie, u32 TrackID, u64 decode_time) +{ + return GF_NOT_SUPPORTED; +} + +#endif /*GPAC_DISABLE_ISOM_FRAGMENTS)*/ + + +void gf_isom_set_next_moof_number(GF_ISOFile *movie, u32 value) +{ +#ifndef GPAC_DISABLE_ISOM_FRAGMENTS + if (movie) movie->NextMoofNumber = value; +#endif +} + +u32 gf_isom_get_next_moof_number(GF_ISOFile *movie) +{ +#ifndef GPAC_DISABLE_ISOM_FRAGMENTS + if (movie) return movie->NextMoofNumber; +#endif + return 0; +} + +#endif /*GPAC_DISABLE_ISOM*/ diff --git a/src/gpacmp4/mp4.cpp b/src/gpacmp4/mp4.cpp new file mode 100644 index 00000000..18678f44 --- /dev/null +++ b/src/gpacmp4/mp4.cpp @@ -0,0 +1,331 @@ +#include +#include +#include + +#include +#include "../ccextractor.h" + +void do_NAL (unsigned char *NALstart, LLONG NAL_length); // From avc_functions.cpp +void set_fts(void); // From timing.cpp + +static short bswap16(short v) +{ + return ((v >> 8) & 0x00FF) | ((v << 8) & 0xFF00); +} + +static long bswap32(long v) +{ + // For 0x12345678 returns 78563412 + long swapped=((v&0xFF)<<24) | ((v&0xFF00)<<8) | ((v&0xFF0000) >>8) | ((v&0xFF000000) >>24); + return swapped; +} +static struct { + unsigned total; + unsigned type[32]; +}s_nalu_stats; + +static struct { + unsigned total; + unsigned type[256]; +}s_sei_stats; + +static int process_avc_sample(u32 timescale, GF_AVCConfig* c, GF_ISOSample* s) +{ + 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) + pts_set=1; + set_fts(); + + for(i = 0; i < s->dataLength; ) + { + u32 nal_length; + + switch(c->nal_unit_size) + { + case 1: + nal_length = s->data[i]; + break; + case 2: + nal_length = bswap16(*(short* )&s->data[i]); + break; + case 4: + nal_length = bswap32(*(long* )&s->data[i]); + break; + } + i += c->nal_unit_size; + + s_nalu_stats.total += 1; + s_nalu_stats.type[s->data[i] & 0x1F] += 1; + + temp_debug=0; + + if (nal_length>0) + do_NAL ((unsigned char *) &(s->data[i]) ,nal_length); + i += nal_length; + } // outer for + assert(i == s->dataLength); + + return status; +} +static int process_xdvb_track(const char* basename, GF_ISOFile* f, u32 track) +{ + u32 timescale, i, sample_count, last_sdi = 0; + int status; + if((sample_count = gf_isom_get_sample_count(f, track)) < 1){ + return 0; + } + + timescale = gf_isom_get_media_timescale(f, track); + + status = 0; + + 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(); + + process_m2v ((unsigned char *) s->data,s->dataLength); + 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 cur_sec = (int) (get_fts() / 1000); + activity_progress(100, cur_sec/60, cur_sec%60); + + return status; +} + +static int process_avc_track(const char* basename, GF_ISOFile* f, u32 track) +{ + 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){ + return 0; + } + + timescale = gf_isom_get_media_timescale(f, track); + + status = 0; + + 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){ + gf_odf_avc_cfg_del(c); + c = NULL; + } + + if((c = gf_isom_avc_config_get(f, track, sdi)) == NULL){ + gf_isom_sample_del(&s); + status = -1; + break; + } + + last_sdi = sdi; + } + + status = process_avc_sample(timescale, c, s); + + gf_isom_sample_del(&s); + + 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 cur_sec = (int) (get_fts() / 1000); + activity_progress(100, cur_sec/60, cur_sec%60); + + if(c != NULL){ + gf_odf_avc_cfg_del(c); + c = NULL; + } + + return status; +} + +/* + Here is application algorithm described in some C-like pseudo code: + main(){ + media = open() + for each track in media + if track is AVC track + for each sample in track + for each NALU in sample + send to avc.cpp for processing + close(media) + } + +*/ +int processmp4 (char *file) +{ + GF_ISOFile* f; + u32 i, j, track_count, avc_track_count, cc_track_count; + + mprint("opening \'%s\': ", file); + + if((f = gf_isom_open(file, GF_ISOM_OPEN_READ, NULL)) == NULL){ + mprint("failed to open\n"); + return -2; + } + + mprint("ok\n"); + + track_count = gf_isom_get_track_count(f); + + avc_track_count = 0; + cc_track_count = 0; + + 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); + mprint ("Track %d, type=%c%c%c%c subtype=%c%c%c%c\n", i+1, (unsigned char) (type>>24%0x100), + (unsigned char) ((type>>16)%0x100),(unsigned char) ((type>>8)%0x100),(unsigned char) (type%0x100), + (unsigned char) (subtype>>24%0x100), + (unsigned char) ((subtype>>16)%0x100),(unsigned char) ((subtype>>8)%0x100),(unsigned char) (subtype%0x100)); + if (type == GF_ISOM_MEDIA_CAPTIONS && subtype == GF_ISOM_SUBTYPE_C608) + cc_track_count++; + if( type == GF_ISOM_MEDIA_VISUAL && subtype == GF_ISOM_SUBTYPE_AVC_H264) + avc_track_count++; + } + + for(i = 0; i < track_count; i++){ + const u32 type = gf_isom_get_media_type(f, i + 1); + const u32 subtype = gf_isom_get_media_subtype(f, i + 1, 1); + + if ( type == GF_ISOM_MEDIA_VISUAL && subtype == GF_ISOM_SUBTYPE_XDVB) + { + if (cc_track_count && !ccx_options.mp4vidtrack) + continue; + if(process_xdvb_track(file, f, i + 1) != 0){ + mprint("error\n"); + return -3; + } } + + if( type == GF_ISOM_MEDIA_VISUAL && subtype == GF_ISOM_SUBTYPE_AVC_H264) + { + if (cc_track_count && !ccx_options.mp4vidtrack) + continue; + GF_AVCConfig *cnf = gf_isom_avc_config_get(f,i+1,1); + if (cnf!=NULL) + { + for (j=0; jsequenceParameterSets);j++) + { + GF_AVCConfigSlot* seqcnf=(GF_AVCConfigSlot* )gf_list_get(cnf->sequenceParameterSets,j); + do_NAL ((unsigned char *) seqcnf->data,seqcnf->size); + } + } + + if(process_avc_track(file, f, i + 1) != 0){ + mprint("error\n"); + return -3; + } + + + } + if (type == GF_ISOM_MEDIA_CAPTIONS && subtype == GF_ISOM_SUBTYPE_C608) + { + if (avc_track_count && ccx_options.mp4vidtrack) + continue; + + unsigned num_streams = gf_isom_get_sample_description_count (f,i+1); + 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); + mprint ("%u sample counts\n",num_samples); + mprint ("%u timescale\n",(unsigned) timescale); + mprint ("%u duration\n",(unsigned) duration); */ + for (unsigned k = 0; k dataLength); + const LLONG timestamp = (LLONG )((sample->DTS + sample->CTS_Offset) * 1000) / timescale; + 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(); + + // 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) + { + // dump (256,( unsigned char *) sample->data+8,sample->dataLength-8,0, 1); + process608 ((const unsigned char *) sample->data+8,sample->dataLength-8,&wbout1); + } + int progress = (int) ((k*100) / num_samples); + 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 cur_sec = (int) (get_fts() / 1000); + activity_progress(100, cur_sec/60, cur_sec%60); + } + } + + mprint("\nclosing media: "); + + gf_isom_close(f); + f = NULL; + mprint ("ok\n"); + + if(avc_track_count == 0){ + mprint("Found no AVC track(s). ", file); + }else{ + mprint("Found %d AVC track(s). ", avc_track_count); + } + if (cc_track_count) + mprint ("Found %d CC track(s).\n", cc_track_count); + else + mprint ("found no dedicated CC track(s).\n"); + + return 0; +} diff --git a/src/gpacmp4/odf_code.c b/src/gpacmp4/odf_code.c new file mode 100644 index 00000000..059079b6 --- /dev/null +++ b/src/gpacmp4/odf_code.c @@ -0,0 +1,3337 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / MPEG-4 ObjectDescriptor sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include +#include + +#define DATE_CODING_BIT_LEN 40 + + +static GFINLINE GF_Err OD_ReadUTF8String(GF_BitStream *bs, char **string, Bool isUTF8, u32 *read) +{ + u32 len; + *read = 1; + len = gf_bs_read_int(bs, 8) + 1; + if (!isUTF8) len *= 2; + (*string) = (char *) gf_malloc(sizeof(char)*len); + if (! (*string) ) return GF_OUT_OF_MEM; + gf_bs_read_data(bs, (*string), len); + *read += len; + return GF_OK; +} + +static GFINLINE u32 OD_SizeUTF8String(char *string, Bool isUTF8) +{ + if (isUTF8) return 1 + strlen(string); + return 1 + 2*gf_utf8_wcslen((const unsigned short *)string); +} + +static GFINLINE void OD_WriteUTF8String(GF_BitStream *bs, char *string, Bool isUTF8) +{ + u32 len; + if (isUTF8) { + len = strlen(string); + gf_bs_write_int(bs, len, 8); + gf_bs_write_data(bs, string, len); + } else { + len = gf_utf8_wcslen((const unsigned short *)string); + gf_bs_write_int(bs, len, 8); + gf_bs_write_data(bs, string, len*2); + } +} + +/*use to parse strings read the length as well - Warning : the alloc is done here !!*/ +GF_Err gf_odf_read_url_string(GF_BitStream *bs, char **string, u32 *readBytes) +{ + u32 length; + *readBytes = 0; + + /*if the string is not NULL, return an error...*/ + if (*string != NULL) return GF_BAD_PARAM; + + /*the len is always on 8 bits*/ + length = gf_bs_read_int(bs, 8); + *readBytes = 1; + /*JLF AMD to MPEG-4 systems :) - This is not conformant at all, just hoping MPEG will accept it soon + since 255bytes URL is a real pain in the neck*/ + if (!length) { + length = gf_bs_read_int(bs, 32); + *readBytes += 4; + } + + /*we want to use strlen to get rid of "stringLength" => we need an extra 0*/ + (*string) = (char *) gf_malloc(length + 1); + if (! string) return GF_OUT_OF_MEM; + gf_bs_read_data(bs, (*string), length); + *readBytes += length; + (*string)[length] = 0; + return GF_OK; +} + +/*writes string*/ +GF_Err gf_odf_write_url_string(GF_BitStream *bs, char *string) +{ + u32 len; + /*we accept NULL strings now*/ + if (!string) { + gf_bs_write_int(bs, 0, 8); + return GF_OK; + } + len = strlen(string); + if (len > 255) { + gf_bs_write_int(bs, 0, 8); + gf_bs_write_int(bs, len, 32); + } else { + gf_bs_write_int(bs, len, 8); + } + gf_bs_write_data(bs, string, len); + return GF_OK; +} + +u32 gf_odf_size_url_string(char *string) +{ + u32 len = strlen(string); + if (len>255) return len+5; + return len+1; +} + +GF_Descriptor *gf_odf_new_esd() +{ + GF_ESD *newDesc = (GF_ESD *) gf_malloc(sizeof(GF_ESD)); + if (!newDesc) return NULL; + memset(newDesc, 0, sizeof(GF_ESD)); + newDesc->IPIDataSet = gf_list_new(); + newDesc->IPMPDescriptorPointers = gf_list_new(); + newDesc->extensionDescriptors = gf_list_new(); + newDesc->tag = GF_ODF_ESD_TAG; + return (GF_Descriptor *) newDesc; +} + + +GF_Err gf_odf_del_esd(GF_ESD *esd) +{ + GF_Err e; + if (!esd) return GF_BAD_PARAM; + if (esd->URLString) gf_free(esd->URLString); + + if (esd->decoderConfig) { + e = gf_odf_delete_descriptor((GF_Descriptor *) esd->decoderConfig); + if (e) return e; + } + if (esd->slConfig) { + e = gf_odf_delete_descriptor((GF_Descriptor *) esd->slConfig); + if (e) return e; + } + if (esd->ipiPtr) { + e = gf_odf_delete_descriptor((GF_Descriptor *) esd->ipiPtr); + if (e) return e; + } + if (esd->qos) { + e = gf_odf_delete_descriptor((GF_Descriptor *) esd->qos); + if (e) return e; + } + if (esd->RegDescriptor) { + e = gf_odf_delete_descriptor((GF_Descriptor *) esd->RegDescriptor); + if (e) return e; + } + if (esd->langDesc) { + e = gf_odf_delete_descriptor((GF_Descriptor *) esd->langDesc); + if (e) return e; + } + + e = gf_odf_delete_descriptor_list(esd->IPIDataSet); + if (e) return e; + e = gf_odf_delete_descriptor_list(esd->IPMPDescriptorPointers); + if (e) return e; + e = gf_odf_delete_descriptor_list(esd->extensionDescriptors); + if (e) return e; + gf_free(esd); + return GF_OK; +} + + +GF_Err AddDescriptorToESD(GF_ESD *esd, GF_Descriptor *desc) +{ + if (!esd || !desc) return GF_BAD_PARAM; + + switch (desc->tag) { + case GF_ODF_DCD_TAG: + if (esd->decoderConfig) return GF_ODF_INVALID_DESCRIPTOR; + esd->decoderConfig = (GF_DecoderConfig *) desc; + break; + + case GF_ODF_SLC_TAG: + if (esd->slConfig) return GF_ODF_INVALID_DESCRIPTOR; + esd->slConfig = (GF_SLConfig *) desc; + break; + + case GF_ODF_MUXINFO_TAG: + gf_list_add(esd->extensionDescriptors, desc); + break; + + case GF_ODF_LANG_TAG: + if (esd->langDesc) return GF_ODF_INVALID_DESCRIPTOR; + esd->langDesc = (GF_Language *) desc; + break; + +#ifndef GPAC_MINIMAL_ODF + //the GF_ODF_ISOM_IPI_PTR_TAG is only used in the file format and replaces GF_ODF_IPI_PTR_TAG... + case GF_ODF_ISOM_IPI_PTR_TAG: + case GF_ODF_IPI_PTR_TAG: + if (esd->ipiPtr) return GF_ODF_INVALID_DESCRIPTOR; + esd->ipiPtr = (GF_IPIPtr *) desc; + break; + + case GF_ODF_QOS_TAG: + if (esd->qos) return GF_ODF_INVALID_DESCRIPTOR; + esd->qos =(GF_QoS_Descriptor *) desc; + break; + + case GF_ODF_CI_TAG: + case GF_ODF_SCI_TAG: + return gf_list_add(esd->IPIDataSet, desc); + + //we use the same struct for v1 and v2 IPMP DPs + case GF_ODF_IPMP_PTR_TAG: + return gf_list_add(esd->IPMPDescriptorPointers, desc); + + case GF_ODF_REG_TAG: + if (esd->RegDescriptor) return GF_ODF_INVALID_DESCRIPTOR; + esd->RegDescriptor =(GF_Registration *) desc; + break; +#endif + + default: + if ( (desc->tag >= GF_ODF_EXT_BEGIN_TAG) && + (desc->tag <= GF_ODF_EXT_END_TAG) ) { + return gf_list_add(esd->extensionDescriptors, desc); + } + gf_odf_delete_descriptor(desc); + return GF_OK; + } + + return GF_OK; +} + +GF_Err gf_odf_read_esd(GF_BitStream *bs, GF_ESD *esd, u32 DescSize) +{ + GF_Err e = GF_OK; + u32 ocrflag, urlflag, streamdependflag, tmp_size, nbBytes, read; + + if (! esd) return GF_BAD_PARAM; + + nbBytes = 0; + + esd->ESID = gf_bs_read_int(bs, 16); + streamdependflag = gf_bs_read_int(bs, 1); + urlflag = gf_bs_read_int(bs, 1); + ocrflag = gf_bs_read_int(bs, 1); + esd->streamPriority = gf_bs_read_int(bs, 5); + nbBytes += 3; + + if (streamdependflag) { + esd->dependsOnESID = gf_bs_read_int(bs, 16); + nbBytes += 2; + } + + if (urlflag) { + e = gf_odf_read_url_string(bs, & esd->URLString, &read); + if (e) return e; + nbBytes += read; + } + if (ocrflag) { + esd->OCRESID = gf_bs_read_int(bs, 16); + nbBytes += 2; + } + /*fix broken sync*/ +// if (esd->OCRESID == esd->ESID) esd->OCRESID = 0; + + while (nbBytes < DescSize) { + GF_Descriptor *tmp = NULL; + e = gf_odf_parse_descriptor(bs, &tmp, &tmp_size); + /*fix for iPod files*/ + if (e==GF_ODF_INVALID_DESCRIPTOR) { + nbBytes += tmp_size; + if (nbBytes>DescSize) return e; + gf_bs_read_int(bs, DescSize-nbBytes); + return GF_OK; + } + if (e) return e; + if (!tmp) return GF_ODF_INVALID_DESCRIPTOR; + e = AddDescriptorToESD(esd, tmp); + if (e) return e; + nbBytes += tmp_size + gf_odf_size_field_size(tmp_size); + + //apple fix + if (!tmp_size) nbBytes = DescSize; + + } + if (DescSize != nbBytes) return GF_ODF_INVALID_DESCRIPTOR; + return e; + +} + +GF_Err gf_odf_size_esd(GF_ESD *esd, u32 *outSize) +{ + GF_Err e; + u32 tmpSize; + if (! esd) return GF_BAD_PARAM; + + *outSize = 0; + *outSize += 3; + + /*this helps keeping proper sync: some people argue that OCR_ES_ID == ES_ID is a circular reference + of streams. Since this is equivalent to no OCR_ES_ID, keep it that way*/ +// if (esd->OCRESID == esd->ESID) esd->OCRESID = 0; + + if (esd->dependsOnESID) *outSize += 2; + if (esd->URLString) *outSize += gf_odf_size_url_string(esd->URLString); + if (esd->OCRESID) *outSize += 2; + + if (esd->decoderConfig) { + e = gf_odf_size_descriptor((GF_Descriptor *) esd->decoderConfig, &tmpSize); + if (e) return e; + *outSize += tmpSize + gf_odf_size_field_size(tmpSize); + } + if (esd->slConfig) { + e = gf_odf_size_descriptor((GF_Descriptor *) esd->slConfig, &tmpSize); + if (e) return e; + *outSize += tmpSize + gf_odf_size_field_size(tmpSize); + } + if (esd->ipiPtr) { + e = gf_odf_size_descriptor((GF_Descriptor *) esd->ipiPtr, &tmpSize); + if (e) return e; + *outSize += tmpSize + gf_odf_size_field_size(tmpSize); + } + if (esd->langDesc) { + e = gf_odf_size_descriptor((GF_Descriptor *) esd->langDesc, &tmpSize); + if (e) return e; + *outSize += tmpSize + gf_odf_size_field_size(tmpSize); + } + + e = gf_odf_size_descriptor_list(esd->IPIDataSet, outSize); + if (e) return e; + e = gf_odf_size_descriptor_list(esd->IPMPDescriptorPointers, outSize); + if (e) return e; + if (esd->qos) { + e = gf_odf_size_descriptor((GF_Descriptor *) esd->qos, &tmpSize); + if (e) return e; + *outSize += tmpSize + gf_odf_size_field_size(tmpSize); + } + if (esd->RegDescriptor) { + e = gf_odf_size_descriptor((GF_Descriptor *) esd->RegDescriptor, &tmpSize); + if (e) return e; + *outSize += tmpSize + gf_odf_size_field_size(tmpSize); + } + return gf_odf_size_descriptor_list(esd->extensionDescriptors, outSize); +} + +GF_Err gf_odf_write_esd(GF_BitStream *bs, GF_ESD *esd) +{ + GF_Err e; + u32 size; + if (! esd) return GF_BAD_PARAM; + + e = gf_odf_size_descriptor((GF_Descriptor *)esd, &size); + if (e) return e; + e = gf_odf_write_base_descriptor(bs, esd->tag, size); + if (e) return e; + + gf_bs_write_int(bs, esd->ESID, 16); + gf_bs_write_int(bs, esd->dependsOnESID ? 1 : 0, 1); + gf_bs_write_int(bs, esd->URLString != NULL ? 1 : 0, 1); + gf_bs_write_int(bs, esd->OCRESID ? 1 : 0, 1); + gf_bs_write_int(bs, esd->streamPriority, 5); + + if (esd->dependsOnESID) { + gf_bs_write_int(bs, esd->dependsOnESID, 16); + } + if (esd->URLString) { + e = gf_odf_write_url_string(bs, esd->URLString); + if (e) return e; + } + + + if (esd->OCRESID) { + gf_bs_write_int(bs, esd->OCRESID, 16); + } + if (esd->decoderConfig) { + e = gf_odf_write_descriptor(bs, (GF_Descriptor *) esd->decoderConfig); + if (e) return e; + } + if (esd->slConfig) { + e = gf_odf_write_descriptor(bs, (GF_Descriptor *) esd->slConfig); + if (e) return e; + } + if (esd->ipiPtr) { + e = gf_odf_write_descriptor(bs, (GF_Descriptor *) esd->ipiPtr); + if (e) return e; + } + if (esd->langDesc) { + e = gf_odf_write_descriptor(bs, (GF_Descriptor *) esd->langDesc); + if (e) return e; + } + + e = gf_odf_write_descriptor_list(bs, esd->IPIDataSet); + if (e) return e; + e = gf_odf_write_descriptor_list(bs, esd->IPMPDescriptorPointers); + if (e) return e; + if (esd->qos) { + e = gf_odf_write_descriptor(bs, (GF_Descriptor *) esd->qos); + if (e) return e; + } + if (esd->RegDescriptor) { + e = gf_odf_write_descriptor(bs, (GF_Descriptor *) esd->RegDescriptor); + if (e) return e; + } + return gf_odf_write_descriptor_list(bs, esd->extensionDescriptors); +} + +GF_Descriptor *gf_odf_new_iod() +{ + GF_InitialObjectDescriptor *newDesc = (GF_InitialObjectDescriptor *) gf_malloc(sizeof(GF_InitialObjectDescriptor)); + if (!newDesc) return NULL; + memset(newDesc, 0, sizeof(GF_InitialObjectDescriptor)); + + newDesc->ESDescriptors = gf_list_new(); + newDesc->OCIDescriptors = gf_list_new(); + newDesc->IPMP_Descriptors = gf_list_new(); + + newDesc->extensionDescriptors = gf_list_new(); + newDesc->tag = GF_ODF_IOD_TAG; + return (GF_Descriptor *) newDesc; +} + +GF_Err gf_odf_del_iod(GF_InitialObjectDescriptor *iod) +{ + GF_Err e; + if (!iod) return GF_BAD_PARAM; + if (iod->URLString) gf_free(iod->URLString); + e = gf_odf_delete_descriptor_list(iod->ESDescriptors); + if (e) return e; + e = gf_odf_delete_descriptor_list(iod->OCIDescriptors); + if (e) return e; + e = gf_odf_delete_descriptor_list(iod->IPMP_Descriptors); + if (e) return e; + e = gf_odf_delete_descriptor_list(iod->extensionDescriptors); + if (e) return e; + if (iod->IPMPToolList) gf_odf_delete_descriptor((GF_Descriptor *) iod->IPMPToolList); + gf_free(iod); + return GF_OK; +} + +GF_Err AddDescriptorToIOD(GF_InitialObjectDescriptor *iod, GF_Descriptor *desc) +{ + if (!iod || !desc) return GF_BAD_PARAM; + + switch (desc->tag) { + case GF_ODF_ESD_TAG: + return gf_list_add(iod->ESDescriptors, desc); + + //we use the same struct for v1 and v2 IPMP DPs + case GF_ODF_IPMP_PTR_TAG: + /*IPMPX*/ + case GF_ODF_IPMP_TAG: + return gf_list_add(iod->IPMP_Descriptors, desc); + + /*IPMPX*/ + case GF_ODF_IPMP_TL_TAG: + if (iod->IPMPToolList) gf_odf_desc_del((GF_Descriptor *)iod->IPMPToolList); + iod->IPMPToolList = (GF_IPMP_ToolList *)desc; + return GF_OK; + + default: + break; + } + if ( (desc->tag >= GF_ODF_OCI_BEGIN_TAG) && (desc->tag <= GF_ODF_OCI_END_TAG) ) return gf_list_add(iod->OCIDescriptors, desc); + if ( (desc->tag >= GF_ODF_EXT_BEGIN_TAG) && (desc->tag <= GF_ODF_EXT_END_TAG) ) return gf_list_add(iod->extensionDescriptors, desc); + return GF_BAD_PARAM; +} + +GF_Err gf_odf_read_iod(GF_BitStream *bs, GF_InitialObjectDescriptor *iod, u32 DescSize) +{ + GF_Err e; + u32 urlflag, read; + u32 tmp_size, nbBytes = 0; + if (! iod) return GF_BAD_PARAM; + + iod->objectDescriptorID = gf_bs_read_int(bs, 10); + urlflag = gf_bs_read_int(bs, 1); + iod->inlineProfileFlag = gf_bs_read_int(bs, 1); + /*reserved = */gf_bs_read_int(bs, 4); + nbBytes += 2; + + if (urlflag) { + e = gf_odf_read_url_string(bs, & iod->URLString, &read); + if (e) return e; + nbBytes += read; + } else { + iod->OD_profileAndLevel = gf_bs_read_int(bs, 8); + iod->scene_profileAndLevel = gf_bs_read_int(bs, 8); + iod->audio_profileAndLevel = gf_bs_read_int(bs, 8); + iod->visual_profileAndLevel = gf_bs_read_int(bs, 8); + iod->graphics_profileAndLevel = gf_bs_read_int(bs, 8); + nbBytes += 5; + } + + while (nbBytes < DescSize) { + GF_Descriptor *tmp = NULL; + e = gf_odf_parse_descriptor(bs, &tmp, &tmp_size); + if (e) return e; + if (!tmp) return GF_ODF_INVALID_DESCRIPTOR; + e = AddDescriptorToIOD(iod, tmp); + if (e) return e; + nbBytes += tmp_size + gf_odf_size_field_size(tmp_size); + } + if (DescSize != nbBytes) return GF_ODF_INVALID_DESCRIPTOR; + return GF_OK; +} + +GF_Err gf_odf_size_iod(GF_InitialObjectDescriptor *iod, u32 *outSize) +{ + GF_Err e; + if (! iod) return GF_BAD_PARAM; + + *outSize = 0; + *outSize += 2; + if (iod->URLString) { + *outSize += gf_odf_size_url_string(iod->URLString); + } else { + *outSize += 5; + e = gf_odf_size_descriptor_list(iod->ESDescriptors, outSize); + if (e) return e; + e = gf_odf_size_descriptor_list(iod->OCIDescriptors, outSize); + if (e) return e; + e = gf_odf_size_descriptor_list(iod->IPMP_Descriptors, outSize); + if (e) return e; + + } + e = gf_odf_size_descriptor_list(iod->extensionDescriptors, outSize); + if (e) return e; + if (iod->IPMPToolList) { + u32 tmpSize; + e = gf_odf_size_descriptor((GF_Descriptor *) iod->IPMPToolList, &tmpSize); + if (e) return e; + *outSize += tmpSize + gf_odf_size_field_size(tmpSize); + } + return GF_OK; +} + +GF_Err gf_odf_write_iod(GF_BitStream *bs, GF_InitialObjectDescriptor *iod) +{ + GF_Err e; + u32 size; + if (! iod) return GF_BAD_PARAM; + + e = gf_odf_size_descriptor((GF_Descriptor *)iod, &size); + if (e) return e; + e = gf_odf_write_base_descriptor(bs, iod->tag, size); + if (e) return e; + + gf_bs_write_int(bs, iod->objectDescriptorID, 10); + gf_bs_write_int(bs, iod->URLString != NULL ? 1 : 0, 1); + gf_bs_write_int(bs, iod->inlineProfileFlag, 1); + gf_bs_write_int(bs, 15, 4); //reserved: 0b1111 == 15 + + if (iod->URLString) { + gf_odf_write_url_string(bs, iod->URLString); + } else { + gf_bs_write_int(bs, iod->OD_profileAndLevel, 8); + gf_bs_write_int(bs, iod->scene_profileAndLevel, 8); + gf_bs_write_int(bs, iod->audio_profileAndLevel, 8); + gf_bs_write_int(bs, iod->visual_profileAndLevel, 8); + gf_bs_write_int(bs, iod->graphics_profileAndLevel, 8); + e = gf_odf_write_descriptor_list(bs, iod->ESDescriptors); + if (e) return e; + e = gf_odf_write_descriptor_list(bs, iod->OCIDescriptors); + if (e) return e; + e = gf_odf_write_descriptor_list_filter(bs, iod->IPMP_Descriptors, GF_ODF_IPMP_PTR_TAG); + if (e) return e; + e = gf_odf_write_descriptor_list_filter(bs, iod->IPMP_Descriptors, GF_ODF_IPMP_TAG); + if (e) return e; + if (iod->IPMPToolList) { + e = gf_odf_write_descriptor(bs, (GF_Descriptor *) iod->IPMPToolList); + if (e) return e; + } + } + e = gf_odf_write_descriptor_list(bs, iod->extensionDescriptors); + return GF_OK; +} + + + +GF_Descriptor *gf_odf_new_od() +{ + GF_ObjectDescriptor *newDesc; + GF_SAFEALLOC(newDesc, GF_ObjectDescriptor); + if (!newDesc) return NULL; + + newDesc->URLString = NULL; + newDesc->ESDescriptors = gf_list_new(); + newDesc->OCIDescriptors = gf_list_new(); + newDesc->IPMP_Descriptors = gf_list_new(); + newDesc->extensionDescriptors = gf_list_new(); + newDesc->objectDescriptorID = 0; + newDesc->tag = GF_ODF_OD_TAG; + return (GF_Descriptor *) newDesc; +} + +GF_Err gf_odf_del_od(GF_ObjectDescriptor *od) +{ + GF_Err e; + if (!od) return GF_BAD_PARAM; + if (od->URLString) gf_free(od->URLString); + e = gf_odf_delete_descriptor_list(od->ESDescriptors); + if (e) return e; + e = gf_odf_delete_descriptor_list(od->OCIDescriptors); + if (e) return e; + e = gf_odf_delete_descriptor_list(od->IPMP_Descriptors); + if (e) return e; + e = gf_odf_delete_descriptor_list(od->extensionDescriptors); + if (e) return e; + gf_free(od); + return GF_OK; +} + +GF_Err AddDescriptorToOD(GF_ObjectDescriptor *od, GF_Descriptor *desc) +{ + if (!od || !desc) return GF_BAD_PARAM; + + //check if we can handle ContentClassif tags + if ( (desc->tag >= GF_ODF_OCI_BEGIN_TAG) && + (desc->tag <= GF_ODF_OCI_END_TAG) ) { + return gf_list_add(od->OCIDescriptors, desc); + } + + //or extensions + if ( (desc->tag >= GF_ODF_EXT_BEGIN_TAG) && + (desc->tag <= GF_ODF_EXT_END_TAG) ) { + return gf_list_add(od->extensionDescriptors, desc); + } + + //to cope with envivio + switch (desc->tag) { + case GF_ODF_ESD_TAG: + case GF_ODF_ESD_REF_TAG: + return gf_list_add(od->ESDescriptors, desc); + + //we use the same struct for v1 and v2 IPMP DPs + case GF_ODF_IPMP_PTR_TAG: + case GF_ODF_IPMP_TAG: + return gf_list_add(od->IPMP_Descriptors, desc); + + default: + return GF_BAD_PARAM; + } +} + +GF_Err gf_odf_read_od(GF_BitStream *bs, GF_ObjectDescriptor *od, u32 DescSize) +{ + GF_Err e; + u32 urlflag; + u32 tmpSize, nbBytes = 0; + if (! od) return GF_BAD_PARAM; + + od->objectDescriptorID = gf_bs_read_int(bs, 10); + urlflag = gf_bs_read_int(bs, 1); + /*reserved = */gf_bs_read_int(bs, 5); + nbBytes += 2; + + if (urlflag) { + u32 read; + e = gf_odf_read_url_string(bs, & od->URLString, &read); + if (e) return e; + nbBytes += read; + } + + while (nbBytes < DescSize) { + GF_Descriptor *tmp = NULL; + e = gf_odf_parse_descriptor(bs, &tmp, &tmpSize); + if (e) return e; + if (!tmp) return GF_ODF_INVALID_DESCRIPTOR; + e = AddDescriptorToOD(od, tmp); + if (e) return e; + nbBytes += tmpSize + gf_odf_size_field_size(tmpSize); + } + if (nbBytes != DescSize) return GF_ODF_INVALID_DESCRIPTOR; + return GF_OK; +} + +GF_Err gf_odf_size_od(GF_ObjectDescriptor *od, u32 *outSize) +{ + GF_Err e; + if (! od) return GF_BAD_PARAM; + + *outSize = 2; + if (od->URLString) { + *outSize += gf_odf_size_url_string(od->URLString); + } else { + e = gf_odf_size_descriptor_list(od->ESDescriptors, outSize); + if (e) return e; + e = gf_odf_size_descriptor_list(od->OCIDescriptors, outSize); + if (e) return e; + e = gf_odf_size_descriptor_list(od->IPMP_Descriptors, outSize); + if (e) return e; + } + return gf_odf_size_descriptor_list(od->extensionDescriptors, outSize); +} + +GF_Err gf_odf_write_od(GF_BitStream *bs, GF_ObjectDescriptor *od) +{ + GF_Err e; + u32 size; + if (! od) return GF_BAD_PARAM; + + e = gf_odf_size_descriptor((GF_Descriptor *)od, &size); + if (e) return e; + e = gf_odf_write_base_descriptor(bs, od->tag, size); + if (e) return e; + + gf_bs_write_int(bs, od->objectDescriptorID, 10); + gf_bs_write_int(bs, od->URLString != NULL ? 1 : 0, 1); + gf_bs_write_int(bs, 31, 5); //reserved: 0b1111.1 == 31 + + if (od->URLString) { + gf_odf_write_url_string(bs, od->URLString); + } else { + e = gf_odf_write_descriptor_list(bs, od->ESDescriptors); + if (e) return e; + e = gf_odf_write_descriptor_list(bs, od->OCIDescriptors); + if (e) return e; + e = gf_odf_write_descriptor_list_filter(bs, od->IPMP_Descriptors, GF_ODF_IPMP_PTR_TAG); + if (e) return e; + e = gf_odf_write_descriptor_list_filter(bs, od->IPMP_Descriptors, GF_ODF_IPMP_TAG); + if (e) return e; + } + e = gf_odf_write_descriptor_list(bs, od->extensionDescriptors); + return GF_OK; +} + +GF_Descriptor *gf_odf_new_isom_iod() +{ + GF_IsomInitialObjectDescriptor *newDesc = (GF_IsomInitialObjectDescriptor *) gf_malloc(sizeof(GF_IsomInitialObjectDescriptor)); + if (!newDesc) return NULL; + memset(newDesc, 0, sizeof(GF_IsomInitialObjectDescriptor)); + + newDesc->ES_ID_IncDescriptors = gf_list_new(); + newDesc->ES_ID_RefDescriptors = gf_list_new(); + newDesc->OCIDescriptors = gf_list_new(); + newDesc->IPMP_Descriptors = gf_list_new(); + newDesc->extensionDescriptors = gf_list_new(); + newDesc->tag = GF_ODF_ISOM_IOD_TAG; + + //by default create an IOD with no inline and no capabilities + newDesc->audio_profileAndLevel = 0xFF; + newDesc->graphics_profileAndLevel = 0xFF; + newDesc->scene_profileAndLevel = 0xFF; + newDesc->OD_profileAndLevel = 0xFF; + newDesc->visual_profileAndLevel = 0xFF; + return (GF_Descriptor *) newDesc; +} + +GF_Err gf_odf_del_isom_iod(GF_IsomInitialObjectDescriptor *iod) +{ + GF_Err e; + if (!iod) return GF_BAD_PARAM; + if (iod->URLString) gf_free(iod->URLString); + e = gf_odf_delete_descriptor_list(iod->ES_ID_IncDescriptors); + if (e) return e; + e = gf_odf_delete_descriptor_list(iod->ES_ID_RefDescriptors); + if (e) return e; + e = gf_odf_delete_descriptor_list(iod->OCIDescriptors); + if (e) return e; + e = gf_odf_delete_descriptor_list(iod->IPMP_Descriptors); + if (e) return e; + e = gf_odf_delete_descriptor_list(iod->extensionDescriptors); + if (e) return e; + if (iod->IPMPToolList) gf_odf_delete_descriptor((GF_Descriptor *) iod->IPMPToolList); + gf_free(iod); + return GF_OK; +} + +GF_Err AddDescriptorToIsomIOD(GF_IsomInitialObjectDescriptor *iod, GF_Descriptor *desc) +{ + if (!iod || !desc) return GF_BAD_PARAM; + + switch (desc->tag) { + case GF_ODF_ESD_TAG: + return GF_ODF_FORBIDDEN_DESCRIPTOR; + + case GF_ODF_ESD_INC_TAG: + //there shouldn't be ref if inc + if (gf_list_count(iod->ES_ID_RefDescriptors)) return GF_ODF_FORBIDDEN_DESCRIPTOR; + return gf_list_add(iod->ES_ID_IncDescriptors, desc); + + case GF_ODF_ESD_REF_TAG: + //there shouldn't be inc if ref + if (gf_list_count(iod->ES_ID_IncDescriptors)) return GF_ODF_FORBIDDEN_DESCRIPTOR; + return gf_list_add(iod->ES_ID_RefDescriptors, desc); + + //we use the same struct for v1 and v2 IPMP DPs + case GF_ODF_IPMP_PTR_TAG: + case GF_ODF_IPMP_TAG: + return gf_list_add(iod->IPMP_Descriptors, desc); + + /*IPMPX*/ + case GF_ODF_IPMP_TL_TAG: + if (iod->IPMPToolList) gf_odf_desc_del((GF_Descriptor *)iod->IPMPToolList); + iod->IPMPToolList = (GF_IPMP_ToolList *)desc; + return GF_OK; + + default: + break; + } + //check if we can handle ContentClassif tags + if ( (desc->tag >= GF_ODF_OCI_BEGIN_TAG) && (desc->tag <= GF_ODF_OCI_END_TAG) ) return gf_list_add(iod->OCIDescriptors, desc); + //or extensions + if ( (desc->tag >= GF_ODF_EXT_BEGIN_TAG) && (desc->tag <= GF_ODF_EXT_END_TAG) ) return gf_list_add(iod->extensionDescriptors, desc); + return GF_BAD_PARAM; +} + +GF_Err gf_odf_read_isom_iod(GF_BitStream *bs, GF_IsomInitialObjectDescriptor *iod, u32 DescSize) +{ + u32 nbBytes = 0, tmpSize; + u32 urlflag; + GF_Err e; + if (! iod) return GF_BAD_PARAM; + + iod->objectDescriptorID = gf_bs_read_int(bs, 10); + urlflag = gf_bs_read_int(bs, 1); + iod->inlineProfileFlag = gf_bs_read_int(bs, 1); + /*reserved = */gf_bs_read_int(bs, 4); + nbBytes += 2; + + if (urlflag) { + u32 read; + e = gf_odf_read_url_string(bs, & iod->URLString, &read); + if (e) return e; + nbBytes += read; + } else { + iod->OD_profileAndLevel = gf_bs_read_int(bs, 8); + iod->scene_profileAndLevel = gf_bs_read_int(bs, 8); + iod->audio_profileAndLevel = gf_bs_read_int(bs, 8); + iod->visual_profileAndLevel = gf_bs_read_int(bs, 8); + iod->graphics_profileAndLevel = gf_bs_read_int(bs, 8); + nbBytes += 5; + } + + while (nbBytes < DescSize) { + GF_Descriptor *tmp = NULL; + e = gf_odf_parse_descriptor(bs, &tmp, &tmpSize); + if (e) return e; + if (!tmp) return GF_ODF_INVALID_DESCRIPTOR; + e = AddDescriptorToIsomIOD(iod, tmp); + if (e) return e; + nbBytes += tmpSize + gf_odf_size_field_size(tmpSize); + } + if (DescSize != nbBytes) return GF_ODF_INVALID_DESCRIPTOR; + return GF_OK; +} + +GF_Err gf_odf_size_isom_iod(GF_IsomInitialObjectDescriptor *iod, u32 *outSize) +{ + GF_Err e; + if (! iod) return GF_BAD_PARAM; + + *outSize = 2; + if (iod->URLString) { + *outSize += gf_odf_size_url_string(iod->URLString); + } else { + *outSize += 5; + e = gf_odf_size_descriptor_list(iod->ES_ID_IncDescriptors, outSize); + if (e) return e; + e = gf_odf_size_descriptor_list(iod->ES_ID_RefDescriptors, outSize); + if (e) return e; + e = gf_odf_size_descriptor_list(iod->OCIDescriptors, outSize); + if (e) return e; + e = gf_odf_size_descriptor_list(iod->IPMP_Descriptors, outSize); + if (e) return e; + } + if (iod->IPMPToolList) { + u32 tmpSize; + e = gf_odf_size_descriptor((GF_Descriptor *) iod->IPMPToolList, &tmpSize); + if (e) return e; + *outSize += tmpSize + gf_odf_size_field_size(tmpSize); + } + return gf_odf_size_descriptor_list(iod->extensionDescriptors, outSize); +} + +GF_Err gf_odf_write_isom_iod(GF_BitStream *bs, GF_IsomInitialObjectDescriptor *iod) +{ + GF_Err e; + u32 size; + if (! iod) return GF_BAD_PARAM; + + e = gf_odf_size_descriptor((GF_Descriptor *)iod, &size); + if (e) return e; + e = gf_odf_write_base_descriptor(bs, iod->tag, size); + if (e) return e; + + gf_bs_write_int(bs, iod->objectDescriptorID, 10); + gf_bs_write_int(bs, iod->URLString != NULL ? 1 : 0, 1); + gf_bs_write_int(bs, iod->inlineProfileFlag, 1); + gf_bs_write_int(bs, 15, 4); //reserved: 0b1111 == 15 + + if (iod->URLString) { + gf_odf_write_url_string(bs, iod->URLString); + } else { + gf_bs_write_int(bs, iod->OD_profileAndLevel, 8); + gf_bs_write_int(bs, iod->scene_profileAndLevel, 8); + gf_bs_write_int(bs, iod->audio_profileAndLevel, 8); + gf_bs_write_int(bs, iod->visual_profileAndLevel, 8); + gf_bs_write_int(bs, iod->graphics_profileAndLevel, 8); + e = gf_odf_write_descriptor_list(bs, iod->ES_ID_IncDescriptors); + if (e) return e; + e = gf_odf_write_descriptor_list(bs, iod->ES_ID_RefDescriptors); + if (e) return e; + e = gf_odf_write_descriptor_list(bs, iod->OCIDescriptors); + if (e) return e; + e = gf_odf_write_descriptor_list_filter(bs, iod->IPMP_Descriptors, GF_ODF_IPMP_PTR_TAG); + if (e) return e; + e = gf_odf_write_descriptor_list_filter(bs, iod->IPMP_Descriptors, GF_ODF_IPMP_TAG); + if (e) return e; + if (iod->IPMPToolList) { + e = gf_odf_write_descriptor(bs, (GF_Descriptor *) iod->IPMPToolList); + if (e) return e; + } + } + e = gf_odf_write_descriptor_list(bs, iod->extensionDescriptors); + if (e) return e; + return GF_OK; +} + + +GF_Descriptor *gf_odf_new_isom_od() +{ + GF_IsomObjectDescriptor *newDesc = (GF_IsomObjectDescriptor *) gf_malloc(sizeof(GF_IsomObjectDescriptor)); + if (!newDesc) return NULL; + + newDesc->URLString = NULL; + newDesc->ES_ID_IncDescriptors = gf_list_new(); + newDesc->ES_ID_RefDescriptors = gf_list_new(); + newDesc->OCIDescriptors = gf_list_new(); + newDesc->IPMP_Descriptors = gf_list_new(); + newDesc->extensionDescriptors = gf_list_new(); + newDesc->objectDescriptorID = 0; + newDesc->tag = GF_ODF_ISOM_OD_TAG; + return (GF_Descriptor *) newDesc; +} + +GF_Err gf_odf_del_isom_od(GF_IsomObjectDescriptor *od) +{ + GF_Err e; + if (!od) return GF_BAD_PARAM; + if (od->URLString) gf_free(od->URLString); + e = gf_odf_delete_descriptor_list(od->ES_ID_IncDescriptors); + if (e) return e; + e = gf_odf_delete_descriptor_list(od->ES_ID_RefDescriptors); + if (e) return e; + e = gf_odf_delete_descriptor_list(od->OCIDescriptors); + if (e) return e; + e = gf_odf_delete_descriptor_list(od->IPMP_Descriptors); + if (e) return e; + e = gf_odf_delete_descriptor_list(od->extensionDescriptors); + if (e) return e; + gf_free(od); + return GF_OK; +} + +GF_Err AddDescriptorToIsomOD(GF_IsomObjectDescriptor *od, GF_Descriptor *desc) +{ + if (!od || !desc) return GF_BAD_PARAM; + + //check if we can handle ContentClassif tags + if ( (desc->tag >= GF_ODF_OCI_BEGIN_TAG) && + (desc->tag <= GF_ODF_OCI_END_TAG) ) { + return gf_list_add(od->OCIDescriptors, desc); + } + + //or extension ... + if ( (desc->tag >= GF_ODF_EXT_BEGIN_TAG) && + (desc->tag <= GF_ODF_EXT_END_TAG) ) { + return gf_list_add(od->extensionDescriptors, desc); + } + + switch (desc->tag) { + case GF_ODF_ESD_TAG: + return GF_ODF_FORBIDDEN_DESCRIPTOR; + + case GF_ODF_ESD_INC_TAG: + //there shouldn't be ref if inc + if (gf_list_count(od->ES_ID_RefDescriptors)) return GF_ODF_FORBIDDEN_DESCRIPTOR; + return gf_list_add(od->ES_ID_IncDescriptors, desc); + + case GF_ODF_ESD_REF_TAG: + //there shouldn't be inc if ref + if (gf_list_count(od->ES_ID_IncDescriptors)) return GF_ODF_FORBIDDEN_DESCRIPTOR; + return gf_list_add(od->ES_ID_RefDescriptors, desc); + + //we use the same struct for v1 and v2 IPMP DPs + case GF_ODF_IPMP_PTR_TAG: + case GF_ODF_IPMP_TAG: + return gf_list_add(od->IPMP_Descriptors, desc); + + default: + return GF_BAD_PARAM; + } +} + +GF_Err gf_odf_read_isom_od(GF_BitStream *bs, GF_IsomObjectDescriptor *od, u32 DescSize) +{ + GF_Err e; + u32 urlflag; + u32 tmpSize, nbBytes = 0; + if (! od) return GF_BAD_PARAM; + + od->objectDescriptorID = gf_bs_read_int(bs, 10); + urlflag = gf_bs_read_int(bs, 1); + /*reserved = */gf_bs_read_int(bs, 5); + nbBytes += 2; + + if (urlflag) { + u32 read; + e = gf_odf_read_url_string(bs, & od->URLString, &read); + if (e) return e; + nbBytes += read; + } + + while (nbBytes < DescSize) { + GF_Descriptor *tmp = NULL; + e = gf_odf_parse_descriptor(bs, &tmp, &tmpSize); + if (e) return e; + if (!tmp) return GF_ODF_INVALID_DESCRIPTOR; + e = AddDescriptorToIsomOD(od, tmp); + if (e) return e; + nbBytes += tmpSize + gf_odf_size_field_size(tmpSize); + } + if (nbBytes != DescSize) return GF_ODF_INVALID_DESCRIPTOR; + return GF_OK; +} + +GF_Err gf_odf_size_isom_od(GF_IsomObjectDescriptor *od, u32 *outSize) +{ + GF_Err e; + if (! od) return GF_BAD_PARAM; + + *outSize = 2; + if (od->URLString) { + *outSize += gf_odf_size_url_string(od->URLString); + } else { + e = gf_odf_size_descriptor_list(od->ES_ID_IncDescriptors, outSize); + if (e) return e; + e = gf_odf_size_descriptor_list(od->ES_ID_RefDescriptors, outSize); + if (e) return e; + e = gf_odf_size_descriptor_list(od->OCIDescriptors, outSize); + if (e) return e; + e = gf_odf_size_descriptor_list(od->IPMP_Descriptors, outSize); + if (e) return e; + } + return gf_odf_size_descriptor_list(od->extensionDescriptors, outSize); +} + +GF_Err gf_odf_write_isom_od(GF_BitStream *bs, GF_IsomObjectDescriptor *od) +{ + GF_Err e; + u32 size; + if (! od) return GF_BAD_PARAM; + + e = gf_odf_size_descriptor((GF_Descriptor *)od, &size); + if (e) return e; + e = gf_odf_write_base_descriptor(bs, od->tag, size); + if (e) return e; + + gf_bs_write_int(bs, od->objectDescriptorID, 10); + gf_bs_write_int(bs, od->URLString != NULL ? 1 : 0, 1); + gf_bs_write_int(bs, 31, 5); //reserved: 0b1111.1 == 31 + + if (od->URLString) { + gf_odf_write_url_string(bs, od->URLString); + } else { + e = gf_odf_write_descriptor_list(bs, od->ES_ID_IncDescriptors); + if (e) return e; + e = gf_odf_write_descriptor_list(bs, od->ES_ID_RefDescriptors); + if (e) return e; + e = gf_odf_write_descriptor_list(bs, od->OCIDescriptors); + if (e) return e; + e = gf_odf_write_descriptor_list_filter(bs, od->IPMP_Descriptors, GF_ODF_IPMP_PTR_TAG); + if (e) return e; + e = gf_odf_write_descriptor_list_filter(bs, od->IPMP_Descriptors, GF_ODF_IPMP_TAG); + if (e) return e; + } + e = gf_odf_write_descriptor_list(bs, od->extensionDescriptors); + if (e) return e; + return GF_OK; +} + + + +GF_Descriptor *gf_odf_new_dcd() +{ + GF_DecoderConfig *newDesc; + GF_SAFEALLOC(newDesc, GF_DecoderConfig); + if (!newDesc) return NULL; + + newDesc->profileLevelIndicationIndexDescriptor = gf_list_new(); + newDesc->tag = GF_ODF_DCD_TAG; + return (GF_Descriptor *) newDesc; +} + +GF_Err gf_odf_del_dcd(GF_DecoderConfig *dcd) +{ + GF_Err e; + if (!dcd) return GF_BAD_PARAM; + + if (dcd->decoderSpecificInfo) { + e = gf_odf_delete_descriptor((GF_Descriptor *) dcd->decoderSpecificInfo); + if (e) return e; + } + if (dcd->rvc_config) { + e = gf_odf_delete_descriptor((GF_Descriptor *) dcd->rvc_config); + if (e) return e; + } + e = gf_odf_delete_descriptor_list(dcd->profileLevelIndicationIndexDescriptor); + if (e) return e; + gf_free(dcd); + return GF_OK; +} + +GF_Err gf_odf_read_dcd(GF_BitStream *bs, GF_DecoderConfig *dcd, u32 DescSize) +{ + GF_Err e; + u32 /*reserved, */tmp_size, nbBytes = 0; + if (! dcd) return GF_BAD_PARAM; + + dcd->objectTypeIndication = gf_bs_read_int(bs, 8); + dcd->streamType = gf_bs_read_int(bs, 6); + dcd->upstream = gf_bs_read_int(bs, 1); + /*reserved = */gf_bs_read_int(bs, 1); + dcd->bufferSizeDB = gf_bs_read_int(bs, 24); + dcd->maxBitrate = gf_bs_read_int(bs, 32); + dcd->avgBitrate = gf_bs_read_int(bs, 32); + nbBytes += 13; + + while (nbBytes < DescSize) { + GF_Descriptor *tmp = NULL; + e = gf_odf_parse_descriptor(bs, &tmp, &tmp_size); + if (e) return e; + if (!tmp) return GF_ODF_INVALID_DESCRIPTOR; + switch (tmp->tag) { + case GF_ODF_DSI_TAG: + if (dcd->decoderSpecificInfo) { + gf_odf_delete_descriptor(tmp); + return GF_ODF_INVALID_DESCRIPTOR; + } + dcd->decoderSpecificInfo = (GF_DefaultDescriptor *) tmp; + break; + + case GF_ODF_EXT_PL_TAG: + e = gf_list_add(dcd->profileLevelIndicationIndexDescriptor, tmp); + if (e < GF_OK) { + gf_odf_delete_descriptor(tmp); + return e; + } + break; + + /*iPod fix: delete and aborts, this will create an InvalidDescriptor at the ESD level with a loaded DSI, + laoding will abort with a partially valid ESD which is all the matters*/ + case GF_ODF_SLC_TAG: + gf_odf_delete_descriptor(tmp); + return GF_OK; + + //what the hell is this descriptor ?? Don't know, so delete it ! + default: + gf_odf_delete_descriptor(tmp); + break; + } + nbBytes += tmp_size + gf_odf_size_field_size(tmp_size); + } + if (DescSize != nbBytes) return GF_ODF_INVALID_DESCRIPTOR; + return GF_OK; +} + +GF_Err gf_odf_size_dcd(GF_DecoderConfig *dcd, u32 *outSize) +{ + GF_Err e; + u32 tmpSize; + if (! dcd) return GF_BAD_PARAM; + + *outSize = 0; + *outSize += 13; + if (dcd->decoderSpecificInfo) { + //warning: we don't know anything about the structure of a generic DecSpecInfo + //we check the tag and size of the descriptor, but we most ofthe time can't parse it + //the decSpecInfo is handle as a defaultDescriptor (opaque data, but same structure....) + e = gf_odf_size_descriptor((GF_Descriptor *) dcd->decoderSpecificInfo , &tmpSize); + if (e) return e; + *outSize += tmpSize + gf_odf_size_field_size(tmpSize); + } + e = gf_odf_size_descriptor_list(dcd->profileLevelIndicationIndexDescriptor, outSize); + if (e) return e; + return GF_OK; +} + +GF_Err gf_odf_write_dcd(GF_BitStream *bs, GF_DecoderConfig *dcd) +{ + GF_Err e; + u32 size; + if (! dcd) return GF_BAD_PARAM; + + e = gf_odf_size_descriptor((GF_Descriptor *)dcd, &size); + if (e) return e; + e = gf_odf_write_base_descriptor(bs, dcd->tag, size); + if (e) return e; + + gf_bs_write_int(bs, dcd->objectTypeIndication, 8); + gf_bs_write_int(bs, dcd->streamType, 6); + gf_bs_write_int(bs, dcd->upstream, 1); + gf_bs_write_int(bs, 1, 1); //reserved field... + gf_bs_write_int(bs, dcd->bufferSizeDB, 24); + gf_bs_write_int(bs, dcd->maxBitrate, 32); + gf_bs_write_int(bs, dcd->avgBitrate, 32); + + if (dcd->decoderSpecificInfo) { + e = gf_odf_write_descriptor(bs, (GF_Descriptor *) dcd->decoderSpecificInfo); + if (e) return e; + } + e = gf_odf_write_descriptor_list(bs, dcd->profileLevelIndicationIndexDescriptor); + return e; +} + + +GF_Descriptor *gf_odf_new_default() +{ + GF_DefaultDescriptor *newDesc = (GF_DefaultDescriptor *) gf_malloc(sizeof(GF_DefaultDescriptor)); + if (!newDesc) return NULL; + + newDesc->dataLength = 0; + newDesc->data = NULL; + //set it to the Max allowed + newDesc->tag = GF_ODF_USER_END_TAG; + return (GF_Descriptor *) newDesc; +} + +GF_Err gf_odf_del_default(GF_DefaultDescriptor *dd) +{ + if (!dd) return GF_BAD_PARAM; + + if (dd->data) gf_free(dd->data); + gf_free(dd); + return GF_OK; +} + +GF_Err gf_odf_read_default(GF_BitStream *bs, GF_DefaultDescriptor *dd, u32 DescSize) +{ + u32 nbBytes = 0; + if (! dd) return GF_BAD_PARAM; + + dd->dataLength = DescSize; + dd->data = NULL; + if (DescSize) { + dd->data = (char*)gf_malloc(dd->dataLength); + if (! dd->data) return GF_OUT_OF_MEM; + gf_bs_read_data(bs, dd->data, dd->dataLength); + nbBytes += dd->dataLength; + } + if (nbBytes != DescSize) return GF_ODF_INVALID_DESCRIPTOR; + return GF_OK; +} + +GF_Err gf_odf_size_default(GF_DefaultDescriptor *dd, u32 *outSize) +{ + if (! dd) return GF_BAD_PARAM; + *outSize = dd->dataLength; + return GF_OK; +} + +GF_Err gf_odf_write_default(GF_BitStream *bs, GF_DefaultDescriptor *dd) +{ + GF_Err e; + u32 size; + if (! dd) return GF_BAD_PARAM; + + e = gf_odf_size_descriptor((GF_Descriptor *)dd, &size); + if (e) return e; + e = gf_odf_write_base_descriptor(bs, dd->tag, size); + if (e) return e; + + if (dd->data) { + gf_bs_write_data(bs, dd->data, dd->dataLength); + } + return GF_OK; +} + +GF_Descriptor *gf_odf_new_esd_inc() +{ + GF_ES_ID_Inc *newDesc = (GF_ES_ID_Inc *) gf_malloc(sizeof(GF_ES_ID_Inc)); + if (!newDesc) return NULL; + newDesc->tag = GF_ODF_ESD_INC_TAG; + newDesc->trackID = 0; + return (GF_Descriptor *) newDesc; +} + +GF_Err gf_odf_del_esd_inc(GF_ES_ID_Inc *esd_inc) +{ + if (!esd_inc) return GF_BAD_PARAM; + gf_free(esd_inc); + return GF_OK; +} +GF_Err gf_odf_read_esd_inc(GF_BitStream *bs, GF_ES_ID_Inc *esd_inc, u32 DescSize) +{ + u32 nbBytes = 0; + if (! esd_inc) return GF_BAD_PARAM; + + esd_inc->trackID = gf_bs_read_int(bs, 32); + nbBytes += 4; + if (nbBytes != DescSize) return GF_ODF_INVALID_DESCRIPTOR; + return GF_OK; +} +GF_Err gf_odf_size_esd_inc(GF_ES_ID_Inc *esd_inc, u32 *outSize) +{ + if (! esd_inc) return GF_BAD_PARAM; + *outSize = 4; + return GF_OK; +} +GF_Err gf_odf_write_esd_inc(GF_BitStream *bs, GF_ES_ID_Inc *esd_inc) +{ + GF_Err e; + u32 size; + if (! esd_inc) return GF_BAD_PARAM; + + e = gf_odf_size_descriptor((GF_Descriptor *)esd_inc, &size); + if (e) return e; + e = gf_odf_write_base_descriptor(bs, esd_inc->tag, size); + if (e) return e; + gf_bs_write_int(bs, esd_inc->trackID, 32); + return GF_OK; +} + +GF_Descriptor *gf_odf_new_esd_ref() +{ + GF_ES_ID_Ref *newDesc = (GF_ES_ID_Ref *) gf_malloc(sizeof(GF_ES_ID_Ref)); + if (!newDesc) return NULL; + newDesc->tag = GF_ODF_ESD_REF_TAG; + newDesc->trackRef = 0; + return (GF_Descriptor *) newDesc; +} + +GF_Err gf_odf_del_esd_ref(GF_ES_ID_Ref *esd_ref) +{ + if (!esd_ref) return GF_BAD_PARAM; + gf_free(esd_ref); + return GF_OK; +} +GF_Err gf_odf_read_esd_ref(GF_BitStream *bs, GF_ES_ID_Ref *esd_ref, u32 DescSize) +{ + u32 nbBytes = 0; + if (! esd_ref) return GF_BAD_PARAM; + + esd_ref->trackRef = gf_bs_read_int(bs, 16); + nbBytes += 2; + if (nbBytes != DescSize) return GF_ODF_INVALID_DESCRIPTOR; + return GF_OK; +} + +GF_Err gf_odf_size_esd_ref(GF_ES_ID_Ref *esd_ref, u32 *outSize) +{ + if (! esd_ref) return GF_BAD_PARAM; + *outSize = 2; + return GF_OK; +} +GF_Err gf_odf_write_esd_ref(GF_BitStream *bs, GF_ES_ID_Ref *esd_ref) +{ + GF_Err e; + u32 size; + if (! esd_ref) return GF_BAD_PARAM; + + e = gf_odf_size_descriptor((GF_Descriptor *)esd_ref, &size); + if (e) return e; + e = gf_odf_write_base_descriptor(bs, esd_ref->tag, size); + if (e) return e; + + gf_bs_write_int(bs, esd_ref->trackRef, 16); + return GF_OK; +} + + + +GF_Descriptor *gf_odf_new_segment() +{ + GF_Segment *newDesc = (GF_Segment *) gf_malloc(sizeof(GF_Segment)); + if (!newDesc) return NULL; + + memset(newDesc, 0, sizeof(GF_Segment)); + newDesc->tag = GF_ODF_SEGMENT_TAG; + return (GF_Descriptor *) newDesc; +} + +GF_Err gf_odf_del_segment(GF_Segment *sd) +{ + if (!sd) return GF_BAD_PARAM; + + if (sd->SegmentName) gf_free(sd->SegmentName); + gf_free(sd); + return GF_OK; +} + +GF_Err gf_odf_read_segment(GF_BitStream *bs, GF_Segment *sd, u32 DescSize) +{ + u32 size, nbBytes = 0; + if (!sd) return GF_BAD_PARAM; + + sd->startTime = gf_bs_read_double(bs); + sd->Duration = gf_bs_read_double(bs); + nbBytes += 16; + size = gf_bs_read_int(bs, 8); + nbBytes += 1; + if (size) { + sd->SegmentName = (char*) gf_malloc(sizeof(char)*(size+1)); + if (!sd->SegmentName) return GF_OUT_OF_MEM; + gf_bs_read_data(bs, sd->SegmentName, size); + sd->SegmentName[size] = 0; + nbBytes += size; + } + if (nbBytes != DescSize) return GF_ODF_INVALID_DESCRIPTOR; + return GF_OK; +} + +GF_Err gf_odf_size_segment(GF_Segment *sd, u32 *outSize) +{ + if (!sd) return GF_BAD_PARAM; + *outSize = 17; + if (sd->SegmentName) *outSize += strlen(sd->SegmentName); + return GF_OK; +} + +GF_Err gf_odf_write_segment(GF_BitStream *bs, GF_Segment *sd) +{ + GF_Err e; + u32 size; + if (!sd) return GF_BAD_PARAM; + e = gf_odf_size_descriptor((GF_Descriptor *)sd, &size); + if (e) return e; + e = gf_odf_write_base_descriptor(bs, sd->tag, size); + if (e) return e; + gf_bs_write_double(bs, sd->startTime); + gf_bs_write_double(bs, sd->Duration); + if (sd->SegmentName) { + gf_bs_write_int(bs, strlen(sd->SegmentName), 8); + gf_bs_write_data(bs, sd->SegmentName, strlen(sd->SegmentName)); + } else { + gf_bs_write_int(bs, 0, 8); + } + return GF_OK; +} +GF_Descriptor *gf_odf_new_mediatime() +{ + GF_MediaTime *newDesc = (GF_MediaTime *) gf_malloc(sizeof(GF_MediaTime)); + if (!newDesc) return NULL; + + memset(newDesc, 0, sizeof(GF_MediaTime)); + newDesc->tag = GF_ODF_MEDIATIME_TAG; + return (GF_Descriptor *) newDesc; +} +GF_Err gf_odf_del_mediatime(GF_MediaTime *mt) +{ + if (!mt) return GF_BAD_PARAM; + gf_free(mt); + return GF_OK; +} +GF_Err gf_odf_read_mediatime(GF_BitStream *bs, GF_MediaTime *mt, u32 DescSize) +{ + if (!mt) return GF_BAD_PARAM; + mt->mediaTimeStamp = gf_bs_read_double(bs); + return GF_OK; +} +GF_Err gf_odf_size_mediatime(GF_MediaTime *mt, u32 *outSize) +{ + if (!mt) return GF_BAD_PARAM; + *outSize = 8; + return GF_OK; +} +GF_Err gf_odf_write_mediatime(GF_BitStream *bs, GF_MediaTime *mt) +{ + GF_Err e; + u32 size; + if (!mt) return GF_BAD_PARAM; + e = gf_odf_size_descriptor((GF_Descriptor *)mt, &size); + if (e) return e; + e = gf_odf_write_base_descriptor(bs, mt->tag, size); + if (e) return e; + gf_bs_write_double(bs, mt->mediaTimeStamp); + return GF_OK; +} + + +GF_Descriptor *gf_odf_new_lang() +{ + GF_Language *newDesc = (GF_Language *) gf_malloc(sizeof(GF_Language)); + if (!newDesc) return NULL; + newDesc->langCode = 0; + newDesc->tag = GF_ODF_LANG_TAG; + return (GF_Descriptor *) newDesc; +} + +GF_Err gf_odf_del_lang(GF_Language *ld) +{ + if (!ld) return GF_BAD_PARAM; + gf_free(ld); + return GF_OK; +} + +GF_Err gf_odf_read_lang(GF_BitStream *bs, GF_Language *ld, u32 DescSize) +{ + u32 nbBytes = 0; + if (!ld) return GF_BAD_PARAM; + + ld->langCode = gf_bs_read_int(bs, 24); + nbBytes += 3; + if (nbBytes != DescSize) return GF_ODF_INVALID_DESCRIPTOR; + return GF_OK; +} + +GF_Err gf_odf_size_lang(GF_Language *ld, u32 *outSize) +{ + if (!ld) return GF_BAD_PARAM; + *outSize = 3; + return GF_OK; +} + +GF_Err gf_odf_write_lang(GF_BitStream *bs, GF_Language *ld) +{ + GF_Err e; + u32 size; + if (!ld) return GF_BAD_PARAM; + + e = gf_odf_size_descriptor((GF_Descriptor *)ld, &size); + if (e) return e; + e = gf_odf_write_base_descriptor(bs, ld->tag, size); + if (e) return e; + gf_bs_write_int(bs, ld->langCode, 24); + return GF_OK; +} + + + +GF_Descriptor *gf_odf_new_auxvid() +{ + GF_AuxVideoDescriptor *newDesc; + GF_SAFEALLOC(newDesc, GF_AuxVideoDescriptor); + if (!newDesc) return NULL; + newDesc->tag = GF_ODF_AUX_VIDEO_DATA; + return (GF_Descriptor *) newDesc; +} + +GF_Err gf_odf_del_auxvid(GF_AuxVideoDescriptor *ld) +{ + if (!ld) return GF_BAD_PARAM; + gf_free(ld); + return GF_OK; +} + +GF_Err gf_odf_read_auxvid(GF_BitStream *bs, GF_AuxVideoDescriptor *ld, u32 DescSize) +{ + u32 nbBytes = 0; + if (!ld) return GF_BAD_PARAM; + + ld->aux_video_type = gf_bs_read_int(bs, 8); + ld->position_offset_h = gf_bs_read_int(bs, 8); + ld->position_offset_v = gf_bs_read_int(bs, 8); + nbBytes += 3; + switch (ld->aux_video_type) { + case 0x10: + ld->kfar = gf_bs_read_int(bs, 8); + ld->knear = gf_bs_read_int(bs, 8); + nbBytes += 2; + break; + case 0x11: + ld->parallax_zero = gf_bs_read_int(bs, 16); + ld->parallax_scale = gf_bs_read_int(bs, 16); + ld->dref = gf_bs_read_int(bs, 16); + ld->wref = gf_bs_read_int(bs, 16); + nbBytes += 8; + break; + } + while (nbBytes < DescSize) { + gf_bs_read_int(bs, 8); + nbBytes ++; + } + return GF_OK; +} + +GF_Err gf_odf_size_auxvid(GF_AuxVideoDescriptor *ld, u32 *outSize) +{ + if (!ld) return GF_BAD_PARAM; + switch (ld->aux_video_type) { + case 0x10: + *outSize = 5; + break; + case 0x11: + *outSize = 11; + break; + default: + *outSize = 3; + break; + } + return GF_OK; +} + +GF_Err gf_odf_write_auxvid(GF_BitStream *bs, GF_AuxVideoDescriptor *ld) +{ + GF_Err e; + u32 size; + if (!ld) return GF_BAD_PARAM; + e = gf_odf_size_descriptor((GF_Descriptor *)ld, &size); + if (e) return e; + e = gf_odf_write_base_descriptor(bs, ld->tag, size); + if (e) return e; + + gf_bs_write_int(bs, ld->aux_video_type, 8); + gf_bs_write_int(bs, ld->position_offset_h, 8); + gf_bs_write_int(bs, ld->position_offset_v, 8); + switch (ld->aux_video_type) { + case 0x10: + gf_bs_write_int(bs, ld->kfar, 8); + gf_bs_write_int(bs, ld->knear, 8); + break; + case 0x11: + gf_bs_write_int(bs, ld->parallax_zero, 16); + gf_bs_write_int(bs, ld->parallax_scale, 16); + gf_bs_write_int(bs, ld->dref, 16); + gf_bs_write_int(bs, ld->wref, 16); + break; + } + return GF_OK; +} + + + +GF_Descriptor *gf_odf_new_muxinfo() +{ + GF_MuxInfo *newDesc = (GF_MuxInfo *) gf_malloc(sizeof(GF_MuxInfo)); + if (!newDesc) return NULL; + memset(newDesc, 0, sizeof(GF_MuxInfo)); + newDesc->tag = GF_ODF_MUXINFO_TAG; + return (GF_Descriptor *) newDesc; +} + +GF_Err gf_odf_del_muxinfo(GF_MuxInfo *mi) +{ + if (!mi) return GF_BAD_PARAM; + if (mi->file_name) gf_free(mi->file_name); + if (mi->streamFormat) gf_free(mi->streamFormat); + if (mi->textNode) gf_free(mi->textNode); + if (mi->fontNode) gf_free(mi->fontNode); + gf_free(mi); + return GF_OK; +} + +GF_Err gf_odf_read_muxinfo(GF_BitStream *bs, GF_MuxInfo *mi, u32 DescSize) +{ + return GF_OK; +} +GF_Err gf_odf_size_muxinfo(GF_MuxInfo *mi, u32 *outSize) +{ + *outSize = 0; + return GF_OK; +} +GF_Err gf_odf_write_muxinfo(GF_BitStream *bs, GF_MuxInfo *mi) +{ + return GF_OK; +} + +GF_Descriptor *gf_odf_New_ElemMask() +{ + GF_ElementaryMask *newDesc = (GF_ElementaryMask*) gf_malloc (sizeof(GF_ElementaryMask)); + if (!newDesc) return NULL; + memset(newDesc, 0, sizeof(GF_ElementaryMask)); + newDesc->tag = GF_ODF_ELEM_MASK_TAG; + return (GF_Descriptor *) newDesc; +} + +GF_Err gf_odf_del_ElemMask(GF_ElementaryMask *desc) +{ + if (desc->node_name) gf_free(desc->node_name); + gf_free(desc); + return GF_OK; +} + +GF_Descriptor *gf_odf_new_bifs_cfg() +{ + GF_BIFSConfig *newDesc = (GF_BIFSConfig *) gf_malloc(sizeof(GF_BIFSConfig)); + if (!newDesc) return NULL; + memset(newDesc, 0, sizeof(GF_BIFSConfig)); + newDesc->tag = GF_ODF_BIFS_CFG_TAG; + return (GF_Descriptor *) newDesc; +} + +GF_Err gf_odf_del_bifs_cfg(GF_BIFSConfig *desc) +{ + if (desc->elementaryMasks) { + u32 i, count = gf_list_count(desc->elementaryMasks); + for (i=0; ielementaryMasks, i); + if (tmp->node_name) gf_free(tmp->node_name); + gf_free(tmp); + } + gf_list_del(desc->elementaryMasks); + } + gf_free(desc); + return GF_OK; +} + +GF_Descriptor *gf_odf_new_laser_cfg() +{ + GF_LASERConfig *newDesc = (GF_LASERConfig *) gf_malloc(sizeof(GF_LASERConfig)); + if (!newDesc) return NULL; + memset(newDesc, 0, sizeof(GF_LASERConfig)); + newDesc->tag = GF_ODF_LASER_CFG_TAG; + return (GF_Descriptor *) newDesc; +} + +GF_Err gf_odf_del_laser_cfg(GF_LASERConfig *desc) +{ + gf_free(desc); + return GF_OK; +} + +GF_Descriptor *gf_odf_new_ui_cfg() +{ + GF_UIConfig *newDesc = (GF_UIConfig *) gf_malloc(sizeof(GF_UIConfig)); + if (!newDesc) return NULL; + memset(newDesc, 0, sizeof(GF_UIConfig)); + newDesc->tag = GF_ODF_UI_CFG_TAG; + return (GF_Descriptor *) newDesc; +} + +GF_Err gf_odf_del_ui_cfg(GF_UIConfig *desc) +{ + if (desc->deviceName) gf_free(desc->deviceName); + if (desc->ui_data) gf_free(desc->ui_data); + gf_free(desc); + return GF_OK; +} + +#ifndef GPAC_MINIMAL_ODF + + + + + + +GF_Descriptor *gf_odf_new_cc() +{ + GF_CCDescriptor *newDesc = (GF_CCDescriptor *) gf_malloc(sizeof(GF_CCDescriptor)); + if (!newDesc) return NULL; + + newDesc->contentClassificationData = NULL; + newDesc->dataLength = 0; + newDesc->classificationEntity = 0; + newDesc->classificationTable = 0; + newDesc->tag = GF_ODF_CC_TAG; + return (GF_Descriptor *) newDesc; +} + +GF_Err gf_odf_del_cc(GF_CCDescriptor *ccd) +{ + if (!ccd) return GF_BAD_PARAM; + if (ccd->contentClassificationData) gf_free(ccd->contentClassificationData); + gf_free(ccd); + return GF_OK; +} + +GF_Err gf_odf_read_cc(GF_BitStream *bs, GF_CCDescriptor *ccd, u32 DescSize) +{ + u32 nbBytes = 0; + if (!ccd) return GF_BAD_PARAM; + + ccd->classificationEntity = gf_bs_read_int(bs, 32); + ccd->classificationTable = gf_bs_read_int(bs, 16); + nbBytes += 6; + ccd->dataLength = DescSize - 6; + ccd->contentClassificationData = (char*)gf_malloc(sizeof(char) * ccd->dataLength); + if (!ccd->contentClassificationData) return GF_OUT_OF_MEM; + gf_bs_read_data(bs, ccd->contentClassificationData, ccd->dataLength); + nbBytes += ccd->dataLength; + + if (DescSize != nbBytes) return GF_ODF_INVALID_DESCRIPTOR; + return GF_OK; +} + +GF_Err gf_odf_size_cc(GF_CCDescriptor *ccd, u32 *outSize) +{ + if (!ccd) return GF_BAD_PARAM; + *outSize = 6 + ccd->dataLength; + return GF_OK; +} + +GF_Err gf_odf_write_cc(GF_BitStream *bs, GF_CCDescriptor *ccd) +{ + u32 size; + GF_Err e; + if (!ccd) return GF_BAD_PARAM; + + e = gf_odf_size_descriptor((GF_Descriptor *)ccd, &size); + if (e) return e; + e = gf_odf_write_base_descriptor(bs, ccd->tag, size); + if (e) return e; + gf_bs_write_int(bs, ccd->classificationEntity, 32); + gf_bs_write_int(bs, ccd->classificationTable, 16); + gf_bs_write_data(bs, ccd->contentClassificationData, ccd->dataLength); + return GF_OK; +} + +GF_Descriptor *gf_odf_new_cc_date() +{ + GF_CC_Date *newDesc = (GF_CC_Date *) gf_malloc(sizeof(GF_CC_Date)); + if (!newDesc) return NULL; + memset(newDesc->contentCreationDate, 0, 5); + newDesc->tag = GF_ODF_CC_DATE_TAG; + return (GF_Descriptor *) newDesc; +} + + +GF_Err gf_odf_del_cc_date(GF_CC_Date *cdd) +{ + if (!cdd) return GF_BAD_PARAM; + gf_free(cdd); + return GF_OK; +} + +GF_Err gf_odf_read_cc_date(GF_BitStream *bs, GF_CC_Date *cdd, u32 DescSize) +{ + u32 nbBytes = 0; + if (!cdd) return GF_BAD_PARAM; + + gf_bs_read_data(bs, cdd->contentCreationDate, DATE_CODING_BIT_LEN); + nbBytes += DATE_CODING_BIT_LEN / 8; + if (DescSize != nbBytes) return GF_ODF_INVALID_DESCRIPTOR; + return GF_OK; +} + +GF_Err gf_odf_size_cc_date(GF_CC_Date *cdd, u32 *outSize) +{ + if (!cdd) return GF_BAD_PARAM; + *outSize = (DATE_CODING_BIT_LEN / 8); + return GF_OK; +} + +GF_Err gf_odf_write_cc_date(GF_BitStream *bs, GF_CC_Date *cdd) +{ + u32 size; + GF_Err e; + if (!cdd) return GF_BAD_PARAM; + + e = gf_odf_size_descriptor((GF_Descriptor *)cdd, &size); + if (e) return e; + e = gf_odf_write_base_descriptor(bs, cdd->tag, size); + if (e) return e; + + gf_bs_write_data(bs, cdd->contentCreationDate , DATE_CODING_BIT_LEN); + return GF_OK; +} + +GF_Descriptor *gf_odf_new_cc_name() +{ + GF_CC_Name *newDesc = (GF_CC_Name *) gf_malloc(sizeof(GF_CC_Name)); + if (!newDesc) return NULL; + + newDesc->ContentCreators = gf_list_new(); + if (! newDesc->ContentCreators) { + gf_free(newDesc); + return NULL; + } + newDesc->tag = GF_ODF_CC_NAME_TAG; + return (GF_Descriptor *) newDesc; +} + +GF_Err gf_odf_del_cc_name(GF_CC_Name *cnd) +{ + u32 i; + GF_ContentCreatorInfo *tmp; + if (!cnd) return GF_BAD_PARAM; + + i=0; + while ((tmp = (GF_ContentCreatorInfo *)gf_list_enum(cnd->ContentCreators, &i))) { + if (tmp->contentCreatorName) gf_free(tmp->contentCreatorName); + gf_free(tmp); + } + gf_list_del(cnd->ContentCreators); + gf_free(cnd); + return GF_OK; +} + +GF_Err gf_odf_read_cc_name(GF_BitStream *bs, GF_CC_Name *cnd, u32 DescSize) +{ + GF_Err e; + u32 i, count, len, nbBytes = 0; + if (!cnd) return GF_BAD_PARAM; + + count = gf_bs_read_int(bs, 8); + nbBytes += 1; + for (i = 0; i< count; i++) { + GF_ContentCreatorInfo *tmp = (GF_ContentCreatorInfo*)gf_malloc(sizeof(GF_ContentCreatorInfo)); + if (! tmp) return GF_OUT_OF_MEM; + memset(tmp , 0, sizeof(GF_ContentCreatorInfo)); + tmp->langCode = gf_bs_read_int(bs, 24); + tmp->isUTF8 = gf_bs_read_int(bs, 1); + /*aligned = */gf_bs_read_int(bs, 7); + nbBytes += 4; + + e = OD_ReadUTF8String(bs, & tmp->contentCreatorName, tmp->isUTF8, &len); + if (e) return e; + nbBytes += len; + e = gf_list_add(cnd->ContentCreators, tmp); + } + if (DescSize != nbBytes) return GF_ODF_INVALID_DESCRIPTOR; + return GF_OK; +} + +GF_Err gf_odf_size_cc_name(GF_CC_Name *cnd, u32 *outSize) +{ + u32 i; + GF_ContentCreatorInfo *tmp; + if (!cnd) return GF_BAD_PARAM; + + *outSize = 1; + i=0; + while ((tmp = (GF_ContentCreatorInfo *)gf_list_enum(cnd->ContentCreators, &i))) { + *outSize += 4 + OD_SizeUTF8String(tmp->contentCreatorName, tmp->isUTF8); + } + return GF_OK; +} + +GF_Err gf_odf_write_cc_name(GF_BitStream *bs, GF_CC_Name *cnd) +{ + GF_Err e; + GF_ContentCreatorInfo *tmp; + u32 i, size; + if (!cnd) return GF_BAD_PARAM; + + e = gf_odf_size_descriptor((GF_Descriptor *)cnd, &size); + if (e) return e; + e = gf_odf_write_base_descriptor(bs, cnd->tag, size); + if (e) return e; + gf_bs_write_int(bs, gf_list_count(cnd->ContentCreators), 8); + + i=0; + while ((tmp = (GF_ContentCreatorInfo *)gf_list_enum(cnd->ContentCreators, &i))) { + gf_bs_write_int(bs, tmp->langCode, 24); + gf_bs_write_int(bs, tmp->isUTF8, 1); + gf_bs_write_int(bs, 0, 7); //aligned + OD_WriteUTF8String(bs, tmp->contentCreatorName, tmp->isUTF8); + } + return GF_OK; +} + + +GF_Descriptor *gf_odf_new_ci() +{ + GF_CIDesc *newDesc = (GF_CIDesc *) gf_malloc(sizeof(GF_CIDesc)); + if (!newDesc) return NULL; + + newDesc->compatibility = 0; + newDesc->contentIdentifier = NULL; + newDesc->tag = GF_ODF_CI_TAG; + newDesc->contentIdentifierFlag = 0; + newDesc->contentIdentifierType = 0; + newDesc->contentType = 0; + newDesc->contentTypeFlag = 0; + newDesc->protectedContent = 0; + return (GF_Descriptor *) newDesc; +} + + +GF_Err gf_odf_del_ci(GF_CIDesc *cid) +{ + if (!cid) return GF_BAD_PARAM; + + if (cid->contentIdentifier) gf_free(cid->contentIdentifier); + gf_free(cid); + return GF_OK; +} + + +GF_Err gf_odf_read_ci(GF_BitStream *bs, GF_CIDesc *cid, u32 DescSize) +{ + u32 nbBytes = 0; + if (! cid) return GF_BAD_PARAM; + + cid->compatibility = gf_bs_read_int(bs, 2); //MUST BE NULL + if (cid->compatibility) return GF_ODF_INVALID_DESCRIPTOR; + + cid->contentTypeFlag = gf_bs_read_int(bs, 1); + cid->contentIdentifierFlag = gf_bs_read_int(bs, 1); + cid->protectedContent = gf_bs_read_int(bs, 1); + /*reserved = */gf_bs_read_int(bs, 3); + nbBytes += 1; + + if (cid->contentTypeFlag) { + cid->contentType = gf_bs_read_int(bs, 8); + nbBytes += 1; + } + if (cid->contentIdentifierFlag) { + cid->contentIdentifierType = gf_bs_read_int(bs, 8); + cid->contentIdentifier = (char*)gf_malloc(DescSize - 2 - cid->contentTypeFlag); + if (! cid->contentIdentifier) return GF_OUT_OF_MEM; + + gf_bs_read_data(bs, cid->contentIdentifier, DescSize - 2 - cid->contentTypeFlag); + nbBytes += DescSize - 1 - cid->contentTypeFlag; + } + if (nbBytes != DescSize) return GF_ODF_INVALID_DESCRIPTOR; + return GF_OK; +} + +GF_Err gf_odf_size_ci(GF_CIDesc *cid, u32 *outSize) +{ + if (! cid) return GF_BAD_PARAM; + + *outSize = 1; + if (cid->contentTypeFlag) *outSize += 1; + + if (cid->contentIdentifierFlag) + *outSize += strlen((const char*)cid->contentIdentifier) - 1 - cid->contentTypeFlag; + return GF_OK; +} + +GF_Err gf_odf_write_ci(GF_BitStream *bs, GF_CIDesc *cid) +{ + GF_Err e; + u32 size; + if (! cid) return GF_BAD_PARAM; + + e = gf_odf_size_descriptor((GF_Descriptor *)cid, &size); + if (e) return e; + e = gf_odf_write_base_descriptor(bs, cid->tag, size); + if (e) return e; + + gf_bs_write_int(bs, cid->compatibility, 2); + gf_bs_write_int(bs, cid->contentTypeFlag, 1); + gf_bs_write_int(bs, cid->contentIdentifierFlag, 1); + gf_bs_write_int(bs, cid->protectedContent, 1); + gf_bs_write_int(bs, 7, 3); //reserved 0b111 = 7 + + if (cid->contentTypeFlag) { + gf_bs_write_int(bs, cid->contentType, 8); + } + + if (cid->contentIdentifierFlag) { + gf_bs_write_int(bs, cid->contentIdentifierType, 8); + gf_bs_write_data(bs, cid->contentIdentifier, size - 2 - cid->contentTypeFlag); + } + return GF_OK; +} + +GF_Descriptor *gf_odf_new_exp_text() +{ + GF_ExpandedTextual *newDesc = (GF_ExpandedTextual *) gf_malloc(sizeof(GF_ExpandedTextual)); + if (!newDesc) return NULL; + + newDesc->itemDescriptionList = gf_list_new(); + if (! newDesc->itemDescriptionList) { + gf_free(newDesc); + return NULL; + } + newDesc->itemTextList = gf_list_new(); + if (! newDesc->itemTextList) { + gf_free(newDesc->itemDescriptionList); + gf_free(newDesc); + return NULL; + } + newDesc->isUTF8 = 0; + newDesc->langCode = 0; + newDesc->NonItemText = NULL; + newDesc->tag = GF_ODF_TEXT_TAG; + return (GF_Descriptor *) newDesc; +} + +GF_Err gf_odf_del_exp_text(GF_ExpandedTextual *etd) +{ + if (!etd) return GF_BAD_PARAM; + + while (gf_list_count(etd->itemDescriptionList)) { + GF_ETD_ItemText *tmp = (GF_ETD_ItemText*)gf_list_get(etd->itemDescriptionList, 0); + if (tmp) { + if (tmp->text) gf_free(tmp->text); + gf_free(tmp); + } + gf_list_rem(etd->itemDescriptionList, 0); + } + gf_list_del(etd->itemDescriptionList); + + while (gf_list_count(etd->itemTextList)) { + GF_ETD_ItemText *tmp = (GF_ETD_ItemText*)gf_list_get(etd->itemTextList, 0); + if (tmp) { + if (tmp->text) gf_free(tmp->text); + gf_free(tmp); + } + gf_list_rem(etd->itemTextList, 0); + } + gf_list_del(etd->itemTextList); + + if (etd->NonItemText) gf_free(etd->NonItemText); + gf_free(etd); + return GF_OK; +} + +GF_Err gf_odf_read_exp_text(GF_BitStream *bs, GF_ExpandedTextual *etd, u32 DescSize) +{ + GF_Err e; + u32 nbBytes = 0; + u32 i, len, nonLen, count; + if (!etd) return GF_BAD_PARAM; + + etd->langCode = gf_bs_read_int(bs, 24); + etd->isUTF8 = gf_bs_read_int(bs, 1); + /*aligned = */gf_bs_read_int(bs, 7); + count = gf_bs_read_int(bs, 8); + nbBytes += 5; + + for (i = 0; i< count; i++) { + //description + GF_ETD_ItemText *description, *Text; + description = (GF_ETD_ItemText*)gf_malloc(sizeof(GF_ETD_ItemText)); + if (! description) return GF_OUT_OF_MEM; + description->text = NULL; + e = OD_ReadUTF8String(bs, & description->text, etd->isUTF8, &len); + if (e) return e; + e = gf_list_add(etd->itemDescriptionList, description); + if (e) return e; + nbBytes += len; + + //text + Text = (GF_ETD_ItemText*)gf_malloc(sizeof(GF_ETD_ItemText)); + if (! Text) return GF_OUT_OF_MEM; + Text->text = NULL; + e = OD_ReadUTF8String(bs, & Text->text, etd->isUTF8, &len); + if (e) return e; + e = gf_list_add(etd->itemTextList, Text); + if (e) return e; + nbBytes += len; + } + len = gf_bs_read_int(bs, 8); + nbBytes += 1; + nonLen = 0; + while (len == 255) { + nonLen += len; + len = gf_bs_read_int(bs, 8); + nbBytes += 1; + } + nonLen += len; + if (nonLen) { + //here we have no choice but do the job ourselves + //because the length is not encoded on 8 bits + etd->NonItemText = (char *) gf_malloc(sizeof(char) * (1+nonLen) * (etd->isUTF8 ? 1 : 2)); + if (! etd->NonItemText) return GF_OUT_OF_MEM; + gf_bs_read_data(bs, etd->NonItemText, nonLen * (etd->isUTF8 ? 1 : 2)); + nbBytes += nonLen * (etd->isUTF8 ? 1 : 2); + } + if (nbBytes != DescSize) return GF_ODF_INVALID_DESCRIPTOR; + return GF_OK; +} + + +GF_Err gf_odf_size_exp_text(GF_ExpandedTextual *etd, u32 *outSize) +{ + u32 i, len, nonLen, lentmp, count; + GF_ETD_ItemText *tmp; + if (!etd) return GF_BAD_PARAM; + + *outSize = 5; + if (gf_list_count(etd->itemDescriptionList) != gf_list_count(etd->itemTextList)) return GF_ODF_INVALID_DESCRIPTOR; + + count = gf_list_count(etd->itemDescriptionList); + for (i=0; iitemDescriptionList, i); + *outSize += OD_SizeUTF8String(tmp->text, etd->isUTF8); + tmp = (GF_ETD_ItemText*)gf_list_get(etd->itemTextList, i); + *outSize += OD_SizeUTF8String(tmp->text, etd->isUTF8); + } + *outSize += 1; + if (etd->NonItemText) { + if (etd->isUTF8) { + nonLen = strlen((const char*)etd->NonItemText); + } else { + nonLen = gf_utf8_wcslen((const unsigned short*)etd->NonItemText); + } + } else { + nonLen = 0; + } + len = 255; + lentmp = nonLen; + if (lentmp < 255) { + len = lentmp; + } + while (len == 255) { + *outSize += 1; + lentmp -= 255; + if (lentmp < 255) { + len = lentmp; + } + } + *outSize += nonLen * (etd->isUTF8 ? 1 : 2); + return GF_OK; +} + +GF_Err gf_odf_write_exp_text(GF_BitStream *bs, GF_ExpandedTextual *etd) +{ + GF_Err e; + u32 size, i, len, nonLen, lentmp, count; + GF_ETD_ItemText *tmp; + if (!etd) return GF_BAD_PARAM; + + if (gf_list_count(etd->itemDescriptionList) != gf_list_count(etd->itemTextList)) return GF_ODF_INVALID_DESCRIPTOR; + + e = gf_odf_size_descriptor((GF_Descriptor *)etd, &size); + if (e) return e; + e = gf_odf_write_base_descriptor(bs, etd->tag, size); + if (e) return e; + + gf_bs_write_int(bs, etd->langCode, 24); + gf_bs_write_int(bs, etd->isUTF8, 1); + gf_bs_write_int(bs, 0, 7); //aligned + gf_bs_write_int(bs, gf_list_count(etd->itemDescriptionList), 8); + + count = gf_list_count(etd->itemDescriptionList); + for (i=0; iitemDescriptionList, i); + OD_WriteUTF8String(bs, tmp->text, etd->isUTF8); + tmp = (GF_ETD_ItemText*)gf_list_get(etd->itemTextList, i); + OD_WriteUTF8String(bs, tmp->text, etd->isUTF8); + } + if (etd->NonItemText) { + nonLen = strlen((const char*)etd->NonItemText) + 1; + if (etd->isUTF8) { + nonLen = strlen((const char*)etd->NonItemText); + } else { + nonLen = gf_utf8_wcslen((const unsigned short*)etd->NonItemText); + } + } else { + nonLen = 0; + } + lentmp = nonLen; + len = lentmp < 255 ? lentmp : 255; + while (len == 255) { + gf_bs_write_int(bs, len, 8); + lentmp -= len; + len = lentmp < 255 ? lentmp : 255; + } + gf_bs_write_int(bs, len, 8); + gf_bs_write_data(bs, etd->NonItemText, nonLen * (etd->isUTF8 ? 1 : 2)); + return GF_OK; +} + +GF_Descriptor *gf_odf_new_pl_ext() +{ + GF_PLExt *newDesc = (GF_PLExt *) gf_malloc(sizeof(GF_PLExt)); + if (!newDesc) return NULL; + newDesc->AudioProfileLevelIndication = 0; + newDesc->GraphicsProfileLevelIndication = 0; + newDesc->MPEGJProfileLevelIndication = 0; + newDesc->ODProfileLevelIndication = 0; + newDesc->profileLevelIndicationIndex = 0; + newDesc->SceneProfileLevelIndication = 0; + newDesc->VisualProfileLevelIndication = 0; + newDesc->tag = GF_ODF_EXT_PL_TAG; + return (GF_Descriptor *) newDesc; +} + +GF_Err gf_odf_del_pl_ext(GF_PLExt *pld) +{ + if (!pld) return GF_BAD_PARAM; + + gf_free(pld); + return GF_OK; +} + +GF_Err gf_odf_read_pl_ext(GF_BitStream *bs, GF_PLExt *pld, u32 DescSize) +{ + u32 nbBytes = 0; + if (!pld) return GF_BAD_PARAM; + + pld->profileLevelIndicationIndex = gf_bs_read_int(bs, 8); + pld->ODProfileLevelIndication = gf_bs_read_int(bs, 8); + pld->SceneProfileLevelIndication = gf_bs_read_int(bs, 8); + pld->AudioProfileLevelIndication = gf_bs_read_int(bs, 8); + pld->VisualProfileLevelIndication = gf_bs_read_int(bs, 8); + pld->GraphicsProfileLevelIndication = gf_bs_read_int(bs, 8); + pld->MPEGJProfileLevelIndication = gf_bs_read_int(bs, 8); + + nbBytes += 7; + if (nbBytes != DescSize) return GF_ODF_INVALID_DESCRIPTOR; + return GF_OK; +} + +GF_Err gf_odf_size_pl_ext(GF_PLExt *pld, u32 *outSize) +{ + if (!pld) return GF_BAD_PARAM; + + *outSize = 7; + return GF_OK; +} + +GF_Err gf_odf_write_pl_ext(GF_BitStream *bs, GF_PLExt *pld) +{ + GF_Err e; + u32 size; + if (!pld) return GF_BAD_PARAM; + + e = gf_odf_size_descriptor((GF_Descriptor *)pld, &size); + if (e) return e; + e = gf_odf_write_base_descriptor(bs, pld->tag, size); + if (e) return e; + + gf_bs_write_int(bs, pld->profileLevelIndicationIndex, 8); + gf_bs_write_int(bs, pld->ODProfileLevelIndication, 8); + gf_bs_write_int(bs, pld->SceneProfileLevelIndication, 8); + gf_bs_write_int(bs, pld->AudioProfileLevelIndication, 8); + gf_bs_write_int(bs, pld->VisualProfileLevelIndication, 8); + gf_bs_write_int(bs, pld->GraphicsProfileLevelIndication, 8); + gf_bs_write_int(bs, pld->MPEGJProfileLevelIndication, 8); + return GF_OK; +} + +GF_Descriptor *gf_odf_new_ipi_ptr() +{ + GF_IPIPtr *newDesc = (GF_IPIPtr *) gf_malloc(sizeof(GF_IPIPtr)); + if (!newDesc) return NULL; + newDesc->IPI_ES_Id = 0; + newDesc->tag = GF_ODF_IPI_PTR_TAG; + return (GF_Descriptor *) newDesc; +} + +GF_Err gf_odf_del_ipi_ptr(GF_IPIPtr *ipid) +{ + if (!ipid) return GF_BAD_PARAM; + gf_free(ipid); + return GF_OK; +} + +GF_Err gf_odf_read_ipi_ptr(GF_BitStream *bs, GF_IPIPtr *ipid, u32 DescSize) +{ + u32 nbBytes = 0; + if (! ipid) return GF_BAD_PARAM; + + ipid->IPI_ES_Id = gf_bs_read_int(bs, 16); + nbBytes += 2; + if (nbBytes != DescSize) return GF_ODF_INVALID_DESCRIPTOR; + return GF_OK; +} + +GF_Err gf_odf_size_ipi_ptr(GF_IPIPtr *ipid, u32 *outSize) +{ + if (! ipid) return GF_BAD_PARAM; + *outSize = 2; + return GF_OK; +} + +GF_Err gf_odf_write_ipi_ptr(GF_BitStream *bs, GF_IPIPtr *ipid) +{ + GF_Err e; + u32 size; + if (! ipid) return GF_BAD_PARAM; + e = gf_odf_size_descriptor((GF_Descriptor *)ipid, &size); + if (e) return e; + e = gf_odf_write_base_descriptor(bs, ipid->tag, size); + if (e) return e; + gf_bs_write_int(bs, ipid->IPI_ES_Id, 16); + return GF_OK; +} + +GF_Descriptor *gf_odf_new_ipmp() +{ + GF_IPMP_Descriptor *newDesc; + GF_SAFEALLOC(newDesc, GF_IPMP_Descriptor); + if (!newDesc) return NULL; + + newDesc->ipmpx_data = gf_list_new(); + newDesc->tag = GF_ODF_IPMP_TAG; + return (GF_Descriptor *) newDesc; +} +GF_Err gf_odf_del_ipmp(GF_IPMP_Descriptor *ipmp) +{ + if (!ipmp) return GF_BAD_PARAM; + if (ipmp->opaque_data) gf_free(ipmp->opaque_data); + /*TODO DELETE IPMPX*/ + while (gf_list_count(ipmp->ipmpx_data)) { + GF_IPMPX_Data *p = (GF_IPMPX_Data *)gf_list_get(ipmp->ipmpx_data, 0); + gf_list_rem(ipmp->ipmpx_data, 0); + gf_ipmpx_data_del(p); + } + gf_list_del(ipmp->ipmpx_data); + gf_free(ipmp); + return GF_OK; +} + +GF_Err gf_odf_read_ipmp(GF_BitStream *bs, GF_IPMP_Descriptor *ipmp, u32 DescSize) +{ + u32 size; + u64 nbBytes = 0; + if (!ipmp) return GF_BAD_PARAM; + + ipmp->IPMP_DescriptorID = gf_bs_read_int(bs, 8); + ipmp->IPMPS_Type = gf_bs_read_int(bs, 16); + nbBytes += 3; + size = DescSize - 3; + + /*IPMPX escape*/ + if ((ipmp->IPMP_DescriptorID==0xFF) && (ipmp->IPMPS_Type==0xFFFF)) { + ipmp->IPMP_DescriptorIDEx = gf_bs_read_int(bs, 16); + gf_bs_read_data(bs, (char*)ipmp->IPMP_ToolID, 16); + ipmp->control_point = gf_bs_read_int(bs, 8); + nbBytes += 19; + if (ipmp->control_point) { + ipmp->cp_sequence_code = gf_bs_read_int(bs, 8); + nbBytes += 1; + } + while (nbBytesipmpx_data, p); + nbBytes += gf_bs_get_position(bs) - pos; + } + } + /*URL*/ + else if (! ipmp->IPMPS_Type) { + ipmp->opaque_data = (char*)gf_malloc(size + 1); + if (! ipmp->opaque_data) return GF_OUT_OF_MEM; + gf_bs_read_data(bs, ipmp->opaque_data, size); + nbBytes += size; + ipmp->opaque_data[size] = 0; + ipmp->opaque_data_size = size; + + } + /*data*/ + else { + ipmp->opaque_data_size = size; + ipmp->opaque_data = (char*)gf_malloc(size); + if (! ipmp->opaque_data) return GF_OUT_OF_MEM; + gf_bs_read_data(bs, ipmp->opaque_data, size); + nbBytes += size; + } + if (nbBytes != DescSize) return GF_ODF_INVALID_DESCRIPTOR; + return GF_OK; +} + +GF_Err gf_odf_size_ipmp(GF_IPMP_Descriptor *ipmp, u32 *outSize) +{ + u32 i, s; + if (!ipmp) return GF_BAD_PARAM; + + *outSize = 3; + /*IPMPX escape*/ + if ((ipmp->IPMP_DescriptorID==0xFF) && (ipmp->IPMPS_Type==0xFFFF)) { + GF_IPMPX_Data *p; + *outSize += 19; + if (ipmp->control_point) *outSize += 1; + s = 0; + i=0; + while ((p = (GF_IPMPX_Data *)gf_list_enum(ipmp->ipmpx_data, &i))) { + s += gf_ipmpx_data_full_size(p); + } + (*outSize) += s; + } + else if (! ipmp->IPMPS_Type) { + if (!ipmp->opaque_data) return GF_ODF_INVALID_DESCRIPTOR; + *outSize += strlen(ipmp->opaque_data); + } else { + *outSize += ipmp->opaque_data_size; + } + return GF_OK; +} + +GF_Err gf_odf_write_ipmp(GF_BitStream *bs, GF_IPMP_Descriptor *ipmp) +{ + GF_Err e; + u32 size, i; + if (!ipmp) return GF_BAD_PARAM; + + e = gf_odf_size_descriptor((GF_Descriptor *)ipmp, &size); + if (e) return e; + e = gf_odf_write_base_descriptor(bs, ipmp->tag, size); + if (e) return e; + gf_bs_write_int(bs, ipmp->IPMP_DescriptorID, 8); + gf_bs_write_int(bs, ipmp->IPMPS_Type, 16); + + if ((ipmp->IPMP_DescriptorID==0xFF) && (ipmp->IPMPS_Type==0xFFFF)) { + GF_IPMPX_Data *p; + gf_bs_write_int(bs, ipmp->IPMP_DescriptorIDEx, 16); + gf_bs_write_data(bs, (char*)ipmp->IPMP_ToolID, 16); + gf_bs_write_int(bs, ipmp->control_point, 8); + if (ipmp->control_point) gf_bs_write_int(bs, ipmp->cp_sequence_code, 8); + + i=0; + while ((p = (GF_IPMPX_Data *) gf_list_enum(ipmp->ipmpx_data, &i))) { + gf_ipmpx_data_write(bs, p); + } + } + else if (!ipmp->IPMPS_Type) { + if (!ipmp->opaque_data) return GF_ODF_INVALID_DESCRIPTOR; + gf_bs_write_data(bs, ipmp->opaque_data, strlen(ipmp->opaque_data)); + } else { + gf_bs_write_data(bs, ipmp->opaque_data, ipmp->opaque_data_size); + } + return GF_OK; +} + +GF_Descriptor *gf_odf_new_ipmp_ptr() +{ + GF_IPMPPtr *newDesc; + GF_SAFEALLOC(newDesc, GF_IPMPPtr); + if (!newDesc) return NULL; + newDesc->tag = GF_ODF_IPMP_PTR_TAG; + return (GF_Descriptor *) newDesc; +} +GF_Err gf_odf_del_ipmp_ptr(GF_IPMPPtr *ipmpd) +{ + if (!ipmpd) return GF_BAD_PARAM; + gf_free(ipmpd); + return GF_OK; +} + +GF_Err gf_odf_read_ipmp_ptr(GF_BitStream *bs, GF_IPMPPtr *ipmpd, u32 DescSize) +{ + u32 nbBytes = 0; + if (! ipmpd) return GF_BAD_PARAM; + ipmpd->IPMP_DescriptorID = gf_bs_read_int(bs, 8); + nbBytes += 1; + if (ipmpd->IPMP_DescriptorID == 0xFF) { + ipmpd->IPMP_DescriptorIDEx = gf_bs_read_int(bs, 16); + ipmpd->IPMP_ES_ID = gf_bs_read_int(bs, 16); + nbBytes += 4; + } + if (nbBytes != DescSize) return GF_ODF_INVALID_DESCRIPTOR; + return GF_OK; +} +GF_Err gf_odf_size_ipmp_ptr(GF_IPMPPtr *ipmpd, u32 *outSize) +{ + if (! ipmpd) return GF_BAD_PARAM; + *outSize = 1; + if (ipmpd->IPMP_DescriptorID == 0xFF) *outSize += 4; + return GF_OK; +} +GF_Err gf_odf_write_ipmp_ptr(GF_BitStream *bs, GF_IPMPPtr *ipmpd) +{ + GF_Err e; + u32 size; + if (! ipmpd) return GF_BAD_PARAM; + + e = gf_odf_size_descriptor((GF_Descriptor *)ipmpd, &size); + if (e) return e; + e = gf_odf_write_base_descriptor(bs, ipmpd->tag, size); + if (e) return e; + gf_bs_write_int(bs, ipmpd->IPMP_DescriptorID, 8); + if (ipmpd->IPMP_DescriptorID == 0xFF) { + gf_bs_write_int(bs, ipmpd->IPMP_DescriptorIDEx, 16); + gf_bs_write_int(bs, ipmpd->IPMP_ES_ID, 16); + } + return GF_OK; +} + +GF_Descriptor *gf_odf_new_kw() +{ + GF_KeyWord *newDesc = (GF_KeyWord *) gf_malloc(sizeof(GF_KeyWord)); + if (!newDesc) return NULL; + + newDesc->keyWordsList = gf_list_new(); + if (! newDesc->keyWordsList) { + gf_free(newDesc); + return NULL; + } + newDesc->isUTF8 = 0; + newDesc->languageCode = 0; + newDesc->tag = GF_ODF_KW_TAG; + return (GF_Descriptor *) newDesc; +} + +GF_Err gf_odf_del_kw(GF_KeyWord *kwd) +{ + if (!kwd) return GF_BAD_PARAM; + + while (gf_list_count(kwd->keyWordsList)) { + GF_KeyWordItem *tmp = (GF_KeyWordItem*)gf_list_get(kwd->keyWordsList, 0); + if (tmp) { + if (tmp->keyWord) gf_free(tmp->keyWord); + gf_free(tmp); + } + } + gf_list_del(kwd->keyWordsList); + gf_free(kwd); + return GF_OK; +} + +GF_Err gf_odf_read_kw(GF_BitStream *bs, GF_KeyWord *kwd, u32 DescSize) +{ + GF_Err e; + u32 nbBytes = 0, i, kwcount, len; + if (!kwd) return GF_BAD_PARAM; + + kwd->languageCode = gf_bs_read_int(bs, 24); + kwd->isUTF8 = gf_bs_read_int(bs, 1); + /*aligned = */gf_bs_read_int(bs, 7); + kwcount = gf_bs_read_int(bs, 8); + nbBytes += 5; + + for (i = 0 ; i < kwcount; i++) { + GF_KeyWordItem *tmp = (GF_KeyWordItem*)gf_malloc(sizeof(GF_KeyWordItem)); + if (! tmp) return GF_OUT_OF_MEM; + e = OD_ReadUTF8String(bs, & tmp->keyWord, kwd->isUTF8, &len); + if (e) return e; + e = gf_list_add(kwd->keyWordsList, tmp); + if (e) return e; + nbBytes += len; + } + if (nbBytes != DescSize) return GF_ODF_INVALID_DESCRIPTOR; + return GF_OK; +} + + +GF_Err gf_odf_size_kw(GF_KeyWord *kwd, u32 *outSize) +{ + u32 i; + GF_KeyWordItem *tmp; + if (!kwd) return GF_BAD_PARAM; + + *outSize = 5; + i=0; + while ((tmp = (GF_KeyWordItem *)gf_list_enum(kwd->keyWordsList, &i))) { + *outSize += OD_SizeUTF8String(tmp->keyWord, kwd->isUTF8); + } + return GF_OK; +} +GF_Err gf_odf_write_kw(GF_BitStream *bs, GF_KeyWord *kwd) +{ + GF_Err e; + u32 size, i; + GF_KeyWordItem *tmp; + if (!kwd) return GF_BAD_PARAM; + + e = gf_odf_size_descriptor((GF_Descriptor *)kwd, &size); + assert(e == GF_OK); + e = gf_odf_write_base_descriptor(bs, kwd->tag, size); + assert(e == GF_OK); + + gf_bs_write_int(bs, kwd->languageCode, 24); + gf_bs_write_int(bs, kwd->isUTF8, 1); + gf_bs_write_int(bs, 0, 7); //aligned(8) + gf_bs_write_int(bs, gf_list_count(kwd->keyWordsList), 8); + + i=0; + while ((tmp = (GF_KeyWordItem *)gf_list_enum(kwd->keyWordsList, &i))) { + OD_WriteUTF8String(bs, tmp->keyWord, kwd->isUTF8); + } + return GF_OK; +} + +GF_Descriptor *gf_odf_new_oci_date() +{ + GF_OCI_Data *newDesc = (GF_OCI_Data *) gf_malloc(sizeof(GF_OCI_Data)); + if (!newDesc) return NULL; + memset(newDesc->OCICreationDate, 0, 5); + newDesc->tag = GF_ODF_OCI_DATE_TAG; + return (GF_Descriptor *) newDesc; +} + +GF_Err gf_odf_del_oci_date(GF_OCI_Data *ocd) +{ + if (!ocd) return GF_BAD_PARAM; + gf_free(ocd); + return GF_OK; +} + +GF_Err gf_odf_read_oci_date(GF_BitStream *bs, GF_OCI_Data *ocd, u32 DescSize) +{ + u32 nbBytes = 0; + if (!ocd) return GF_BAD_PARAM; + + gf_bs_read_data(bs, ocd->OCICreationDate, DATE_CODING_BIT_LEN); + nbBytes += DATE_CODING_BIT_LEN / 8; + if (nbBytes != DescSize) return GF_ODF_INVALID_DESCRIPTOR; + return GF_OK; +} + +GF_Err gf_odf_size_oci_date(GF_OCI_Data *ocd, u32 *outSize) +{ + if (!ocd) return GF_BAD_PARAM; + *outSize = (DATE_CODING_BIT_LEN / 8); + return GF_OK; +} + +GF_Err gf_odf_write_oci_date(GF_BitStream *bs, GF_OCI_Data *ocd) +{ + GF_Err e; + u32 size; + if (!ocd) return GF_BAD_PARAM; + + e = gf_odf_size_descriptor((GF_Descriptor *)ocd, &size); + if (e) return e; + e = gf_odf_write_base_descriptor(bs, ocd->tag, size); + if (e) return e; + gf_bs_write_data(bs, ocd->OCICreationDate , DATE_CODING_BIT_LEN); + return GF_OK; +} + +GF_Descriptor *gf_odf_new_oci_name() +{ + GF_OCICreators *newDesc = (GF_OCICreators *) gf_malloc(sizeof(GF_OCICreators)); + if (!newDesc) return NULL; + + newDesc->OCICreators = gf_list_new(); + if (! newDesc->OCICreators) { + gf_free(newDesc); + return NULL; + } + newDesc->tag = GF_ODF_OCI_NAME_TAG; + return (GF_Descriptor *) newDesc; +} +GF_Err gf_odf_del_oci_name(GF_OCICreators *ocn) +{ + u32 i; + GF_OCICreator_item *tmp; + if (!ocn) return GF_BAD_PARAM; + + i=0; + while ((tmp = (GF_OCICreator_item *)gf_list_enum(ocn->OCICreators, &i))) { + if (tmp->OCICreatorName) gf_free(tmp->OCICreatorName); + gf_free(tmp); + } + gf_list_del(ocn->OCICreators); + gf_free(ocn); + return GF_OK; +} + +GF_Err gf_odf_read_oci_name(GF_BitStream *bs, GF_OCICreators *ocn, u32 DescSize) +{ + GF_Err e; + u32 nbBytes = 0; + u32 i, count, len; + if (!ocn) return GF_BAD_PARAM; + + count = gf_bs_read_int(bs, 8); + nbBytes += 1; + for (i = 0; i< count; i++) { + GF_OCICreator_item *tmp = (GF_OCICreator_item*)gf_malloc(sizeof(GF_OCICreator_item)); + if (! tmp) return GF_OUT_OF_MEM; + tmp->langCode = gf_bs_read_int(bs, 24); + tmp->isUTF8 = gf_bs_read_int(bs, 1); + /*aligned = */gf_bs_read_int(bs, 7); + nbBytes += 4; + e = OD_ReadUTF8String(bs, & tmp->OCICreatorName, tmp->isUTF8, &len); + if (e) return e; + nbBytes += len; + e = gf_list_add(ocn->OCICreators, tmp); + if (e) return e; + } + if (nbBytes != DescSize) return GF_ODF_INVALID_DESCRIPTOR; + return GF_OK; +} + +GF_Err gf_odf_size_oci_name(GF_OCICreators *ocn, u32 *outSize) +{ + u32 i; + GF_OCICreator_item *tmp; + if (!ocn) return GF_BAD_PARAM; + + *outSize = 1; + i=0; + while ((tmp = (GF_OCICreator_item *)gf_list_enum(ocn->OCICreators, &i))) { + *outSize += 4 + OD_SizeUTF8String(tmp->OCICreatorName, tmp->isUTF8); + } + return GF_OK; +} + +GF_Err gf_odf_write_oci_name(GF_BitStream *bs, GF_OCICreators *ocn) +{ + GF_Err e; + u32 size; + u32 i; + GF_OCICreator_item *tmp; + if (!ocn) return GF_BAD_PARAM; + + e = gf_odf_size_descriptor((GF_Descriptor *)ocn, &size); + if (e) return e; + e = gf_odf_write_base_descriptor(bs, ocn->tag, size); + if (e) return e; + gf_bs_write_int(bs, gf_list_count(ocn->OCICreators), 8); + + i=0; + while ((tmp = (GF_OCICreator_item *)gf_list_enum(ocn->OCICreators, &i))) { + gf_bs_write_int(bs, tmp->langCode, 24); + gf_bs_write_int(bs, tmp->isUTF8, 1); + gf_bs_write_int(bs, 0, 7); //aligned + gf_bs_write_int(bs, strlen(tmp->OCICreatorName) , 8); + OD_WriteUTF8String(bs, tmp->OCICreatorName, tmp->isUTF8); + } + return GF_OK; +} + + +GF_Descriptor *gf_odf_new_pl_idx() +{ + GF_PL_IDX *newDesc = (GF_PL_IDX *) gf_malloc(sizeof(GF_PL_IDX)); + if (!newDesc) return NULL; + newDesc->profileLevelIndicationIndex = 0; + newDesc->tag = GF_ODF_PL_IDX_TAG; + return (GF_Descriptor *) newDesc; +} + +GF_Err gf_odf_del_pl_idx(GF_PL_IDX *plid) +{ + if (!plid) return GF_BAD_PARAM; + gf_free(plid); + return GF_OK; +} + +GF_Err gf_odf_read_pl_idx(GF_BitStream *bs, GF_PL_IDX *plid, u32 DescSize) +{ + u32 nbBytes = 0; + if (!plid) return GF_BAD_PARAM; + + plid->profileLevelIndicationIndex = gf_bs_read_int(bs, 8); + nbBytes += 1; + if (nbBytes != DescSize) return GF_ODF_INVALID_DESCRIPTOR; + return GF_OK; +} +GF_Err gf_odf_size_pl_idx(GF_PL_IDX *plid, u32 *outSize) +{ + if (!plid) return GF_BAD_PARAM; + *outSize = 1; + return GF_OK; +} +GF_Err gf_odf_write_pl_idx(GF_BitStream *bs, GF_PL_IDX *plid) +{ + GF_Err e; + u32 size; + if (!plid) return GF_BAD_PARAM; + e = gf_odf_size_descriptor((GF_Descriptor *)plid, &size); + if (e) return e; + e = gf_odf_write_base_descriptor(bs, plid->tag, size); + if (e) return e; + gf_bs_write_int(bs, plid->profileLevelIndicationIndex, 8); + return GF_OK; +} + + +GF_Descriptor *gf_odf_new_rating() +{ + GF_Rating *newDesc = (GF_Rating *) gf_malloc(sizeof(GF_Rating)); + if (!newDesc) return NULL; + + newDesc->infoLength = 0; + newDesc->ratingInfo = NULL; + newDesc->ratingCriteria = 0; + newDesc->ratingEntity = 0; + newDesc->tag = GF_ODF_RATING_TAG; + return (GF_Descriptor *) newDesc; +} + +GF_Err gf_odf_del_rating(GF_Rating *rd) +{ + if (!rd) return GF_BAD_PARAM; + + if (rd->ratingInfo) gf_free(rd->ratingInfo); + gf_free(rd); + return GF_OK; +} + +GF_Err gf_odf_read_rating(GF_BitStream *bs, GF_Rating *rd, u32 DescSize) +{ + u32 nbBytes = 0; + if (!rd) return GF_BAD_PARAM; + + rd->ratingEntity = gf_bs_read_int(bs, 32); + rd->ratingCriteria = gf_bs_read_int(bs, 16); + rd->infoLength = DescSize - 6; + nbBytes += 6; + + rd->ratingInfo = (char*)gf_malloc(rd->infoLength); + if (! rd->ratingInfo) return GF_OUT_OF_MEM; + gf_bs_read_data(bs, rd->ratingInfo, rd->infoLength); + nbBytes += rd->infoLength; + if (nbBytes != DescSize) return GF_ODF_INVALID_DESCRIPTOR; + return GF_OK; +} + +GF_Err gf_odf_size_rating(GF_Rating *rd, u32 *outSize) +{ + if (!rd) return GF_BAD_PARAM; + + *outSize = 6 + rd->infoLength; + return GF_OK; +} + +GF_Err gf_odf_write_rating(GF_BitStream *bs, GF_Rating *rd) +{ + GF_Err e; + u32 size; + if (!rd) return GF_BAD_PARAM; + e = gf_odf_size_descriptor((GF_Descriptor *)rd, &size); + if (e) return e; + e = gf_odf_write_base_descriptor(bs, rd->tag, size); + if (e) return e; + gf_bs_write_int(bs, rd->ratingEntity, 32); + gf_bs_write_int(bs, rd->ratingCriteria, 16); + gf_bs_write_data(bs, rd->ratingInfo, rd->infoLength); + return GF_OK; +} + + +GF_Descriptor *gf_odf_new_reg() +{ + GF_Registration *newDesc = (GF_Registration *) gf_malloc(sizeof(GF_Registration)); + if (!newDesc) return NULL; + newDesc->additionalIdentificationInfo = NULL; + newDesc->dataLength = 0; + newDesc->formatIdentifier = 0; + newDesc->tag = GF_ODF_REG_TAG; + return (GF_Descriptor *) newDesc; +} + +GF_Err gf_odf_del_reg(GF_Registration *reg) +{ + if (!reg) return GF_BAD_PARAM; + + if (reg->additionalIdentificationInfo) gf_free(reg->additionalIdentificationInfo); + gf_free(reg); + return GF_OK; +} + +GF_Err gf_odf_read_reg(GF_BitStream *bs, GF_Registration *reg, u32 DescSize) +{ + u32 nbBytes = 0; + if (!reg) return GF_BAD_PARAM; + + reg->formatIdentifier = gf_bs_read_int(bs, 32); + reg->dataLength = DescSize - 4; + reg->additionalIdentificationInfo = (char*)gf_malloc(reg->dataLength); + if (! reg->additionalIdentificationInfo) return GF_OUT_OF_MEM; + gf_bs_read_data(bs, reg->additionalIdentificationInfo, reg->dataLength); + nbBytes += reg->dataLength + 4; + if (nbBytes != DescSize) return GF_ODF_INVALID_DESCRIPTOR; + return GF_OK; +} + + +GF_Err gf_odf_size_reg(GF_Registration *reg, u32 *outSize) +{ + if (!reg) return GF_BAD_PARAM; + + *outSize = 4 + reg->dataLength; + return GF_OK; +} + +GF_Err gf_odf_write_reg(GF_BitStream *bs, GF_Registration *reg) +{ + GF_Err e; + u32 size; + if (!reg) return GF_BAD_PARAM; + + e = gf_odf_size_descriptor((GF_Descriptor *)reg, &size); + if (e) return e; + e = gf_odf_write_base_descriptor(bs, reg->tag, size); + if (e) return e; + + gf_bs_write_int(bs, reg->formatIdentifier, 32); + gf_bs_write_data(bs, reg->additionalIdentificationInfo, reg->dataLength); + return GF_OK; +} + +GF_Descriptor *gf_odf_new_short_text() +{ + GF_ShortTextual *newDesc = (GF_ShortTextual *) gf_malloc(sizeof(GF_ShortTextual)); + if (!newDesc) return NULL; + + newDesc->eventName = NULL; + newDesc->eventText = NULL; + newDesc->isUTF8 = 0; + newDesc->langCode = 0; + newDesc->tag = GF_ODF_SHORT_TEXT_TAG; + return (GF_Descriptor *) newDesc; +} + +GF_Err gf_odf_del_short_text(GF_ShortTextual *std) +{ + if (!std) return GF_BAD_PARAM; + + if (std->eventName) gf_free(std->eventName); + if (std->eventText) gf_free(std->eventText); + gf_free(std); + return GF_OK; +} + +GF_Err gf_odf_read_short_text(GF_BitStream *bs, GF_ShortTextual *std, u32 DescSize) +{ + GF_Err e; + u32 nbBytes = 0, len; + if (!std) return GF_BAD_PARAM; + + std->langCode = gf_bs_read_int(bs, 24); + std->isUTF8 = gf_bs_read_int(bs, 1); + /*aligned = */gf_bs_read_int(bs, 7); + nbBytes += 4; + + e = OD_ReadUTF8String(bs, & std->eventName, std->isUTF8, &len); + if (e) return e; + nbBytes += len; + e = OD_ReadUTF8String(bs, & std->eventText, std->isUTF8, &len); + if (e) return e; + nbBytes += len; + if (nbBytes != DescSize) return GF_ODF_INVALID_DESCRIPTOR; + return GF_OK; +} + +GF_Err gf_odf_size_short_text(GF_ShortTextual *std, u32 *outSize) +{ + if (!std) return GF_BAD_PARAM; + *outSize = 4; + *outSize += OD_SizeUTF8String(std->eventName, std->isUTF8) + OD_SizeUTF8String(std->eventText, std->isUTF8); + return GF_OK; +} + +GF_Err gf_odf_write_short_text(GF_BitStream *bs, GF_ShortTextual *std) +{ + GF_Err e; + u32 size; + if (!std) return GF_BAD_PARAM; + + e = gf_odf_size_descriptor((GF_Descriptor *)std, &size); + if (e) return e; + e = gf_odf_write_base_descriptor(bs, std->tag, size); + if (e) return e; + gf_bs_write_int(bs, std->langCode, 24); + gf_bs_write_int(bs, std->isUTF8, 1); + gf_bs_write_int(bs, 0, 7); + OD_WriteUTF8String(bs, std->eventName, std->isUTF8); + OD_WriteUTF8String(bs, std->eventText, std->isUTF8); + return GF_OK; +} + +GF_Descriptor *gf_odf_new_smpte_camera() +{ + GF_SMPTECamera *newDesc = (GF_SMPTECamera *) gf_malloc(sizeof(GF_SMPTECamera)); + if (!newDesc) return NULL; + + newDesc->ParamList = gf_list_new(); + if (! newDesc->ParamList) { + gf_free(newDesc); + return NULL; + } + newDesc->cameraID = 0; + newDesc->tag = GF_ODF_SMPTE_TAG; + return (GF_Descriptor *) newDesc; +} + +GF_Err gf_odf_del_smpte_camera(GF_SMPTECamera *cpd) +{ + u32 i; + GF_SmpteParam *tmp; + if (!cpd) return GF_BAD_PARAM; + + i=0; + while ((tmp = (GF_SmpteParam *)gf_list_enum(cpd->ParamList, &i))) { + gf_free(tmp); + } + gf_list_del(cpd->ParamList); + gf_free(cpd); + return GF_OK; +} +GF_Err gf_odf_read_smpte_camera(GF_BitStream *bs, GF_SMPTECamera *cpd, u32 DescSize) +{ + GF_Err e; + u32 nbBytes = 0, i, count; + if (!cpd) return GF_BAD_PARAM; + + cpd->cameraID = gf_bs_read_int(bs, 8); + count = gf_bs_read_int(bs, 8); + nbBytes += 2; + + for (i=0; i< count ; i++) { + GF_SmpteParam *tmp = (GF_SmpteParam*)gf_malloc(sizeof(GF_SmpteParam)); + if (! tmp) return GF_OUT_OF_MEM; + tmp->paramID = gf_bs_read_int(bs, 8); + tmp->param = gf_bs_read_int(bs, 32); + nbBytes += 5; + e = gf_list_add(cpd->ParamList, tmp); + if (e) return e; + } + if (nbBytes != DescSize) return GF_ODF_INVALID_DESCRIPTOR; + return GF_OK; +} + +GF_Err gf_odf_size_smpte_camera(GF_SMPTECamera *cpd, u32 *outSize) +{ + if (!cpd) return GF_BAD_PARAM; + *outSize = 2 + 5 * gf_list_count(cpd->ParamList); + return GF_OK; +} + +GF_Err gf_odf_write_smpte_camera(GF_BitStream *bs, GF_SMPTECamera *cpd) +{ + GF_Err e; + GF_SmpteParam *tmp; + u32 size, i; + if (!cpd) return GF_BAD_PARAM; + + e = gf_odf_size_descriptor((GF_Descriptor *)cpd, &size); + if (e) return e; + e = gf_odf_write_base_descriptor(bs, cpd->tag, size); + if (e) return e; + gf_bs_write_int(bs, cpd->cameraID, 8); + gf_bs_write_int(bs, gf_list_count(cpd->ParamList), 8); + + i=0; + while ((tmp = (GF_SmpteParam *)gf_list_enum(cpd->ParamList, &i))) { + gf_bs_write_int(bs, tmp->paramID, 8); + gf_bs_write_int(bs, tmp->param, 32); + } + return GF_OK; +} + +GF_Descriptor *gf_odf_new_sup_cid() +{ + GF_SCIDesc *newDesc = (GF_SCIDesc *) gf_malloc(sizeof(GF_SCIDesc)); + if (!newDesc) return NULL; + newDesc->supplContentIdentifierTitle = NULL; + newDesc->supplContentIdentifierValue =NULL; + newDesc->languageCode = 0; + newDesc->tag = GF_ODF_SCI_TAG; + return (GF_Descriptor *) newDesc; +} + +GF_Err gf_odf_del_sup_cid(GF_SCIDesc *scid) +{ + if (!scid) return GF_BAD_PARAM; + + if (scid->supplContentIdentifierTitle) gf_free(scid->supplContentIdentifierTitle); + if (scid->supplContentIdentifierValue) gf_free(scid->supplContentIdentifierValue); + gf_free(scid); + return GF_OK; +} + +GF_Err gf_odf_read_sup_cid(GF_BitStream *bs, GF_SCIDesc *scid, u32 DescSize) +{ + GF_Err e; + u32 nbBytes = 0, len; + if (! scid) return GF_BAD_PARAM; + + scid->languageCode = gf_bs_read_int(bs, 24); + nbBytes += 3; + e = OD_ReadUTF8String(bs, & scid->supplContentIdentifierTitle, 1, &len); + if (e) return e; + nbBytes += len; + e = OD_ReadUTF8String(bs, & scid->supplContentIdentifierValue, 1, &len); + if (e) return e; + nbBytes += len; + if (nbBytes != DescSize) return GF_ODF_INVALID_DESCRIPTOR; + return GF_OK; +} + + +GF_Err gf_odf_size_sup_cid(GF_SCIDesc *scid, u32 *outSize) +{ + if (! scid) return GF_BAD_PARAM; + *outSize = 3 + OD_SizeUTF8String(scid->supplContentIdentifierTitle, 1) + OD_SizeUTF8String(scid->supplContentIdentifierValue, 1); + return GF_OK; +} +GF_Err gf_odf_write_sup_cid(GF_BitStream *bs, GF_SCIDesc *scid) +{ + GF_Err e; + u32 size; + if (! scid) return GF_BAD_PARAM; + e = gf_odf_size_descriptor((GF_Descriptor *)scid, &size); + if (e) return e; + e = gf_odf_write_base_descriptor(bs, scid->tag, size); + if (e) return e; + gf_bs_write_int(bs, scid->languageCode, 24); + OD_WriteUTF8String(bs, scid->supplContentIdentifierTitle, 1); + OD_WriteUTF8String(bs, scid->supplContentIdentifierValue, 1); + return GF_OK; +} + + +/*IPMPX stuff*/ +GF_Descriptor *gf_odf_new_ipmp_tool_list() +{ + GF_IPMP_ToolList*newDesc = (GF_IPMP_ToolList*) gf_malloc(sizeof(GF_IPMP_ToolList)); + if (!newDesc) return NULL; + newDesc->ipmp_tools = gf_list_new(); + newDesc->tag = GF_ODF_IPMP_TL_TAG; + return (GF_Descriptor *) newDesc; +} + +GF_Err gf_odf_del_ipmp_tool_list(GF_IPMP_ToolList *ipmptl) +{ + if (!ipmptl) return GF_BAD_PARAM; + + while (gf_list_count(ipmptl->ipmp_tools)) { + GF_IPMP_Tool *t = (GF_IPMP_Tool *) gf_list_get(ipmptl->ipmp_tools, 0); + gf_list_rem(ipmptl->ipmp_tools, 0); + if (t->tool_url) gf_free(t->tool_url); + gf_free(t); + } + gf_list_del(ipmptl->ipmp_tools); + gf_free(ipmptl); + return GF_OK; +} + +GF_Err gf_odf_read_ipmp_tool_list(GF_BitStream *bs, GF_IPMP_ToolList *ipmptl, u32 DescSize) +{ + GF_Err e; + u32 tmpSize; + u32 nbBytes = 0; + if (! ipmptl) return GF_BAD_PARAM; + + while (nbBytes < DescSize) { + GF_Descriptor *tmp = NULL; + e = gf_odf_parse_descriptor(bs, &tmp, &tmpSize); + if (e) return e; + if (!tmp) return GF_ODF_INVALID_DESCRIPTOR; + e = gf_list_add(ipmptl->ipmp_tools, tmp); + if (e) return e; + nbBytes += tmpSize + gf_odf_size_field_size(tmpSize); + } + if (nbBytes != DescSize) return GF_ODF_INVALID_DESCRIPTOR; + return GF_OK; +} + + +GF_Err gf_odf_size_ipmp_tool_list(GF_IPMP_ToolList *ipmptl, u32 *outSize) +{ + if (!ipmptl) return GF_BAD_PARAM; + *outSize = 0; + return gf_odf_size_descriptor_list(ipmptl->ipmp_tools, outSize); +} + +GF_Err gf_odf_write_ipmp_tool_list(GF_BitStream *bs, GF_IPMP_ToolList *ipmptl) +{ + GF_Err e; + u32 size; + if (!ipmptl) return GF_BAD_PARAM; + e = gf_odf_size_descriptor((GF_Descriptor *)ipmptl, &size); + if (e) return e; + e = gf_odf_write_base_descriptor(bs, ipmptl->tag, size); + if (e) return e; + e = gf_odf_write_descriptor_list(bs, ipmptl->ipmp_tools); + return GF_OK; +} + +GF_Descriptor *gf_odf_new_ipmp_tool() +{ + GF_IPMP_Tool *newDesc = (GF_IPMP_Tool*) gf_malloc(sizeof(GF_IPMP_Tool)); + if (!newDesc) return NULL; + memset(newDesc, 0, sizeof(GF_IPMP_Tool)); + newDesc->tag = GF_ODF_IPMP_TL_TAG; + return (GF_Descriptor *) newDesc; +} + +GF_Err gf_odf_del_ipmp_tool(GF_IPMP_Tool *ipmpt) +{ + if (!ipmpt) return GF_BAD_PARAM; + if (ipmpt->tool_url) gf_free(ipmpt->tool_url); + gf_free(ipmpt); + return GF_OK; +} + +GF_Err gf_odf_read_ipmp_tool(GF_BitStream *bs, GF_IPMP_Tool *ipmpt, u32 DescSize) +{ + Bool is_alt, is_param; + u32 nbBytes = 0; + if (! ipmpt) return GF_BAD_PARAM; + gf_bs_read_data(bs, (char*) ipmpt->IPMP_ToolID, 16); + is_alt = gf_bs_read_int(bs, 1); + is_param = gf_bs_read_int(bs, 1); + gf_bs_read_int(bs, 6); + nbBytes = 17; + + if (is_alt) { + u32 i; + ipmpt->num_alternate = gf_bs_read_int(bs, 8); + nbBytes += 1; + for (i=0; inum_alternate; i++) { + gf_bs_read_data(bs, (char*)ipmpt->specificToolID[i], 16); + nbBytes += 16; + if (nbBytes>DescSize) break; + } + } + if (nbBytes>DescSize) return GF_ODF_INVALID_DESCRIPTOR; + + if (is_param) { } + + if (nbBytestool_url = (char*)gf_malloc(sizeof(char)*(s+1)); + gf_bs_read_data(bs, ipmpt->tool_url, s); + ipmpt->tool_url[s] = 0; + nbBytes += s; + } + } + + if (nbBytes!=DescSize) return GF_NON_COMPLIANT_BITSTREAM; + return GF_OK; +} + + +GF_Err gf_odf_size_ipmp_tool(GF_IPMP_Tool *ipmpt, u32 *outSize) +{ + if (!ipmpt) return GF_BAD_PARAM; + *outSize = 17; + if (ipmpt->num_alternate) *outSize += 1 + 16*ipmpt->num_alternate; + + if (ipmpt->tool_url) { + u32 s = strlen(ipmpt->tool_url); + *outSize += gf_odf_size_field_size(s) - 1 + s; + } + return GF_OK; +} + +GF_Err gf_odf_write_ipmp_tool(GF_BitStream *bs, GF_IPMP_Tool *ipmpt) +{ + GF_Err e; + u32 size; + if (!ipmpt) return GF_BAD_PARAM; + e = gf_odf_size_descriptor((GF_Descriptor *)ipmpt, &size); + if (e) return e; + e = gf_odf_write_base_descriptor(bs, ipmpt->tag, size); + if (e) return e; + + gf_bs_write_data(bs, (char*)ipmpt->IPMP_ToolID, 16); + gf_bs_write_int(bs, ipmpt->num_alternate ? 1 : 0, 1); + gf_bs_write_int(bs, 0, 1); + gf_bs_write_int(bs, 0, 6); + + if (ipmpt->num_alternate) { + u32 i; + gf_bs_write_int(bs, ipmpt->num_alternate, 8); + for (i=0;inum_alternate; i++) gf_bs_write_data(bs, (char*)ipmpt->specificToolID[i], 16); + } + if (ipmpt->tool_url) gf_ipmpx_write_array(bs, ipmpt->tool_url, strlen(ipmpt->tool_url)); + return GF_OK; +} + +#endif /*GPAC_MINIMAL_ODF*/ diff --git a/src/gpacmp4/odf_codec.c b/src/gpacmp4/odf_codec.c new file mode 100644 index 00000000..1fec14e0 --- /dev/null +++ b/src/gpacmp4/odf_codec.c @@ -0,0 +1,607 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / MPEG-4 ObjectDescriptor sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include + +/************************************************************ + Object GF_Descriptor Codec Functions +************************************************************/ + +GF_EXPORT +GF_ODCodec *gf_odf_codec_new() +{ + GF_ODCodec *codec; + GF_List *comList; + + comList = gf_list_new(); + if (!comList) return NULL; + + codec = (GF_ODCodec *) gf_malloc(sizeof(GF_ODCodec)); + if (!codec) { + gf_list_del(comList); + return NULL; + } + //the bitstream is always NULL. It is created on the fly for access unit processing only + codec->bs = NULL; + codec->CommandList = comList; + return codec; +} + +GF_EXPORT +void gf_odf_codec_del(GF_ODCodec *codec) +{ + if (!codec) return; + + while (gf_list_count(codec->CommandList)) { + GF_ODCom *com = (GF_ODCom *)gf_list_get(codec->CommandList, 0); + gf_odf_delete_command(com); + gf_list_rem(codec->CommandList, 0); + } + gf_list_del(codec->CommandList); + if (codec->bs) gf_bs_del(codec->bs); + gf_free(codec); +} + + +/************************************************************ + Codec Encoder Functions +************************************************************/ + +GF_EXPORT +GF_Err gf_odf_codec_add_com(GF_ODCodec *codec, GF_ODCom *command) +{ + if (!codec || !command) return GF_BAD_PARAM; + return gf_list_add(codec->CommandList, command); +} + +GF_EXPORT +GF_Err gf_odf_codec_encode(GF_ODCodec *codec, u32 cleanup_type) +{ + GF_ODCom *com; + GF_Err e = GF_OK; + u32 i; + + if (!codec) return GF_BAD_PARAM; + + //check our bitstream: if existing, this means the previous encoded AU was not retrieved + //we DON'T allow that + if (codec->bs) return GF_BAD_PARAM; + codec->bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); + if (!codec->bs) return GF_OUT_OF_MEM; + + /*encode each command*/ + i = 0; + while ((com = (GF_ODCom *)gf_list_enum(codec->CommandList, &i))) { + e = gf_odf_write_command(codec->bs, com); if (e) goto err_exit; + //don't forget OD Commands are aligned... + gf_bs_align(codec->bs); + } + +//if an error occurs, delete the GF_BitStream and empty the codec +err_exit: + if (e) { + gf_bs_del(codec->bs); + codec->bs = NULL; + } + if (cleanup_type==1) { + while (gf_list_count(codec->CommandList)) { + com = (GF_ODCom *)gf_list_get(codec->CommandList, 0); + gf_odf_delete_command(com); + gf_list_rem(codec->CommandList, 0); + } + } + if (cleanup_type==0) { + gf_list_reset(codec->CommandList); + } + return e; +} + +GF_EXPORT +GF_Err gf_odf_codec_get_au(GF_ODCodec *codec, char **outAU, u32 *au_length) +{ + if (!codec || !codec->bs || !outAU || *outAU) return GF_BAD_PARAM; + gf_bs_get_content(codec->bs, outAU, au_length); + gf_bs_del(codec->bs); + codec->bs = NULL; + return GF_OK; +} + + + +/************************************************************ + Codec Decoder Functions +************************************************************/ + +GF_EXPORT +GF_Err gf_odf_codec_set_au(GF_ODCodec *codec, const char *au, u32 au_length) +{ + if (!codec ) return GF_BAD_PARAM; + if (!au || !au_length) return GF_OK; + + //if the command list is not empty, this is an error + if (gf_list_count(codec->CommandList)) return GF_BAD_PARAM; + + //the bitStream should not be here + if (codec->bs) return GF_BAD_PARAM; + + codec->bs = gf_bs_new(au, (u64) au_length, (unsigned char)GF_BITSTREAM_READ); + if (!codec->bs) return GF_OUT_OF_MEM; + return GF_OK; +} + + +GF_EXPORT +GF_Err gf_odf_codec_decode(GF_ODCodec *codec) +{ + GF_Err e = GF_OK; + u32 size = 0, comSize, bufSize; + GF_ODCom *com; + + if (!codec || !codec->bs) return GF_BAD_PARAM; + + bufSize = (u32) gf_bs_available(codec->bs); + while (size < bufSize) { + e = gf_odf_parse_command(codec->bs, &com, &comSize); if (e) goto err_exit; + gf_list_add(codec->CommandList, com); + size += comSize + gf_odf_size_field_size(comSize); + //OD Commands are aligned + gf_bs_align(codec->bs); + } + //then delete our bitstream + gf_bs_del(codec->bs); + codec->bs = NULL; + if (size != bufSize) { + e = GF_ODF_INVALID_COMMAND; + goto err_exit; + } + return e; + +err_exit: + if (codec->bs) { + gf_bs_del(codec->bs); + codec->bs = NULL; + } + while (gf_list_count(codec->CommandList)) { + com = (GF_ODCom*)gf_list_get(codec->CommandList, 0); + gf_odf_delete_command(com); + gf_list_rem(codec->CommandList, 0); + } + return e; +} + +//get the first command in the codec and remove the entry +GF_EXPORT +GF_ODCom *gf_odf_codec_get_com(GF_ODCodec *codec) +{ + GF_ODCom *com; + if (!codec || codec->bs) return NULL; + com = (GF_ODCom*)gf_list_get(codec->CommandList, 0); + if (com) gf_list_rem(codec->CommandList, 0); + return com; +} + + + +/************************************************************ + OD Commands Functions +************************************************************/ + +//some easy way to get an OD GF_ODCom... +GF_EXPORT +GF_ODCom *gf_odf_com_new(u8 tag) +{ + GF_ODCom *newcom; + + newcom = gf_odf_create_command(tag); + newcom->tag = tag; + return (GF_ODCom *)newcom; +} + +// ... and to delete it +GF_EXPORT +GF_Err gf_odf_com_del(GF_ODCom **com) +{ + GF_Err e; + e = gf_odf_delete_command(*com); + *com = NULL; + return e; +} + + +/************************************************************ + Object Descriptors Functions +************************************************************/ + +//some easy way to get an mpeg4 descriptor ... +GF_EXPORT +GF_Descriptor *gf_odf_desc_new(u8 tag) +{ + GF_Descriptor *newdesc; + newdesc = gf_odf_create_descriptor(tag); + newdesc->tag = tag; + return (GF_Descriptor *)newdesc; +} + +// ... and to delete it +GF_EXPORT +void gf_odf_desc_del(GF_Descriptor *desc) +{ + if (desc) gf_odf_delete_descriptor(desc); +} + +//this functions will destroy the descriptors in a list but not the list +GF_EXPORT +GF_Err gf_odf_desc_list_del(GF_List *descList) +{ + GF_Err e; + GF_Descriptor *tmp; + + if (! descList) return GF_BAD_PARAM; + + while (gf_list_count(descList)) { + tmp = (GF_Descriptor*)gf_list_get(descList, 0); + gf_list_rem(descList, 0); + e = gf_odf_delete_descriptor(tmp); + if (e) return e; + } + return GF_OK; +} + + + +GF_EXPORT +GF_ESD *gf_odf_desc_esd_new(u32 sl_predefined) +{ + GF_ESD *esd; + esd = (GF_ESD *) gf_odf_desc_new(GF_ODF_ESD_TAG); + esd->decoderConfig = (GF_DecoderConfig *) gf_odf_desc_new(GF_ODF_DCD_TAG); + esd->decoderConfig->decoderSpecificInfo = (GF_DefaultDescriptor *) gf_odf_desc_new(GF_ODF_DSI_TAG); + esd->slConfig = (GF_SLConfig *) gf_odf_new_slc((u8) sl_predefined); + return esd; +} + + +//use this function to decode a standalone descriptor +//the desc MUST be formatted with tag and size field!!! +GF_EXPORT +GF_Err gf_odf_desc_read(char *raw_desc, u32 descSize, GF_Descriptor * *outDesc) +{ + GF_Err e; + u32 size; + GF_BitStream *bs; + + if (!raw_desc || !descSize) return GF_BAD_PARAM; + + bs = gf_bs_new(raw_desc, (u64) descSize, GF_BITSTREAM_READ); + if (!bs) return GF_OUT_OF_MEM; + + size = 0; + e = gf_odf_parse_descriptor(bs, outDesc, &size); + //the size dosn't have the header in it + size += gf_odf_size_field_size(size); +/* + if (size != descSize) { + if (*outDesc) gf_odf_delete_descriptor(*outDesc); + *outDesc = NULL; + e = GF_ODF_INVALID_DESCRIPTOR; + } +*/ + + gf_bs_del(bs); + return e; +} + +//use this function to encode a standalone descriptor +//the desc will be formatted with tag and size field +GF_EXPORT +GF_Err gf_odf_desc_write(GF_Descriptor *desc, char **outEncDesc, u32 *outSize) +{ + GF_Err e; + GF_BitStream *bs; + + if (!desc || !outEncDesc || !outSize) return GF_BAD_PARAM; + + *outEncDesc = NULL; + *outSize = 0; + + bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); + if (!bs) return GF_OUT_OF_MEM; + //then encode our desc... + e = gf_odf_write_descriptor(bs, desc); + if (e) { + gf_bs_del(bs); + return e; + } + //then get the content from our bitstream + gf_bs_get_content(bs, outEncDesc, outSize); + gf_bs_del(bs); + return GF_OK; +} + +//use this function to get the size of a standalone descriptor +GF_EXPORT +u32 gf_odf_desc_size(GF_Descriptor *desc) +{ + u32 descSize; + GF_Err e; + + if (!desc) return GF_BAD_PARAM; + //get the descriptor length + e = gf_odf_size_descriptor(desc, &descSize); + if (e) return 0; + //add the header length + descSize += gf_odf_size_field_size(descSize); + return descSize; + +} + +//this is usefull to duplicate on the fly a descriptor (mainly for authoring purposes) +GF_EXPORT +GF_Err gf_odf_desc_copy(GF_Descriptor *inDesc, GF_Descriptor **outDesc) +{ + GF_Err e; + char *desc; + u32 size; + + //warning: here we get some data allocated + e = gf_odf_desc_write(inDesc, &desc, &size); + if (e) return e; + e = gf_odf_desc_read(desc, size, outDesc); + gf_free(desc); + return e; +} + +/************************************************************ + Object Descriptors Edit Functions +************************************************************/ + +//This functions handles internally what desc can be added to another desc +//and adds it. NO DUPLICATION of the descriptor, so +//once a desc is added to its parent, destroying the parent WILL destroy this desc +GF_EXPORT +GF_Err gf_odf_desc_add_desc(GF_Descriptor *parentDesc, GF_Descriptor *newDesc) +{ + GF_DecoderConfig *dcd; + + //our ADD definition + GF_Err AddDescriptorToOD(GF_ObjectDescriptor *od, GF_Descriptor *desc); + GF_Err AddDescriptorToIOD(GF_InitialObjectDescriptor *iod, GF_Descriptor *desc); + GF_Err AddDescriptorToESD(GF_ESD *esd, GF_Descriptor *desc); + GF_Err AddDescriptorToIsomIOD(GF_IsomInitialObjectDescriptor *iod, GF_Descriptor *desc); + GF_Err AddDescriptorToIsomOD(GF_IsomObjectDescriptor *od, GF_Descriptor *desc); + + if (!parentDesc || !newDesc) return GF_BAD_PARAM; + + switch (parentDesc->tag) { + //these are container descriptors + case GF_ODF_OD_TAG: + return AddDescriptorToOD((GF_ObjectDescriptor *)parentDesc, newDesc); + case GF_ODF_IOD_TAG: + return AddDescriptorToIOD((GF_InitialObjectDescriptor *)parentDesc, newDesc); + case GF_ODF_ESD_TAG: + return AddDescriptorToESD((GF_ESD *)parentDesc, newDesc); + case GF_ODF_DCD_TAG: + dcd = (GF_DecoderConfig *)parentDesc; + if ((newDesc->tag == GF_ODF_DSI_TAG) + || (newDesc->tag == GF_ODF_BIFS_CFG_TAG) + || (newDesc->tag == GF_ODF_UI_CFG_TAG) + || (newDesc->tag == GF_ODF_TEXT_CFG_TAG) + ) { + if (dcd->decoderSpecificInfo) return GF_ODF_FORBIDDEN_DESCRIPTOR; + dcd->decoderSpecificInfo = (GF_DefaultDescriptor *) newDesc; + return GF_OK; + } else if (newDesc->tag == GF_ODF_EXT_PL_TAG) { + return gf_list_add(dcd->profileLevelIndicationIndexDescriptor, newDesc); + } + return GF_ODF_FORBIDDEN_DESCRIPTOR; + + case GF_ODF_TEXT_CFG_TAG: + if (newDesc->tag != GF_ODF_TX3G_TAG) return GF_ODF_FORBIDDEN_DESCRIPTOR; + return gf_list_add(((GF_TextConfig *)parentDesc)->sample_descriptions, newDesc); + + case GF_ODF_QOS_TAG: + return GF_BAD_PARAM; + + //MP4 File Format tags + case GF_ODF_ISOM_IOD_TAG: + return AddDescriptorToIsomIOD((GF_IsomInitialObjectDescriptor *)parentDesc, newDesc); + case GF_ODF_ISOM_OD_TAG: + return AddDescriptorToIsomOD((GF_IsomObjectDescriptor *)parentDesc, newDesc); + + case GF_ODF_IPMP_TL_TAG: + if (newDesc->tag!=GF_ODF_IPMP_TOOL_TAG) return GF_BAD_PARAM; + return gf_list_add(((GF_IPMP_ToolList *)parentDesc)->ipmp_tools, newDesc); + + case GF_ODF_BIFS_CFG_TAG: + { + GF_BIFSConfig *cfg = (GF_BIFSConfig *)parentDesc; + if (newDesc->tag!=GF_ODF_ELEM_MASK_TAG) return GF_BAD_PARAM; + if (!cfg->elementaryMasks) cfg->elementaryMasks = gf_list_new(); + return gf_list_add(cfg->elementaryMasks, newDesc); + } + default: + return GF_ODF_FORBIDDEN_DESCRIPTOR; + } +} + + + +/***************************************************************************************** + Since IPMP V2, we introduce a new set of functions to read / write a list of + descriptors that have no containers (a bit like an OD command, but for descriptors) + This is usefull for IPMPv2 DecoderSpecificInfo which contains a set of + IPMP_Declarators. + As it could be used for other purposes we keep it generic + You must create the list yourself, the functions just encode/decode from/to the list +*****************************************************************************************/ + +GF_EXPORT +GF_Err gf_odf_desc_list_read(char *raw_list, u32 raw_size, GF_List *descList) +{ + GF_BitStream *bs; + u32 size, desc_size; + GF_Descriptor *desc; + GF_Err e = GF_OK; + + if (!descList || !raw_list || !raw_size) return GF_BAD_PARAM; + + bs = gf_bs_new(raw_list, raw_size, GF_BITSTREAM_READ); + if (!bs) return GF_OUT_OF_MEM; + + size = 0; + while (size < raw_size) { + e = gf_odf_parse_descriptor(bs, &desc, &desc_size); + if (e) goto exit; + gf_list_add(descList, desc); + size += desc_size + gf_odf_size_field_size(desc_size); + } + +exit: + //then delete our bitstream + gf_bs_del(bs); + if (size != raw_size) e = GF_ODF_INVALID_DESCRIPTOR; + return e; +} + + +GF_EXPORT +GF_Err gf_odf_desc_list_write(GF_List *descList, char **outEncList, u32 *outSize) +{ + GF_BitStream *bs; + GF_Err e; + + if (!descList || !outEncList || *outEncList || !outSize) return GF_BAD_PARAM; + + *outSize = 0; + + bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); + if (!bs) return GF_OUT_OF_MEM; + + e = gf_odf_write_descriptor_list(bs, descList); + if (e) { + gf_bs_del(bs); + return e; + } + + gf_bs_get_content(bs, outEncList, outSize); + gf_bs_del(bs); + return GF_OK; +} + +GF_EXPORT +GF_Err gf_odf_desc_list_size(GF_List *descList, u32 *outSize) +{ + return gf_odf_size_descriptor_list(descList, outSize); +} + + + +GF_Err gf_odf_codec_apply_com(GF_ODCodec *codec, GF_ODCom *command) +{ + GF_ODCom *com; + GF_ODUpdate *odU, *odU_o; + u32 i, count; + count = gf_list_count(codec->CommandList); + + switch (command->tag) { + case GF_ODF_OD_REMOVE_TAG: + for (i=0; iCommandList, i); + /*process OD updates*/ + if (com->tag==GF_ODF_OD_UPDATE_TAG) { + u32 count, j, k; + GF_ODRemove *odR = (GF_ODRemove *) command; + odU = (GF_ODUpdate *)com; + count = gf_list_count(odU->objectDescriptors); + /*remove all descs*/ + for (k=0; kobjectDescriptors, k); + for (j=0; jNbODs; j++) { + if (od->objectDescriptorID==odR->OD_ID[j]) { + gf_list_rem(odU->objectDescriptors, k); + k--; count--; + gf_odf_desc_del((GF_Descriptor *)od); + break; + } + } + } + if (!gf_list_count(odU->objectDescriptors)) { + gf_list_rem(codec->CommandList, i); + i--; + count--; + } + } + /*process ESD updates*/ + else if (com->tag==GF_ODF_ESD_UPDATE_TAG) { + u32 j; + GF_ODRemove *odR = (GF_ODRemove *) command; + GF_ESDUpdate *esdU = (GF_ESDUpdate*)com; + for (j=0; jNbODs; j++) { + if (esdU->ODID==odR->OD_ID[j]) { + gf_list_rem(codec->CommandList, i); + i--; + count--; + gf_odf_com_del((GF_ODCom**)&esdU); + break; + } + } + } + } + return GF_OK; + case GF_ODF_OD_UPDATE_TAG: + odU_o = NULL; + for (i=0; iCommandList, i); + /*process OD updates*/ + if (odU_o->tag==GF_ODF_OD_UPDATE_TAG) break; + odU_o = NULL; + } + if (!odU_o) { + odU_o = (GF_ODUpdate *)gf_odf_com_new(GF_ODF_OD_UPDATE_TAG); + gf_list_add(codec->CommandList, odU_o); + } + odU = (GF_ODUpdate*)command; + count = gf_list_count(odU->objectDescriptors); + for (i=0; iobjectDescriptors, i); + u32 j, count2 = gf_list_count(odU_o->objectDescriptors); + for (j=0; jobjectDescriptors, j); + if (od2->objectDescriptorID==od->objectDescriptorID) { + found = 1; + break; + } + } + if (!found) { + GF_ObjectDescriptor *od_new; + gf_odf_desc_copy((GF_Descriptor*)od, (GF_Descriptor**)&od_new); + gf_list_add(odU_o->objectDescriptors, od_new); + } + + } + return GF_OK; + } + return GF_NOT_SUPPORTED; +} diff --git a/src/gpacmp4/odf_command.c b/src/gpacmp4/odf_command.c new file mode 100644 index 00000000..a24d02b1 --- /dev/null +++ b/src/gpacmp4/odf_command.c @@ -0,0 +1,652 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / MPEG-4 ObjectDescriptor sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +#include + + +GF_Err gf_odf_parse_command(GF_BitStream *bs, GF_ODCom **com, u32 *com_size) +{ + u32 val, size, sizeHeader; + u8 tag; + GF_Err err; + GF_ODCom *newCom; + if (!bs) return GF_BAD_PARAM; + + *com_size = 0; + + //tag + tag = gf_bs_read_int(bs, 8); + sizeHeader = 1; + + //size + size = 0; + do { + val = gf_bs_read_int(bs, 8); + sizeHeader++; + size <<= 7; + size |= val & 0x7F; + } while ( val & 0x80 ); + *com_size = size; + + newCom = gf_odf_create_command(tag); + if (! newCom) { + *com = NULL; + return GF_OUT_OF_MEM; + } + + newCom->tag = tag; + + err = gf_odf_read_command(bs, newCom, *com_size); + //little trick to handle lazy bitstreams that encode + //SizeOfInstance on a fix number of bytes + //This nb of bytes is added in Read methods + *com_size += sizeHeader - gf_odf_size_field_size(*com_size); + *com = newCom; + if (err) { + gf_odf_delete_command(newCom); + *com = NULL; + } + return err; +} + + +GF_ODCom *gf_odf_new_base_command() +{ + GF_BaseODCom *newCom = (GF_BaseODCom *) gf_malloc(sizeof(GF_BaseODCom)); + if (!newCom) return NULL; + newCom->dataSize = 0; + newCom->data = NULL; + return (GF_ODCom *)newCom; +} +GF_Err gf_odf_del_base_command(GF_BaseODCom *bcRemove) +{ + if (! bcRemove) return GF_BAD_PARAM; + if (bcRemove->data) gf_free(bcRemove->data); + gf_free(bcRemove); + return GF_OK; +} + +GF_Err gf_odf_read_base_command(GF_BitStream *bs, GF_BaseODCom *bcRem, u32 gf_odf_size_command) +{ + if (! bcRem) return GF_BAD_PARAM; + + bcRem->dataSize = gf_odf_size_command; + bcRem->data = (char *) gf_malloc(sizeof(char) * bcRem->dataSize); + if (! bcRem->data) return GF_OUT_OF_MEM; + gf_bs_read_data(bs, bcRem->data, bcRem->dataSize); + return GF_OK; +} +GF_Err gf_odf_size_base_command(GF_BaseODCom *bcRem, u32 *outSize) +{ + if (!bcRem) return GF_BAD_PARAM; + *outSize = bcRem->dataSize; + return GF_OK; +} +GF_Err gf_odf_write_base_command(GF_BitStream *bs, GF_BaseODCom *bcRem) +{ + u32 size; + GF_Err e; + if (! bcRem) return GF_BAD_PARAM; + + e = gf_odf_size_base_command(bcRem, &size); + if (e) return e; + e = gf_odf_write_base_descriptor(bs, bcRem->tag, size); + if (e) return e; + gf_bs_write_data(bs, bcRem->data, bcRem->dataSize); + return GF_OK; +} + +GF_ODCom *gf_odf_new_od_remove() +{ + GF_ODRemove *newCom = (GF_ODRemove *) gf_malloc(sizeof(GF_ODRemove)); + if (!newCom) return NULL; + newCom->NbODs = 0; + newCom->OD_ID = NULL; + newCom->tag = GF_ODF_OD_REMOVE_TAG; + return (GF_ODCom *)newCom; +} +GF_Err gf_odf_del_od_remove(GF_ODRemove *ODRemove) +{ + if (! ODRemove) return GF_BAD_PARAM; + if (ODRemove->OD_ID) gf_free(ODRemove->OD_ID); + gf_free(ODRemove); + return GF_OK; +} +GF_Err gf_odf_read_od_remove(GF_BitStream *bs, GF_ODRemove *odRem, u32 gf_odf_size_command) +{ + u32 i = 0, nbBits; + if (! odRem) return GF_BAD_PARAM; + + odRem->NbODs = (u32 ) (gf_odf_size_command * 8) / 10; + odRem->OD_ID = (u16 *) gf_malloc(sizeof(u16) * odRem->NbODs); + if (! odRem->OD_ID) return GF_OUT_OF_MEM; + + for (i = 0; i < odRem->NbODs ; i++) { + odRem->OD_ID[i] = gf_bs_read_int(bs, 10); + } + nbBits = odRem->NbODs * 10; + //now we need to align !!! + nbBits += gf_bs_align(bs); + if (nbBits != (gf_odf_size_command * 8)) return GF_ODF_INVALID_COMMAND; + return GF_OK; +} + +GF_Err gf_odf_size_od_remove(GF_ODRemove *odRem, u32 *outSize) +{ + u32 size; + if (!odRem) return GF_BAD_PARAM; + + size = 10 * odRem->NbODs; + *outSize = 0; + *outSize = size/8; + if (*outSize * 8 != size) *outSize += 1; + return GF_OK; +} + +GF_Err gf_odf_write_od_remove(GF_BitStream *bs, GF_ODRemove *odRem) +{ + GF_Err e; + u32 size, i; + if (! odRem) return GF_BAD_PARAM; + + e = gf_odf_size_od_remove(odRem, &size); + assert(e == GF_OK); + e = gf_odf_write_base_descriptor(bs, odRem->tag, size); + assert(e == GF_OK); + + for (i = 0; i < odRem->NbODs; i++) { + gf_bs_write_int(bs, odRem->OD_ID[i], 10); + } + //OD commands are aligned + gf_bs_align(bs); + return GF_OK; +} + + + +GF_ODCom *gf_odf_new_od_update() +{ + GF_ODUpdate *newCom = (GF_ODUpdate *) gf_malloc(sizeof(GF_ODUpdate)); + if (!newCom) return NULL; + + newCom->objectDescriptors = gf_list_new(); + if (! newCom->objectDescriptors) { + gf_free(newCom); + return NULL; + } + newCom->tag = GF_ODF_OD_UPDATE_TAG; + return (GF_ODCom *)newCom; +} + +GF_Err gf_odf_del_od_update(GF_ODUpdate *ODUpdate) +{ + GF_Err e; + if (! ODUpdate) return GF_BAD_PARAM; + while (gf_list_count(ODUpdate->objectDescriptors)) { + GF_Descriptor *tmp = (GF_Descriptor*)gf_list_get(ODUpdate->objectDescriptors, 0); + e = gf_odf_delete_descriptor(tmp); + if (e) return e; + e = gf_list_rem(ODUpdate->objectDescriptors, 0); + if (e) return e; + } + gf_list_del(ODUpdate->objectDescriptors); + gf_free(ODUpdate); + return GF_OK; +} + + + +GF_Err AddToODUpdate(GF_ODUpdate *odUp, GF_Descriptor *desc) +{ + if (! odUp) return GF_BAD_PARAM; + if (! desc) return GF_OK; + + switch (desc->tag) { + case GF_ODF_OD_TAG: + case GF_ODF_IOD_TAG: + case GF_ODF_ISOM_IOD_TAG: + case GF_ODF_ISOM_OD_TAG: + return gf_list_add(odUp->objectDescriptors, desc); + + default: + gf_odf_delete_descriptor(desc); + return GF_OK; + } +} + +GF_Err gf_odf_read_od_update(GF_BitStream *bs, GF_ODUpdate *odUp, u32 gf_odf_size_command) +{ + GF_Descriptor *tmp; + GF_Err e = GF_OK; + u32 tmpSize = 0, nbBytes = 0; + if (! odUp) return GF_BAD_PARAM; + + while (nbBytes < gf_odf_size_command) { + e = gf_odf_parse_descriptor(bs, &tmp, &tmpSize); + if (e) return e; + e = AddToODUpdate(odUp, tmp); + if (e) return e; + nbBytes += tmpSize + gf_odf_size_field_size(tmpSize); + } + //OD commands are aligned + gf_bs_align(bs); + if (nbBytes != gf_odf_size_command) return GF_ODF_INVALID_COMMAND; + return e; +} +GF_Err gf_odf_size_od_update(GF_ODUpdate *odUp, u32 *outSize) +{ + GF_Descriptor *tmp; + u32 i, tmpSize; + if (!odUp) return GF_BAD_PARAM; + + *outSize = 0; + i=0; + while ((tmp = (GF_Descriptor *)gf_list_enum(odUp->objectDescriptors, &i))) { + gf_odf_size_descriptor(tmp, &tmpSize); + *outSize += tmpSize + gf_odf_size_field_size(tmpSize); + } + return GF_OK; +} +GF_Err gf_odf_write_od_update(GF_BitStream *bs, GF_ODUpdate *odUp) +{ + GF_Err e; + GF_Descriptor *tmp; + u32 size, i; + if (! odUp) return GF_BAD_PARAM; + + e = gf_odf_size_od_update(odUp, &size); + if (e) return e; + gf_odf_write_base_descriptor(bs, odUp->tag, size); + if (e) return e; + + i=0; + while ((tmp = (GF_Descriptor *)gf_list_enum(odUp->objectDescriptors, &i))) { + e = gf_odf_write_descriptor(bs, tmp); + if (e) return e; + } + //OD commands are aligned + gf_bs_align(bs); + return GF_OK; +} + + +GF_ODCom *gf_odf_new_esd_update() +{ + GF_ESDUpdate *newCom = (GF_ESDUpdate *) gf_malloc(sizeof(GF_ESDUpdate)); + if (!newCom) return NULL; + + newCom->ESDescriptors = gf_list_new(); + if (! newCom->ESDescriptors) { + gf_free(newCom); + return NULL; + } + newCom->tag = GF_ODF_ESD_UPDATE_TAG; + return (GF_ODCom *)newCom; +} + +GF_Err gf_odf_del_esd_update(GF_ESDUpdate *ESDUpdate) +{ + GF_Err e; + if (! ESDUpdate) return GF_BAD_PARAM; + while (gf_list_count(ESDUpdate->ESDescriptors)) { + GF_Descriptor *tmp = (GF_Descriptor*)gf_list_get(ESDUpdate->ESDescriptors, 0); + e = gf_odf_delete_descriptor(tmp); + if (e) return e; + e = gf_list_rem(ESDUpdate->ESDescriptors, 0); + if (e) return e; + } + gf_list_del(ESDUpdate->ESDescriptors); + gf_free(ESDUpdate); + return GF_OK; +} + +GF_Err AddToESDUpdate(GF_ESDUpdate *esdUp, GF_Descriptor *desc) +{ + if (! esdUp) return GF_BAD_PARAM; + if (!desc) return GF_OK; + + switch (desc->tag) { + case GF_ODF_ESD_TAG: + case GF_ODF_ESD_REF_TAG: + return gf_list_add(esdUp->ESDescriptors, desc); + + default: + gf_odf_delete_descriptor(desc); + return GF_OK; + } +} + +GF_Err gf_odf_read_esd_update(GF_BitStream *bs, GF_ESDUpdate *esdUp, u32 gf_odf_size_command) +{ + GF_Descriptor *tmp; + u32 tmpSize = 0, nbBits = 0; + GF_Err e = GF_OK; + if (! esdUp) return GF_BAD_PARAM; + + esdUp->ODID = gf_bs_read_int(bs, 10); + nbBits += 10; + //very tricky, we're at the bit level here... + while (1) { + e = gf_odf_parse_descriptor(bs, &tmp, &tmpSize); + if (e) return e; + e = AddToESDUpdate(esdUp, tmp); + if (e) return e; + nbBits += ( tmpSize + gf_odf_size_field_size(tmpSize) ) * 8; + //our com is aligned, so nbBits is between (gf_odf_size_command-1)*8 and gf_odf_size_command*8 + if ( ( (nbBits >(gf_odf_size_command-1)*8) && (nbBits <= gf_odf_size_command * 8)) + || (nbBits > gf_odf_size_command*8) ) { //this one is a security break + break; + } + } + if (nbBits > gf_odf_size_command * 8) return GF_ODF_INVALID_COMMAND; + //Align our bitstream + nbBits += gf_bs_align(bs); + if (nbBits != gf_odf_size_command *8) return GF_ODF_INVALID_COMMAND; + return e; +} + + + +GF_Err gf_odf_size_esd_update(GF_ESDUpdate *esdUp, u32 *outSize) +{ + u32 i, BitSize, tmpSize; + GF_Descriptor *tmp; + if (!esdUp) return GF_BAD_PARAM; + + *outSize = 0; + BitSize = 10; + i=0; + while ((tmp = (GF_Descriptor *)gf_list_enum(esdUp->ESDescriptors, &i))) { + gf_odf_size_descriptor(tmp, &tmpSize); + BitSize += ( tmpSize + gf_odf_size_field_size(tmpSize) ) * 8; + } + while ((s32) BitSize > 0) { + BitSize -= 8; + *outSize += 1; + } + return GF_OK; +} +GF_Err gf_odf_write_esd_update(GF_BitStream *bs, GF_ESDUpdate *esdUp) +{ + GF_Descriptor *tmp; + GF_Err e; + u32 size, i; + if (! esdUp) return GF_BAD_PARAM; + + e = gf_odf_size_esd_update(esdUp, &size); + if (e) return e; + e = gf_odf_write_base_descriptor(bs, esdUp->tag, size); + if (e) return e; + + gf_bs_write_int(bs, esdUp->ODID, 10); + i=0; + while ((tmp = (GF_Descriptor *)gf_list_enum(esdUp->ESDescriptors, &i))) { + e = gf_odf_write_descriptor(bs, tmp); + if (e) return e; + } + //OD commands are aligned + gf_bs_align(bs); + return GF_OK; +} + + +GF_ODCom *gf_odf_new_esd_remove() +{ + GF_ESDRemove *newCom = (GF_ESDRemove *) gf_malloc(sizeof(GF_ESDRemove)); + if (!newCom) return NULL; + newCom->NbESDs = 0; + newCom->ES_ID = NULL; + newCom->tag = GF_ODF_ESD_REMOVE_TAG; + return (GF_ODCom *)newCom; +} + +GF_Err gf_odf_del_esd_remove(GF_ESDRemove *ESDRemove) +{ + if (! ESDRemove) return GF_BAD_PARAM; + if (ESDRemove->ES_ID) gf_free(ESDRemove->ES_ID); + gf_free(ESDRemove); + return GF_OK; +} + + +GF_Err gf_odf_read_esd_remove(GF_BitStream *bs, GF_ESDRemove *esdRem, u32 gf_odf_size_command) +{ + u32 i = 0; + if (! esdRem) return GF_BAD_PARAM; + + esdRem->ODID = gf_bs_read_int(bs, 10); + /*aligned = */gf_bs_read_int(bs, 6); //aligned + + //we have gf_odf_size_command - 2 bytes left, and this is our ES_ID[1...255] + //this works because OD commands are aligned + if (gf_odf_size_command < 2) return GF_ODF_INVALID_DESCRIPTOR; + if (gf_odf_size_command == 2) { + esdRem->NbESDs = 0; + esdRem->ES_ID = NULL; + return GF_OK; + } + esdRem->NbESDs = (gf_odf_size_command - 2) / 2; + esdRem->ES_ID = (u16 *) gf_malloc(sizeof(u16) * esdRem->NbESDs); + if (! esdRem->ES_ID) return GF_OUT_OF_MEM; + for (i = 0; i < esdRem->NbESDs ; i++) { + esdRem->ES_ID[i] = gf_bs_read_int(bs, 16); + } + //OD commands are aligned (but we should already be aligned.... + /*nbBits = */gf_bs_align(bs); + return GF_OK; +} + +GF_Err gf_odf_size_esd_remove(GF_ESDRemove *esdRem, u32 *outSize) +{ + if (!esdRem) return GF_BAD_PARAM; + *outSize = 2 + 2 * esdRem->NbESDs; + return GF_OK; +} +GF_Err gf_odf_write_esd_remove(GF_BitStream *bs, GF_ESDRemove *esdRem) +{ + GF_Err e; + u32 size, i; + if (! esdRem) return GF_BAD_PARAM; + + e = gf_odf_size_esd_remove(esdRem, &size); + if (e) return e; + e = gf_odf_write_base_descriptor(bs, esdRem->tag, size); + if (e) return e; + + gf_bs_write_int(bs, esdRem->ODID, 10); + gf_bs_write_int(bs, 0, 6); //aligned + for (i = 0; i < esdRem->NbESDs ; i++) { + gf_bs_write_int(bs, esdRem->ES_ID[i], 16); + } + //OD commands are aligned (but we are already aligned.... + gf_bs_align(bs); + return GF_OK; +} + + +GF_ODCom *gf_odf_new_ipmp_remove() +{ + GF_IPMPRemove *newCom = (GF_IPMPRemove *) gf_malloc(sizeof(GF_IPMPRemove)); + if (!newCom) return NULL; + newCom->IPMPDescID =NULL; + newCom->NbIPMPDs = 0; + newCom->tag = GF_ODF_IPMP_REMOVE_TAG; + return (GF_ODCom *)newCom; +} + +GF_Err gf_odf_del_ipmp_remove(GF_IPMPRemove *IPMPDRemove) +{ + if (! IPMPDRemove) return GF_BAD_PARAM; + if (IPMPDRemove->IPMPDescID) gf_free(IPMPDRemove->IPMPDescID); + gf_free(IPMPDRemove); + return GF_OK; +} + +GF_Err gf_odf_read_ipmp_remove(GF_BitStream *bs, GF_IPMPRemove *ipmpRem, u32 gf_odf_size_command) +{ + u32 i; + if (! ipmpRem) return GF_BAD_PARAM; + + //we have gf_odf_size_command bytes left, and this is our IPMPD_ID[1...255] + //this works because OD commands are aligned + if (!gf_odf_size_command) return GF_OK; + + ipmpRem->NbIPMPDs = gf_odf_size_command; + ipmpRem->IPMPDescID = (u8 *) gf_malloc(sizeof(u8) * ipmpRem->NbIPMPDs); + if (! ipmpRem->IPMPDescID) return GF_OUT_OF_MEM; + + for (i = 0; i < ipmpRem->NbIPMPDs; i++) { + ipmpRem->IPMPDescID[i] = gf_bs_read_int(bs, 8); + } + //OD commands are aligned + gf_bs_align(bs); + return GF_OK; +} + +GF_Err gf_odf_size_ipmp_remove(GF_IPMPRemove *ipmpRem, u32 *outSize) +{ + if (!ipmpRem) return GF_BAD_PARAM; + + *outSize = ipmpRem->NbIPMPDs; + return GF_OK; +} +GF_Err gf_odf_write_ipmp_remove(GF_BitStream *bs, GF_IPMPRemove *ipmpRem) +{ + GF_Err e; + u32 size, i; + if (! ipmpRem) return GF_BAD_PARAM; + + e = gf_odf_size_ipmp_remove(ipmpRem, &size); + if (e) return e; + e = gf_odf_write_base_descriptor(bs, ipmpRem->tag, size); + if (e) return e; + + for (i = 0; i < ipmpRem->NbIPMPDs; i++) { + gf_bs_write_int(bs, ipmpRem->IPMPDescID[i], 8); + } + //OD commands are aligned + gf_bs_align(bs); + return GF_OK; +} + +GF_ODCom *gf_odf_new_ipmp_update() +{ + GF_IPMPUpdate *newCom = (GF_IPMPUpdate *) gf_malloc(sizeof(GF_IPMPUpdate)); + if (!newCom) return NULL; + newCom->IPMPDescList = gf_list_new(); + if (! newCom->IPMPDescList) { + gf_free(newCom); + return NULL; + } + newCom->tag = GF_ODF_IPMP_UPDATE_TAG; + return (GF_ODCom *)newCom; +} + +GF_Err gf_odf_del_ipmp_update(GF_IPMPUpdate *IPMPDUpdate) +{ + GF_Err e; + if (! IPMPDUpdate) return GF_BAD_PARAM; + while (gf_list_count(IPMPDUpdate->IPMPDescList)) { + GF_Descriptor *tmp = (GF_Descriptor*)gf_list_get(IPMPDUpdate->IPMPDescList, 0); + e = gf_odf_delete_descriptor(tmp); + assert(e == GF_OK); + e = gf_list_rem(IPMPDUpdate->IPMPDescList, 0); + assert(e == GF_OK); + } + gf_list_del(IPMPDUpdate->IPMPDescList); + gf_free(IPMPDUpdate); + return GF_OK; +} + +GF_Err AddToIPMPDUpdate(GF_IPMPUpdate *ipmpUp, GF_Descriptor *desc) +{ + if (! ipmpUp) return GF_BAD_PARAM; + if (!desc) return GF_OK; + + switch (desc->tag) { + case GF_ODF_IPMP_TAG: + return gf_list_add(ipmpUp->IPMPDescList, desc); + default: + gf_odf_delete_descriptor(desc); + return GF_OK; + } +} + +GF_Err gf_odf_read_ipmp_update(GF_BitStream *bs, GF_IPMPUpdate *ipmpUp, u32 gf_odf_size_command) +{ + GF_Descriptor *tmp; + u32 tmpSize = 0, nbBytes = 0; + GF_Err e = GF_OK; + if (! ipmpUp) return GF_BAD_PARAM; + + while (nbBytes < gf_odf_size_command) { + e = gf_odf_parse_descriptor(bs, &tmp, &tmpSize); + if (e) return e; + e = AddToIPMPDUpdate(ipmpUp, tmp); + if (e) return e; + nbBytes += tmpSize + gf_odf_size_field_size(tmpSize); + } + //OD commands are aligned + gf_bs_align(bs); + if (nbBytes != gf_odf_size_command) return GF_ODF_INVALID_COMMAND; + return e; +} + + +GF_Err gf_odf_size_ipmp_update(GF_IPMPUpdate *ipmpUp, u32 *outSize) +{ + GF_Descriptor *tmp; + u32 i, tmpSize; + if (!ipmpUp) return GF_BAD_PARAM; + + *outSize = 0; + i=0; + while ((tmp = (GF_Descriptor *)gf_list_enum(ipmpUp->IPMPDescList, &i))) { + gf_odf_size_descriptor(tmp, &tmpSize); + *outSize += tmpSize + gf_odf_size_field_size(tmpSize); + } + return GF_OK; +} +GF_Err gf_odf_write_ipmp_update(GF_BitStream *bs, GF_IPMPUpdate *ipmpUp) +{ + GF_Err e; + GF_Descriptor *tmp; + u32 size, i; + if (! ipmpUp) return GF_BAD_PARAM; + + e = gf_odf_size_ipmp_update(ipmpUp, &size); + if (e) return e; + e = gf_odf_write_base_descriptor(bs, ipmpUp->tag, size); + if (e) return e; + + i=0; + while ((tmp = (GF_Descriptor *)gf_list_enum(ipmpUp->IPMPDescList, &i))) { + e = gf_odf_write_descriptor(bs, tmp); + if (e) return e; + } + //OD commands are aligned + gf_bs_align(bs); + return GF_OK; +} diff --git a/src/gpacmp4/os_divers.c b/src/gpacmp4/os_divers.c new file mode 100644 index 00000000..66033304 --- /dev/null +++ b/src/gpacmp4/os_divers.c @@ -0,0 +1,1801 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / common tools sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ +#include "../disable_warnings.h" +#include + +#if defined(_WIN32_WCE) + +#include +#include +#include + +#if !defined(__GNUC__) +#pragma comment(lib, "toolhelp") +#endif + +#elif defined(WIN32) + +#include +#include +#include +#include +#include + +#if !defined(__GNUC__) +#pragma comment(lib, "winmm") +#endif + +#else + +#include +#include +#include +#include +#include +#include +#include + +#ifndef __BEOS__ +#include +#endif + + +#define SLEEP_ABS_SELECT 1 + +static u32 sys_start_time = 0; +#endif + + +#ifndef _WIN32_WCE +#include +#endif + + +#ifndef WIN32 +u32 gf_sys_clock() +{ + struct timeval now; + gettimeofday(&now, NULL); + return ( (now.tv_sec)*1000 + (now.tv_usec) / 1000) - sys_start_time; +} +#endif + + +void gf_sleep(u32 ms) +{ +#ifdef WIN32 + Sleep(ms); +#else + s32 sel_err; + struct timeval tv; + +#ifndef SLEEP_ABS_SELECT + u32 prev, now, elapsed; +#endif + +#ifdef SLEEP_ABS_SELECT + tv.tv_sec = ms/1000; + tv.tv_usec = (ms%1000)*1000; +#else + prev = gf_sys_clock(); +#endif + + do { + errno = 0; + +#ifndef SLEEP_ABS_SELECT + now = gf_sys_clock(); + elapsed = (now - prev); + if ( elapsed >= ms ) { + break; + } + prev = now; + ms -= elapsed; + tv.tv_sec = ms/1000; + tv.tv_usec = (ms%1000)*1000; +#endif + + sel_err = select(0, NULL, NULL, NULL, &tv); + } while ( sel_err && (errno == EINTR) ); +#endif +} + +#ifndef gettimeofday +#ifdef _WIN32_WCE + +/* + Conversion code for WinCE from pthreads WinCE + (FILETIME in Win32 is from jan 1, 1601) + time between jan 1, 1601 and jan 1, 1970 in units of 100 nanoseconds +*/ +#define TIMESPEC_TO_FILETIME_OFFSET (((LONGLONG)27111902 << 32) + (LONGLONG)3577643008) + +s32 gettimeofday(struct timeval *tp, void *tz) +{ + FILETIME ft; + SYSTEMTIME st; + s32 val; + + GetSystemTime(&st); + SystemTimeToFileTime(&st, &ft); + + val = (s32) ((*(LONGLONG *) &ft - TIMESPEC_TO_FILETIME_OFFSET) / 10000000); + tp->tv_sec = (u32) val; + val = (s32 ) ((*(LONGLONG *) &ft - TIMESPEC_TO_FILETIME_OFFSET - ((LONGLONG) val * (LONGLONG) 10000000)) * 100); + tp->tv_usec = val; + return 0; +} + +#elif defined(WIN32) + +s32 gettimeofday(struct timeval *tp, void *tz) +{ + struct _timeb timebuffer; + + _ftime( &timebuffer ); + tp->tv_sec = (long) (timebuffer.time); + tp->tv_usec = timebuffer.millitm * 1000; + return 0; +} +#endif + +#endif + +#ifdef _WIN32_WCE + +void CE_Assert(u32 valid, char *file, u32 line) +{ + if (!valid) { + char szBuf[2048]; + u16 wcBuf[2048]; + sprintf(szBuf, "File %s : line %d", file, line); + CE_CharToWide(szBuf, wcBuf); + MessageBox(NULL, wcBuf, _T("GPAC Assertion Failure"), MB_OK); + exit(EXIT_FAILURE); + } +} + +void CE_WideToChar(unsigned short *w_str, char *str) +{ + WideCharToMultiByte(CP_ACP, 0, w_str, -1, str, GF_MAX_PATH, NULL, NULL); +} + +void CE_CharToWide(char *str, unsigned short *w_str) +{ + MultiByteToWideChar(CP_ACP, 0, str, -1, w_str, GF_MAX_PATH); +} + + +#endif + +GF_Err gf_delete_file(const char *fileName) +{ +#if defined(_WIN32_WCE) + TCHAR swzName[MAX_PATH]; + CE_CharToWide((char*)fileName, swzName); + return (DeleteFile(swzName)==0) ? GF_IO_ERR : GF_OK; +#elif defined(WIN32) + /* success if != 0 */ + return (DeleteFile(fileName)==0) ? GF_IO_ERR : GF_OK; +#else + /* success is == 0 */ + return ( remove(fileName) == 0) ? GF_OK : GF_IO_ERR; +#endif +} + +void gf_move_file(const char *fileName, const char *newFileName) +{ +#if defined(_WIN32_WCE) + TCHAR swzName[MAX_PATH]; + TCHAR swzNewName[MAX_PATH]; + CE_CharToWide((char*)fileName, swzName); + CE_CharToWide((char*)newFileName, swzNewName); + MoveFile(swzName, swzNewName); +#elif defined(WIN32) + MoveFile(fileName, newFileName); +#else + rename(fileName, newFileName); +#endif +} + +void gf_rand_init(Bool Reset) +{ + if (Reset) { + srand(1); + } else { +#if defined(_WIN32_WCE) + srand( (u32) GetTickCount() ); +#else + srand( (u32) time(NULL) ); +#endif + } +} + +u32 gf_rand() +{ + return rand(); +} + +#ifndef _WIN32_WCE +#include +#endif + +u64 gf_file_modification_time(const char *filename) +{ +#if defined(_WIN32_WCE) + WCHAR _file[GF_MAX_PATH]; + WIN32_FIND_DATA FindData; + HANDLE fh; + ULARGE_INTEGER uli; + ULONGLONG time_ms; + BOOL ret; + CE_CharToWide((char *) filename, _file); + fh = FindFirstFile(_file, &FindData); + if (fh == INVALID_HANDLE_VALUE) return 0; + uli.LowPart = FindData.ftLastWriteTime.dwLowDateTime; + uli.HighPart = FindData.ftLastWriteTime.dwHighDateTime; + ret = FindClose(fh); + if (!ret) { + DWORD err = GetLastError(); + GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[core] FindClose() in gf_file_modification_time() returned the following error code: %d\n", err)); + } + time_ms = uli.QuadPart/10000; + return time_ms; +#elif defined(WIN32) && !defined(__GNUC__) + struct _stat64 sb; + if (_stat64(filename, &sb) != 0) return 0; + return sb.st_mtime; +#else + struct stat sb; + if (stat(filename, &sb) != 0) return 0; + return sb.st_mtime; +#endif + return 0; +} + +FILE *gf_temp_file_new() +{ +#if defined(_WIN32_WCE) + TCHAR pPath[MAX_PATH+1]; + TCHAR pTemp[MAX_PATH+1]; + if (!GetTempPath(MAX_PATH, pPath)) { + pPath[0] = '.'; + pPath[1] = '.'; + } + if (GetTempFileName(pPath, TEXT("git"), 0, pTemp)) + return _wfopen(pTemp, TEXT("w+b")); + + return NULL; +#elif defined(WIN32) + char tmp[MAX_PATH], t_file[100]; + FILE *res = tmpfile(); + if (res) return res; + /*tmpfile() may fail under vista ...*/ + if (!GetEnvironmentVariable("TEMP",tmp,MAX_PATH)) return NULL; + sprintf(t_file, "\\gpac_%08x.tmp", (u32) tmp); + strcat(tmp, t_file); + return gf_f64_open(tmp, "w+b"); +#else + return tmpfile(); +#endif +} + + +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 +} + +void gf_get_user_name(char *buf, u32 buf_size) +{ + strcpy(buf, "mpeg4-user"); + +#if 0 + s32 len; + char *t; + strcpy(buf, ""); + len = 1024; + GetUserName(buf, &len); + if (!len) { + t = getenv("USER"); + if (t) strcpy(buf, t); + } +#endif +#if 0 + struct passwd *pw; + pw = getpwuid(getuid()); + strcpy(buf, ""); + if (pw && pw->pw_name) strcpy(name, pw->pw_name); +#endif +} + + +/*enumerate directories*/ +GF_Err gf_enum_directory(const char *dir, Bool enum_directory, gf_enum_dir_item enum_dir_fct, void *cbck, const char *filter) +{ + char item_path[GF_MAX_PATH]; + +#if defined(_WIN32_WCE) + char _path[GF_MAX_PATH]; + unsigned short path[GF_MAX_PATH]; + unsigned short w_filter[GF_MAX_PATH]; + char file[GF_MAX_PATH]; +#else + char path[GF_MAX_PATH], *file; +#endif + +#ifdef WIN32 + WIN32_FIND_DATA FindData; + HANDLE SearchH; +#else + DIR *the_dir; + struct dirent* the_file; + struct stat st; +#endif + + if (!dir || !enum_dir_fct) return GF_BAD_PARAM; + + if (filter && (!strcmp(filter, "*") || !filter[0])) filter=NULL; + + if (!strcmp(dir, "/")) { +#if defined(WIN32) && !defined(_WIN32_WCE) + u32 len; + char *drives, *volume; + len = GetLogicalDriveStrings(0, NULL); + drives = gf_malloc(sizeof(char)*(len+1)); + drives[0]=0; + GetLogicalDriveStrings(len, drives); + len = strlen(drives); + volume = drives; + while (len) { + enum_dir_fct(cbck, volume, ""); + volume += len+1; + len = strlen(volume); + } + gf_free(drives); + return GF_OK; +#elif defined(__SYMBIAN32__) + RFs iFs; + TDriveList aList; + iFs.Connect(); + iFs.DriveList(aList); + for (TInt i=0;id_name, "..")) goto next; + if (the_file->d_name[0] == '.') goto next; +#endif + +#ifdef WIN32 + if (!enum_directory && (FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) goto next; + if (enum_directory && !(FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) goto next; +#endif + + if (filter) { +#if defined (_WIN32_WCE) + short ext[30]; + short *sep = wcsrchr(FindData.cFileName, (wchar_t) '.'); + if (!sep) goto next; + wcscpy(ext, sep+1); + wcslwr(ext); + if (!wcsstr(w_filter, ext)) goto next; +#elif defined(WIN32) + char ext[30]; + char *sep = strrchr(FindData.cFileName, '.'); + if (!sep) goto next; + strcpy(ext, sep+1); + strlwr(ext); + if (!strstr(filter, ext)) goto next; +#else + char ext[30]; + char *sep = strrchr(the_file->d_name, '.'); + if (!sep) goto next; + strcpy(ext, sep+1); + strlwr(ext); + if (!strstr(filter, sep+1)) goto next; +#endif + } + +#if defined (_WIN32_WCE) + CE_WideToChar(FindData.cFileName, file); + strcpy(item_path, _path); + strcat(item_path, file); +#elif defined(WIN32) + strcpy(item_path, path); + strcat(item_path, FindData.cFileName); + file = FindData.cFileName; +#else + strcpy(item_path, path); + strcat(item_path, the_file->d_name); + GF_LOG(GF_LOG_DEBUG, GF_LOG_CORE, ("[Core] Checking file %s for enum\n", item_path)); + + if (stat( item_path, &st ) != 0) goto next; + if (enum_directory && ( (st.st_mode & S_IFMT) != S_IFDIR)) goto next; + if (!enum_directory && ((st.st_mode & S_IFMT) == S_IFDIR)) goto next; + file = the_file->d_name; +#endif + if (enum_dir_fct(cbck, file, item_path)) { +#ifdef WIN32 + BOOL ret = FindClose(SearchH); + if (!ret) { + DWORD err = GetLastError(); + GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[core] FindClose() in gf_enum_directory() returned(1) the following error code: %d\n", err)); + } +#endif + break; + } + +next: +#ifdef WIN32 + if (!FindNextFile(SearchH, &FindData)) { + BOOL ret = FindClose(SearchH); + if (!ret) { + DWORD err = GetLastError(); + GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[core] FindClose() in gf_enum_directory() returned(2) the following error code: %d\n", err)); + } + break; + } +#else + the_file = readdir(the_dir); +#endif + } +#ifndef WIN32 + closedir(the_dir); +#endif + return GF_OK; +} + + +#ifndef WIN32 +char * my_str_upr(char *str) +{ + u32 i; + for (i=0; i +#include + +Bool gf_prompt_has_input() +{ + return kbhit(); +} +char gf_prompt_get_char() +{ + return getchar(); +} +void gf_prompt_set_echo_off(Bool echo_off) +{ + DWORD flags; + HANDLE hStdin = GetStdHandle(STD_INPUT_HANDLE); + BOOL ret = GetConsoleMode(hStdin, &flags); + if (!ret) { + DWORD err = GetLastError(); + GF_LOG(GF_LOG_ERROR, GF_LOG_CONSOLE, ("[Console] GetConsoleMode() return with the following error code: %d\n", err)); + } + if (echo_off) flags &= ~ENABLE_ECHO_INPUT; + else flags |= ENABLE_ECHO_INPUT; + SetConsoleMode(hStdin, flags); +} +#endif +#else +/*linux kbhit/getchar- borrowed on debian mailing lists, (author Mike Brownlow)*/ +#include + +static struct termios t_orig, t_new; +static s32 ch_peek = -1; + +static void init_keyboard() +{ + tcgetattr(0, &t_orig); + t_new = t_orig; + t_new.c_lflag &= ~ICANON; + t_new.c_lflag &= ~ECHO; + t_new.c_lflag &= ~ISIG; + t_new.c_cc[VMIN] = 1; + t_new.c_cc[VTIME] = 0; + tcsetattr(0, TCSANOW, &t_new); +} +static void close_keyboard(Bool new_line) +{ + tcsetattr(0,TCSANOW, &t_orig); + if (new_line) fprintf(stdout, "\n"); +} + +void gf_prompt_set_echo_off(Bool echo_off) +{ + init_keyboard(); + if (echo_off) t_orig.c_lflag &= ~ECHO; + else t_orig.c_lflag |= ECHO; + close_keyboard(0); +} + +Bool gf_prompt_has_input() +{ + u8 ch; + s32 nread; + + init_keyboard(); + if (ch_peek != -1) return 1; + t_new.c_cc[VMIN]=0; + tcsetattr(0, TCSANOW, &t_new); + nread = read(0, &ch, 1); + t_new.c_cc[VMIN]=1; + tcsetattr(0, TCSANOW, &t_new); + if(nread == 1) { + ch_peek = ch; + return 1; + } + close_keyboard(0); + return 0; +} + +char gf_prompt_get_char() +{ + char ch; + if (ch_peek != -1) { + ch = ch_peek; + ch_peek = -1; + close_keyboard(1); + return ch; + } + if (0==read(0,&ch,1)) + ch = 0; + close_keyboard(1); + return ch; +} + +#endif + + + + +static u32 sys_init = 0; +static u32 last_update_time = 0; +static u64 last_process_k_u_time = 0; +GF_SystemRTInfo the_rti; + + +#if defined(_WIN32_WCE) +static LARGE_INTEGER frequency , init_counter; +static u64 last_total_k_u_time = 0; +static u32 mem_usage_at_startup = 0; + + +#ifndef GetCurrentPermissions +DWORD GetCurrentPermissions(); +#endif +#ifndef SetProcPermissions +void SetProcPermissions(DWORD ); +#endif + +#elif defined(WIN32) +static LARGE_INTEGER frequency , init_counter; +static u64 last_proc_idle_time = 0; +static u64 last_proc_k_u_time = 0; + +static HINSTANCE psapi_hinst = NULL; +typedef BOOL(WINAPI* NTGetSystemTimes)(VOID *,VOID *,VOID *); +NTGetSystemTimes MyGetSystemTimes = NULL; +typedef BOOL(WINAPI* NTGetProcessMemoryInfo)(HANDLE,VOID *,DWORD); +NTGetProcessMemoryInfo MyGetProcessMemoryInfo = NULL; +typedef int(WINAPI* NTQuerySystemInfo)(ULONG,PVOID,ULONG,PULONG); +NTQuerySystemInfo MyQuerySystemInfo = NULL; + +#ifndef PROCESS_MEMORY_COUNTERS +typedef struct _PROCESS_MEMORY_COUNTERS +{ + DWORD cb; + DWORD PageFaultCount; + SIZE_T PeakWorkingSetSize; + SIZE_T WorkingSetSize; + SIZE_T QuotaPeakPagedPoolUsage; + SIZE_T QuotaPagedPoolUsage; + SIZE_T QuotaPeakNonPagedPoolUsage; + SIZE_T QuotaNonPagedPoolUsage; + SIZE_T PagefileUsage; + SIZE_T PeakPagefileUsage; +} PROCESS_MEMORY_COUNTERS; +#endif + +#ifndef SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION +typedef struct _SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION +{ + LARGE_INTEGER IdleTime; + LARGE_INTEGER KernelTime; + LARGE_INTEGER UserTime; + LARGE_INTEGER Reserved1[2]; + ULONG Reserved2; +} SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION; +#endif + + +#else + +static u64 last_cpu_u_k_time = 0; +static u64 last_cpu_idle_time = 0; +static u64 mem_at_startup = 0; + +#endif + +#ifdef WIN32 +static u32 (*OS_GetSysClock)(); + +u32 gf_sys_clock() +{ + return OS_GetSysClock(); +} +#endif + + +#ifdef WIN32 + +static u32 OS_GetSysClockHIGHRES() +{ + LARGE_INTEGER now; + QueryPerformanceCounter(&now); + now.QuadPart -= init_counter.QuadPart; + return (u32) ((now.QuadPart * 1000) / frequency.QuadPart); +} + +static u32 OS_GetSysClockNORMAL() +{ +#ifdef _WIN32_WCE + return GetTickCount(); +#else + return timeGetTime(); +#endif +} + +#endif /* WIN32 */ + +#if defined(__sh__) +/* Avoid exception for denormalized floating point values */ +static int +sh4_get_fpscr() +{ + int ret; + asm volatile ("sts fpscr,%0" : "=r" (ret)); + return ret; +} + +static void +sh4_put_fpscr(int nv) +{ + asm volatile ("lds %0,fpscr" : : "r" (nv)); +} + +#define SH4_FPSCR_FR 0x00200000 +#define SH4_FPSCR_SZ 0x00100000 +#define SH4_FPSCR_PR 0x00080000 +#define SH4_FPSCR_DN 0x00040000 +#define SH4_FPSCR_RN 0x00000003 +#define SH4_FPSCR_RN_N 0 +#define SH4_FPSCR_RN_Z 1 + +extern int __fpscr_values[2]; + +void +sh4_change_fpscr(int off, int on) +{ + int b = sh4_get_fpscr(); + off = ~off; + off |= 0x00180000; + on &= ~ 0x00180000; + b &= off; + b |= on; + sh4_put_fpscr(b); + __fpscr_values[0] &= off; + __fpscr_values[0] |= on; + __fpscr_values[1] &= off; + __fpscr_values[1] |= on; +} + +#endif + +#ifdef GPAC_MEMORY_TRACKING +void gf_mem_enable_tracker(); +#endif + +static u64 memory_at_gpac_startup = 0; + +void gf_sys_init(Bool enable_memory_tracker) +{ + if (!sys_init) { +#if defined(_WIN32_WCE) + MEMORYSTATUS ms; +#endif + + if (enable_memory_tracker) { +#ifdef GPAC_MEMORY_TRACKING + gf_mem_enable_tracker(); +#endif + } + /*by default log subsystem is initialized to error on all tools, and info on console to debug scripts*/ + gf_log_set_tool_level(GF_LOG_ALL, GF_LOG_ERROR); + gf_log_set_tool_level(GF_LOG_CONSOLE, GF_LOG_INFO); + + +#if defined(__sh__) + /* Round all denormalized floatting point number to 0.0 */ + sh4_change_fpscr(0,SH4_FPSCR_DN) ; +#endif + +#if defined(WIN32) + frequency.QuadPart = 0; + /*clock setup*/ + if (QueryPerformanceFrequency(&frequency)) { + QueryPerformanceCounter(&init_counter); + OS_GetSysClock = OS_GetSysClockHIGHRES; + GF_LOG(GF_LOG_INFO, GF_LOG_CORE, ("[core] using WIN32 performance timer\n")); + } else { + OS_GetSysClock = OS_GetSysClockNORMAL; + GF_LOG(GF_LOG_INFO, GF_LOG_CORE, ("[core] using WIN32 regular timer\n")); + } + +#ifndef _WIN32_WCE + timeBeginPeriod(1); +#endif + + GF_LOG(GF_LOG_INFO, GF_LOG_CORE, ("[core] checking for run-time info tools")); +#if defined(_WIN32_WCE) + last_total_k_u_time = last_process_k_u_time = 0; + last_update_time = 0; + memset(&the_rti, 0, sizeof(GF_SystemRTInfo)); + the_rti.pid = GetCurrentProcessId(); + GlobalMemoryStatus(&ms); + mem_usage_at_startup = ms.dwAvailPhys; +#else + /*cpu usage tools are buried in win32 dlls...*/ + MyGetSystemTimes = (NTGetSystemTimes) GetProcAddress(GetModuleHandle("kernel32.dll"), "GetSystemTimes"); + if (!MyGetSystemTimes) { + MyQuerySystemInfo = (NTQuerySystemInfo) GetProcAddress(GetModuleHandle("ntdll.dll"), "NtQuerySystemInformation"); + if (MyQuerySystemInfo) { + GF_LOG(GF_LOG_INFO, GF_LOG_CORE, (" - CPU: QuerySystemInformation")); + } + } else { + GF_LOG(GF_LOG_INFO, GF_LOG_CORE, (" - CPU: GetSystemsTimes")); + } + psapi_hinst = LoadLibrary("psapi.dll"); + MyGetProcessMemoryInfo = (NTGetProcessMemoryInfo) GetProcAddress(psapi_hinst, "GetProcessMemoryInfo"); + if (MyGetProcessMemoryInfo) { + GF_LOG(GF_LOG_INFO, GF_LOG_CORE, (" - memory: GetProcessMemoryInfo")); + } + last_process_k_u_time = last_proc_idle_time = last_proc_k_u_time = 0; + last_update_time = 0; + memset(&the_rti, 0, sizeof(GF_SystemRTInfo)); + the_rti.pid = GetCurrentProcessId(); +#endif + GF_LOG(GF_LOG_INFO, GF_LOG_CORE, ("\n")); + +#else + /*linux threads...*/ + last_process_k_u_time = 0; + last_cpu_u_k_time = last_cpu_idle_time = 0; + last_update_time = 0; + memset(&the_rti, 0, sizeof(GF_SystemRTInfo)); + the_rti.pid = getpid(); + sys_start_time = gf_sys_clock(); +#endif + GF_LOG(GF_LOG_INFO, GF_LOG_CORE, ("[core] process id %d\n", the_rti.pid)); + +#ifndef _WIN32_WCE + setlocale( LC_NUMERIC, "C" ); +#endif + } + sys_init += 1; + + + /*init RTI stats*/ + if (!memory_at_gpac_startup) { + GF_SystemRTInfo rti; + gf_sys_get_rti(500, &rti, GF_RTI_SYSTEM_MEMORY_ONLY); + memory_at_gpac_startup = rti.physical_memory_avail; + } +} + +void gf_sys_close() +{ + if (sys_init > 0) { + sys_init --; + if (sys_init) return; + /*prevent any call*/ + last_update_time = 0xFFFFFFFF; + +#if defined(WIN32) && !defined(_WIN32_WCE) + timeEndPeriod(1); + + MyGetSystemTimes = NULL; + MyGetProcessMemoryInfo = NULL; + MyQuerySystemInfo = NULL; + if (psapi_hinst) FreeLibrary(psapi_hinst); + psapi_hinst = NULL; +#endif + +#ifdef GPAC_MEMORY_TRACKING + gf_memory_print(); +#endif + } +} + +#ifdef GPAC_MEMORY_TRACKING +extern size_t gpac_allocated_memory; +extern size_t gpac_nb_alloc_blocs; +#endif + +/*CPU and Memory Usage*/ +#ifdef WIN32 + +Bool gf_sys_get_rti_os(u32 refresh_time_ms, GF_SystemRTInfo *rti, u32 flags) +{ +#if defined(_WIN32_WCE) + THREADENTRY32 tentry; + u64 total_cpu_time, process_cpu_time; + DWORD orig_perm; +#endif + MEMORYSTATUS ms; + u64 creation, exit, kernel, user, process_k_u_time, proc_idle_time, proc_k_u_time; + u32 nb_threads, entry_time; + HANDLE hSnapShot; + + assert(sys_init); + + if (!rti) return 0; + + proc_idle_time = proc_k_u_time = process_k_u_time = 0; + nb_threads = 0; + + entry_time = gf_sys_clock(); + if (last_update_time && (entry_time - last_update_time < refresh_time_ms)) { + memcpy(rti, &the_rti, sizeof(GF_SystemRTInfo)); + return 0; + } + + if (flags & GF_RTI_SYSTEM_MEMORY_ONLY) { + memset(rti, 0, sizeof(GF_SystemRTInfo)); + rti->sampling_instant = last_update_time; + GlobalMemoryStatus(&ms); + rti->physical_memory = ms.dwTotalPhys; + rti->physical_memory_avail = ms.dwAvailPhys; +#ifdef GPAC_MEMORY_TRACKING + rti->gpac_memory = (u64) gpac_allocated_memory; +#endif + return 1; + } + +#if defined (_WIN32_WCE) + + total_cpu_time = process_cpu_time = 0; + + /*get a snapshot of all running threads*/ + orig_perm = GetCurrentPermissions(); + SetProcPermissions(0xFFFFFFFF); + hSnapShot = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0); + if (hSnapShot) { + tentry.dwSize = sizeof(THREADENTRY32); + the_rti.thread_count = 0; + /*note we always act as if GF_RTI_ALL_PROCESSES_TIMES flag is set, since there is no other way + to enumerate threads from a process, and GetProcessTimes doesn't exist on CE*/ + if (Thread32First(hSnapShot, &tentry)) { + do { + /*get thread times*/ + if (GetThreadTimes( (HANDLE) tentry.th32ThreadID, (FILETIME *) &creation, (FILETIME *) &exit, (FILETIME *) &kernel, (FILETIME *) &user)) { + total_cpu_time += user + kernel; + if (tentry.th32OwnerProcessID==the_rti.pid) { + process_cpu_time += user + kernel; + the_rti.thread_count ++; + } + } + } while (Thread32Next(hSnapShot, &tentry)); + } + CloseToolhelp32Snapshot(hSnapShot); + } + + if (flags & GF_RTI_PROCESS_MEMORY) { + HEAPLIST32 hlentry; + HEAPENTRY32 hentry; + the_rti.process_memory = 0; + hlentry.dwSize = sizeof(HEAPLIST32); + hSnapShot = CreateToolhelp32Snapshot(TH32CS_SNAPHEAPLIST, the_rti.pid); + if (hSnapShot && Heap32ListFirst(hSnapShot, &hlentry)) { + do { + hentry.dwSize = sizeof(hentry); + if (Heap32First(hSnapShot, &hentry, hlentry.th32ProcessID, hlentry.th32HeapID)) { + do { + the_rti.process_memory += hentry.dwBlockSize; + } while (Heap32Next(hSnapShot, &hentry)); + } + } while (Heap32ListNext(hSnapShot, &hlentry)); + } + CloseToolhelp32Snapshot(hSnapShot); + } + SetProcPermissions(orig_perm); + total_cpu_time /= 10; + process_cpu_time /= 10; + +#else + /*XP-SP1 and Win2003 servers only have GetSystemTimes support. This will give a better estimation + of CPU usage since we can take into account the idle time*/ + if (MyGetSystemTimes) { + u64 u_time; + MyGetSystemTimes(&proc_idle_time, &proc_k_u_time, &u_time); + proc_k_u_time += u_time; + proc_idle_time /= 10; + proc_k_u_time /= 10; + } + /*same rq for NtQuerySystemInformation*/ + else if (MyQuerySystemInfo) { + DWORD ret; + SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION info; + MyQuerySystemInfo(0x8 /*SystemProcessorPerformanceInformation*/, &info, sizeof(info), &ret); + if (ret && (ret<=sizeof(info))) { + proc_idle_time = info.IdleTime.QuadPart / 10; + proc_k_u_time = (info.KernelTime.QuadPart + info.UserTime.QuadPart) / 10; + } + } + /*no special API available, ONLY FETCH TIMES if requested (may eat up some time)*/ + else if (flags & GF_RTI_ALL_PROCESSES_TIMES) { + PROCESSENTRY32 pentry; + /*get a snapshot of all running threads*/ + hSnapShot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); + if (!hSnapShot) return 0; + pentry.dwSize = sizeof(PROCESSENTRY32); + if (Process32First(hSnapShot, &pentry)) { + do { + HANDLE procH = NULL; + if (pentry.th32ProcessID) procH = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pentry.th32ProcessID); + if (procH && GetProcessTimes(procH, (FILETIME *) &creation, (FILETIME *) &exit, (FILETIME *) &kernel, (FILETIME *) &user) ) { + user += kernel; + proc_k_u_time += user; + if (pentry.th32ProcessID==the_rti.pid) { + process_k_u_time = user; + nb_threads = pentry.cntThreads; + } + } + if (procH) CloseHandle(procH); + } while (Process32Next(hSnapShot, &pentry)); + } + CloseHandle(hSnapShot); + proc_k_u_time /= 10; + } + + + if (!process_k_u_time) { + HANDLE procH = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, the_rti.pid); + if (procH && GetProcessTimes(procH, (FILETIME *) &creation, (FILETIME *) &exit, (FILETIME *) &kernel, (FILETIME *) &user) ) { + process_k_u_time = user + kernel; + } + if (procH) CloseHandle(procH); + if (!process_k_u_time) return 0; + } + process_k_u_time /= 10; + + /*this won't cost a lot*/ + if (MyGetProcessMemoryInfo) { + PROCESS_MEMORY_COUNTERS pmc; + HANDLE procH = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, the_rti.pid); + MyGetProcessMemoryInfo(procH, &pmc, sizeof (pmc)); + the_rti.process_memory = pmc.WorkingSetSize; + if (procH) CloseHandle(procH); + } + /*THIS IS VERY HEAVY (eats up mem and time) - only perform if requested*/ + else if (flags & GF_RTI_PROCESS_MEMORY) { + HEAPLIST32 hlentry; + HEAPENTRY32 hentry; + the_rti.process_memory = 0; + hlentry.dwSize = sizeof(HEAPLIST32); + hSnapShot = CreateToolhelp32Snapshot(TH32CS_SNAPHEAPLIST, the_rti.pid); + if (hSnapShot && Heap32ListFirst(hSnapShot, &hlentry)) { + do { + hentry.dwSize = sizeof(hentry); + if (Heap32First(&hentry, hlentry.th32ProcessID, hlentry.th32HeapID)) { + do { + the_rti.process_memory += hentry.dwBlockSize; + } while (Heap32Next(&hentry)); + } + } while (Heap32ListNext(hSnapShot, &hlentry)); + } + CloseHandle(hSnapShot); + } +#endif + + the_rti.sampling_instant = last_update_time; + + if (last_update_time) { + the_rti.sampling_period_duration = entry_time - last_update_time; + the_rti.process_cpu_time_diff = (u32) ((process_k_u_time - last_process_k_u_time)/1000); + +#if defined(_WIN32_WCE) + the_rti.total_cpu_time_diff = (u32) ((total_cpu_time - last_total_k_u_time)/1000); + /*we're not that accurate....*/ + if (the_rti.total_cpu_time_diff > the_rti.sampling_period_duration) + the_rti.sampling_period_duration = the_rti.total_cpu_time_diff; + + /*rough values*/ + the_rti.cpu_idle_time = the_rti.sampling_period_duration - the_rti.total_cpu_time_diff; + the_rti.total_cpu_usage = (u32) (100 * the_rti.total_cpu_time_diff / the_rti.sampling_period_duration); + the_rti.process_cpu_usage = (u32) (100*the_rti.process_cpu_time_diff / (the_rti.total_cpu_time_diff + the_rti.cpu_idle_time) ); + +#else + /*oops, we have no choice but to assume 100% cpu usage during this period*/ + if (!proc_k_u_time) { + the_rti.total_cpu_time_diff = the_rti.sampling_period_duration; + proc_k_u_time = last_proc_k_u_time + the_rti.sampling_period_duration; + the_rti.cpu_idle_time = 0; + the_rti.total_cpu_usage = 100; + if (the_rti.sampling_period_duration) + the_rti.process_cpu_usage = (u32) (100*the_rti.process_cpu_time_diff / the_rti.sampling_period_duration); + } else { + u64 samp_sys_time, idle; + the_rti.total_cpu_time_diff = (u32) ((proc_k_u_time - last_proc_k_u_time)/1000); + + /*we're not that accurate....*/ + if (the_rti.total_cpu_time_diff > the_rti.sampling_period_duration) { + the_rti.sampling_period_duration = the_rti.total_cpu_time_diff; + } + + if (!proc_idle_time) + proc_idle_time = last_proc_idle_time + (the_rti.sampling_period_duration - the_rti.total_cpu_time_diff); + + samp_sys_time = proc_k_u_time - last_proc_k_u_time; + idle = proc_idle_time - last_proc_idle_time; + the_rti.cpu_idle_time = (u32) (idle/1000); + if (samp_sys_time) { + the_rti.total_cpu_usage = (u32) ( (samp_sys_time - idle) / (samp_sys_time / 100) ); + the_rti.process_cpu_usage = (u32) (100*the_rti.process_cpu_time_diff / (samp_sys_time/1000)); + } + } +#endif + } + last_update_time = entry_time; + last_process_k_u_time = process_k_u_time; + + GlobalMemoryStatus(&ms); + the_rti.physical_memory = ms.dwTotalPhys; +#ifdef GPAC_MEMORY_TRACKING + the_rti.gpac_memory = (u64) gpac_allocated_memory; +#endif + the_rti.physical_memory_avail = ms.dwAvailPhys; + +#if defined(_WIN32_WCE) + last_total_k_u_time = total_cpu_time; + if (!the_rti.process_memory) the_rti.process_memory = mem_usage_at_startup - ms.dwAvailPhys; +#else + last_proc_idle_time = proc_idle_time; + last_proc_k_u_time = proc_k_u_time; +#endif + + if (!the_rti.gpac_memory) the_rti.gpac_memory = the_rti.process_memory; + + memcpy(rti, &the_rti, sizeof(GF_SystemRTInfo)); + return 1; +} + + +#elif defined(GPAC_CONFIG_DARWIN) && !defined(GPAC_IPHONE) + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ >= 1060 +#include +#else +#include +#endif +#include + +static u64 total_physical_memory = 0; + +Bool gf_sys_get_rti_os(u32 refresh_time_ms, GF_SystemRTInfo *rti, u32 flags) +{ + size_t length; + u32 entry_time, i, percent; + int mib[6]; + int result; + int pagesize; + u64 process_u_k_time; + double utime, stime; + vm_statistics_data_t vmstat; + task_t task; + kern_return_t error; + thread_array_t thread_table; + unsigned table_size; + thread_basic_info_t thi; + thread_basic_info_data_t thi_data; + struct task_basic_info ti; + mach_msg_type_number_t count = HOST_VM_INFO_COUNT, size = sizeof(ti); + + entry_time = gf_sys_clock(); + if (last_update_time && (entry_time - last_update_time < refresh_time_ms)) { + memcpy(rti, &the_rti, sizeof(GF_SystemRTInfo)); + return 0; + } + + mib[0] = CTL_HW; + mib[1] = HW_PAGESIZE; + length = sizeof(pagesize); + if (sysctl(mib, 2, &pagesize, &length, NULL, 0) < 0) { + return 0; + } + + if (host_statistics(mach_host_self(), HOST_VM_INFO, (host_info_t)&vmstat, &count) != KERN_SUCCESS) { + return 0; + } + + the_rti.physical_memory = (vmstat.wire_count + vmstat.active_count + vmstat.inactive_count + vmstat.free_count)* pagesize; + the_rti.physical_memory_avail = vmstat.free_count * pagesize; + + if (!total_physical_memory) { + mib[0] = CTL_HW; + mib[1] = HW_PHYSMEM; + length = sizeof(result); + if (sysctl(mib, 2, &result, &length, NULL, 0) >= 0) { + total_physical_memory = result; + } + } + + error = task_for_pid(mach_task_self(), the_rti.pid, &task); + if (error) { + GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[RTI] Cannot get process task for PID %d: error %d\n", the_rti.pid, error)); + return 0; + } + + error = task_info(mach_task_self(), TASK_BASIC_INFO, (task_info_t)&ti, &size); + if (error) { + GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[RTI] Cannot get process task info (PID %d): error %d\n", the_rti.pid, error)); + return 0; + } + + percent = 0; + utime = ti.user_time.seconds + ti.user_time.microseconds * 1e-6; + stime = ti.system_time.seconds + ti.system_time.microseconds * 1e-6; + error = task_threads(task, &thread_table, &table_size); + if (error != KERN_SUCCESS) { + GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[RTI] Cannot get threads task for PID %d: error %d\n", the_rti.pid, error)); + return 0; + } + thi = &thi_data; + for (i = 0; i != table_size; ++i) { + count = THREAD_BASIC_INFO_COUNT; + error = thread_info(thread_table[i], THREAD_BASIC_INFO, (thread_info_t)thi, &count); + if (error != KERN_SUCCESS) { + mach_error("[RTI] Unexpected thread_info() call return", error); + GF_LOG(GF_LOG_WARNING, GF_LOG_CORE, ("[RTI] Unexpected thread info for PID %d\n", the_rti.pid)); + break; + } + if ((thi->flags & TH_FLAGS_IDLE) == 0) { + utime += thi->user_time.seconds + thi->user_time.microseconds * 1e-6; + stime += thi->system_time.seconds + thi->system_time.microseconds * 1e-6; + percent += (u32) (100 * (double)thi->cpu_usage / TH_USAGE_SCALE); + } + } + error = vm_deallocate(mach_task_self(), (vm_offset_t)thread_table, table_size * sizeof(thread_array_t)); + mach_port_deallocate(mach_task_self(), task); + + process_u_k_time = utime + stime; + + the_rti.sampling_instant = last_update_time; + + if (last_update_time) { + the_rti.sampling_period_duration = (entry_time - last_update_time); + the_rti.process_cpu_time_diff = (process_u_k_time - last_process_k_u_time) * 10; + + the_rti.total_cpu_time_diff = the_rti.sampling_period_duration; + /*TODO*/ + the_rti.cpu_idle_time = 0; + the_rti.total_cpu_usage = 0; + if (!the_rti.process_cpu_time_diff) the_rti.process_cpu_time_diff = the_rti.total_cpu_time_diff; + + the_rti.process_cpu_usage = percent; + } else { + mem_at_startup = the_rti.physical_memory_avail; + } + the_rti.process_memory = mem_at_startup - the_rti.physical_memory_avail; + +#ifdef GPAC_MEMORY_TRACKING + the_rti.gpac_memory = gpac_allocated_memory; +#endif + + last_process_k_u_time = process_u_k_time; + last_cpu_idle_time = 0; + last_update_time = entry_time; + memcpy(rti, &the_rti, sizeof(GF_SystemRTInfo)); + return 1; +} + +//linux +#else + +Bool gf_sys_get_rti_os(u32 refresh_time_ms, GF_SystemRTInfo *rti, u32 flags) +{ + u32 entry_time; + u64 process_u_k_time; + u32 u_k_time, idle_time; +#if 0 + char szProc[100]; +#endif + char line[2048]; + FILE *f; + + assert(sys_init); + + entry_time = gf_sys_clock(); + if (last_update_time && (entry_time - last_update_time < refresh_time_ms)) { + memcpy(rti, &the_rti, sizeof(GF_SystemRTInfo)); + return 0; + } + + u_k_time = idle_time = 0; + f = gf_f64_open("/proc/stat", "r"); + if (f) { + u32 k_time, nice_time, u_time; + if (fgets(line, 128, f) != NULL) { + if (sscanf(line, "cpu %u %u %u %u\n", &u_time, &k_time, &nice_time, &idle_time) == 4) { + u_k_time = u_time + k_time + nice_time; + } + } + fclose(f); + } + + process_u_k_time = 0; + the_rti.process_memory = 0; + + /*FIXME? under LinuxThreads this will only fetch stats for the calling thread, we would have to enumerate /proc to get + the complete CPU usage of all therads of the process...*/ +#if 0 + sprintf(szProc, "/proc/%d/stat", the_rti.pid); + f = gf_f64_open(szProc, "r"); + if (f) { + fflush(f); + if (fgets(line, 2048, f) != NULL) { + char state; + char *start; + long cutime, cstime, priority, nice, itrealvalue, rss; + int exit_signal, processor; + unsigned long flags, minflt, cminflt, majflt, cmajflt, utime, stime,starttime, vsize, rlim, startcode, endcode, startstack, kstkesp, kstkeip, signal, blocked, sigignore, sigcatch, wchan, nswap, cnswap, rem; + int ppid, pgrp ,session, tty_nr, tty_pgrp, res; + start = strchr(line, ')'); + if (start) start += 2; + else { + start = strchr(line, ' '); + start++; + } + res = sscanf(start,"%c %d %d %d %d %d %lu %lu %lu %lu \ +%lu %lu %lu %ld %ld %ld %ld %ld %ld %lu \ +%lu %ld %lu %lu %lu %lu %lu %lu %lu %lu \ +%lu %lu %lu %lu %lu %d %d", + &state, &ppid, &pgrp, &session, &tty_nr, &tty_pgrp, &flags, &minflt, &cminflt, &majflt, + &cmajflt, &utime, &stime, &cutime, &cstime, &priority, &nice, &itrealvalue, &rem, &starttime, + &vsize, &rss, &rlim, &startcode, &endcode, &startstack, &kstkesp, &kstkeip, &signal, &blocked, + &sigignore, &sigcatch, &wchan, &nswap, &cnswap, &exit_signal, &processor); + + if (res) process_u_k_time = (u64) (cutime + cstime); + else { + GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[RTI] PROC %s parse error\n", szProc)); + } + } else { + GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[RTI] error reading pid/stat\n\n", szProc)); + } + fclose(f); + } else { + GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[RTI] cannot open %s\n", szProc)); + } + sprintf(szProc, "/proc/%d/status", the_rti.pid); + f = gf_f64_open(szProc, "r"); + if (f) { + while (fgets(line, 1024, f) != NULL) { + if (!strnicmp(line, "VmSize:", 7)) { + sscanf(line, "VmSize: %"LLD" kB", &the_rti.process_memory); + the_rti.process_memory *= 1024; + } + } + fclose(f); + } else { + GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[RTI] cannot open %s\n", szProc)); + } +#endif + + + the_rti.physical_memory = the_rti.physical_memory_avail = 0; + f = gf_f64_open("/proc/meminfo", "r"); + if (f) { + while (fgets(line, 1024, f) != NULL) { + if (!strnicmp(line, "MemTotal:", 9)) { + sscanf(line, "MemTotal: "LLU" kB", &the_rti.physical_memory); + the_rti.physical_memory *= 1024; + }else if (!strnicmp(line, "MemFree:", 8)) { + sscanf(line, "MemFree: "LLU" kB", &the_rti.physical_memory_avail); + the_rti.physical_memory_avail *= 1024; + break; + } + } + fclose(f); + } else { + GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[RTI] cannot open /proc/meminfo\n")); + } + + + the_rti.sampling_instant = last_update_time; + + if (last_update_time) { + the_rti.sampling_period_duration = (entry_time - last_update_time); + the_rti.process_cpu_time_diff = (process_u_k_time - last_process_k_u_time) * 10; + + /*oops, we have no choice but to assume 100% cpu usage during this period*/ + if (!u_k_time) { + the_rti.total_cpu_time_diff = the_rti.sampling_period_duration; + u_k_time = last_cpu_u_k_time + the_rti.sampling_period_duration; + the_rti.cpu_idle_time = 0; + the_rti.total_cpu_usage = 100; + if (!the_rti.process_cpu_time_diff) the_rti.process_cpu_time_diff = the_rti.total_cpu_time_diff; + the_rti.process_cpu_usage = (u32) ( 100 * the_rti.process_cpu_time_diff / the_rti.sampling_period_duration); + } else { + u64 samp_sys_time; + /*move to ms (/proc/stat gives times in 100 ms unit*/ + the_rti.total_cpu_time_diff = (u_k_time - last_cpu_u_k_time)*10; + + /*we're not that accurate....*/ + if (the_rti.total_cpu_time_diff > the_rti.sampling_period_duration) + the_rti.sampling_period_duration = the_rti.total_cpu_time_diff; + + + if (!idle_time) idle_time = (the_rti.sampling_period_duration - the_rti.total_cpu_time_diff)/10; + samp_sys_time = u_k_time - last_cpu_u_k_time; + the_rti.cpu_idle_time = idle_time - last_cpu_idle_time; + the_rti.total_cpu_usage = (u32) ( 100 * samp_sys_time / (the_rti.cpu_idle_time + samp_sys_time ) ); + /*move to ms (/proc/stat gives times in 100 ms unit*/ + the_rti.cpu_idle_time *= 10; + if (!the_rti.process_cpu_time_diff) the_rti.process_cpu_time_diff = the_rti.total_cpu_time_diff; + the_rti.process_cpu_usage = (u32) ( 100 * the_rti.process_cpu_time_diff / (the_rti.cpu_idle_time + 10*samp_sys_time ) ); + } + } else { + mem_at_startup = the_rti.physical_memory_avail; + } + the_rti.process_memory = mem_at_startup - the_rti.physical_memory_avail; +#ifdef GPAC_MEMORY_TRACKING + the_rti.gpac_memory = gpac_allocated_memory; +#endif + + last_process_k_u_time = process_u_k_time; + last_cpu_idle_time = idle_time; + last_cpu_u_k_time = u_k_time; + last_update_time = entry_time; + memcpy(rti, &the_rti, sizeof(GF_SystemRTInfo)); + return 1; +} + +#endif + + +Bool gf_sys_get_rti(u32 refresh_time_ms, GF_SystemRTInfo *rti, u32 flags) +{ + Bool res = gf_sys_get_rti_os(refresh_time_ms, rti, flags); + if (res) { + if (!rti->process_memory) rti->process_memory = memory_at_gpac_startup - rti->physical_memory_avail; + if (!rti->gpac_memory) rti->gpac_memory = memory_at_gpac_startup - rti->physical_memory_avail; + } + return res; +} + + +char * gf_get_default_cache_directory(){ +#ifdef _WIN32_WCE + return gf_strdup( "\\windows\\temp" ); +#elif defined(WIN32) + char szPath[MAX_PATH]; + /*ivica patch*/ + GetTempPath(MAX_PATH, szPath); + return gf_strdup( szPath ); +#else + return gf_strdup("/tmp"); +#endif +} + + + + +Bool gf_sys_get_battery_state(Bool *onBattery, u32 *onCharge, u32*level, u32 *batteryLifeTime, u32 *batteryFullLifeTime) +{ +#if defined(_WIN32_WCE) + SYSTEM_POWER_STATUS_EX sps; + GetSystemPowerStatusEx(&sps, 0); + if (onBattery) *onBattery = sps.ACLineStatus ? 0 : 1; + if (onCharge) *onCharge = (sps.BatteryFlag & BATTERY_FLAG_CHARGING) ? 1 : 0; + if (level) *level = sps.BatteryLifePercent; + if (batteryLifeTime) *batteryLifeTime = sps.BatteryLifeTime; + if (batteryFullLifeTime) *batteryFullLifeTime = sps.BatteryFullLifeTime; +#elif defined(WIN32) + SYSTEM_POWER_STATUS sps; + GetSystemPowerStatus(&sps); + if (onBattery) *onBattery = sps.ACLineStatus ? 0 : 1; + if (onCharge) *onCharge = (sps.BatteryFlag & BATTERY_FLAG_CHARGING) ? 1 : 0; + if (level) *level = sps.BatteryLifePercent; + if (batteryLifeTime) *batteryLifeTime = sps.BatteryLifeTime; + if (batteryFullLifeTime) *batteryFullLifeTime = sps.BatteryFullLifeTime; +#endif + return 1; +} + + +//struct GF_GlobalLock { + //const char * resourceName; +//}; + + +#ifndef WIN32 +#define CPF_CLOEXEC 1 + +#include +#include +#include + +struct _GF_GlobalLock_opaque { + char * resourceName; + char * pidFile; + int fd; +}; + +GF_GlobalLock * gf_create_PID_file( const char * resourceName ) +{ + const char * prefix = "/gpac_lock_"; + const char * dir = gf_get_default_cache_directory(); + char * pidfile; + int flags; + int status; + pidfile = (char *) gf_malloc(strlen(dir)+strlen(prefix)+strlen(resourceName)+1); + strcpy(pidfile, dir); + strcat(pidfile, prefix); + /* Use only valid names for file */ + { + const char *res; + char * pid = &(pidfile[strlen(pidfile)]); + for (res = resourceName; *res ; res++){ + if (*res >= 'A' && *res <= 'z') + *pid = * res; + else + *pid = '_'; + pid++; + } + *pid = '\0'; + } + int fd = open(pidfile, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR); + if (fd == -1) + goto exit; + /* Get the flags */ + flags = fcntl(fd, F_GETFD); + if (flags == -1){ + goto exit; + } + /* Set FD_CLOEXEC, so exclusive lock will be removed on exit, so even if GPAC crashes, + * lock will be allowed for next instance */ + flags |= FD_CLOEXEC; + /* Now, update the flags */ + if (fcntl(fd, F_SETFD, flags) == -1){ + goto exit; + } + + /* Now, we try to lock the file */ + { + struct flock fl; + fl.l_type = F_WRLCK; + fl.l_whence = SEEK_SET; + fl.l_start = fl.l_len = 0; + status = fcntl(fd, F_SETLK, &fl); + } + + if (status == -1) { + goto exit; + } + + if (ftruncate(fd, 0) == -1){ + goto exit; + } + /* Write the PID */ + { + int sz = 100; + char * buf = (char *) gf_malloc( sz ); + sz = snprintf(buf, sz, "%ld\n", (long) getpid()); + if (write(fd, buf, sz) != sz){ + gf_free(buf); + goto exit; + } + } + sync(); + { + GF_GlobalLock * lock = (GF_GlobalLock *) gf_malloc( sizeof(GF_GlobalLock)); + lock->resourceName = gf_strdup(resourceName); + lock->pidFile = pidfile; + lock->fd = fd; + return lock; + } +exit: + if (fd >= 0) + close(fd); + return NULL; +} +#else /* WIN32 */ +struct _GF_GlobalLock_opaque { + char * resourceName; + HANDLE hMutex; /*a named mutex is a system-mode object on windows*/ +}; +#endif + + +GF_GlobalLock * gf_global_resource_lock(const char * resourceName){ +#ifdef WIN32 +#ifdef _WIN32_WCE + unsigned short sWResourceName[MAX_PATH]; +#endif + GF_GlobalLock *lock = gf_malloc(sizeof(GF_GlobalLock)); + lock->resourceName = gf_strdup(resourceName); + + /*first ensure mutex is created*/ +#ifdef _WIN32_WCE + CE_CharToWide((char *)resourceName, sWResourceName); + lock->hMutex = CreateMutex(NULL, TRUE, sWResourceName); +#else + lock->hMutex = CreateMutex(NULL, TRUE, resourceName); +#endif + if (!lock->hMutex) { + DWORD lastErr = GetLastError(); + if (lastErr != ERROR_ALREADY_EXISTS) + return NULL; + } + + /*then lock it*/ + switch (WaitForSingleObject(lock->hMutex, INFINITE)) { + case WAIT_ABANDONED: + case WAIT_TIMEOUT: + assert(0); /*serious error: someone has modified the object elsewhere*/ + GF_LOG(GF_LOG_ERROR, GF_LOG_MUTEX, ("[Mutex] Couldn't get the global lock\n")); + gf_global_resource_unlock(lock); + return NULL; + } + + return lock; +#else /* WIN32 */ + return gf_create_PID_file(resourceName); +#endif /* WIN32 */ +} + +/*! + * Unlock a previouly locked resource + * \param lock The resource to unlock + * \return GF_OK if evertything went fine + */ +GF_Err gf_global_resource_unlock(GF_GlobalLock * lock){ + if (!lock) + return GF_BAD_PARAM; +#ifndef WIN32 + assert( lock->pidFile); + close(lock->fd); + if (unlink(lock->pidFile)) + perror("Failed to unlink lock file"); + gf_free(lock->pidFile); + lock->pidFile = NULL; + lock->fd = -1; +#else /* WIN32 */ + { + /*MSDN: "The mutex object is destroyed when its last handle has been closed."*/ + BOOL ret = ReleaseMutex(lock->hMutex); + if (!ret) { + DWORD err = GetLastError(); + GF_LOG(GF_LOG_ERROR, GF_LOG_MUTEX, ("[Mutex] Couldn't release mutex for global lock: %d\n", err)); + } + ret = CloseHandle(lock->hMutex); + if (!ret) { + DWORD err = GetLastError(); + GF_LOG(GF_LOG_ERROR, GF_LOG_MUTEX, ("[Mutex] Couldn't destroy mutex for global lock: %d\n", err)); + } + } +#endif + if (lock->resourceName) + gf_free(lock->resourceName); + lock->resourceName = NULL; + gf_free(lock); + return GF_OK; +} + +#ifdef GPAC_ANDROID + +fm_callback_func fm_cbk = NULL; +static void *fm_cbk_obj = NULL; + +void gf_fm_request_set_callback(void *cbk_obj, fm_callback_func cbk_func) { + fm_cbk = cbk_func; + fm_cbk_obj = cbk_obj; +} + +void gf_fm_request_call(u32 type, u32 param, int *value) { + if (fm_cbk) + fm_cbk(fm_cbk_obj, type, param, value); +} + +#endif //GPAC_ANDROID + diff --git a/src/gpacmp4/qos.c b/src/gpacmp4/qos.c new file mode 100644 index 00000000..f20702aa --- /dev/null +++ b/src/gpacmp4/qos.c @@ -0,0 +1,437 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / MPEG-4 ObjectDescriptor sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include + +#ifndef GPAC_MINIMAL_ODF + +/************************************************************ + QoSQualifiers Functions +************************************************************/ + +GF_EXPORT +GF_QoS_Default *gf_odf_qos_new(u8 tag) +{ + + GF_QoS_Default *NewQoS(u8 tag); + + GF_QoS_Default *qos; + + qos = NewQoS(tag); + return qos; +} + +GF_EXPORT +GF_Err gf_odf_qos_del(GF_QoS_Default **qos) +{ + if (*qos) gf_odf_delete_qos_qual(*qos); + *qos = NULL; + return GF_OK; +} + + +//same function, but for QoS, as a Qualifier IS NOT a descriptor +GF_EXPORT +GF_Err gf_odf_qos_add_qualif(GF_QoS_Descriptor *desc, GF_QoS_Default *qualif) +{ + u32 i; + GF_QoS_Default *def; + + if (desc->tag != GF_ODF_QOS_TAG) return GF_BAD_PARAM; + if (desc->predefined) return GF_ODF_FORBIDDEN_DESCRIPTOR; + + i=0; + while ((def = (GF_QoS_Default *)gf_list_enum(desc->QoS_Qualifiers, &i))) { + //if same Qualifier, not allowed... + if (def->tag == qualif->tag) return GF_ODF_FORBIDDEN_DESCRIPTOR; + } + return gf_list_add(desc->QoS_Qualifiers, qualif); +} + +void gf_odf_delete_qos_qual(GF_QoS_Default *qos) +{ + switch (qos->tag) { + case QoSMaxDelayTag : + case QoSPrefMaxDelayTag: + case QoSLossProbTag: + case QoSMaxGapLossTag: + case QoSMaxAUSizeTag: + case QoSAvgAUSizeTag: + case QoSMaxAURateTag: + gf_free(qos); + return; + + default: + if ( ((GF_QoS_Private *)qos)->DataLength) + gf_free(((GF_QoS_Private *)qos)->Data); + gf_free( (GF_QoS_Private *) qos); + return; + } +} + + +GF_Err gf_odf_size_qos_qual(GF_QoS_Default *qos) +{ + if (! qos) return GF_BAD_PARAM; + qos->size = 0; + + switch (qos->tag) { + case QoSMaxDelayTag: + case QoSPrefMaxDelayTag: + case QoSLossProbTag: + case QoSMaxGapLossTag: + case QoSMaxAUSizeTag: + case QoSAvgAUSizeTag: + case QoSMaxAURateTag: + qos->size += 4; + return GF_OK; + + case 0x00: + case 0xFF: + return GF_ODF_FORBIDDEN_DESCRIPTOR; + + default : + qos->size += ((GF_QoS_Private *)qos)->DataLength; + } + return GF_OK; +} + +GF_Err gf_odf_write_qos_qual(GF_BitStream *bs, GF_QoS_Default *qos) +{ + GF_Err e; + if (!bs || !qos) return GF_BAD_PARAM; + + e = gf_odf_size_qos_qual(qos); + if (e) return e; + e = gf_odf_write_base_descriptor(bs, qos->tag, qos->size); + if (e) return e; + + switch (qos->tag) { + case QoSMaxDelayTag: + gf_bs_write_int(bs, ((GF_QoS_MaxDelay *)qos)->MaxDelay, 32); + break; + + case QoSPrefMaxDelayTag: + gf_bs_write_int(bs, ((GF_QoS_PrefMaxDelay *)qos)->PrefMaxDelay, 32); + break; + + case QoSLossProbTag: + //FLOAT (double on 4 bytes) + gf_bs_write_float(bs, ((GF_QoS_LossProb *)qos)->LossProb); + break; + + case QoSMaxGapLossTag: + gf_bs_write_int(bs, ((GF_QoS_MaxGapLoss *)qos)->MaxGapLoss, 32); + break; + + case QoSMaxAUSizeTag: + gf_bs_write_int(bs, ((GF_QoS_MaxAUSize *)qos)->MaxAUSize, 32); + break; + + case QoSAvgAUSizeTag: + gf_bs_write_int(bs, ((GF_QoS_AvgAUSize *)qos)->AvgAUSize, 32); + break; + + case QoSMaxAURateTag: + gf_bs_write_int(bs, ((GF_QoS_MaxAURate *)qos)->MaxAURate, 32); + break; + + case 0x00: + case 0xFF: + return GF_ODF_FORBIDDEN_DESCRIPTOR; + + default: + //we defined the private qos... + gf_bs_write_data(bs, ((GF_QoS_Private *)qos)->Data, ((GF_QoS_Private *)qos)->DataLength); + break; + } + return GF_OK; +} + + + +GF_Err gf_odf_parse_qos(GF_BitStream *bs, GF_QoS_Default **qos_qual, u32 *qual_size) +{ + u32 tag, qos_size, val, bytesParsed, sizeHeader; + GF_QoS_Default *newQoS; + + //tag + tag = gf_bs_read_int(bs, 8); + bytesParsed = 1; + //size of instance + qos_size = 0; + sizeHeader = 0; + do { + val = gf_bs_read_int(bs, 8); + sizeHeader++; + qos_size <<= 7; + qos_size |= val & 0x7F; + } while ( val & 0x80 ); + bytesParsed += sizeHeader; + + //Payload + switch (tag) { + case QoSMaxDelayTag: + newQoS = (GF_QoS_Default *) gf_malloc(sizeof(GF_QoS_MaxDelay)); + ((GF_QoS_MaxDelay *)newQoS)->MaxDelay = gf_bs_read_int(bs, 32); + bytesParsed += 4; + break; + + case QoSPrefMaxDelayTag: + newQoS = (GF_QoS_Default *) gf_malloc(sizeof(GF_QoS_PrefMaxDelay)); + ((GF_QoS_PrefMaxDelay *)newQoS)->PrefMaxDelay = gf_bs_read_int(bs, 32); + bytesParsed += 4; + break; + + case QoSLossProbTag: + newQoS = (GF_QoS_Default *) gf_malloc(sizeof(GF_QoS_LossProb)); + ((GF_QoS_LossProb *)newQoS)->LossProb = gf_bs_read_float(bs); + bytesParsed += 4; + break; + + case QoSMaxGapLossTag: + newQoS = (GF_QoS_Default *) gf_malloc(sizeof(GF_QoS_MaxGapLoss)); + ((GF_QoS_MaxGapLoss *)newQoS)->MaxGapLoss = gf_bs_read_int(bs, 32); + bytesParsed += 4; + break; + + case QoSMaxAUSizeTag: + newQoS = (GF_QoS_Default *) gf_malloc(sizeof(GF_QoS_MaxAUSize)); + ((GF_QoS_MaxAUSize *)newQoS)->MaxAUSize = gf_bs_read_int(bs, 32); + bytesParsed += 4; + break; + + case QoSAvgAUSizeTag: + newQoS = (GF_QoS_Default *) gf_malloc(sizeof(GF_QoS_AvgAUSize)); + ((GF_QoS_AvgAUSize *)newQoS)->AvgAUSize = gf_bs_read_int(bs, 32); + bytesParsed += 4; + break; + + case QoSMaxAURateTag: + newQoS = (GF_QoS_Default *) gf_malloc(sizeof(GF_QoS_MaxAURate)); + ((GF_QoS_MaxAURate *)newQoS)->MaxAURate = gf_bs_read_int(bs, 32); + bytesParsed += 4; + break; + + case 0x00: + case 0xFF: + return GF_ODF_FORBIDDEN_DESCRIPTOR; + + default: + //we defined the private qos... + newQoS = (GF_QoS_Default *) gf_malloc(sizeof(GF_QoS_Private)); + ((GF_QoS_Private *)newQoS)->DataLength = qos_size; + gf_bs_read_data(bs, ((GF_QoS_Private *)newQoS)->Data, ((GF_QoS_Private *)newQoS)->DataLength); + bytesParsed += ((GF_QoS_Private *)newQoS)->DataLength; + break; + } + newQoS->size = qos_size; + newQoS->tag = tag; + if (bytesParsed != 1 + qos_size + sizeHeader) { + gf_odf_delete_qos_qual(newQoS); + return GF_ODF_INVALID_DESCRIPTOR; + } + *qos_qual = newQoS; + *qual_size = bytesParsed; + return GF_OK; +} + + +GF_QoS_Default *NewQoS(u8 tag) +{ + GF_QoS_Default *newQoS; + + switch (tag) { + case QoSMaxDelayTag: + newQoS = (GF_QoS_Default *) gf_malloc(sizeof(GF_QoS_MaxDelay)); + ((GF_QoS_MaxDelay *)newQoS)->MaxDelay = 0; + ((GF_QoS_MaxDelay *)newQoS)->size = 4; + break; + + case QoSPrefMaxDelayTag: + newQoS = (GF_QoS_Default *) gf_malloc(sizeof(GF_QoS_PrefMaxDelay)); + ((GF_QoS_PrefMaxDelay *)newQoS)->PrefMaxDelay = 0; + ((GF_QoS_PrefMaxDelay *)newQoS)->size = 4; + break; + + case QoSLossProbTag: + newQoS = (GF_QoS_Default *) gf_malloc(sizeof(GF_QoS_LossProb)); + ((GF_QoS_LossProb *)newQoS)->LossProb = 0; + ((GF_QoS_LossProb *)newQoS)->size = 4; + break; + + case QoSMaxGapLossTag: + newQoS = (GF_QoS_Default *) gf_malloc(sizeof(GF_QoS_MaxGapLoss)); + ((GF_QoS_MaxGapLoss *)newQoS)->MaxGapLoss = 0; + ((GF_QoS_MaxGapLoss *)newQoS)->size = 4; + break; + + case QoSMaxAUSizeTag: + newQoS = (GF_QoS_Default *) gf_malloc(sizeof(GF_QoS_MaxAUSize)); + ((GF_QoS_MaxAUSize *)newQoS)->MaxAUSize = 0; + ((GF_QoS_MaxAUSize *)newQoS)->size = 0; + break; + + case QoSAvgAUSizeTag: + newQoS = (GF_QoS_Default *) gf_malloc(sizeof(GF_QoS_AvgAUSize)); + ((GF_QoS_AvgAUSize *)newQoS)->AvgAUSize = 0; + ((GF_QoS_AvgAUSize *)newQoS)->size = 4; + break; + + case QoSMaxAURateTag: + newQoS = (GF_QoS_Default *) gf_malloc(sizeof(GF_QoS_MaxAURate)); + ((GF_QoS_MaxAURate *)newQoS)->MaxAURate = 0; + ((GF_QoS_MaxAURate *)newQoS)->size = 4; + break; + + case 0x00: + case 0xFF: + return NULL; + + default: + //we defined the private qos... + newQoS = (GF_QoS_Default *) gf_malloc(sizeof(GF_QoS_Private)); + ((GF_QoS_Private *)newQoS)->DataLength = 0; + ((GF_QoS_Private *)newQoS)->Data = NULL; + break; + } + newQoS->tag = tag; + return newQoS; +} + +// +// Constructor +// +GF_Descriptor *gf_odf_new_qos() +{ + GF_QoS_Descriptor *newDesc = (GF_QoS_Descriptor *) gf_malloc(sizeof(GF_QoS_Descriptor)); + if (!newDesc) return NULL; + newDesc->QoS_Qualifiers = gf_list_new(); + newDesc->predefined = 0; + newDesc->tag = GF_ODF_QOS_TAG; + return (GF_Descriptor *) newDesc; +} + +// +// Desctructor +// +GF_Err gf_odf_del_qos(GF_QoS_Descriptor *qos) +{ + if (!qos) return GF_BAD_PARAM; + + while (gf_list_count(qos->QoS_Qualifiers)) { + GF_QoS_Default *tmp = (GF_QoS_Default*)gf_list_get(qos->QoS_Qualifiers, 0); + gf_odf_delete_qos_qual(tmp); + gf_list_rem(qos->QoS_Qualifiers, 0); + } + gf_list_del(qos->QoS_Qualifiers); + return GF_OK; +} + + +// +// Reader +// +GF_Err gf_odf_read_qos(GF_BitStream *bs, GF_QoS_Descriptor *qos, u32 DescSize) +{ + GF_Err e; + GF_QoS_Default *tmp; + u32 tmp_size, nbBytes = 0; + if (!qos) return GF_BAD_PARAM; + + qos->predefined = gf_bs_read_int(bs, 8); + nbBytes += 1; + + if (qos->predefined) { + if (nbBytes != DescSize) return GF_ODF_INVALID_DESCRIPTOR; + return GF_OK; + } + + while (nbBytes < DescSize) { + tmp = NULL; + e = gf_odf_parse_qos(bs, &tmp, &tmp_size); + if (!tmp) return GF_ODF_INVALID_DESCRIPTOR; + e = gf_list_add(qos->QoS_Qualifiers, tmp); + if (e) return e; + nbBytes += tmp_size; + } + if (nbBytes != DescSize) return GF_ODF_INVALID_DESCRIPTOR; + return GF_OK; +} + + + + +// +// Size +// +GF_Err gf_odf_size_qos(GF_QoS_Descriptor *qos, u32 *outSize) +{ + GF_Err e; + u32 i; + GF_QoS_Default *tmp; + + if (!qos) return GF_BAD_PARAM; + + *outSize = 1; + + i=0; + while ((tmp = (GF_QoS_Default *)gf_list_enum(qos->QoS_Qualifiers, &i))) { + e = gf_odf_size_qos_qual(tmp); + if (e) return e; + *outSize += tmp->size + gf_odf_size_field_size(tmp->size); + } + return GF_OK; +} + +// +// Writer +// +GF_Err gf_odf_write_qos(GF_BitStream *bs, GF_QoS_Descriptor *qos) +{ + GF_Err e; + u32 size, i; + GF_QoS_Default *tmp; + if (!qos) return GF_BAD_PARAM; + + e = gf_odf_size_descriptor((GF_Descriptor *)qos, &size); + if (e) return e; + e = gf_odf_write_base_descriptor(bs, qos->tag, size); + if (e) return e; + + gf_bs_write_int(bs, qos->predefined, 8); + + if (! qos->predefined) { + i=0; + while ((tmp = (GF_QoS_Default *)gf_list_enum(qos->QoS_Qualifiers, &i))) { + e = gf_odf_write_qos_qual(bs, tmp); + if (e) return e; + } + } + return GF_OK; +} + + +#endif /*GPAC_MINIMAL_ODF*/ + diff --git a/src/gpacmp4/sample_descs.c b/src/gpacmp4/sample_descs.c new file mode 100644 index 00000000..c4dfc554 --- /dev/null +++ b/src/gpacmp4/sample_descs.c @@ -0,0 +1,555 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / ISO Media File Format sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ +#include "../disable_warnings.h" +#include + +#ifndef GPAC_DISABLE_ISOM + +void gf_isom_video_sample_entry_init(GF_VisualSampleEntryBox *ent) +{ + ent->horiz_res = ent->vert_res = 0x00480000; + ent->frames_per_sample = 1; + ent->bit_depth = 0x18; + ent->color_table_index = -1; +} + +GF_Err gf_isom_video_sample_entry_read(GF_VisualSampleEntryBox *ptr, GF_BitStream *bs) +{ + if (ptr->size < 78) return GF_ISOM_INVALID_FILE; + ptr->size -= 78; + gf_bs_read_data(bs, ptr->reserved, 6); + ptr->dataReferenceIndex = gf_bs_read_u16(bs); + ptr->version = gf_bs_read_u16(bs); + ptr->revision = gf_bs_read_u16(bs); + ptr->vendor = gf_bs_read_u32(bs); + ptr->temporal_quality = gf_bs_read_u32(bs); + ptr->spacial_quality = gf_bs_read_u32(bs); + ptr->Width = gf_bs_read_u16(bs); + ptr->Height = gf_bs_read_u16(bs); + ptr->horiz_res = gf_bs_read_u32(bs); + ptr->vert_res = gf_bs_read_u32(bs); + ptr->entry_data_size = gf_bs_read_u32(bs); + ptr->frames_per_sample = gf_bs_read_u16(bs); + gf_bs_read_data(bs, ptr->compressor_name, 32); + ptr->compressor_name[32] = 0; + ptr->bit_depth = gf_bs_read_u16(bs); + ptr->color_table_index = gf_bs_read_u16(bs); + return GF_OK; +} + +#ifndef GPAC_DISABLE_ISOM_WRITE +void gf_isom_video_sample_entry_write(GF_VisualSampleEntryBox *ptr, GF_BitStream *bs) +{ + + gf_bs_write_data(bs, ptr->reserved, 6); + gf_bs_write_u16(bs, ptr->dataReferenceIndex); + + gf_bs_write_u16(bs, ptr->version); + gf_bs_write_u16(bs, ptr->revision); + gf_bs_write_u32(bs, ptr->vendor); + gf_bs_write_u32(bs, ptr->temporal_quality); + gf_bs_write_u32(bs, ptr->spacial_quality); + gf_bs_write_u16(bs, ptr->Width); + gf_bs_write_u16(bs, ptr->Height); + gf_bs_write_u32(bs, ptr->horiz_res); + gf_bs_write_u32(bs, ptr->vert_res); + gf_bs_write_u32(bs, ptr->entry_data_size); + gf_bs_write_u16(bs, ptr->frames_per_sample); + gf_bs_write_data(bs, ptr->compressor_name, 32); + gf_bs_write_u16(bs, ptr->bit_depth); + gf_bs_write_u16(bs, ptr->color_table_index); +} + +void gf_isom_video_sample_entry_size(GF_VisualSampleEntryBox *ent) +{ + ent->size += 78; +} + +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + + + +void gf_isom_audio_sample_entry_init(GF_AudioSampleEntryBox *ptr) +{ + ptr->channel_count = 2; + ptr->bitspersample = 16; +} + +GF_Err gf_isom_audio_sample_entry_read(GF_AudioSampleEntryBox *ptr, GF_BitStream *bs) +{ + if (ptr->size<28) return GF_ISOM_INVALID_FILE; + + gf_bs_read_data(bs, ptr->reserved, 6); + ptr->dataReferenceIndex = gf_bs_read_u16(bs); + ptr->version = gf_bs_read_u16(bs); + ptr->revision = gf_bs_read_u16(bs); + ptr->vendor = gf_bs_read_u32(bs); + ptr->channel_count = gf_bs_read_u16(bs); + ptr->bitspersample = gf_bs_read_u16(bs); + ptr->compression_id = gf_bs_read_u16(bs); + ptr->packet_size = gf_bs_read_u16(bs); + ptr->samplerate_hi = gf_bs_read_u16(bs); + ptr->samplerate_lo = gf_bs_read_u16(bs); + + ptr->size -= 28; + if (ptr->version==1) { + if (ptr->size<16) return GF_ISOM_INVALID_FILE; + gf_bs_skip_bytes(bs, 16); + ptr->size-=16; + } else if (ptr->version==2) { + if (ptr->size<36) return GF_ISOM_INVALID_FILE; + gf_bs_skip_bytes(bs, 36); + ptr->size -= 36; + } + return GF_OK; +} + +#ifndef GPAC_DISABLE_ISOM_WRITE + +void gf_isom_audio_sample_entry_write(GF_AudioSampleEntryBox *ptr, GF_BitStream *bs) +{ + gf_bs_write_data(bs, ptr->reserved, 6); + gf_bs_write_u16(bs, ptr->dataReferenceIndex); + + gf_bs_write_u16(bs, ptr->version); + gf_bs_write_u16(bs, ptr->revision); + gf_bs_write_u32(bs, ptr->vendor); + gf_bs_write_u16(bs, ptr->channel_count); + gf_bs_write_u16(bs, ptr->bitspersample); + gf_bs_write_u16(bs, ptr->compression_id); + gf_bs_write_u16(bs, ptr->packet_size); + gf_bs_write_u16(bs, ptr->samplerate_hi); + gf_bs_write_u16(bs, ptr->samplerate_lo); +} + +void gf_isom_audio_sample_entry_size(GF_AudioSampleEntryBox *ptr) +{ + ptr->size += 28; +} + + +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + + + +GF_EXPORT +GF_3GPConfig *gf_isom_3gp_config_get(GF_ISOFile *the_file, u32 trackNumber, u32 StreamDescriptionIndex) +{ + GF_3GPConfig *config, *res; + GF_TrackBox *trak; + GF_SampleEntryBox *entry; + trak = gf_isom_get_track_from_file(the_file, trackNumber); + if (!trak || !StreamDescriptionIndex) return NULL; + + config = NULL; + entry = (GF_SampleEntryBox *)gf_list_get(trak->Media->information->sampleTable->SampleDescription->boxList, StreamDescriptionIndex-1); + if (!entry) return NULL; + switch (entry->type) { + case GF_ISOM_SUBTYPE_3GP_AMR: + case GF_ISOM_SUBTYPE_3GP_AMR_WB: + case GF_ISOM_SUBTYPE_3GP_EVRC: + case GF_ISOM_SUBTYPE_3GP_QCELP: + case GF_ISOM_SUBTYPE_3GP_SMV: + if (! ((GF_3GPPAudioSampleEntryBox*)entry)->info) return NULL; + config = & ((GF_3GPPAudioSampleEntryBox*)entry)->info->cfg; + break; + case GF_ISOM_SUBTYPE_3GP_H263: + if (! ((GF_3GPPVisualSampleEntryBox*)entry)->info) return NULL; + config = & ((GF_3GPPVisualSampleEntryBox*)entry)->info->cfg; + break; + default: + return NULL; + } + if (!config) return NULL; + + res = (GF_3GPConfig*)gf_malloc(sizeof(GF_3GPConfig)); + memcpy(res, config, sizeof(GF_3GPConfig)); + return res; +} + +GF_EXPORT +GF_AC3Config *gf_isom_ac3_config_get(GF_ISOFile *the_file, u32 trackNumber, u32 StreamDescriptionIndex) +{ + GF_AC3Config *res; + GF_TrackBox *trak; + GF_AC3SampleEntryBox *entry; + trak = gf_isom_get_track_from_file(the_file, trackNumber); + if (!trak || !StreamDescriptionIndex) return NULL; + + entry = (GF_AC3SampleEntryBox *)gf_list_get(trak->Media->information->sampleTable->SampleDescription->boxList, StreamDescriptionIndex-1); + if (!entry || !entry->info || (entry->type!=GF_ISOM_BOX_TYPE_AC3) || (entry->info->type!=GF_ISOM_BOX_TYPE_DAC3) ) return NULL; + + res = (GF_AC3Config*)gf_malloc(sizeof(GF_AC3Config)); + memcpy(res, &entry->info->cfg, sizeof(GF_AC3Config)); + return res; +} + +#ifndef GPAC_DISABLE_ISOM_WRITE + +GF_EXPORT +GF_Err gf_isom_3gp_config_new(GF_ISOFile *the_file, u32 trackNumber, GF_3GPConfig *cfg, char *URLname, char *URNname, u32 *outDescriptionIndex) +{ + GF_TrackBox *trak; + GF_Err e; + u32 dataRefIndex; + u32 cfg_type; + + e = CanAccessMovie(the_file, GF_ISOM_OPEN_WRITE); + if (e) return e; + + trak = gf_isom_get_track_from_file(the_file, trackNumber); + if (!trak || !trak->Media || !cfg) return GF_BAD_PARAM; + + switch (cfg->type) { + case GF_ISOM_SUBTYPE_3GP_AMR: + case GF_ISOM_SUBTYPE_3GP_AMR_WB: + if (trak->Media->handler->handlerType!=GF_ISOM_MEDIA_AUDIO) return GF_BAD_PARAM; + cfg_type = GF_ISOM_BOX_TYPE_DAMR; + break; + case GF_ISOM_SUBTYPE_3GP_EVRC: + if (trak->Media->handler->handlerType!=GF_ISOM_MEDIA_AUDIO) return GF_BAD_PARAM; + cfg_type = GF_ISOM_BOX_TYPE_DEVC; + break; + case GF_ISOM_SUBTYPE_3GP_QCELP: + if (trak->Media->handler->handlerType!=GF_ISOM_MEDIA_AUDIO) return GF_BAD_PARAM; + cfg_type = GF_ISOM_BOX_TYPE_DQCP; + break; + case GF_ISOM_SUBTYPE_3GP_SMV: + if (trak->Media->handler->handlerType!=GF_ISOM_MEDIA_AUDIO) return GF_BAD_PARAM; + cfg_type = GF_ISOM_BOX_TYPE_DSMV; + break; + case GF_ISOM_SUBTYPE_3GP_H263: + if (trak->Media->handler->handlerType!=GF_ISOM_MEDIA_VISUAL) return GF_BAD_PARAM; + cfg_type = GF_ISOM_BOX_TYPE_D263; + break; + case 0: return GF_BAD_PARAM; + default: + return GF_NOT_SUPPORTED; + } + + //get or create the data ref + e = Media_FindDataRef(trak->Media->information->dataInformation->dref, URLname, URNname, &dataRefIndex); + if (e) return e; + if (!dataRefIndex) { + e = Media_CreateDataRef(trak->Media->information->dataInformation->dref, URLname, URNname, &dataRefIndex); + if (e) return e; + } + trak->Media->mediaHeader->modificationTime = gf_isom_get_mp4time(); + + switch (cfg->type) { + case GF_ISOM_SUBTYPE_3GP_AMR: + case GF_ISOM_SUBTYPE_3GP_AMR_WB: + case GF_ISOM_SUBTYPE_3GP_EVRC: + case GF_ISOM_SUBTYPE_3GP_QCELP: + case GF_ISOM_SUBTYPE_3GP_SMV: + { + GF_3GPPAudioSampleEntryBox *entry = (GF_3GPPAudioSampleEntryBox *) gf_isom_box_new(cfg->type); + if (!entry) return GF_OUT_OF_MEM; + entry->info = (GF_3GPPConfigBox *) gf_isom_box_new(cfg_type); + if (!entry->info) { + gf_isom_box_del((GF_Box *) entry); + return GF_OUT_OF_MEM; + } + memcpy(&entry->info->cfg, cfg, sizeof(GF_3GPConfig)); + entry->samplerate_hi = trak->Media->mediaHeader->timeScale; + entry->dataReferenceIndex = dataRefIndex; + e = gf_list_add(trak->Media->information->sampleTable->SampleDescription->boxList, entry); + *outDescriptionIndex = gf_list_count(trak->Media->information->sampleTable->SampleDescription->boxList); + } + break; + case GF_ISOM_SUBTYPE_3GP_H263: + { + GF_3GPPVisualSampleEntryBox *entry = (GF_3GPPVisualSampleEntryBox *) gf_isom_box_new(cfg->type); + if (!entry) return GF_OUT_OF_MEM; + entry->info = (GF_3GPPConfigBox *) gf_isom_box_new(cfg_type); + if (!entry->info) { + gf_isom_box_del((GF_Box *) entry); + return GF_OUT_OF_MEM; + } + memcpy(&entry->info->cfg, cfg, sizeof(GF_3GPConfig)); + entry->dataReferenceIndex = dataRefIndex; + e = gf_list_add(trak->Media->information->sampleTable->SampleDescription->boxList, entry); + *outDescriptionIndex = gf_list_count(trak->Media->information->sampleTable->SampleDescription->boxList); + } + break; + } + return e; +} + +GF_Err gf_isom_3gp_config_update(GF_ISOFile *the_file, u32 trackNumber, GF_3GPConfig *param, u32 DescriptionIndex) +{ + GF_TrackBox *trak; + GF_Err e; + GF_3GPConfig *cfg; + GF_3GPPAudioSampleEntryBox *entry; + + e = CanAccessMovie(the_file, GF_ISOM_OPEN_WRITE); + if (e) return e; + + trak = gf_isom_get_track_from_file(the_file, trackNumber); + if (!trak || !trak->Media || !param || !DescriptionIndex) return GF_BAD_PARAM; + + cfg = NULL; + entry = (GF_3GPPAudioSampleEntryBox *)gf_list_get(trak->Media->information->sampleTable->SampleDescription->boxList, DescriptionIndex-1); + if (!entry) return GF_BAD_PARAM; + switch (entry->type) { + case GF_ISOM_SUBTYPE_3GP_AMR: + case GF_ISOM_SUBTYPE_3GP_AMR_WB: + case GF_ISOM_SUBTYPE_3GP_EVRC: + case GF_ISOM_SUBTYPE_3GP_QCELP: + case GF_ISOM_SUBTYPE_3GP_SMV: + cfg = &entry->info->cfg; + break; + case GF_ISOM_SUBTYPE_3GP_H263: + cfg = & ((GF_3GPPVisualSampleEntryBox *)entry)->info->cfg; + break; + default: + break; + } + if (!cfg || (cfg->type != param->type)) return GF_BAD_PARAM; + memcpy(cfg, param, sizeof(GF_3GPConfig)); + return GF_OK; +} + +GF_EXPORT +GF_Err gf_isom_ac3_config_new(GF_ISOFile *the_file, u32 trackNumber, GF_AC3Config *cfg, char *URLname, char *URNname, u32 *outDescriptionIndex) +{ + GF_TrackBox *trak; + GF_Err e; + u32 dataRefIndex; + GF_AC3SampleEntryBox *entry; + + e = CanAccessMovie(the_file, GF_ISOM_OPEN_WRITE); + if (e) return e; + + trak = gf_isom_get_track_from_file(the_file, trackNumber); + if (!trak || !trak->Media || !cfg) return GF_BAD_PARAM; + + //get or create the data ref + e = Media_FindDataRef(trak->Media->information->dataInformation->dref, URLname, URNname, &dataRefIndex); + if (e) return e; + if (!dataRefIndex) { + e = Media_CreateDataRef(trak->Media->information->dataInformation->dref, URLname, URNname, &dataRefIndex); + if (e) return e; + } + trak->Media->mediaHeader->modificationTime = gf_isom_get_mp4time(); + + entry = (GF_AC3SampleEntryBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_AC3); + if (!entry) return GF_OUT_OF_MEM; + entry->info = (GF_AC3ConfigBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_DAC3); + if (!entry->info) { + gf_isom_box_del((GF_Box *) entry); + return GF_OUT_OF_MEM; + } + memcpy(&entry->info->cfg, cfg, sizeof(GF_AC3Config)); + entry->samplerate_hi = trak->Media->mediaHeader->timeScale; + entry->dataReferenceIndex = dataRefIndex; + e = gf_list_add(trak->Media->information->sampleTable->SampleDescription->boxList, entry); + *outDescriptionIndex = gf_list_count(trak->Media->information->sampleTable->SampleDescription->boxList); + return e; +} +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + + + + +GF_Err gf_isom_get_dims_description(GF_ISOFile *movie, u32 trackNumber, u32 descriptionIndex, GF_DIMSDescription *desc) +{ + GF_DIMSSampleEntryBox *dims; + GF_TrackBox *trak; + trak = gf_isom_get_track_from_file(movie, trackNumber); + if (!trak || !descriptionIndex || !desc) return GF_BAD_PARAM; + + dims = (GF_DIMSSampleEntryBox *)gf_list_get(trak->Media->information->sampleTable->SampleDescription->boxList, descriptionIndex-1); + if (!dims) return GF_BAD_PARAM; + if (dims->type != GF_ISOM_BOX_TYPE_DIMS) return GF_BAD_PARAM; + + memset(desc, 0, sizeof(GF_DIMSDescription)); + if (dims->config) { + desc->profile = dims->config->profile; + desc->level = dims->config->level; + desc->pathComponents = dims->config->pathComponents; + desc->fullRequestHost = dims->config->fullRequestHost; + desc->containsRedundant = dims->config->containsRedundant; + desc->streamType = dims->config->streamType; + desc->textEncoding = dims->config->textEncoding; + desc->contentEncoding = dims->config->contentEncoding; + } + if (dims->scripts) { + desc->content_script_types = dims->scripts->content_script_types; + } + return GF_OK; +} + +#ifndef GPAC_DISABLE_ISOM_WRITE +GF_Err gf_isom_new_dims_description(GF_ISOFile *movie, u32 trackNumber, GF_DIMSDescription *desc, char *URLname, char *URNname, u32 *outDescriptionIndex) +{ + GF_TrackBox *trak; + GF_Err e; + u32 dataRefIndex; + GF_DIMSSampleEntryBox *dims; + + e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE); + if (e) return e; + + trak = gf_isom_get_track_from_file(movie, trackNumber); + if (!trak || !trak->Media) return GF_BAD_PARAM; + + if (trak->Media->handler->handlerType != GF_ISOM_MEDIA_SCENE) return GF_BAD_PARAM; + + //get or create the data ref + e = Media_FindDataRef(trak->Media->information->dataInformation->dref, URLname, URNname, &dataRefIndex); + if (e) return e; + if (!dataRefIndex) { + e = Media_CreateDataRef(trak->Media->information->dataInformation->dref, URLname, URNname, &dataRefIndex); + if (e) return e; + } + trak->Media->mediaHeader->modificationTime = gf_isom_get_mp4time(); + + dims = (GF_DIMSSampleEntryBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_DIMS); + dims->dataReferenceIndex = dataRefIndex; + gf_list_add(trak->Media->information->sampleTable->SampleDescription->boxList, dims); + if (outDescriptionIndex) *outDescriptionIndex = gf_list_count(trak->Media->information->sampleTable->SampleDescription->boxList); + + dims->config = (GF_DIMSSceneConfigBox*) gf_isom_box_new(GF_ISOM_BOX_TYPE_DIMC); + dims->config->profile = desc->profile; + dims->config->level = desc->level; + dims->config->pathComponents = desc->pathComponents; + dims->config->fullRequestHost = desc->fullRequestHost; + dims->config->containsRedundant = desc->containsRedundant; + if (!dims->config->containsRedundant) dims->config->containsRedundant = 1; + dims->config->streamType = desc->streamType; + dims->config->textEncoding = gf_strdup(desc->textEncoding ? desc->textEncoding : ""); + dims->config->contentEncoding = gf_strdup(desc->contentEncoding ? desc->contentEncoding : ""); + + if (desc->content_script_types) { + dims->scripts = (GF_DIMSScriptTypesBox*) gf_isom_box_new(GF_ISOM_BOX_TYPE_DIST); + dims->scripts->content_script_types = gf_strdup(desc->content_script_types); + } + return e; +} + + +GF_Err gf_isom_update_dims_description(GF_ISOFile *movie, u32 trackNumber, GF_DIMSDescription *desc, char *URLname, char *URNname, u32 DescriptionIndex) +{ + GF_TrackBox *trak; + GF_Err e; + GF_DIMSSampleEntryBox *dims; + + e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE); + if (e) return e; + + trak = gf_isom_get_track_from_file(movie, trackNumber); + if (!trak || !trak->Media || !desc || !DescriptionIndex) return GF_BAD_PARAM; + + dims = (GF_DIMSSampleEntryBox *)gf_list_get(trak->Media->information->sampleTable->SampleDescription->boxList, DescriptionIndex-1); + if (!dims) return GF_BAD_PARAM; + if (dims->type != GF_ISOM_BOX_TYPE_DIMS) return GF_BAD_PARAM; + if (!dims->config) + dims->config = (GF_DIMSSceneConfigBox*) gf_isom_box_new(GF_ISOM_BOX_TYPE_DIMC); + + trak->Media->mediaHeader->modificationTime = gf_isom_get_mp4time(); + + dims->config->profile = desc->profile; + dims->config->level = desc->level; + dims->config->pathComponents = desc->pathComponents; + dims->config->fullRequestHost = desc->fullRequestHost; + dims->config->containsRedundant = desc->containsRedundant; + dims->config->streamType = desc->streamType; + + if (dims->config->textEncoding) gf_free(dims->config->textEncoding); + dims->config->textEncoding = gf_strdup(desc->textEncoding ? desc->textEncoding : ""); + + if (dims->config->contentEncoding) gf_free(dims->config->contentEncoding); + dims->config->contentEncoding = gf_strdup(desc->contentEncoding ? desc->contentEncoding : ""); + + if (desc->content_script_types) { + if (!dims->scripts) + dims->scripts = (GF_DIMSScriptTypesBox*) gf_isom_box_new(GF_ISOM_BOX_TYPE_DIST); + if (dims->scripts->content_script_types) gf_free(dims->scripts->content_script_types); + dims->scripts->content_script_types = gf_strdup(desc->content_script_types ? desc->content_script_types :""); + } else if (dims->scripts) { + gf_isom_box_del((GF_Box *) dims->scripts); + dims->scripts = NULL; + } + return e; +} +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + + + +GF_Err LSR_UpdateESD(GF_LASeRSampleEntryBox *lsr, GF_ESD *esd) +{ + if (!lsr->bitrate) lsr->bitrate = (GF_MPEG4BitRateBox*)gf_isom_box_new(GF_ISOM_BOX_TYPE_BTRT); + if (lsr->descr) gf_isom_box_del((GF_Box *) lsr->descr); + lsr->descr = NULL; + lsr->bitrate->avgBitrate = esd->decoderConfig->avgBitrate; + lsr->bitrate->maxBitrate = esd->decoderConfig->maxBitrate; + lsr->bitrate->bufferSizeDB = esd->decoderConfig->bufferSizeDB; + + if (gf_list_count(esd->IPIDataSet) + || gf_list_count(esd->IPMPDescriptorPointers) + || esd->langDesc + || gf_list_count(esd->extensionDescriptors) + || esd->ipiPtr || esd->qos || esd->RegDescriptor) { + + lsr->descr = (GF_MPEG4ExtensionDescriptorsBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_M4DS); + if (esd->RegDescriptor) { gf_list_add(lsr->descr->descriptors, esd->RegDescriptor); esd->RegDescriptor = NULL; } + if (esd->qos) { gf_list_add(lsr->descr->descriptors, esd->qos); esd->qos = NULL; } + if (esd->ipiPtr) { gf_list_add(lsr->descr->descriptors, esd->ipiPtr); esd->ipiPtr= NULL; } + + while (gf_list_count(esd->IPIDataSet)) { + GF_Descriptor *desc = (GF_Descriptor *)gf_list_get(esd->IPIDataSet, 0); + gf_list_rem(esd->IPIDataSet, 0); + gf_list_add(lsr->descr->descriptors, desc); + } + while (gf_list_count(esd->IPMPDescriptorPointers)) { + GF_Descriptor *desc = (GF_Descriptor *)gf_list_get(esd->IPMPDescriptorPointers, 0); + gf_list_rem(esd->IPMPDescriptorPointers, 0); + gf_list_add(lsr->descr->descriptors, desc); + } + if (esd->langDesc) { + gf_list_add(lsr->descr->descriptors, esd->langDesc); + esd->langDesc = NULL; + } + while (gf_list_count(esd->extensionDescriptors)) { + GF_Descriptor *desc = (GF_Descriptor *)gf_list_get(esd->extensionDescriptors, 0); + gf_list_rem(esd->extensionDescriptors, 0); + gf_list_add(lsr->descr->descriptors, desc); + } + } + + /*update GF_AVCConfig*/ + if (!lsr->lsr_config) lsr->lsr_config = (GF_LASERConfigurationBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_LSRC); + if (esd->decoderConfig->decoderSpecificInfo && esd->decoderConfig->decoderSpecificInfo->data) { + lsr->lsr_config->hdr = (char *) gf_realloc(lsr->lsr_config->hdr, sizeof(char) * esd->decoderConfig->decoderSpecificInfo->dataLength); + lsr->lsr_config->hdr_size = esd->decoderConfig->decoderSpecificInfo->dataLength; + memcpy(lsr->lsr_config->hdr, esd->decoderConfig->decoderSpecificInfo->data, sizeof(char)*esd->decoderConfig->decoderSpecificInfo->dataLength); + } + gf_odf_desc_del((GF_Descriptor *)esd); + return GF_OK; +} + + +#endif /*GPAC_DISABLE_ISOM*/ diff --git a/src/gpacmp4/slc.c b/src/gpacmp4/slc.c new file mode 100644 index 00000000..b0bd1e63 --- /dev/null +++ b/src/gpacmp4/slc.c @@ -0,0 +1,449 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / MPEG-4 ObjectDescriptor sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include + + +// +// Constructor +// +GF_Descriptor *gf_odf_new_slc(u8 predef) +{ + GF_SLConfig *newDesc = (GF_SLConfig *) gf_malloc(sizeof(GF_SLConfig)); + if (!newDesc) return NULL; + memset(newDesc, 0, sizeof(GF_SLConfig)); + newDesc->tag = GF_ODF_SLC_TAG; + newDesc->predefined = predef; + if (predef) gf_odf_slc_set_pref(newDesc); + newDesc->useTimestampsFlag = 1; + + return (GF_Descriptor *)newDesc; +} + +// +// Destructor +// +GF_Err gf_odf_del_slc(GF_SLConfig *sl) +{ + if (!sl) return GF_BAD_PARAM; + gf_free(sl); + return GF_OK; +} + +// +// Set the SL to the ISO predefined value +// +GF_EXPORT +GF_Err gf_odf_slc_set_pref(GF_SLConfig *sl) +{ + if (! sl) return GF_BAD_PARAM; + + switch (sl->predefined) { + + case SLPredef_MP4: + sl->useAccessUnitStartFlag = 0; + sl->useAccessUnitEndFlag = 0; + //each packet is an AU, and we need RAP signaling + sl->useRandomAccessPointFlag = 1; + sl->hasRandomAccessUnitsOnlyFlag = 0; + sl->usePaddingFlag = 0; + //in MP4 file, we use TimeStamps + sl->useTimestampsFlag = 1; + sl->useIdleFlag = 0; + sl->durationFlag = 0; + sl->timestampLength = 0; + sl->OCRLength = 0; + sl->AULength = 0; + sl->instantBitrateLength = 0; + sl->degradationPriorityLength = 0; + sl->AUSeqNumLength = 0; + sl->packetSeqNumLength = 0; + break; + + case SLPredef_Null: + sl->useAccessUnitStartFlag = 0; + sl->useAccessUnitEndFlag = 0; + sl->useRandomAccessPointFlag = 0; + sl->hasRandomAccessUnitsOnlyFlag = 0; + sl->usePaddingFlag = 0; + sl->useTimestampsFlag = 0; + sl->useIdleFlag = 0; + sl->AULength = 0; + sl->degradationPriorityLength = 0; + sl->AUSeqNumLength = 0; + sl->packetSeqNumLength = 0; + + //for MPEG4 IP + sl->timestampResolution = 1000; + sl->timestampLength = 32; + break; + case SLPredef_SkipSL: + sl->predefined = SLPredef_SkipSL; + break; + /*handle all unknown predefined as predef-null*/ + default: + sl->useAccessUnitStartFlag = 0; + sl->useAccessUnitEndFlag = 0; + sl->useRandomAccessPointFlag = 0; + sl->hasRandomAccessUnitsOnlyFlag = 0; + sl->usePaddingFlag = 0; + sl->useTimestampsFlag = 1; + sl->useIdleFlag = 0; + sl->AULength = 0; + sl->degradationPriorityLength = 0; + sl->AUSeqNumLength = 0; + sl->packetSeqNumLength = 0; + + sl->timestampResolution = 1000; + sl->timestampLength = 32; + break; + } + + return GF_OK; +} + +u32 SLIsPredefined(GF_SLConfig *sl) +{ + if (!sl) return 0; + if (sl->predefined) return sl->predefined; + + if (!sl->useAccessUnitStartFlag + && !sl->useAccessUnitEndFlag + && !sl->usePaddingFlag + && sl->useTimestampsFlag + && !sl->useIdleFlag + && !sl->durationFlag + && !sl->timestampLength + && !sl->OCRLength + && !sl->AULength + && !sl->instantBitrateLength + && !sl->degradationPriorityLength + && !sl->AUSeqNumLength + && !sl->packetSeqNumLength) + return SLPredef_MP4; + + return 0; +} + +//this function gets the real amount of bytes needed to store the timeStamp +static u32 GetTSbytesLen(GF_SLConfig *sl) +{ + u32 TSlen, TSbytes; + if (! sl) return 0; + + TSlen = sl->timestampLength * 2; + TSbytes = TSlen / 8; + TSlen = TSlen % 8; + if (TSlen) TSbytes += 1; + return TSbytes; +} + +// +// Reader +// +GF_Err gf_odf_read_slc(GF_BitStream *bs, GF_SLConfig *sl, u32 DescSize) +{ + GF_Err e; + u32 nbBytes = 0; + + if (!sl) return GF_BAD_PARAM; + + //APPLE fix + if (!DescSize) { + sl->predefined = SLPredef_MP4; + return gf_odf_slc_set_pref(sl); + } + + sl->predefined = gf_bs_read_int(bs, 8); + nbBytes += 1; + + /*MPEG4 IP fix*/ + if (!sl->predefined && nbBytes==DescSize) { + sl->predefined = SLPredef_Null; + gf_odf_slc_set_pref(sl); + return GF_OK; + } + + if (sl->predefined) { + //predefined configuration + e = gf_odf_slc_set_pref(sl); + if (e) return e; + } else { + sl->useAccessUnitStartFlag = gf_bs_read_int(bs, 1); + sl->useAccessUnitEndFlag = gf_bs_read_int(bs, 1); + sl->useRandomAccessPointFlag = gf_bs_read_int(bs, 1); + sl->hasRandomAccessUnitsOnlyFlag = gf_bs_read_int(bs, 1); + sl->usePaddingFlag = gf_bs_read_int(bs, 1); + sl->useTimestampsFlag = gf_bs_read_int(bs, 1); + sl->useIdleFlag = gf_bs_read_int(bs, 1); + sl->durationFlag = gf_bs_read_int(bs, 1); + sl->timestampResolution = gf_bs_read_int(bs, 32); + sl->OCRResolution = gf_bs_read_int(bs, 32); + + sl->timestampLength = gf_bs_read_int(bs, 8); + if (sl->timestampLength > 64) return GF_ODF_INVALID_DESCRIPTOR; + + sl->OCRLength = gf_bs_read_int(bs, 8); + if (sl->OCRLength > 64) return GF_ODF_INVALID_DESCRIPTOR; + + sl->AULength = gf_bs_read_int(bs, 8); + if (sl->AULength > 32) return GF_ODF_INVALID_DESCRIPTOR; + + sl->instantBitrateLength = gf_bs_read_int(bs, 8); + sl->degradationPriorityLength = gf_bs_read_int(bs, 4); + sl->AUSeqNumLength = gf_bs_read_int(bs, 5); + if (sl->AUSeqNumLength > 16) return GF_ODF_INVALID_DESCRIPTOR; + sl->packetSeqNumLength = gf_bs_read_int(bs, 5); + if (sl->packetSeqNumLength > 16) return GF_ODF_INVALID_DESCRIPTOR; + + /*reserved = */gf_bs_read_int(bs, 2); + nbBytes += 15; + } + + if (sl->durationFlag) { + sl->timeScale = gf_bs_read_int(bs, 32); + sl->AUDuration = gf_bs_read_int(bs, 16); + sl->CUDuration = gf_bs_read_int(bs, 16); + nbBytes += 8; + } + if (! sl->useTimestampsFlag) { + sl->startDTS = gf_bs_read_long_int(bs, sl->timestampLength); + sl->startCTS = gf_bs_read_long_int(bs, sl->timestampLength); + nbBytes += GetTSbytesLen(sl); + } + + if (DescSize != nbBytes) return GF_ODF_INVALID_DESCRIPTOR; + return GF_OK; +} + + +// +// Size +// +GF_Err gf_odf_size_slc(GF_SLConfig *sl, u32 *outSize) +{ + if (! sl) return GF_BAD_PARAM; + + *outSize = 1; + if (! sl->predefined) *outSize += 15; + if (sl->durationFlag) *outSize += 8; + if (! sl->useTimestampsFlag) *outSize += GetTSbytesLen(sl); + return GF_OK; +} + +// +// Writer +// +GF_Err gf_odf_write_slc(GF_BitStream *bs, GF_SLConfig *sl) +{ + GF_Err e; + u32 size; + if (! sl) return GF_BAD_PARAM; + + e = gf_odf_size_descriptor((GF_Descriptor *)sl, &size); + assert(e == GF_OK); + e = gf_odf_write_base_descriptor(bs, sl->tag, size); + assert(e == GF_OK); + + gf_bs_write_int(bs, sl->predefined, 8); + if (! sl->predefined) { + gf_bs_write_int(bs, sl->useAccessUnitStartFlag, 1); + gf_bs_write_int(bs, sl->useAccessUnitEndFlag, 1); + gf_bs_write_int(bs, sl->useRandomAccessPointFlag, 1); + gf_bs_write_int(bs, sl->hasRandomAccessUnitsOnlyFlag, 1); + gf_bs_write_int(bs, sl->usePaddingFlag, 1); + gf_bs_write_int(bs, sl->useTimestampsFlag, 1); + gf_bs_write_int(bs, sl->useIdleFlag, 1); + gf_bs_write_int(bs, sl->durationFlag, 1); + gf_bs_write_int(bs, sl->timestampResolution, 32); + gf_bs_write_int(bs, sl->OCRResolution, 32); + gf_bs_write_int(bs, sl->timestampLength, 8); + gf_bs_write_int(bs, sl->OCRLength, 8); + gf_bs_write_int(bs, sl->AULength, 8); + gf_bs_write_int(bs, sl->instantBitrateLength, 8); + gf_bs_write_int(bs, sl->degradationPriorityLength, 4); + gf_bs_write_int(bs, sl->AUSeqNumLength, 5); + gf_bs_write_int(bs, sl->packetSeqNumLength, 5); + gf_bs_write_int(bs, 3, 2); //reserved: 0b11 == 3 + } + if (sl->durationFlag) { + gf_bs_write_int(bs, sl->timeScale, 32); + gf_bs_write_int(bs, sl->AUDuration, 16); + gf_bs_write_int(bs, sl->CUDuration, 16); + } + if (! sl->useTimestampsFlag) { + gf_bs_write_long_int(bs, sl->startDTS, sl->timestampLength); + gf_bs_write_long_int(bs, sl->startCTS, sl->timestampLength); + } + + return GF_OK; +} + + +/*allocates and writes the SL-PDU (Header + PDU) given the SLConfig and the GF_SLHeader +for this PDU. AUs must be split in PDUs by another process if needed (packetizer).*/ +GF_EXPORT +void gf_sl_packetize(GF_SLConfig* slConfig, + GF_SLHeader *Header, + char *PDU, + u32 size, + char **outPacket, + u32 *OutSize) +{ + GF_BitStream *bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); + *OutSize = 0; + if (!bs) return; + + if (slConfig->useAccessUnitStartFlag) gf_bs_write_int(bs, Header->accessUnitStartFlag, 1); + if (slConfig->useAccessUnitEndFlag) gf_bs_write_int(bs, Header->accessUnitEndFlag, 1); + if (slConfig->OCRLength > 0) gf_bs_write_int(bs, Header->OCRflag, 1); + if (slConfig->useIdleFlag) gf_bs_write_int(bs, Header->idleFlag, 1); + if (slConfig->usePaddingFlag) { + gf_bs_write_int(bs, Header->paddingFlag, 1); + if (Header->paddingFlag) gf_bs_write_int(bs, Header->paddingBits, 3); + } + if (! Header->idleFlag && (! Header->paddingFlag || Header->paddingBits != 0)) { + if (slConfig->packetSeqNumLength > 0) gf_bs_write_int(bs, Header->packetSequenceNumber, slConfig->packetSeqNumLength); + if (slConfig->degradationPriorityLength > 0) { + gf_bs_write_int(bs, Header->degradationPriorityFlag, 1); + if (Header->degradationPriorityFlag) gf_bs_write_int(bs, Header->degradationPriority, slConfig->degradationPriorityLength); + } + if (Header->OCRflag) gf_bs_write_long_int(bs, Header->objectClockReference, slConfig->OCRLength); + if (Header->accessUnitStartFlag) { + if (slConfig->useRandomAccessPointFlag) gf_bs_write_int(bs, Header->randomAccessPointFlag, 1); + if (slConfig->AUSeqNumLength > 0) gf_bs_write_int(bs, Header->AU_sequenceNumber, slConfig->AUSeqNumLength); + if (slConfig->useTimestampsFlag) { + gf_bs_write_int(bs, Header->decodingTimeStampFlag, 1); + gf_bs_write_int(bs, Header->compositionTimeStampFlag, 1); + } + if (slConfig->instantBitrateLength > 0) gf_bs_write_int(bs, Header->instantBitrateFlag, 1); + if (Header->decodingTimeStampFlag) gf_bs_write_long_int(bs, Header->decodingTimeStamp, slConfig->timestampLength); + if (Header->compositionTimeStampFlag) gf_bs_write_long_int(bs, Header->compositionTimeStamp, slConfig->timestampLength); + if (slConfig->AULength > 0) gf_bs_write_int(bs, Header->accessUnitLength, slConfig->AULength); + if (Header->instantBitrateFlag) gf_bs_write_int(bs, Header->instantBitrate, slConfig->instantBitrateLength); + } + } + /*done with the Header, Alin*/ + gf_bs_align(bs); + + /*write the PDU - already byte aligned with stuffing (paddingBits in SL Header)*/ + if (PDU && size) + gf_bs_write_data(bs, PDU, size); + + gf_bs_align(bs); + gf_bs_get_content(bs, outPacket, OutSize); + gf_bs_del(bs); +} + +/*allocates and writes the SL-PDU (Header + PDU) given the SLConfig and the GF_SLHeader +for this PDU. AUs must be split in PDUs by another process if needed (packetizer).*/ +GF_EXPORT +u32 gf_sl_get_header_size(GF_SLConfig* slConfig, GF_SLHeader *Header) +{ + u32 nb_bits = 0; + + if (slConfig->useAccessUnitStartFlag) nb_bits++; + if (slConfig->useAccessUnitEndFlag) nb_bits++; + if (slConfig->OCRLength > 0) nb_bits++; + if (slConfig->useIdleFlag) nb_bits++; + if (slConfig->usePaddingFlag) { + nb_bits++; + if (Header->paddingFlag) nb_bits+=3; + } + if (!Header->idleFlag && (! Header->paddingFlag || Header->paddingBits != 0)) { + if (slConfig->packetSeqNumLength > 0) nb_bits += slConfig->packetSeqNumLength; + if (slConfig->degradationPriorityLength > 0) { + nb_bits++; + if (Header->degradationPriorityFlag) nb_bits += slConfig->degradationPriorityLength; + } + if (Header->OCRflag) nb_bits += slConfig->OCRLength; + if (Header->accessUnitStartFlag) { + if (slConfig->useRandomAccessPointFlag) nb_bits++; + if (slConfig->AUSeqNumLength > 0) nb_bits += slConfig->AUSeqNumLength; + if (slConfig->useTimestampsFlag) { + nb_bits++; + nb_bits++; + } + if (slConfig->instantBitrateLength > 0) nb_bits++; + if (Header->decodingTimeStampFlag) nb_bits += slConfig->timestampLength; + if (Header->compositionTimeStampFlag) nb_bits += slConfig->timestampLength; + if (slConfig->AULength > 0) nb_bits += slConfig->AULength; + if (Header->instantBitrateFlag) nb_bits += slConfig->instantBitrateLength; + } + } + while (nb_bits%8) nb_bits++; + return nb_bits/8; +} + + +GF_EXPORT +void gf_sl_depacketize (GF_SLConfig *slConfig, GF_SLHeader *Header, const char *PDU, u32 PDULength, u32 *HeaderLen) +{ + GF_BitStream *bs; + *HeaderLen = 0; + if (!Header) return; + //reset the input header + memset(Header, 0, sizeof(GF_SLHeader)); + + bs = gf_bs_new(PDU, PDULength, GF_BITSTREAM_READ); + if (!bs) return; + + if (slConfig->useAccessUnitStartFlag) Header->accessUnitStartFlag = gf_bs_read_int(bs, 1); + if (slConfig->useAccessUnitEndFlag) Header->accessUnitEndFlag = gf_bs_read_int(bs, 1); + if ( !slConfig->useAccessUnitStartFlag && !slConfig->useAccessUnitEndFlag) { + Header->accessUnitStartFlag = 1; + Header->accessUnitEndFlag = 1; + } + if (slConfig->OCRLength > 0) Header->OCRflag = gf_bs_read_int(bs, 1); + if (slConfig->useIdleFlag) Header->idleFlag = gf_bs_read_int(bs, 1); + if (slConfig->usePaddingFlag) { + Header->paddingFlag = gf_bs_read_int(bs, 1); + if (Header->paddingFlag) Header->paddingBits = gf_bs_read_int(bs, 3); + } + if (!Header->paddingFlag || (Header->paddingBits != 0)) { + + if (slConfig->packetSeqNumLength > 0) Header->packetSequenceNumber = gf_bs_read_int(bs, slConfig->packetSeqNumLength); + if (slConfig->degradationPriorityLength > 0) { + Header->degradationPriorityFlag = gf_bs_read_int(bs, 1); + if (Header->degradationPriorityFlag) Header->degradationPriority = gf_bs_read_int(bs, slConfig->degradationPriorityLength); + } + if (Header->OCRflag) Header->objectClockReference = gf_bs_read_long_int(bs, slConfig->OCRLength); + if (Header->accessUnitStartFlag) { + if (slConfig->useRandomAccessPointFlag) Header->randomAccessPointFlag = gf_bs_read_int(bs, 1); + if (slConfig->AUSeqNumLength > 0) Header->AU_sequenceNumber = gf_bs_read_int(bs, slConfig->AUSeqNumLength); + if (slConfig->useTimestampsFlag) { + Header->decodingTimeStampFlag = gf_bs_read_int(bs, 1); + Header->compositionTimeStampFlag = gf_bs_read_int(bs, 1); + } + if (slConfig->instantBitrateLength > 0) Header->instantBitrateFlag = gf_bs_read_int(bs, 1); + if (Header->decodingTimeStampFlag) Header->decodingTimeStamp = gf_bs_read_long_int(bs, slConfig->timestampLength); + if (Header->compositionTimeStampFlag) Header->compositionTimeStamp = gf_bs_read_long_int(bs, slConfig->timestampLength); + if (slConfig->AULength > 0) Header->accessUnitLength = gf_bs_read_int(bs, slConfig->AULength); + if (Header->instantBitrateFlag) Header->instantBitrate = gf_bs_read_int(bs, slConfig->instantBitrateLength); + } + } + gf_bs_align(bs); + *HeaderLen = (u32) gf_bs_get_position(bs); + gf_bs_del(bs); +} diff --git a/src/gpacmp4/stbl_read.c b/src/gpacmp4/stbl_read.c new file mode 100644 index 00000000..51f8e258 --- /dev/null +++ b/src/gpacmp4/stbl_read.c @@ -0,0 +1,518 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / ISO Media File Format sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include + +#ifndef GPAC_DISABLE_ISOM + +//Get the sample number +GF_Err findEntryForTime(GF_SampleTableBox *stbl, u64 DTS, u8 useCTS, u32 *sampleNumber, u32 *prevSampleNumber) +{ + u32 i, j, curSampNum, CTSOffset, count; + u64 curDTS; + GF_SttsEntry *ent; + (*sampleNumber) = 0; + (*prevSampleNumber) = 0; + + if (!stbl->CompositionOffset) useCTS = 0; + /*FIXME: CTS is ALWAYS disabled for now to make sure samples are fetched in + decoding order. */ + useCTS = 0; + + //our cache + if (stbl->TimeToSample->r_FirstSampleInEntry && + (DTS >= stbl->TimeToSample->r_CurrentDTS) ) { + //if we're using CTS, we don't really know whether we're in the good entry or not + //(eg, the real DTS of the sample could be in a previous entry + i = stbl->TimeToSample->r_currentEntryIndex; + curDTS = stbl->TimeToSample->r_CurrentDTS; + curSampNum = stbl->TimeToSample->r_FirstSampleInEntry; + } else { + i = 0; + curDTS = stbl->TimeToSample->r_CurrentDTS = 0; + curSampNum = stbl->TimeToSample->r_FirstSampleInEntry = 1; + stbl->TimeToSample->r_currentEntryIndex = 0; + } + + //we need to validate our cache if we are using CTS because of B-frames and co... + if (i && useCTS) { + while (1) { + stbl_GetSampleCTS(stbl->CompositionOffset, curSampNum, &CTSOffset); + //we're too far, rewind + if ( i && (curDTS + CTSOffset > DTS) ) { + ent = &stbl->TimeToSample->entries[i]; + curSampNum -= ent->sampleCount; + curDTS -= ent->sampleDelta * ent->sampleCount; + i --; + } else if (!i) { + //begining of the table, no choice + curDTS = stbl->TimeToSample->r_CurrentDTS = 0; + curSampNum = stbl->TimeToSample->r_FirstSampleInEntry = 1; + stbl->TimeToSample->r_currentEntryIndex = 0; + break; + } else { + //OK now we're good + break; + } + } + } + + //look for the DTS from this entry + count = stbl->TimeToSample->nb_entries; + for (; iTimeToSample->entries[i]; + if (useCTS) { + stbl_GetSampleCTS(stbl->CompositionOffset, curSampNum, &CTSOffset); + } else { + CTSOffset = 0; + } + for (j=0; jsampleCount; j++) { + if (curDTS + CTSOffset >= DTS) goto entry_found; + curSampNum += 1; + curDTS += ent->sampleDelta; + } + //we're switching to the next entry, update the cache! + stbl->TimeToSample->r_CurrentDTS += ent->sampleCount * ent->sampleDelta; + stbl->TimeToSample->r_currentEntryIndex += 1; + stbl->TimeToSample->r_FirstSampleInEntry += ent->sampleCount; + } + //return as is + return GF_OK; + +entry_found: + //do we have the exact time ? + if (curDTS + CTSOffset == DTS) { + (*sampleNumber) = curSampNum; + } + //if we match the exact DTS also select this sample + else if (curDTS == DTS) { + (*sampleNumber) = curSampNum; + } else { + //exception for the first sample (we need to "load" the playback) + if (curSampNum != 1) { + (*prevSampleNumber) = curSampNum - 1; + } else { + (*prevSampleNumber) = 1; + } + } + return GF_OK; +} + +//Get the Size of a given sample +GF_Err stbl_GetSampleSize(GF_SampleSizeBox *stsz, u32 SampleNumber, u32 *Size) +{ + if (!stsz || !SampleNumber || SampleNumber > stsz->sampleCount) return GF_BAD_PARAM; + + (*Size) = 0; + + if (stsz->sampleSize && (stsz->type != GF_ISOM_BOX_TYPE_STZ2)) { + (*Size) = stsz->sampleSize; + } else { + (*Size) = stsz->sizes[SampleNumber - 1]; + } + return GF_OK; +} + + + +//Get the CTS offset of a given sample +GF_Err stbl_GetSampleCTS(GF_CompositionOffsetBox *ctts, u32 SampleNumber, u32 *CTSoffset) +{ + u32 i; + + (*CTSoffset) = 0; + //test on SampleNumber is done before + if (!ctts || !SampleNumber) return GF_BAD_PARAM; + + if (ctts->r_FirstSampleInEntry && (ctts->r_FirstSampleInEntry < SampleNumber) ) { + i = ctts->r_currentEntryIndex; + } else { + ctts->r_FirstSampleInEntry = 1; + ctts->r_currentEntryIndex = 0; + i = 0; + } + for (; i< ctts->nb_entries; i++) { + if (SampleNumber < ctts->r_FirstSampleInEntry + ctts->entries[i].sampleCount) break; + //update our cache + ctts->r_currentEntryIndex += 1; + ctts->r_FirstSampleInEntry += ctts->entries[i].sampleCount; + } + //no ent, set everything to 0... + if (i==ctts->nb_entries) return GF_OK; + /*asked for a sample not in table - this means CTTS is 0 (that's due to out internal packing construction of CTTS)*/ + if (SampleNumber >= ctts->r_FirstSampleInEntry + ctts->entries[i].sampleCount) return GF_OK; + (*CTSoffset) = ctts->entries[i].decodingOffset; + return GF_OK; +} + +//Get the DTS of a sample +GF_Err stbl_GetSampleDTS_and_Duration(GF_TimeToSampleBox *stts, u32 SampleNumber, u64 *DTS, u32 *duration) +{ + u32 i, j, count; + GF_SttsEntry *ent; + + (*DTS) = 0; + if (!stts || !SampleNumber) return GF_BAD_PARAM; + + ent = NULL; + //use our cache + count = stts->nb_entries; + if (stts->r_FirstSampleInEntry + && (stts->r_FirstSampleInEntry <= SampleNumber) + //this is for read/write access + && (stts->r_currentEntryIndex < count) ) { + + i = stts->r_currentEntryIndex; + } else { + i = stts->r_currentEntryIndex = 0; + stts->r_FirstSampleInEntry = 1; + stts->r_CurrentDTS = 0; + } + + for (; i < count; i++) { + ent = &stts->entries[i]; + + //in our entry + if (ent->sampleCount + stts->r_FirstSampleInEntry >= 1 + SampleNumber) { + j = SampleNumber - stts->r_FirstSampleInEntry; + goto found; + } + + //update our cache + stts->r_CurrentDTS += ent->sampleCount * ent->sampleDelta; + stts->r_currentEntryIndex += 1; + stts->r_FirstSampleInEntry += ent->sampleCount; + } +// if (SampleNumber >= stts->r_FirstSampleInEntry + ent->sampleCount) return GF_BAD_PARAM; + + //no ent, this is really weird. Let's assume the DTS is then what is written in the table + if (!ent || (i == count)) (*DTS) = stts->r_CurrentDTS; + return GF_OK; + +found: + (*DTS) = stts->r_CurrentDTS + j * (u64) ent->sampleDelta; + if (duration) *duration = ent->sampleDelta; + if (stts->r_FirstSampleInEntry == 1) + stts->r_FirstSampleInEntry = 1; + + return GF_OK; +} + +GF_Err stbl_GetSampleDTS(GF_TimeToSampleBox *stts, u32 SampleNumber, u64 *DTS) +{ + return stbl_GetSampleDTS_and_Duration(stts, SampleNumber, DTS, NULL); +} +//Set the RAP flag of a sample +GF_Err stbl_GetSampleRAP(GF_SyncSampleBox *stss, u32 SampleNumber, u8 *IsRAP, u32 *prevRAP, u32 *nextRAP) +{ + u32 i; + + if (prevRAP) *prevRAP = 0; + if (nextRAP) *nextRAP = 0; + + (*IsRAP) = 0; + if (!stss || !SampleNumber) return GF_BAD_PARAM; + + if (stss->r_LastSyncSample && (stss->r_LastSyncSample < SampleNumber) ) { + i = stss->r_LastSampleIndex; + } else { + i = 0; + } + for (; i < stss->nb_entries; i++) { + //get the entry + if (stss->sampleNumbers[i] == SampleNumber) { + //update the cache + stss->r_LastSyncSample = SampleNumber; + stss->r_LastSampleIndex = i; + (*IsRAP) = 1; + } + else if (stss->sampleNumbers[i] > SampleNumber) { + if (nextRAP) *nextRAP = stss->sampleNumbers[i]; + return GF_OK; + } + if (prevRAP) *prevRAP = stss->sampleNumbers[i]; + } + return GF_OK; +} + +//get the number of "ghost chunk" (implicit chunks described by an entry) +void GetGhostNum(GF_StscEntry *ent, u32 EntryIndex, u32 count, GF_SampleTableBox *stbl) +{ + GF_StscEntry *nextEnt; + GF_ChunkOffsetBox *stco; + GF_ChunkLargeOffsetBox *co64; + u32 ghostNum = 1; + + if (!ent->nextChunk) { + if (EntryIndex+1 == count) { + //not specified in the spec, what if the last sample to chunk is no written? + if (stbl->ChunkOffset->type == GF_ISOM_BOX_TYPE_STCO) { + stco = (GF_ChunkOffsetBox *)stbl->ChunkOffset; + ghostNum = (stco->nb_entries > ent->firstChunk) ? (1 + stco->nb_entries - ent->firstChunk) : 1; + } else { + co64 = (GF_ChunkLargeOffsetBox *)stbl->ChunkOffset; + ghostNum = (co64->nb_entries > ent->firstChunk) ? (1 + co64->nb_entries - ent->firstChunk) : 1; + } + } else { + //this is an unknown case due to edit mode... + nextEnt = &stbl->SampleToChunk->entries[EntryIndex+1]; + ghostNum = nextEnt->firstChunk - ent->firstChunk; + } + } else { + ghostNum = (ent->nextChunk > ent->firstChunk) ? (ent->nextChunk - ent->firstChunk) : 1; + } + stbl->SampleToChunk->ghostNumber = ghostNum; +} + +//Get the offset, descIndex and chunkNumber of a sample... +GF_Err stbl_GetSampleInfos(GF_SampleTableBox *stbl, u32 sampleNumber, u64 *offset, u32 *chunkNumber, u32 *descIndex, u8 *isEdited) +{ + GF_Err e; + u32 i, j, k, offsetInChunk, size; + GF_ChunkOffsetBox *stco; + GF_ChunkLargeOffsetBox *co64; + GF_StscEntry *ent; + + (*offset) = 0; + (*chunkNumber) = (*descIndex) = 0; + (*isEdited) = 0; + if (!stbl || !sampleNumber) return GF_BAD_PARAM; + + if (stbl->SampleToChunk->nb_entries == stbl->SampleSize->sampleCount) { + ent = &stbl->SampleToChunk->entries[sampleNumber-1]; + if (!ent) return GF_BAD_PARAM; + (*descIndex) = ent->sampleDescriptionIndex; + (*chunkNumber) = sampleNumber; + (*isEdited) = ent->isEdited; + if ( stbl->ChunkOffset->type == GF_ISOM_BOX_TYPE_STCO) { + stco = (GF_ChunkOffsetBox *)stbl->ChunkOffset; + (*offset) = (u64) stco->offsets[sampleNumber - 1]; + } else { + co64 = (GF_ChunkLargeOffsetBox *)stbl->ChunkOffset; + (*offset) = co64->offsets[sampleNumber - 1]; + } + return GF_OK; + } + + //check our cache + if (stbl->SampleToChunk->firstSampleInCurrentChunk && + (stbl->SampleToChunk->firstSampleInCurrentChunk < sampleNumber)) { + + i = stbl->SampleToChunk->currentIndex; +// ent = gf_list_get(stbl->SampleToChunk->entryList, i); + ent = &stbl->SampleToChunk->entries[stbl->SampleToChunk->currentIndex]; + GetGhostNum(ent, i, stbl->SampleToChunk->nb_entries, stbl); + k = stbl->SampleToChunk->currentChunk; + } else { + i = 0; + stbl->SampleToChunk->currentIndex = 0; + stbl->SampleToChunk->currentChunk = 1; + stbl->SampleToChunk->firstSampleInCurrentChunk = 1; + ent = &stbl->SampleToChunk->entries[0]; + GetGhostNum(ent, 0, stbl->SampleToChunk->nb_entries, stbl); + k = stbl->SampleToChunk->currentChunk; + } + + //first get the chunk + for (; i < stbl->SampleToChunk->nb_entries; i++) { + //browse from the current chunk we're browsing from index 1 + for (; k <= stbl->SampleToChunk->ghostNumber; k++) { + //browse all the samples in this chunk + for (j = 0; j < ent->samplesPerChunk; j++) { + //ok, this is our sample + if (stbl->SampleToChunk->firstSampleInCurrentChunk + j == sampleNumber ) + goto sample_found; + } + //nope, get to next chunk + stbl->SampleToChunk->firstSampleInCurrentChunk += ent->samplesPerChunk; + stbl->SampleToChunk->currentChunk ++; + } + //not in this entry, get the next entry if not the last one + if (i+1 != stbl->SampleToChunk->nb_entries) { + ent = &stbl->SampleToChunk->entries[i+1]; + //update the GhostNumber + GetGhostNum(ent, i+1, stbl->SampleToChunk->nb_entries, stbl); + //update the entry in our cache + stbl->SampleToChunk->currentIndex = i+1; + stbl->SampleToChunk->currentChunk = 1; + k = 1; + } + } + //if we get here, gasp, the sample was not found + return GF_ISOM_INVALID_FILE; + +sample_found: + + (*descIndex) = ent->sampleDescriptionIndex; + (*chunkNumber) = ent->firstChunk + stbl->SampleToChunk->currentChunk - 1; + (*isEdited) = ent->isEdited; + + //ok, get the size of all the previous sample + offsetInChunk = 0; + //warning, firstSampleInChunk is at least 1 - not 0 + for (i = stbl->SampleToChunk->firstSampleInCurrentChunk; i < sampleNumber; i++) { + e = stbl_GetSampleSize(stbl->SampleSize, i, &size); + if (e) return e; + offsetInChunk += size; + } + //OK, that's the size of our offset in the chunk + //now get the chunk + if ( stbl->ChunkOffset->type == GF_ISOM_BOX_TYPE_STCO) { + stco = (GF_ChunkOffsetBox *)stbl->ChunkOffset; + if (stco->nb_entries < (*chunkNumber) ) return GF_ISOM_INVALID_FILE; + (*offset) = (u64) stco->offsets[(*chunkNumber) - 1] + (u64) offsetInChunk; + } else { + co64 = (GF_ChunkLargeOffsetBox *)stbl->ChunkOffset; + if (co64->nb_entries < (*chunkNumber) ) return GF_ISOM_INVALID_FILE; + (*offset) = co64->offsets[(*chunkNumber) - 1] + (u64) offsetInChunk; + } + return GF_OK; +} + + +GF_Err stbl_GetSampleShadow(GF_ShadowSyncBox *stsh, u32 *sampleNumber, u32 *syncNum) +{ + u32 i, count; + GF_StshEntry *ent; + + if (stsh->r_LastFoundSample && (stsh->r_LastFoundSample <= *sampleNumber)) { + i = stsh->r_LastEntryIndex; + } else { + i = 0; + stsh->r_LastFoundSample = 1; + } + + ent = NULL; + (*syncNum) = 0; + + count = gf_list_count(stsh->entries); + for (; ientries, i); + //we get the exact desired sample ! + if (ent->shadowedSampleNumber == *sampleNumber) { + (*syncNum) = ent->syncSampleNumber; + stsh->r_LastFoundSample = *sampleNumber; + stsh->r_LastEntryIndex = i; + return GF_OK; + } else if (ent->shadowedSampleNumber > *sampleNumber) { + //do we have an entry before ? If not, there is no shadowing available + //for this sample + if (!i) return GF_OK; + //ok, indicate the previous ShadowedSample + ent = (GF_StshEntry*)gf_list_get(stsh->entries, i-1); + (*syncNum) = ent->syncSampleNumber; + //change the sample number + (*sampleNumber) = ent->shadowedSampleNumber; + //reset the cache to the last ShadowedSample + stsh->r_LastEntryIndex = i-1; + stsh->r_LastFoundSample = ent->shadowedSampleNumber; + } + } + stsh->r_LastEntryIndex = i-1; + stsh->r_LastFoundSample = ent ? ent->shadowedSampleNumber : 0; + return GF_OK; +} + + + +GF_Err stbl_GetPaddingBits(GF_PaddingBitsBox *padb, u32 SampleNumber, u8 *PadBits) +{ + if (!PadBits) return GF_BAD_PARAM; + *PadBits = 0; + if (!padb || !padb->padbits) return GF_OK; + //the spec says "should" not shall. return 0 padding + if (padb->SampleCount < SampleNumber) return GF_OK; + *PadBits = padb->padbits[SampleNumber-1]; + return GF_OK; +} + +//Set the RAP flag of a sample +GF_Err stbl_GetSampleDepType(GF_SampleDependencyTypeBox *sdep, u32 SampleNumber, u32 *dependsOn, u32 *dependedOn, u32 *redundant) +{ + u8 flag; + + assert(dependsOn && dependedOn && redundant); + *dependsOn = *dependedOn = *redundant = 0; + + if (SampleNumber > sdep->sampleCount) return GF_BAD_PARAM; + flag = sdep->sample_info[SampleNumber-1]; + *dependsOn = (flag >> 4) & 3; + *dependedOn = (flag >> 2) & 3; + *redundant = (flag) & 3; + return GF_OK; +} + +u32 stbl_GetSampleFragmentCount(GF_SampleFragmentBox *stsf, u32 sampleNumber) +{ + GF_StsfEntry *ent; + u32 i, count; + if (!stsf) return 0; + + //check cache + if (!stsf->r_currentEntry || (stsf->r_currentEntry->SampleNumber < sampleNumber)) { + stsf->r_currentEntry = NULL; + stsf->r_currentEntryIndex = 0; + } + i = stsf->r_currentEntryIndex; + + count = gf_list_count(stsf->entryList); + for (; ientryList, i); + if (ent->SampleNumber == sampleNumber) { + stsf->r_currentEntry = ent; + stsf->r_currentEntryIndex = i; + return ent->fragmentCount; + } + } + //not found + return 0; +} + +u32 stbl_GetSampleFragmentSize(GF_SampleFragmentBox *stsf, u32 sampleNumber, u32 FragmentIndex) +{ + GF_StsfEntry *ent; + u32 i, count; + if (!stsf || !FragmentIndex) return 0; + + //check cache + if (!stsf->r_currentEntry || (stsf->r_currentEntry->SampleNumber < sampleNumber)) { + stsf->r_currentEntry = NULL; + stsf->r_currentEntryIndex = 0; + } + i = stsf->r_currentEntryIndex; + count = gf_list_count(stsf->entryList); + for (; ientryList, i); + if (ent->SampleNumber == sampleNumber) { + stsf->r_currentEntry = ent; + stsf->r_currentEntryIndex = i; + if (FragmentIndex > ent->fragmentCount) return 0; + return ent->fragmentSizes[FragmentIndex - 1]; + } + } + //not found + return 0; +} + +#endif /*GPAC_DISABLE_ISOM*/ diff --git a/src/gpacmp4/stbl_write.c b/src/gpacmp4/stbl_write.c new file mode 100644 index 00000000..bb8ffc29 --- /dev/null +++ b/src/gpacmp4/stbl_write.c @@ -0,0 +1,1746 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / ISO Media File Format sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include + +#ifndef GPAC_DISABLE_ISOM + +/*macro used for table gf_realloc - we allocate much more than needed in order to keep the number of +gf_realloc low, which greatly impacts performances for large files*/ +#define ALLOC_INC(a) a = ((a<10) ? 100 : (a*3)/2); + +#ifndef GPAC_DISABLE_ISOM_WRITE + +//adds a DTS in the table and get the sample number of this new sample +//we could return an error if a sample with the same DTS already exists +//but this is not true for QT or MJ2K, only for MP4... +//we assume the authoring tool tries to create a compliant MP4 file. +GF_Err stbl_AddDTS(GF_SampleTableBox *stbl, u64 DTS, u32 *sampleNumber, u32 LastAUDefDuration) +{ + u32 i, j, sampNum; + u64 *DTSs, curDTS; + Bool inserted; + GF_SttsEntry *ent; + + GF_TimeToSampleBox *stts = stbl->TimeToSample; + + //reset the reading cache when adding a sample + stts->r_FirstSampleInEntry = 0; + + *sampleNumber = 0; + //if we don't have an entry, that's the first one... + if (!stts->nb_entries) { + //assert the first DTS is 0. If not, that will break the whole file + if (DTS) return GF_BAD_PARAM; + stts->alloc_size = 1; + stts->nb_entries = 1; + stts->entries = (GF_SttsEntry *) gf_malloc(sizeof(GF_SttsEntry)); + if (!stts->entries) return GF_OUT_OF_MEM; + stts->entries[0].sampleCount = 1; + stts->entries[0].sampleDelta = LastAUDefDuration; + stts->w_currentSampleNum = (*sampleNumber) = 1; + return GF_OK; + } + + //check the last DTS... + if (DTS > stts->w_LastDTS) { + ent = &stts->entries[stts->nb_entries-1]; + //OK, we're adding at the end + if (DTS == stts->w_LastDTS + ent->sampleDelta) { + ent->sampleCount ++; + stts->w_currentSampleNum ++; + (*sampleNumber) = stts->w_currentSampleNum; + stts->w_LastDTS = DTS; + return GF_OK; + } + //we need to split the entry + if (ent->sampleCount == 1) { + //use this one and adjust... + ent->sampleDelta = (u32) (DTS - stts->w_LastDTS); + ent->sampleCount ++; + stts->w_currentSampleNum ++; + stts->w_LastDTS = DTS; + (*sampleNumber) = stts->w_currentSampleNum; + return GF_OK; + } + //we definitely need to split the entry ;) + ent->sampleCount --; + if (stts->alloc_size==stts->nb_entries) { + ALLOC_INC(stts->alloc_size); + stts->entries = (GF_SttsEntry *) gf_realloc(stts->entries, sizeof(GF_SttsEntry)*stts->alloc_size); + if (!stts->entries) return GF_OUT_OF_MEM; + memset(&stts->entries[stts->nb_entries], 0, sizeof(GF_SttsEntry)*(stts->alloc_size-stts->nb_entries) ); + } + ent = &stts->entries[stts->nb_entries]; + stts->nb_entries++; + + ent->sampleCount = 2; + ent->sampleDelta = (u32) (DTS - stts->w_LastDTS); + stts->w_LastDTS = DTS; + stts->w_currentSampleNum ++; + (*sampleNumber) = stts->w_currentSampleNum; + return GF_OK; + } + + + //unpack the DTSs and locate new sample... + DTSs = (u64*)gf_malloc(sizeof(u64) * (stbl->SampleSize->sampleCount+2) ); + if (!DTSs) return GF_OUT_OF_MEM; + curDTS = 0; + sampNum = 0; + ent = NULL; + inserted = 0; + for (i=0; inb_entries; i++) { + ent = & stts->entries[i]; + for (j = 0; jsampleCount; j++) { + if (!inserted && (curDTS > DTS)) { + DTSs[sampNum] = DTS; + sampNum++; + *sampleNumber = sampNum; + inserted = 1; + } + DTSs[sampNum] = curDTS; + curDTS += ent->sampleDelta; + sampNum ++; + } + } + if (!inserted) { + gf_free(DTSs); + return GF_BAD_PARAM; + } + + /*we will at most insert 2 new entries*/ + if (stts->nb_entries+2 >= stts->alloc_size) { + stts->alloc_size += 2; + stts->entries = (GF_SttsEntry *) gf_realloc(stts->entries, sizeof(GF_SttsEntry)*stts->alloc_size); + if (!stts->entries) return GF_OUT_OF_MEM; + memset(&stts->entries[stts->nb_entries], 0, sizeof(GF_SttsEntry)*(stts->alloc_size - stts->nb_entries) ); + } + + /*repack the DTSs*/ + j=0; + stts->nb_entries = 1; + stts->entries[0].sampleCount = 1; + stts->entries[0].sampleDelta = (u32) DTSs[1] /* - (DTS[0] wichis 0)*/; + for (i=1; iSampleSize->sampleCount+1; i++) { + if (i == stbl->SampleSize->sampleCount) { + //and by default, our last sample has the same delta as the prev + stts->entries[j].sampleCount++; + } else if (stts->entries[j].sampleDelta == (u32) ( DTSs[i+1] - DTSs[i]) ) { + stts->entries[j].sampleCount ++; + } else { + stts->nb_entries ++; + j++; + stts->entries[j].sampleCount = 1; + stts->entries[j].sampleDelta = (u32) (DTSs[i+1] - DTSs[i]); + } + } + gf_free(DTSs); + + //reset the cache to the end + stts->w_currentSampleNum = stbl->SampleSize->sampleCount + 1; + return GF_OK; +} + +GF_Err AddCompositionOffset(GF_CompositionOffsetBox *ctts, u32 offset) +{ + if (!ctts) return GF_BAD_PARAM; + + if (ctts->nb_entries && (ctts->entries[ctts->nb_entries-1].decodingOffset==offset)) { + ctts->entries[ctts->nb_entries-1].sampleCount++; + } else { + if (ctts->alloc_size==ctts->nb_entries) { + ALLOC_INC(ctts->alloc_size); + ctts->entries = (GF_DttsEntry*) gf_realloc(ctts->entries, sizeof(GF_DttsEntry)*ctts->alloc_size); + if (!ctts->entries) return GF_OUT_OF_MEM; + memset(&ctts->entries[ctts->nb_entries], 0, sizeof(GF_DttsEntry)*(ctts->alloc_size-ctts->nb_entries) ); + } + ctts->entries[ctts->nb_entries].decodingOffset = offset; + ctts->entries[ctts->nb_entries].sampleCount = 1; + ctts->nb_entries++; + } + ctts->w_LastSampleNumber++; + return GF_OK; +} + +//adds a CTS offset for a new sample +GF_Err stbl_AddCTS(GF_SampleTableBox *stbl, u32 sampleNumber, u32 CTSoffset) +{ + u32 i, j, sampNum, *CTSs; + + GF_CompositionOffsetBox *ctts = stbl->CompositionOffset; + + /*in unpack mode we're sure to have 1 ctts entry per sample*/ + if (ctts->unpack_mode) { + if (ctts->nb_entries==ctts->alloc_size) { + ALLOC_INC(ctts->alloc_size); + ctts->entries = (GF_DttsEntry*) gf_realloc(ctts->entries, sizeof(GF_DttsEntry)*ctts->alloc_size); + if (!ctts->entries) return GF_OUT_OF_MEM; + memset(&ctts->entries[ctts->nb_entries], 0, sizeof(GF_DttsEntry)*(ctts->alloc_size - ctts->nb_entries) ); + } + ctts->entries[ctts->nb_entries].decodingOffset = CTSoffset; + ctts->entries[ctts->nb_entries].sampleCount = 1; + ctts->nb_entries++; + return GF_OK; + } + //check if we're working in order... + if (ctts->w_LastSampleNumber < sampleNumber) { + //add some 0 till we get to the sample + while (ctts->w_LastSampleNumber + 1 != sampleNumber) { + AddCompositionOffset(ctts, 0); + } + return AddCompositionOffset(ctts, CTSoffset); + } + + //NOPE we are inserting a sample... + CTSs = (u32*)gf_malloc(sizeof(u32) * (stbl->SampleSize->sampleCount+1) ); + if (!CTSs) return GF_OUT_OF_MEM; + sampNum = 0; + for (i=0; inb_entries; i++) { + for (j = 0; jentries[i].sampleCount; j++) { + if (sampNum+1==sampleNumber) { + CTSs[sampNum] = CTSoffset; + sampNum ++; + } + CTSs[sampNum] = ctts->entries[i].decodingOffset; + sampNum ++; + } + } + + /*we will at most add 2 new entries (spliting of an existing one)*/ + if (ctts->nb_entries+2>=ctts->alloc_size) { + ctts->alloc_size += 2; + ctts->entries = (GF_DttsEntry*) gf_realloc(ctts->entries, sizeof(GF_DttsEntry)*ctts->alloc_size); + memset(&ctts->entries[ctts->nb_entries], 0, sizeof(GF_DttsEntry)*(ctts->alloc_size-ctts->nb_entries) ); + } + + ctts->entries[0].sampleCount = 1; + ctts->entries[0].decodingOffset = CTSs[0]; + ctts->nb_entries = 1; + j=0; + for (i=1; iSampleSize->sampleCount + 1; i++) { + if (CTSs[i]==ctts->entries[j].decodingOffset) { + ctts->entries[j].sampleCount++; + } else { + j++; + ctts->nb_entries++; + ctts->entries[j].sampleCount = 1; + ctts->entries[j].decodingOffset = CTSs[i]; + } + } + gf_free(CTSs); + + /*we've inserted a sample, therefore the last sample (n) has now number n+1 + we cannot use SampleCount because we have probably skipped some samples + (we're calling AddCTS only if the sample has a CTSOffset !!!)*/ + ctts->w_LastSampleNumber += 1; + return GF_OK; +} + +GF_Err stbl_repackCTS(GF_CompositionOffsetBox *ctts) +{ + u32 i, j; + + if (!ctts->unpack_mode) return GF_OK; + ctts->unpack_mode = 0; + + j=0; + for (i=1; inb_entries; i++) { + if (ctts->entries[i].decodingOffset==ctts->entries[j].decodingOffset) { + ctts->entries[j].sampleCount++; + } else { + j++; + ctts->entries[j].sampleCount = 1; + ctts->entries[j].decodingOffset = ctts->entries[i].decodingOffset; + } + } + ctts->nb_entries=j+1; + /*note we don't realloc*/ + return GF_OK; +} + +GF_Err stbl_unpackCTS(GF_SampleTableBox *stbl) +{ + GF_DttsEntry *packed; + u32 i, j, remain, count; + GF_CompositionOffsetBox *ctts; + ctts = stbl->CompositionOffset; + if (ctts->unpack_mode) return GF_OK; + ctts->unpack_mode = 1; + + packed = ctts->entries; + count = ctts->nb_entries; + ctts->entries = NULL; + ctts->nb_entries = 0; + ctts->alloc_size = 0; + for (i=0;inb_entries == ctts->alloc_size) { + ALLOC_INC(ctts->alloc_size); + ctts->entries = (GF_DttsEntry*) gf_realloc(ctts->entries, sizeof(GF_DttsEntry)*ctts->alloc_size); + memset(&ctts->entries[ctts->nb_entries], 0, sizeof(GF_DttsEntry)*(ctts->alloc_size-ctts->nb_entries) ); + } + ctts->entries[ctts->nb_entries].decodingOffset = packed[i].decodingOffset; + ctts->entries[ctts->nb_entries].sampleCount = 1; + ctts->nb_entries++; + } + } + gf_free(packed); + + remain = stbl->SampleSize->sampleCount - ctts->nb_entries; + while (remain) { + if (ctts->nb_entries == ctts->alloc_size) { + ALLOC_INC(ctts->alloc_size); + ctts->entries = (GF_DttsEntry*) gf_realloc(ctts->entries, sizeof(GF_DttsEntry)*ctts->alloc_size); + memset(&ctts->entries[ctts->nb_entries], 0, sizeof(GF_DttsEntry)*(ctts->alloc_size-ctts->nb_entries) ); + } + ctts->entries[ctts->nb_entries].decodingOffset = 0; + ctts->entries[ctts->nb_entries].sampleCount = 1; + ctts->nb_entries++; + remain--; + } + return GF_OK; +} + +//add size +GF_Err stbl_AddSize(GF_SampleSizeBox *stsz, u32 sampleNumber, u32 size) +{ + u32 i, k; + u32 *newSizes; + if (!stsz || !size || !sampleNumber) return GF_BAD_PARAM; + + if (sampleNumber > stsz->sampleCount + 1) return GF_BAD_PARAM; + + //all samples have the same size + if (stsz->sizes == NULL) { + //1 first sample added in NON COMPACT MODE + if (! stsz->sampleCount && (stsz->type != GF_ISOM_BOX_TYPE_STZ2) ) { + stsz->sampleCount = 1; + stsz->sampleSize = size; + return GF_OK; + } + //2- sample has the same size + if (stsz->sampleSize == size) { + stsz->sampleCount++; + return GF_OK; + } + //3- no, need to alloc a size table + stsz->sizes = (u32*)gf_malloc(sizeof(u32) * (stsz->sampleCount + 1)); + if (!stsz->sizes) return GF_OUT_OF_MEM; + stsz->alloc_size = stsz->sampleCount + 1; + + k = 0; + for (i = 0 ; i < stsz->sampleCount; i++) { + if (i + 1 == sampleNumber) { + stsz->sizes[i + k] = size; + k = 1; + } + stsz->sizes[i+k] = stsz->sampleSize; + } + //this if we append a new sample + if (stsz->sampleCount + 1 == sampleNumber) { + stsz->sizes[stsz->sampleCount] = size; + } + stsz->sampleSize = 0; + stsz->sampleCount++; + return GF_OK; + } + + + /*append*/ + if (stsz->sampleCount + 1 == sampleNumber) { + if (!stsz->alloc_size) stsz->alloc_size = stsz->sampleCount; + if (stsz->sampleCount == stsz->alloc_size) { + ALLOC_INC(stsz->alloc_size); + stsz->sizes = (u32 *) gf_realloc(stsz->sizes, sizeof(u32)*(stsz->alloc_size) ); + if (!stsz->sizes) return GF_OUT_OF_MEM; + memset(&stsz->sizes[stsz->sampleCount], 0, sizeof(u32)*(stsz->alloc_size - stsz->sampleCount) ); + } + stsz->sizes[stsz->sampleCount] = size; + } else { + newSizes = (u32*)gf_malloc(sizeof(u32)*(1 + stsz->sampleCount) ); + if (!newSizes) return GF_OUT_OF_MEM; + k = 0; + for (i = 0; i < stsz->sampleCount; i++) { + if (i + 1 == sampleNumber) { + newSizes[i + k] = size; + k = 1; + } + newSizes[i + k] = stsz->sizes[i]; + } + gf_free(stsz->sizes); + stsz->sizes = newSizes; + stsz->alloc_size = 1 + stsz->sampleCount; + } + stsz->sampleCount++; + return GF_OK; +} + + +GF_Err stbl_AddRAP(GF_SyncSampleBox *stss, u32 sampleNumber) +{ + u32 i, k; + u32 *newNumbers; + + if (!stss || !sampleNumber) return GF_BAD_PARAM; + + if (stss->sampleNumbers == NULL) { + ALLOC_INC(stss->alloc_size); + stss->sampleNumbers = (u32*)gf_malloc(sizeof(u32)*stss->alloc_size); + if (!stss->sampleNumbers) return GF_OUT_OF_MEM; + stss->sampleNumbers[0] = sampleNumber; + stss->nb_entries = 1; + return GF_OK; + } + + if (stss->sampleNumbers[stss->nb_entries-1] == sampleNumber) return GF_OK; + + if (stss->sampleNumbers[stss->nb_entries-1] < sampleNumber) { + if (stss->nb_entries==stss->alloc_size) { + ALLOC_INC(stss->alloc_size); + stss->sampleNumbers = (u32 *) gf_realloc(stss->sampleNumbers, sizeof(u32) * stss->alloc_size); + if (!stss->sampleNumbers) return GF_OUT_OF_MEM; + memset(&stss->sampleNumbers[stss->nb_entries], 0, sizeof(u32) * (stss->alloc_size-stss->nb_entries) ); + } + stss->sampleNumbers[stss->nb_entries] = sampleNumber; + } else { + newNumbers = (u32*)gf_malloc(sizeof(u32) * (stss->nb_entries + 1)); + if (!newNumbers) return GF_OUT_OF_MEM; + //the table is in increasing order of sampleNumber + k = 0; + for (i = 0; i < stss->nb_entries; i++) { + if (stss->sampleNumbers[i] >= sampleNumber) { + newNumbers[i + k] = sampleNumber; + k = 1; + } + newNumbers[i + k] = stss->sampleNumbers[i] + k; + } + gf_free(stss->sampleNumbers); + stss->sampleNumbers = newNumbers; + stss->alloc_size = stss->nb_entries+1; + } + //update our list + stss->nb_entries ++; + return GF_OK; +} + +GF_Err stbl_AddRedundant(GF_SampleTableBox *stbl, u32 sampleNumber) +{ + GF_SampleDependencyTypeBox *sdtp; + + if (stbl->SampleDep == NULL) { + stbl->SampleDep = (GF_SampleDependencyTypeBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_SDTP); + if (!stbl->SampleDep) return GF_OUT_OF_MEM; + } + sdtp = stbl->SampleDep; + if (sdtp->sampleCount + 1 < sampleNumber) { + u32 missed = sampleNumber-1 - sdtp->sampleCount; + sdtp->sample_info = (u8*) gf_realloc(sdtp->sample_info, sizeof(u8) * (sdtp->sampleCount+missed) ); + memset(&sdtp->sample_info[sdtp->sampleCount], 0, sizeof(u8) * missed ); + while (missed) { + u8 isRAP; + if (stbl->SyncSample) stbl_GetSampleRAP(stbl->SyncSample, sdtp->sampleCount+1, &isRAP, NULL, NULL); + else isRAP = 1; + sdtp->sample_info[sdtp->sampleCount] = isRAP ? 0x20 : 0; + sdtp->sampleCount++; + missed--; + } + } + + sdtp->sample_info = (u8*) gf_realloc(sdtp->sample_info, sizeof(u8) * (sdtp->sampleCount + 1)); + if (!sdtp->sample_info) return GF_OUT_OF_MEM; + if (sdtp->sampleCount < sampleNumber) { + sdtp->sample_info[sdtp->sampleCount] = 0x29; + } else { + u32 snum = sampleNumber-1; + memmove(sdtp->sample_info+snum+1, sdtp->sample_info+snum, sizeof(u8) * (sdtp->sampleCount - snum) ); + sdtp->sample_info[snum] = 0x29; + } + //update our list + sdtp->sampleCount ++; + return GF_OK; +} + +//this function is always called in INCREASING order of shadow sample numbers +GF_Err stbl_AddShadow(GF_ShadowSyncBox *stsh, u32 sampleNumber, u32 shadowNumber) +{ + GF_StshEntry *ent; + u32 i, count; + count = gf_list_count(stsh->entries); + for (i=0; ientries, i); + if (ent->shadowedSampleNumber == shadowNumber) { + ent->syncSampleNumber = sampleNumber; + return GF_OK; + } else if (ent->shadowedSampleNumber > shadowNumber) break; + } + ent = (GF_StshEntry*)gf_malloc(sizeof(GF_StshEntry)); + if (!ent) return GF_OUT_OF_MEM; + ent->shadowedSampleNumber = shadowNumber; + ent->syncSampleNumber = sampleNumber; + if (i == gf_list_count(stsh->entries)) { + return gf_list_add(stsh->entries, ent); + } else { + return gf_list_insert(stsh->entries, ent, i ? i-1 : 0); + } +} + +//used in edit/write, where sampleNumber == chunkNumber +GF_Err stbl_AddChunkOffset(GF_MediaBox *mdia, u32 sampleNumber, u32 StreamDescIndex, u64 offset) +{ + GF_SampleTableBox *stbl; + GF_ChunkOffsetBox *stco; + GF_SampleToChunkBox *stsc; + GF_ChunkLargeOffsetBox *co64; + GF_StscEntry *ent; + u32 i, k, *newOff; + u64 *newLarge; + + stbl = mdia->information->sampleTable; + stsc = stbl->SampleToChunk; + + if (stsc->nb_entries + 1 < sampleNumber ) return GF_BAD_PARAM; + + //add the offset to the chunk... + //and we change our offset + if (stbl->ChunkOffset->type == GF_ISOM_BOX_TYPE_STCO) { + stco = (GF_ChunkOffsetBox *)stbl->ChunkOffset; + //if the new offset is a large one, we have to rewrite our table entry by entry (32->64 bit conv)... + if (offset > 0xFFFFFFFF) { + co64 = (GF_ChunkLargeOffsetBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_CO64); + co64->nb_entries = stco->nb_entries + 1; + co64->alloc_size = co64->nb_entries; + co64->offsets = (u64*)gf_malloc(sizeof(u64) * co64->nb_entries); + if (!co64->offsets) return GF_OUT_OF_MEM; + k = 0; + for (i=0;inb_entries; i++) { + if (i + 1 == sampleNumber) { + co64->offsets[i] = offset; + k = 1; + } + co64->offsets[i+k] = (u64) stco->offsets[i]; + } + if (!k) co64->offsets[co64->nb_entries - 1] = offset; + gf_isom_box_del(stbl->ChunkOffset); + stbl->ChunkOffset = (GF_Box *) co64; + } else { + //no, we can use this one. + if (sampleNumber > stco->nb_entries) { + if (!stco->alloc_size) stco->alloc_size = stco->nb_entries; + if (stco->nb_entries == stco->alloc_size) { + ALLOC_INC(stco->alloc_size); + stco->offsets = (u32*)gf_realloc(stco->offsets, sizeof(u32) * stco->alloc_size); + if (!stco->offsets) return GF_OUT_OF_MEM; + memset(&stco->offsets[stco->nb_entries], 0, sizeof(u32) * (stco->alloc_size-stco->nb_entries) ); + } + stco->offsets[stco->nb_entries] = (u32) offset; + stco->nb_entries += 1; + } else { + //nope. we're inserting + newOff = (u32*)gf_malloc(sizeof(u32) * (stco->nb_entries + 1)); + if (!newOff) return GF_OUT_OF_MEM; + k=0; + for (i=0; inb_entries; i++) { + if (i+1 == sampleNumber) { + newOff[i] = (u32) offset; + k=1; + } + newOff[i+k] = stco->offsets[i]; + } + gf_free(stco->offsets); + stco->offsets = newOff; + stco->nb_entries ++; + stco->alloc_size = stco->nb_entries; + } + } + } else { + //use large offset... + co64 = (GF_ChunkLargeOffsetBox *)stbl->ChunkOffset; + if (sampleNumber > co64->nb_entries) { + if (!co64->alloc_size) co64->alloc_size = co64->nb_entries; + if (co64->nb_entries == co64->alloc_size) { + ALLOC_INC(co64->alloc_size); + co64->offsets = (u64*)gf_realloc(co64->offsets, sizeof(u64) * co64->alloc_size); + if (!co64->offsets) return GF_OUT_OF_MEM; + memset(&co64->offsets[co64->nb_entries], 0, sizeof(u64) * (co64->alloc_size - co64->nb_entries) ); + } + co64->offsets[co64->nb_entries] = offset; + co64->nb_entries += 1; + } else { + //nope. we're inserting + newLarge = (u64*)gf_malloc(sizeof(u64) * (co64->nb_entries + 1)); + if (!newLarge) return GF_OUT_OF_MEM; + k=0; + for (i=0; inb_entries; i++) { + if (i+1 == sampleNumber) { + newLarge[i] = offset; + k=1; + } + newLarge[i+k] = co64->offsets[i]; + } + gf_free(co64->offsets); + co64->offsets = newLarge; + co64->nb_entries++; + co64->alloc_size++; + } + } + + if (stsc->nb_entries==stsc->alloc_size) { + ALLOC_INC(stsc->alloc_size); + stsc->entries = (GF_StscEntry *) gf_realloc(stsc->entries, sizeof(GF_StscEntry)*stsc->alloc_size); + if (!stsc->entries) return GF_OUT_OF_MEM; + memset(&stsc->entries[stsc->nb_entries], 0, sizeof(GF_StscEntry)*(stsc->alloc_size-stsc->nb_entries) ); + } + if (sampleNumber == stsc->nb_entries + 1) { + ent = &stsc->entries[stsc->nb_entries]; + } else { + memmove(&stsc->entries[sampleNumber], &stsc->entries[sampleNumber-1], sizeof(GF_StscEntry)*(stsc->nb_entries+1-sampleNumber)); + ent = &stsc->entries[sampleNumber-1]; + } + ent->isEdited = 0; + ent->isEdited = (Media_IsSelfContained(mdia, StreamDescIndex)) ? 1 : 0; + ent->sampleDescriptionIndex = StreamDescIndex; + ent->samplesPerChunk = 1; + ent->firstChunk = sampleNumber; + ent->nextChunk = sampleNumber + 1; + + //OK, now if we've inserted a chunk, update the sample to chunk info... + if (sampleNumber == stsc->nb_entries + 1) { + ent->nextChunk = stsc->nb_entries + 1; + if (stsc->nb_entries) + stsc->entries[stsc->nb_entries-1].nextChunk = ent->firstChunk; + + stbl->SampleToChunk->currentIndex = stsc->nb_entries; + stbl->SampleToChunk->firstSampleInCurrentChunk = sampleNumber; + //write - edit mode: sample number = chunk number + stbl->SampleToChunk->currentChunk = sampleNumber; + stbl->SampleToChunk->ghostNumber = 1; + } else { + /*offset remaining entries*/ + for (i = sampleNumber; inb_entries+1; i++) { + stsc->entries[i].firstChunk++; + } + } + stsc->nb_entries++; + return GF_OK; +} + + + + +GF_Err stbl_SetChunkOffset(GF_MediaBox *mdia, u32 sampleNumber, u64 offset) +{ + GF_StscEntry *ent; + u32 i; + GF_ChunkLargeOffsetBox *co64; + GF_SampleTableBox *stbl = mdia->information->sampleTable; + + if (!sampleNumber || !stbl) return GF_BAD_PARAM; + + ent = &stbl->SampleToChunk->entries[sampleNumber - 1]; + + //we edit our entry if self contained + if (Media_IsSelfContained(mdia, ent->sampleDescriptionIndex)) + ent->isEdited = 1; + + //and we change our offset + if (stbl->ChunkOffset->type == GF_ISOM_BOX_TYPE_STCO) { + //if the new offset is a large one, we have to rewrite our table... + if (offset > 0xFFFFFFFF) { + co64 = (GF_ChunkLargeOffsetBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_CO64); + co64->nb_entries = ((GF_ChunkOffsetBox *)stbl->ChunkOffset)->nb_entries; + co64->alloc_size = co64->nb_entries; + co64->offsets = (u64*)gf_malloc(sizeof(u64)*co64->nb_entries); + if (!co64->offsets) return GF_OUT_OF_MEM; + for (i=0;inb_entries; i++) { + co64->offsets[i] = (u64) ((GF_ChunkOffsetBox *)stbl->ChunkOffset)->offsets[i]; + } + co64->offsets[ent->firstChunk - 1] = offset; + gf_isom_box_del(stbl->ChunkOffset); + stbl->ChunkOffset = (GF_Box *) co64; + return GF_OK; + } + ((GF_ChunkOffsetBox *)stbl->ChunkOffset)->offsets[ent->firstChunk - 1] = (u32) offset; + } else { + ((GF_ChunkLargeOffsetBox *)stbl->ChunkOffset)->offsets[ent->firstChunk - 1] = offset; + } + return GF_OK; +} + + +GF_Err stbl_SetSampleCTS(GF_SampleTableBox *stbl, u32 sampleNumber, u32 offset) +{ + GF_CompositionOffsetBox *ctts = stbl->CompositionOffset; + + assert(ctts->unpack_mode); + + //if we're setting the CTS of a sample we've skipped... + if (ctts->w_LastSampleNumber < sampleNumber) { + //add some 0 till we get to the sample + while (ctts->w_LastSampleNumber + 1 != sampleNumber) { + AddCompositionOffset(ctts, 0); + } + return AddCompositionOffset(ctts, offset); + } + ctts->entries[sampleNumber-1].decodingOffset = offset; + return GF_OK; +} + +GF_Err stbl_SetSampleSize(GF_SampleSizeBox *stsz, u32 SampleNumber, u32 size) +{ + u32 i; + if (!SampleNumber || (stsz->sampleCount < SampleNumber)) return GF_BAD_PARAM; + + if (stsz->sampleSize) { + if (stsz->sampleSize == size) return GF_OK; + if (stsz->sampleCount == 1) { + stsz->sampleSize = size; + return GF_OK; + } + //nope, we have to rewrite a table + stsz->sizes = (u32*)gf_malloc(sizeof(u32)*stsz->sampleCount); + if (!stsz->sizes) return GF_OUT_OF_MEM; + for (i=0; isampleCount; i++) stsz->sizes[i] = stsz->sampleSize; + stsz->sampleSize = 0; + } + stsz->sizes[SampleNumber - 1] = size; + return GF_OK; +} + + +GF_Err stbl_SetSampleRAP(GF_SyncSampleBox *stss, u32 SampleNumber, u8 isRAP) +{ + u32 i; + + //check if we have already a sync sample + for (i = 0; i < stss->nb_entries; i++) { + + if (stss->sampleNumbers[i] < SampleNumber) continue; + else if (stss->sampleNumbers[i] > SampleNumber) break; + + /*found our sample number*/ + if (isRAP) return GF_OK; + /*remove it...*/ + if (i+1 < stss->nb_entries) + memcpy(stss->sampleNumbers + i, stss->sampleNumbers + i + 1, sizeof(u32) * (stss->nb_entries - i - 1)); + stss->nb_entries--; + return GF_OK; + } + //we need to insert a RAP somewhere if RAP ... + if (!isRAP) return GF_OK; + if (stss->nb_entries==stss->alloc_size) { + ALLOC_INC(stss->alloc_size); + stss->sampleNumbers = (u32 *) gf_realloc(stss->sampleNumbers, sizeof(u32)*stss->alloc_size); + if (!stss->sampleNumbers) return GF_OUT_OF_MEM; + memset(&stss->sampleNumbers[stss->nb_entries], 0, sizeof(u32)*(stss->alloc_size - stss->nb_entries) ); + } + + if (i+1 < stss->nb_entries) + memcpy(stss->sampleNumbers + i + 1, stss->sampleNumbers + i, sizeof(u32) * (stss->nb_entries - i - 1)); + stss->sampleNumbers[i] = SampleNumber; + stss->nb_entries ++; + return GF_OK; +} + +GF_Err stbl_SetRedundant(GF_SampleTableBox *stbl, u32 sampleNumber) +{ + if (stbl->SampleDep->sampleCount < sampleNumber) { + return stbl_AddRedundant(stbl, sampleNumber); + } else { + stbl->SampleDep->sample_info[sampleNumber-1] = 0x29; + return GF_OK; + } +} + +GF_Err stbl_SetSyncShadow(GF_ShadowSyncBox *stsh, u32 sampleNumber, u32 syncSample) +{ + u32 i, count; + GF_StshEntry *ent; + + count = gf_list_count(stsh->entries); + for (i=0; ientries, i); + if (ent->shadowedSampleNumber == sampleNumber) { + ent->syncSampleNumber = syncSample; + return GF_OK; + } + if (ent->shadowedSampleNumber > sampleNumber) break; + } + //we need a new one... + ent = (GF_StshEntry*)gf_malloc(sizeof(GF_StshEntry)); + if (!ent) return GF_OUT_OF_MEM; + ent->shadowedSampleNumber = sampleNumber; + ent->syncSampleNumber = syncSample; + //insert or append ? + if (i == gf_list_count(stsh->entries)) { + //don't update the cache ... + return gf_list_add(stsh->entries, ent); + } else { + //update the cache + stsh->r_LastEntryIndex = i; + stsh->r_LastFoundSample = sampleNumber; + return gf_list_insert(stsh->entries, ent, i); + } +} + + +//always called before removing the sample from SampleSize +GF_Err stbl_RemoveDTS(GF_SampleTableBox *stbl, u32 sampleNumber, u32 LastAUDefDuration) +{ + u64 *DTSs, curDTS; + u32 i, j, k, sampNum; + GF_SttsEntry *ent; + GF_TimeToSampleBox *stts; + + stts = stbl->TimeToSample; + + //we're removing the only sample: empty the sample table + if (stbl->SampleSize->sampleCount == 1) { + stts->nb_entries = 0; + stts->r_FirstSampleInEntry = stts->r_currentEntryIndex = 0; + stts->r_CurrentDTS = 0; + return GF_OK; + } + //we're removing the last sample + if (sampleNumber == stbl->SampleSize->sampleCount) { + ent = &stts->entries[stts->nb_entries-1]; + ent->sampleCount--; + if (!ent->sampleCount) stts->nb_entries--; + } else { + //unpack the DTSs... + DTSs = (u64*)gf_malloc(sizeof(u64) * (stbl->SampleSize->sampleCount - 1)); + if (!DTSs) return GF_OUT_OF_MEM; + curDTS = 0; + sampNum = 0; + ent = NULL; + k=0; + for (i=0; inb_entries; i++) { + ent = & stts->entries[i]; + for (j=0; jsampleCount; j++) { + if (sampNum == sampleNumber - 1) { + k=1; + } else { + DTSs[sampNum-k] = curDTS; + } + curDTS += ent->sampleDelta; + sampNum ++; + } + } + j=0; + stts->nb_entries = 1; + stts->entries[0].sampleCount = 1; + if (stbl->SampleSize->sampleCount == 2) { + stts->entries[0].sampleDelta = LastAUDefDuration; + } else { + stts->entries[0].sampleDelta = (u32) DTSs[1] /*- DTS[0]==0 */; + } + for (i=0; iSampleSize->sampleCount-1; i++) { + if (i+1 == stbl->SampleSize->sampleCount-1) { + //and by default, our last sample has the same delta as the prev + // stts->entries[j].sampleCount++; + } else if (DTSs[i+1] - DTSs[i] == stts->entries[j].sampleDelta) { + stts->entries[j].sampleCount += 1; + } else { + j++; + stts->nb_entries++; + stts->entries[j].sampleCount = 1; + stts->entries[j].sampleDelta = (u32) (DTSs[i+1] - DTSs[i]); + } + } + stts->w_LastDTS = DTSs[stbl->SampleSize->sampleCount - 2]; + gf_free(DTSs); + + } + //reset write the cache to the end + stts->w_currentSampleNum = stbl->SampleSize->sampleCount - 1; + //reset read the cache to the begining + stts->r_FirstSampleInEntry = stts->r_currentEntryIndex = 0; + stts->r_CurrentDTS = 0; + return GF_OK; +} + + +//always called before removing the sample from SampleSize +GF_Err stbl_RemoveCTS(GF_SampleTableBox *stbl, u32 sampleNumber) +{ + GF_CompositionOffsetBox *ctts = stbl->CompositionOffset; + assert(ctts->unpack_mode); + + //last one... + if (stbl->SampleSize->sampleCount == 1) { + gf_isom_box_del((GF_Box *) ctts); + stbl->CompositionOffset = NULL; + return GF_OK; + } + + //the number of entries is NOT ALWAYS the number of samples ! + //instead, use the cache + //first case, we're removing a sample that was not added yet + if (sampleNumber > ctts->w_LastSampleNumber) return GF_OK; + + ctts->nb_entries--; + memmove(&ctts->entries[sampleNumber-1], &ctts->entries[sampleNumber], sizeof(GF_DttsEntry)*ctts->nb_entries); + + ctts->w_LastSampleNumber -= 1; + return GF_OK; +} + +GF_Err stbl_RemoveSize(GF_SampleSizeBox *stsz, u32 sampleNumber) +{ + //last sample + if (stsz->sampleCount == 1) { + if (stsz->sizes) gf_free(stsz->sizes); + stsz->sizes = NULL; + stsz->sampleCount = 0; + return GF_OK; + } + //one single size + if (stsz->sampleSize) { + stsz->sampleCount -= 1; + return GF_OK; + } + if (sampleNumber < stsz->sampleCount) { + memcpy(stsz->sizes + sampleNumber - 1, stsz->sizes + sampleNumber, sizeof(u32) * (stsz->sampleCount - sampleNumber)); + } + stsz->sampleCount--; + return GF_OK; +} + +//always called after removing the sample from SampleSize +GF_Err stbl_RemoveChunk(GF_SampleTableBox *stbl, u32 sampleNumber) +{ + u32 i, k; + u32 *offsets; + u64 *Loffsets; + GF_SampleToChunkBox *stsc = stbl->SampleToChunk; + + //remove the entry in SampleToChunk (1 <-> 1 in edit mode) + memmove(&stsc->entries[sampleNumber-1], &stsc->entries[sampleNumber], sizeof(GF_StscEntry)*(stsc->nb_entries-sampleNumber)); + stsc->nb_entries--; + + //update the firstchunk info + for (i=sampleNumber-1; i < stsc->nb_entries; i++) { + stsc->entries[i].firstChunk -= 1; + stsc->entries[i].nextChunk -= 1; + } + //update the cache + stbl->SampleToChunk->firstSampleInCurrentChunk = 1; + stbl->SampleToChunk->currentIndex = 0; + stbl->SampleToChunk->currentChunk = 1; + stbl->SampleToChunk->ghostNumber = 1; + + //realloc the chunk offset + if (stbl->ChunkOffset->type == GF_ISOM_BOX_TYPE_STCO) { + if (!stbl->SampleSize->sampleCount) { + gf_free(((GF_ChunkOffsetBox *)stbl->ChunkOffset)->offsets); + ((GF_ChunkOffsetBox *)stbl->ChunkOffset)->offsets = NULL; + ((GF_ChunkOffsetBox *)stbl->ChunkOffset)->nb_entries = 0; + ((GF_ChunkOffsetBox *)stbl->ChunkOffset)->alloc_size = 0; + return GF_OK; + } + offsets = (u32*)gf_malloc(sizeof(u32) * (stbl->SampleSize->sampleCount)); + if (!offsets) return GF_OUT_OF_MEM; + k=0; + for (i=0; iSampleSize->sampleCount+1; i++) { + if (i+1 == sampleNumber) { + k=1; + } else { + offsets[i-k] = ((GF_ChunkOffsetBox *)stbl->ChunkOffset)->offsets[i]; + } + } + gf_free(((GF_ChunkOffsetBox *)stbl->ChunkOffset)->offsets); + ((GF_ChunkOffsetBox *)stbl->ChunkOffset)->offsets = offsets; + ((GF_ChunkOffsetBox *)stbl->ChunkOffset)->alloc_size = stbl->SampleSize->sampleCount; + ((GF_ChunkOffsetBox *)stbl->ChunkOffset)->nb_entries -= 1; + } else { + if (!stbl->SampleSize->sampleCount) { + gf_free(((GF_ChunkLargeOffsetBox *)stbl->ChunkOffset)->offsets); + ((GF_ChunkLargeOffsetBox *)stbl->ChunkOffset)->offsets = NULL; + ((GF_ChunkLargeOffsetBox *)stbl->ChunkOffset)->nb_entries = 0; + ((GF_ChunkLargeOffsetBox *)stbl->ChunkOffset)->alloc_size = 0; + return GF_OK; + } + + Loffsets = (u64*)gf_malloc(sizeof(u64) * (stbl->SampleSize->sampleCount)); + if (!Loffsets) return GF_OUT_OF_MEM; + k=0; + for (i=0; iSampleSize->sampleCount+1; i++) { + if (i+1 == sampleNumber) { + k=1; + } else { + Loffsets[i-k] = ((GF_ChunkLargeOffsetBox *)stbl->ChunkOffset)->offsets[i]; + } + } + gf_free(((GF_ChunkLargeOffsetBox *)stbl->ChunkOffset)->offsets); + ((GF_ChunkLargeOffsetBox *)stbl->ChunkOffset)->offsets = Loffsets; + ((GF_ChunkLargeOffsetBox *)stbl->ChunkOffset)->alloc_size = stbl->SampleSize->sampleCount; + ((GF_ChunkLargeOffsetBox *)stbl->ChunkOffset)->nb_entries -= 1; + } + return GF_OK; +} + + +GF_Err stbl_RemoveRAP(GF_SampleTableBox *stbl, u32 sampleNumber) +{ + u32 i; + + GF_SyncSampleBox *stss = stbl->SyncSample; + //we remove the only one around... + if (stss->nb_entries == 1) { + if (stss->sampleNumbers[0] != sampleNumber) return GF_OK; + //free our numbers but don't delete (all samples are NON-sync + gf_free(stss->sampleNumbers); + stss->sampleNumbers = NULL; + stss->r_LastSampleIndex = stss->r_LastSyncSample = 0; + stss->alloc_size = stss->nb_entries = 0; + return GF_OK; + } + //the real pain is that we may actually not have to change anything.. + for (i=0; inb_entries; i++) { + if (sampleNumber == stss->sampleNumbers[i]) goto found; + } + //nothing to do + return GF_OK; + +found: + //a small opt: the sample numbers are in order... + i++; + for (;inb_entries; i++) { + stss->sampleNumbers[i-1] = stss->sampleNumbers[i]; + } + stss->nb_entries -= 1; + return GF_OK; +} + +GF_Err stbl_RemoveRedundant(GF_SampleTableBox *stbl, u32 SampleNumber) +{ + u32 i; + + if (!stbl->SampleDep) return GF_OK; + if (stbl->SampleDep->sampleCount < SampleNumber) return GF_BAD_PARAM; + + i = stbl->SampleDep->sampleCount - SampleNumber; + if (i) memmove(&stbl->SampleDep->sample_info[SampleNumber-1], & stbl->SampleDep->sample_info[SampleNumber], sizeof(u8)*i); + stbl->SampleDep->sample_info = (u8*)gf_realloc(stbl->SampleDep->sample_info, sizeof(u8) * (stbl->SampleDep->sampleCount-1)); + stbl->SampleDep->sampleCount-=1; + return GF_OK; +} + +GF_Err stbl_RemoveShadow(GF_ShadowSyncBox *stsh, u32 sampleNumber) +{ + u32 i; + GF_StshEntry *ent; + + //we loop for the whole chain cause the spec doesn't say if we can have several + //shadows for 1 sample... + i=0; + while ((ent = (GF_StshEntry *)gf_list_enum(stsh->entries, &i))) { + if (ent->shadowedSampleNumber == sampleNumber) { + i--; + gf_list_rem(stsh->entries, i); + } + } + //reset the cache + stsh->r_LastEntryIndex = 0; + stsh->r_LastFoundSample = 0; + return GF_OK; +} + + +GF_Err stbl_SetPaddingBits(GF_SampleTableBox *stbl, u32 SampleNumber, u8 bits) +{ + u8 *p; + //make sure the sample is a good one + if (SampleNumber > stbl->SampleSize->sampleCount) return GF_BAD_PARAM; + + //create the table + if (!stbl->PaddingBits) stbl->PaddingBits = (GF_PaddingBitsBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_FADB); + + //alloc + if (!stbl->PaddingBits->padbits || !stbl->PaddingBits->SampleCount) { + stbl->PaddingBits->SampleCount = stbl->SampleSize->sampleCount; + stbl->PaddingBits->padbits = (u8*)gf_malloc(sizeof(u8)*stbl->PaddingBits->SampleCount); + if (!stbl->PaddingBits->padbits) return GF_OUT_OF_MEM; + memset(stbl->PaddingBits->padbits, 0, sizeof(u8)*stbl->PaddingBits->SampleCount); + } + //realloc (this is needed in case n out of k samples get padding added) + if (stbl->PaddingBits->SampleCount < stbl->SampleSize->sampleCount) { + p = (u8*)gf_malloc(sizeof(u8) * stbl->SampleSize->sampleCount); + if (!p) return GF_OUT_OF_MEM; + //set everything to 0 + memset(p, 0, stbl->SampleSize->sampleCount); + //copy our previous table + memcpy(p, stbl->PaddingBits->padbits, stbl->PaddingBits->SampleCount); + gf_free(stbl->PaddingBits->padbits); + stbl->PaddingBits->padbits = p; + stbl->PaddingBits->SampleCount = stbl->SampleSize->sampleCount; + } + stbl->PaddingBits->padbits[SampleNumber-1] = bits; + return GF_OK; +} + +GF_Err stbl_RemovePaddingBits(GF_SampleTableBox *stbl, u32 SampleNumber) +{ + u8 *p; + u32 i, k; + + if (!stbl->PaddingBits) return GF_OK; + if (stbl->PaddingBits->SampleCount < SampleNumber) return GF_BAD_PARAM; + + //last sample - remove the table + if (stbl->PaddingBits->SampleCount == 1) { + gf_isom_box_del((GF_Box *) stbl->PaddingBits); + stbl->PaddingBits = NULL; + return GF_OK; + } + + //reallocate and check size by the way... + p = (u8 *)gf_malloc(sizeof(u8) * (stbl->PaddingBits->SampleCount - 1)); + if (!p) return GF_OUT_OF_MEM; + + k=0; + for (i=0; iPaddingBits->SampleCount; i++) { + if (i+1 != SampleNumber) { + p[k] = stbl->PaddingBits->padbits[i]; + k++; + } + } + + stbl->PaddingBits->SampleCount -= 1; + gf_free(stbl->PaddingBits->padbits); + stbl->PaddingBits->padbits = p; + return GF_OK; +} + + +GF_Err stbl_AddSampleFragment(GF_SampleTableBox *stbl, u32 sampleNumber, u16 size) +{ + GF_Err e; + u32 i, count; + GF_StsfEntry *ent; + GF_SampleFragmentBox *stsf; + stsf = stbl->Fragments; + + if (!stsf) { + //create table if any + stsf = (GF_SampleFragmentBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_STSF); + if (!stsf) return GF_OUT_OF_MEM; + e = stbl_AddBox(stbl, (GF_Box *) stsf); + if (e) return e; + } + + //check cache + if (!stsf->w_currentEntry || (stsf->w_currentEntry->SampleNumber < sampleNumber)) { + stsf->w_currentEntry = NULL; + stsf->w_currentEntryIndex = 0; + } + i = stsf->w_currentEntryIndex; + + count = gf_list_count(stsf->entryList); + for (; ientryList, i); + if (ent->SampleNumber > sampleNumber) { + ent = (GF_StsfEntry *)gf_malloc(sizeof(GF_StsfEntry)); + if (!ent) return GF_OUT_OF_MEM; + memset(ent, 0, sizeof(GF_StsfEntry)); + ent->SampleNumber = sampleNumber; + gf_list_insert(stsf->entryList, ent, i); + stsf->w_currentEntry = ent; + stsf->w_currentEntryIndex = i; + goto ent_found; + } + else if (ent->SampleNumber == sampleNumber) { + stsf->w_currentEntry = ent; + stsf->w_currentEntryIndex = i; + goto ent_found; + } + } + //if we get here add a new entry + GF_SAFEALLOC(ent, GF_StsfEntry); + ent->SampleNumber = sampleNumber; + gf_list_add(stsf->entryList, ent); + stsf->w_currentEntry = ent; + stsf->w_currentEntryIndex = gf_list_count(stsf->entryList)-1; + +ent_found: + if (!ent->fragmentCount) { + ent->fragmentCount = 1; + ent->fragmentSizes = (u16*)gf_malloc(sizeof(u16)); + if (!ent->fragmentSizes) return GF_OUT_OF_MEM; + ent->fragmentSizes[0] = size; + return GF_OK; + } + ent->fragmentSizes = (u16*)gf_realloc(ent->fragmentSizes, sizeof(u16) * (ent->fragmentCount+1)); + if (!ent->fragmentSizes) return GF_OUT_OF_MEM; + ent->fragmentSizes[ent->fragmentCount] = size; + ent->fragmentCount += 1; + + return GF_OK; +} + +GF_Err stbl_RemoveSampleFragments(GF_SampleTableBox *stbl, u32 sampleNumber) +{ + u32 i; + GF_StsfEntry *ent; + GF_SampleFragmentBox *stsf = stbl->Fragments; + + i=0; + while ((ent = (GF_StsfEntry *)gf_list_enum(stsf->entryList, &i))) { + if (ent->SampleNumber == sampleNumber) { + gf_list_rem(stsf->entryList, i-1); + if (ent->fragmentSizes) gf_free(ent->fragmentSizes); + gf_free(ent); + goto exit; + } + } +exit: + //empty table, remove it + if (!gf_list_count(stsf->entryList)) { + stbl->Fragments = NULL; + gf_isom_box_del((GF_Box *)stsf); + } + return GF_OK; +} + +GF_Err stbl_SampleSizeAppend(GF_SampleSizeBox *stsz, u32 data_size) +{ + u32 i; + if (!stsz || !stsz->sampleCount) return GF_BAD_PARAM; + + //we must realloc our table + if (stsz->sampleSize) { + stsz->sizes = (u32*)gf_malloc(sizeof(u32)*stsz->sampleCount); + if (!stsz->sizes) return GF_OUT_OF_MEM; + for (i=0; isampleCount; i++) stsz->sizes[i] = stsz->sampleSize; + stsz->sampleSize = 0; + } + stsz->sizes[stsz->sampleCount-1] += data_size; + return GF_OK; +} + +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + + + +void stbl_AppendTime(GF_SampleTableBox *stbl, u32 duration) +{ + GF_TimeToSampleBox *stts = stbl->TimeToSample; + + if (stts->nb_entries) { + if (stts->entries[stts->nb_entries-1].sampleDelta == duration) { + stts->entries[stts->nb_entries-1].sampleCount += 1; + return; + } + } + if (stts->nb_entries==stts->alloc_size) { + ALLOC_INC(stts->alloc_size); + stts->entries = (GF_SttsEntry *) gf_realloc(stts->entries, sizeof(GF_SttsEntry)*stts->alloc_size); + if (!stts->entries) return; + memset(&stts->entries[stts->nb_entries], 0, sizeof(GF_SttsEntry)*(stts->alloc_size-stts->nb_entries) ); + } + stts->entries[stts->nb_entries].sampleCount = 1; + stts->entries[stts->nb_entries].sampleDelta = duration; + stts->nb_entries++; +} + +void stbl_AppendSize(GF_SampleTableBox *stbl, u32 size) +{ + u32 i; + + if (!stbl->SampleSize->sampleCount) { + stbl->SampleSize->sampleSize = size; + stbl->SampleSize->sampleCount = 1; + return; + } + if (stbl->SampleSize->sampleSize && (stbl->SampleSize->sampleSize==size)) { + stbl->SampleSize->sampleCount += 1; + return; + } + if (!stbl->SampleSize->sizes || (stbl->SampleSize->sampleCount==stbl->SampleSize->alloc_size)) { + Bool init_table = (stbl->SampleSize->sizes==NULL) ? 1 : 0; + ALLOC_INC(stbl->SampleSize->alloc_size); + if (stbl->SampleSize->sampleCount>stbl->SampleSize->alloc_size) + stbl->SampleSize->alloc_size = stbl->SampleSize->sampleCount+1; + + stbl->SampleSize->sizes = (u32 *)gf_realloc(stbl->SampleSize->sizes, sizeof(u32)*stbl->SampleSize->alloc_size); + if (!stbl->SampleSize->sizes) return; + memset(&stbl->SampleSize->sizes[stbl->SampleSize->sampleCount], 0, sizeof(u32) * (stbl->SampleSize->alloc_size - stbl->SampleSize->sampleCount) ); + + if (init_table) { + for (i=0; iSampleSize->sampleCount;i++) + stbl->SampleSize->sizes[i] = stbl->SampleSize->sampleSize; + } + } + stbl->SampleSize->sampleSize = 0; + stbl->SampleSize->sizes[stbl->SampleSize->sampleCount] = size; + stbl->SampleSize->sampleCount += 1; +} + +void stbl_AppendChunk(GF_SampleTableBox *stbl, u64 offset) +{ + GF_ChunkOffsetBox *stco; + GF_ChunkLargeOffsetBox *co64; + u32 *new_offsets, i; + u64 *off_64; + + //we may have to convert the table... + if (stbl->ChunkOffset->type==GF_ISOM_BOX_TYPE_STCO) { + stco = (GF_ChunkOffsetBox *)stbl->ChunkOffset; + + if (offset>0xFFFFFFFF) { + co64 = (GF_ChunkLargeOffsetBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_CO64); + co64->nb_entries = stco->nb_entries + 1; + co64->alloc_size = co64->nb_entries; + co64->offsets = (u64*)gf_malloc(sizeof(u64) * co64->nb_entries); + if (!co64->offsets) return; + for (i=0; inb_entries; i++) co64->offsets[i] = stco->offsets[i]; + co64->offsets[i] = offset; + gf_isom_box_del(stbl->ChunkOffset); + stbl->ChunkOffset = (GF_Box *) co64; + return; + } + //we're fine + new_offsets = (u32*)gf_malloc(sizeof(u32)*(stco->nb_entries+1)); + if (!new_offsets) return; + for (i=0; inb_entries; i++) new_offsets[i] = stco->offsets[i]; + new_offsets[i] = (u32) offset; + if (stco->offsets) gf_free(stco->offsets); + stco->offsets = new_offsets; + stco->nb_entries += 1; + stco->alloc_size = stco->nb_entries; + } + //large offsets + else { + co64 = (GF_ChunkLargeOffsetBox *)stbl->ChunkOffset; + off_64 = (u64*)gf_malloc(sizeof(u32)*(co64->nb_entries+1)); + if (!off_64) return; + for (i=0; inb_entries; i++) off_64[i] = co64->offsets[i]; + off_64[i] = offset; + if (co64->offsets) gf_free(co64->offsets); + co64->offsets = off_64; + co64->nb_entries += 1; + co64->alloc_size = co64->nb_entries; + } +} + +void stbl_AppendSampleToChunk(GF_SampleTableBox *stbl, u32 DescIndex, u32 samplesInChunk) +{ + u32 nextChunk; + GF_SampleToChunkBox *stsc= stbl->SampleToChunk; + GF_StscEntry *ent; + + nextChunk = ((GF_ChunkOffsetBox *) stbl->ChunkOffset)->nb_entries; + + if (stsc->nb_entries) { + ent = &stsc->entries[stsc->nb_entries-1]; + //good we can use this one + if ( (ent->sampleDescriptionIndex == DescIndex) && (ent->samplesPerChunk==samplesInChunk)) + return; + + //set the next chunk btw ... + ent->nextChunk = nextChunk; + } + if (stsc->nb_entries==stsc->alloc_size) { + ALLOC_INC(stsc->alloc_size); + stsc->entries = (GF_StscEntry *) gf_realloc(stsc->entries, sizeof(GF_StscEntry)*stsc->alloc_size); + if (!stsc->entries) return; + memset(&stsc->entries[stsc->nb_entries], 0, sizeof(GF_StscEntry)*(stsc->alloc_size - stsc->nb_entries) ); + } + //ok we need a new entry - this assumes this function is called AFTER AppendChunk + ent = &stsc->entries[stsc->nb_entries]; + ent->firstChunk = nextChunk; + ent->sampleDescriptionIndex = DescIndex; + ent->samplesPerChunk = samplesInChunk; + ent->isEdited = 0; + stsc->nb_entries++; +} + +//called AFTER AddSize +void stbl_AppendRAP(GF_SampleTableBox *stbl, u8 isRap) +{ + u32 i; + + //no sync table + if (!stbl->SyncSample) { + //all samples RAP - no table + if (isRap) return; + + //nope, create one + stbl->SyncSample = (GF_SyncSampleBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_STSS); + if (stbl->SampleSize->sampleCount > 1) { + stbl->SyncSample->sampleNumbers = (u32*)gf_malloc(sizeof(u32) * (stbl->SampleSize->sampleCount-1)); + if (!stbl->SyncSample->sampleNumbers) return; + for (i=0; iSampleSize->sampleCount-1; i++) + stbl->SyncSample->sampleNumbers[i] = i+1; + + } + stbl->SyncSample->nb_entries = stbl->SampleSize->sampleCount-1; + stbl->SyncSample->alloc_size = stbl->SyncSample->nb_entries; + return; + } + if (!isRap) return; + + if (stbl->SyncSample->alloc_size == stbl->SyncSample->nb_entries) { + ALLOC_INC(stbl->SyncSample->alloc_size); + stbl->SyncSample->sampleNumbers = (u32*) gf_realloc(stbl->SyncSample->sampleNumbers, sizeof(u32) * stbl->SyncSample->alloc_size); + if (!stbl->SyncSample->sampleNumbers) return; + memset(&stbl->SyncSample->sampleNumbers[stbl->SyncSample->nb_entries], 0, sizeof(u32) * (stbl->SyncSample->alloc_size-stbl->SyncSample->nb_entries) ); + } + stbl->SyncSample->sampleNumbers[stbl->SyncSample->nb_entries] = stbl->SampleSize->sampleCount; + stbl->SyncSample->nb_entries += 1; +} + +void stbl_AppendPadding(GF_SampleTableBox *stbl, u8 padding) +{ + u32 i; + u8 *pad_bits; + if (!stbl->PaddingBits) stbl->PaddingBits = (GF_PaddingBitsBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_FADB); + + pad_bits = (u8*)gf_malloc(sizeof(u8) * stbl->SampleSize->sampleCount); + if (!pad_bits) return; + memset(pad_bits, 0, sizeof(pad_bits)); +// for (i=0; iSampleSize->sampleCount; i++) pad_bits[i] = 0; + for (i=0; iPaddingBits->SampleCount; i++) pad_bits[i] = stbl->PaddingBits->padbits[i]; + pad_bits[stbl->SampleSize->sampleCount-1] = padding; + if (stbl->PaddingBits->padbits) gf_free(stbl->PaddingBits->padbits); + stbl->PaddingBits->padbits = pad_bits; + stbl->PaddingBits->SampleCount = stbl->SampleSize->sampleCount; +} + +void stbl_AppendCTSOffset(GF_SampleTableBox *stbl, u32 CTSOffset) +{ + GF_CompositionOffsetBox *ctts; + + if (!stbl->CompositionOffset) stbl->CompositionOffset = (GF_CompositionOffsetBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_CTTS); + + ctts = stbl->CompositionOffset; + + if (ctts->nb_entries && (ctts->entries[ctts->nb_entries-1].decodingOffset == CTSOffset) ){ + ctts->entries[ctts->nb_entries-1].sampleCount++; + return; + } + if (ctts->nb_entries==ctts->alloc_size) { + ALLOC_INC(ctts->alloc_size); + ctts->entries = (GF_DttsEntry*) gf_realloc(ctts->entries, sizeof(GF_DttsEntry)*ctts->alloc_size); + memset(&ctts->entries[ctts->nb_entries], 0, sizeof(GF_DttsEntry)*(ctts->alloc_size-ctts->nb_entries) ); + } + ctts->entries[ctts->nb_entries].decodingOffset = CTSOffset; + ctts->entries[ctts->nb_entries].sampleCount = 1; + ctts->nb_entries++; +} + +void stbl_AppendDegradation(GF_SampleTableBox *stbl, u16 DegradationPriority) +{ + if (!stbl->DegradationPriority) stbl->DegradationPriority = (GF_DegradationPriorityBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_STDP); + + stbl->DegradationPriority->priorities = (u16 *)gf_realloc(stbl->DegradationPriority->priorities, sizeof(u16) * stbl->SampleSize->sampleCount); + stbl->DegradationPriority->priorities[stbl->SampleSize->sampleCount-1] = DegradationPriority; + stbl->DegradationPriority->nb_entries = stbl->SampleSize->sampleCount; +} + +void stbl_AppendDepType(GF_SampleTableBox *stbl, u32 DepType) +{ + if (!stbl->SampleDep) stbl->SampleDep= (GF_SampleDependencyTypeBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_SDTP); + stbl->SampleDep->sample_info = (u8*)gf_realloc(stbl->SampleDep->sample_info, sizeof(u8)*stbl->SampleSize->sampleCount ); + stbl->SampleDep->sample_info[stbl->SampleDep->sampleCount] = DepType; + stbl->SampleDep->sampleCount = stbl->SampleSize->sampleCount; + +} + + + +//This functions unpack the offset for easy editing, eg each sample +//is contained in one chunk... +GF_Err stbl_UnpackOffsets(GF_SampleTableBox *stbl) +{ + GF_Err e; + u8 isEdited; + u32 i, chunkNumber, sampleDescIndex; + u64 dataOffset; + GF_StscEntry *ent; + GF_ChunkOffsetBox *stco_tmp; + GF_ChunkLargeOffsetBox *co64_tmp; + GF_SampleToChunkBox *stsc_tmp; + + if (!stbl) return GF_ISOM_INVALID_FILE; + + //we should have none of the mandatory boxes (allowed in the spec) + if (!stbl->ChunkOffset && !stbl->SampleDescription && !stbl->SampleSize && !stbl->SampleToChunk && !stbl->TimeToSample) + return GF_OK; + /*empty track (just created)*/ + if (!stbl->SampleToChunk && !stbl->TimeToSample) return GF_OK; + + //or all the mandatory ones ... + if (!stbl->ChunkOffset || !stbl->SampleDescription || !stbl->SampleSize || !stbl->SampleToChunk || !stbl->TimeToSample) + return GF_ISOM_INVALID_FILE; + + //do we need to unpack? Not if we have only one sample per chunk. + if (stbl->SampleSize->sampleCount == stbl->SampleToChunk->nb_entries) return GF_OK; + + //check the offset type and create a new table... + if (stbl->ChunkOffset->type == GF_ISOM_BOX_TYPE_STCO) { + co64_tmp = NULL; + stco_tmp = (GF_ChunkOffsetBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_STCO); + if (!stco_tmp) return GF_OUT_OF_MEM; + stco_tmp->nb_entries = stbl->SampleSize->sampleCount; + stco_tmp->offsets = (u32*)gf_malloc(stco_tmp->nb_entries * sizeof(u32)); + if (!stco_tmp->offsets) { + gf_isom_box_del((GF_Box*)stco_tmp); + return GF_OUT_OF_MEM; + } + stco_tmp->alloc_size = stco_tmp->nb_entries; + } else if (stbl->ChunkOffset->type == GF_ISOM_BOX_TYPE_CO64) { + stco_tmp = NULL; + co64_tmp = (GF_ChunkLargeOffsetBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_CO64); + if (!co64_tmp) return GF_OUT_OF_MEM; + co64_tmp->nb_entries = stbl->SampleSize->sampleCount; + co64_tmp->offsets = (u64*)gf_malloc(co64_tmp->nb_entries * sizeof(u64)); + if (!co64_tmp->offsets) { + gf_isom_box_del((GF_Box*)co64_tmp); + return GF_OUT_OF_MEM; + } + co64_tmp->alloc_size = co64_tmp->nb_entries; + } else { + return GF_ISOM_INVALID_FILE; + } + + //create a new SampleToChunk table + stsc_tmp = (GF_SampleToChunkBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_STSC); + + stsc_tmp->nb_entries = stsc_tmp->alloc_size = stbl->SampleSize->sampleCount; + stsc_tmp->entries = (GF_StscEntry *) gf_malloc(sizeof(GF_StscEntry)*stsc_tmp->nb_entries); + if (!stsc_tmp->entries) return GF_OUT_OF_MEM; + + //OK write our two tables... + ent = NULL; + for (i = 0; i < stbl->SampleSize->sampleCount; i++) { + //get the data info for the sample + e = stbl_GetSampleInfos(stbl, i+1, &dataOffset, &chunkNumber, &sampleDescIndex, &isEdited); + if (e) goto err_exit; + ent = &stsc_tmp->entries[i]; + ent->isEdited = 0; + ent->sampleDescriptionIndex = sampleDescIndex; + //here's the trick: each sample is in ONE chunk + ent->firstChunk = i+1; + ent->nextChunk = i+2; + ent->samplesPerChunk = 1; + if (stco_tmp) { + stco_tmp->offsets[i] = (u32) dataOffset; + } else { + co64_tmp->offsets[i] = dataOffset; + } + } + //close the list + if (ent) ent->nextChunk = 0; + + + //done, remove our previous tables + gf_isom_box_del(stbl->ChunkOffset); + gf_isom_box_del((GF_Box *)stbl->SampleToChunk); + //and set these ones... + if (stco_tmp) { + stbl->ChunkOffset = (GF_Box *)stco_tmp; + } else { + stbl->ChunkOffset = (GF_Box *)co64_tmp; + } + stbl->SampleToChunk = stsc_tmp; + stbl->SampleToChunk->currentIndex = 0; + stbl->SampleToChunk->currentChunk = 0; + stbl->SampleToChunk->firstSampleInCurrentChunk = 0; + return GF_OK; + +err_exit: + if (stco_tmp) gf_isom_box_del((GF_Box *) stco_tmp); + if (co64_tmp) gf_isom_box_del((GF_Box *) co64_tmp); + if (stsc_tmp) gf_isom_box_del((GF_Box *) stsc_tmp); + return e; +} + +#ifndef GPAC_DISABLE_ISOM_WRITE + +static GFINLINE GF_Err stbl_AddOffset(GF_Box **a, u64 offset) +{ + GF_ChunkOffsetBox *stco; + GF_ChunkLargeOffsetBox *co64; + u32 i; + + if ((*a)->type == GF_ISOM_BOX_TYPE_STCO) { + stco = (GF_ChunkOffsetBox *) *a; + //if dataOffset is bigger than 0xFFFFFFFF, move to LARGE offset + if (offset > 0xFFFFFFFF) { + co64 = (GF_ChunkLargeOffsetBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_CO64); + if (!co64) return GF_OUT_OF_MEM; + co64->nb_entries = stco->nb_entries + 1; + co64->alloc_size = co64->nb_entries; + co64->offsets = (u64*)gf_malloc(co64->nb_entries * sizeof(u64)); + if (!co64->offsets) { + gf_isom_box_del((GF_Box *)co64); + return GF_OUT_OF_MEM; + } + for (i = 0; i< co64->nb_entries - 1; i++) { + co64->offsets[i] = (u64) stco->offsets[i]; + } + co64->offsets[i] = offset; + //delete the box... + gf_isom_box_del(*a); + *a = (GF_Box *)co64; + return GF_OK; + } + //OK, stick with regular... + if (stco->nb_entries==stco->alloc_size) { + ALLOC_INC(stco->alloc_size); + stco->offsets = (u32*)gf_realloc(stco->offsets, stco->alloc_size * sizeof(u32)); + if (!stco->offsets) return GF_OUT_OF_MEM; + memset(&stco->offsets[stco->nb_entries], 0, (stco->alloc_size - stco->nb_entries) * sizeof(u32)); + } + + stco->offsets[stco->nb_entries] = (u32) offset; + stco->nb_entries += 1; + } else { + //this is a large offset + co64 = (GF_ChunkLargeOffsetBox *) *a; + if (co64->nb_entries==co64->alloc_size) { + ALLOC_INC(co64->alloc_size); + co64->offsets = (u64*)gf_realloc(co64->offsets, co64->alloc_size * sizeof(u64)); + if (!co64->offsets) return GF_OUT_OF_MEM; + memset(&co64->offsets[co64->nb_entries], 0, (co64->alloc_size - co64->nb_entries) * sizeof(u64) ); + } + co64->offsets[co64->nb_entries] = offset; + co64->nb_entries += 1; + } + return GF_OK; +} + +//This function packs the offset after easy editing, eg samples +//are re-arranged in chunks according to the chunkOffsets +//NOTE: this has to be called once interleaving or whatever is done and +//the final MDAT is written!!! +GF_Err stbl_SetChunkAndOffset(GF_SampleTableBox *stbl, u32 sampleNumber, u32 StreamDescIndex, GF_SampleToChunkBox *the_stsc, GF_Box **the_stco, u64 data_offset, u8 forceNewChunk) +{ + GF_Err e; + u8 newChunk; + GF_StscEntry *ent, *newEnt, *cur_ent; + + if (!stbl) return GF_ISOM_INVALID_FILE; + + newChunk = 0; + //do we need a new chunk ??? For that, we need + //1 - make sure this sample data is contiguous to the prev one + + //force new chunk is set during writing (flat / interleaved) + //it is set to 1 when data is not contiguous in the media (eg, interleaving) + //when writing flat files, it is never used + if (forceNewChunk) newChunk = 1; + + cur_ent = NULL; + //2 - make sure we have the table inited (i=0) + if (! the_stsc->entries) { + newChunk = 1; + } else { + cur_ent = &the_stsc->entries[the_stsc->nb_entries - 1]; + //3 - make sure we do not exceed the MaxSamplesPerChunk and we have the same descIndex + if (StreamDescIndex != cur_ent->sampleDescriptionIndex) + newChunk = 1; + if (stbl->MaxSamplePerChunk && cur_ent->samplesPerChunk == stbl->MaxSamplePerChunk) + newChunk = 1; + } + + //no need for a new chunk + if (!newChunk) { + cur_ent->samplesPerChunk += 1; + return GF_OK; + } + + //OK, we have to create a new chunk... + //check if we can remove the current sampleToChunk entry (same properties) + if (the_stsc->nb_entries > 1) { + ent = &the_stsc->entries[the_stsc->nb_entries - 2]; + if ( (ent->sampleDescriptionIndex == cur_ent->sampleDescriptionIndex) + && (ent->samplesPerChunk == cur_ent->samplesPerChunk) + ) { + //OK, it's the same SampleToChunk, so delete it + ent->nextChunk = cur_ent->firstChunk; + the_stsc->nb_entries--; + } + } + + //add our offset + e = stbl_AddOffset(the_stco, data_offset); + if (e) return e; + + if (the_stsc->nb_entries==the_stsc->alloc_size) { + ALLOC_INC(the_stsc->alloc_size); + the_stsc->entries = (GF_StscEntry *) gf_realloc(the_stsc->entries, sizeof(GF_StscEntry)*the_stsc->alloc_size); + if (!the_stsc->entries) return GF_OUT_OF_MEM; + memset(&the_stsc->entries[the_stsc->nb_entries], 0, sizeof(GF_StscEntry)*(the_stsc->alloc_size-the_stsc->nb_entries)); + } + //create a new entry (could be the first one, BTW) + newEnt = &the_stsc->entries[the_stsc->nb_entries]; + + //get the first chunk value + if ((*the_stco)->type == GF_ISOM_BOX_TYPE_STCO) { + newEnt->firstChunk = ((GF_ChunkOffsetBox *) (*the_stco) )->nb_entries; + } else { + newEnt->firstChunk = ((GF_ChunkLargeOffsetBox *) (*the_stco) )->nb_entries; + } + newEnt->sampleDescriptionIndex = StreamDescIndex; + newEnt->samplesPerChunk = 1; + newEnt->nextChunk = 0; + //if we already have an entry, adjust its next chunk to point to our new chunk + if (the_stsc->nb_entries) + the_stsc->entries[the_stsc->nb_entries-1].nextChunk = newEnt->firstChunk; + the_stsc->nb_entries++; + return GF_OK; +} + + +GF_Err gf_isom_refresh_size_info(GF_ISOFile *file, u32 trackNumber) +{ + u32 i, size; + GF_TrackBox *trak; + GF_SampleSizeBox *stsz; + trak = gf_isom_get_track_from_file(file, trackNumber); + if (!trak) return GF_BAD_PARAM; + + stsz = trak->Media->information->sampleTable->SampleSize; + if (stsz->sampleSize || !stsz->sampleCount) return GF_OK; + + size = stsz->sizes[0]; + for (i=1; isampleCount; i++) { + if (stsz->sizes[i] != size) { + size = 0; + break; + } + } + if (size) { + gf_free(stsz->sizes); + stsz->sizes = NULL; + stsz->sampleSize = size; + } + return GF_OK; +} + +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + +#endif /*GPAC_DISABLE_ISOM*/ diff --git a/src/gpacmp4/targetver.h b/src/gpacmp4/targetver.h new file mode 100644 index 00000000..87c0086d --- /dev/null +++ b/src/gpacmp4/targetver.h @@ -0,0 +1,8 @@ +#pragma once + +// Including SDKDDKVer.h defines the highest available Windows platform. + +// If you wish to build your application for a previous Windows platform, include WinSDKVer.h and +// set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h. + +#include diff --git a/src/gpacmp4/track.c b/src/gpacmp4/track.c new file mode 100644 index 00000000..a48a577f --- /dev/null +++ b/src/gpacmp4/track.c @@ -0,0 +1,862 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / ISO Media File Format sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ +#include "../disable_warnings.h" +#include +#include + +#ifndef GPAC_DISABLE_ISOM + +GF_TrackBox *GetTrackbyID(GF_MovieBox *moov, u32 TrackID) +{ + GF_TrackBox *trak; + u32 i; + if (!moov) return NULL; + i=0; + while ((trak = (GF_TrackBox *)gf_list_enum(moov->trackList, &i))) { + if (trak->Header->trackID == TrackID) return trak; + } + return NULL; +} + +GF_TrackBox *gf_isom_get_track(GF_MovieBox *moov, u32 trackNumber) +{ + GF_TrackBox *trak; + if (!moov || !trackNumber || (trackNumber > gf_list_count(moov->trackList))) return NULL; + trak = (GF_TrackBox*)gf_list_get(moov->trackList, trackNumber - 1); + return trak; + +} + +//get the number of a track given its ID +//return 0 if not found error +u32 gf_isom_get_tracknum_from_id(GF_MovieBox *moov, u32 trackID) +{ + u32 i; + GF_TrackBox *trak; + i=0; + while ((trak = (GF_TrackBox *)gf_list_enum(moov->trackList, &i))) { + if (trak->Header->trackID == trackID) return i; + } + return 0; +} + +//extraction of the ESD from the track +GF_Err GetESD(GF_MovieBox *moov, u32 trackID, u32 StreamDescIndex, GF_ESD **outESD) +{ + GF_Err e; + GF_ESD *esd; + u32 track_num = 0; + GF_SampleTableBox *stbl; + GF_TrackBox *trak, *OCRTrack; + GF_TrackReferenceTypeBox *dpnd; + GF_SLConfig *slc; + GF_MPEGSampleEntryBox *entry; + + track_num = gf_isom_get_tracknum_from_id(moov, trackID); + dpnd = NULL; + *outESD = NULL; + + trak = gf_isom_get_track(moov, track_num); + if (!trak) return GF_ISOM_INVALID_FILE; + + e = Media_GetESD(trak->Media, StreamDescIndex, &esd, 0); + if (e) return e; + + e = Media_GetSampleDesc(trak->Media, StreamDescIndex, (GF_SampleEntryBox **) &entry, NULL); + if (e) return e; + //set the ID + esd->ESID = trackID; + + //find stream dependencies + e = Track_FindRef(trak, GF_ISOM_BOX_TYPE_DPND , &dpnd); + if (e) return e; + if (dpnd) { + //ONLY ONE STREAM DEPENDENCY IS ALLOWED + if (dpnd->trackIDCount != 1) return GF_ISOM_INVALID_MEDIA; + //fix the spec: where is the index located ?? + esd->dependsOnESID = dpnd->trackIDs[0]; + } else { + esd->dependsOnESID = 0; + } + + if (trak->udta) { + GF_UserDataMap *map; + u32 i = 0; + while ((map = (GF_UserDataMap*)gf_list_enum(trak->udta->recordList, &i))) { + if (map->boxType == GF_4CC('A','U','X','V')) { + GF_Descriptor *d = gf_odf_desc_new(GF_ODF_AUX_VIDEO_DATA); + gf_list_add(esd->extensionDescriptors, d); + break; + } + } + } + + //OK, get the OCR (in a REAL MP4File, OCR is 0 in ESD and is specified through track reference + dpnd = NULL; + OCRTrack = NULL; + //find OCR dependencies + e = Track_FindRef(trak, GF_ISOM_BOX_TYPE_SYNC, &dpnd); + if (e) return e; + if (dpnd) { + if (dpnd->trackIDCount != 1) return GF_ISOM_INVALID_MEDIA; + esd->OCRESID = dpnd->trackIDs[0]; + OCRTrack = gf_isom_get_track_from_id(trak->moov, dpnd->trackIDs[0]); + + while (OCRTrack) { + /*if we have a dependency on a track that doesn't have OCR dep, remove that dependency*/ + e = Track_FindRef(OCRTrack, GF_ISOM_BOX_TYPE_SYNC, &dpnd); + if (e || !dpnd || !dpnd->trackIDCount) { + OCRTrack = NULL; + goto default_sync; + } + /*this is explicit desync*/ + if (dpnd && ((dpnd->trackIDs[0]==0) || (dpnd->trackIDs[0]==OCRTrack->Header->trackID))) break; + /*loop in OCRs, break it*/ + if (esd->ESID == OCRTrack->Header->trackID) { + OCRTrack = NULL; + goto default_sync; + } + /*check next*/ + OCRTrack = gf_isom_get_track_from_id(trak->moov, dpnd->trackIDs[0]); + } + if (!OCRTrack) goto default_sync; + } else { +default_sync: + /*all tracks are sync'ed by default*/ + if (trak->moov->mov->es_id_default_sync<0) { + if (esd->OCRESID) + trak->moov->mov->es_id_default_sync = esd->OCRESID; + else + trak->moov->mov->es_id_default_sync = esd->ESID; + } + if (trak->moov->mov->es_id_default_sync) esd->OCRESID = (u16) trak->moov->mov->es_id_default_sync; + /*cf ESD writer*/ + if (esd->OCRESID == esd->ESID) esd->OCRESID = 0; + } + + + + //update the IPI stuff if needed + if (esd->ipiPtr != NULL) { + dpnd = NULL; + e = Track_FindRef(trak, GF_ISOM_BOX_TYPE_IPIR, &dpnd); + if (e) return e; + if (dpnd) { + if (esd->ipiPtr->tag != GF_ODF_ISOM_IPI_PTR_TAG) return GF_ISOM_INVALID_FILE; + //OK, retrieve the ID: the IPI_ES_Id is currently the ref track + esd->ipiPtr->IPI_ES_Id = dpnd->trackIDs[esd->ipiPtr->IPI_ES_Id - 1]; + //and change the tag + esd->ipiPtr->tag = GF_ODF_IPI_PTR_TAG; + } else { + return GF_ISOM_INVALID_FILE; + } + } + + if ((trak->Media->mediaHeader->packedLanguage[0] != 'u') + || (trak->Media->mediaHeader->packedLanguage[1] != 'n') + || (trak->Media->mediaHeader->packedLanguage[2] != 'd') ) { + if (!esd->langDesc) esd->langDesc = (GF_Language *) gf_odf_desc_new(GF_ODF_LANG_TAG); + + esd->langDesc->langCode = trak->Media->mediaHeader->packedLanguage[0]; esd->langDesc->langCode <<= 8; + esd->langDesc->langCode |= trak->Media->mediaHeader->packedLanguage[1]; esd->langDesc->langCode <<= 8; + esd->langDesc->langCode |= trak->Media->mediaHeader->packedLanguage[2]; + } + + + { + u16 rvc_predefined; + char *rvc_cfg_data; + const char *mime_type; + u32 rvc_cfg_size; + e = gf_isom_get_rvc_config(moov->mov, track_num, 1, &rvc_predefined, &rvc_cfg_data, &rvc_cfg_size, &mime_type); + if (e==GF_OK) { + if (rvc_predefined) { + esd->decoderConfig->predefined_rvc_config = rvc_predefined; + } else { + esd->decoderConfig->rvc_config = (GF_DefaultDescriptor *) gf_odf_desc_new(GF_ODF_DSI_TAG); + if (mime_type && !strcmp(mime_type, "application/rvc-config+xml+gz") ) { + gf_gz_decompress_payload(rvc_cfg_data, rvc_cfg_size, &esd->decoderConfig->rvc_config->data, &esd->decoderConfig->rvc_config->dataLength); + gf_free(rvc_cfg_data); + } else { + esd->decoderConfig->rvc_config->data = rvc_cfg_data; + esd->decoderConfig->rvc_config->dataLength = rvc_cfg_size; + } + } + } + } + + + /*normally all files shall be stored with predefined=SLPredef_MP4, but of course some are broken (philips) + so we just check the ESD_URL. If set, use the given cfg, otherwise always rewrite it*/ + if (esd->URLString != NULL) { + *outESD = esd; + return GF_OK; + } + + //if we are in publishing mode and we have an SLConfig specified, use it as is + switch (entry->type) { + case GF_ISOM_BOX_TYPE_MP4V: + slc = ((GF_MPEGVisualSampleEntryBox *)entry)->slc; + break; + case GF_ISOM_BOX_TYPE_MP4A: + slc = ((GF_MPEGAudioSampleEntryBox *)entry)->slc; + break; + case GF_ISOM_BOX_TYPE_MP4S: + slc = entry->slc; + break; + default: + slc = NULL; + break; + } + if (slc) { + gf_odf_desc_del((GF_Descriptor *)esd->slConfig); + gf_odf_desc_copy((GF_Descriptor *)slc, (GF_Descriptor **)&esd->slConfig); + *outESD = esd; + return GF_OK; + } + //otherwise use the regular mapping + + //this is a desc for a media in the file, let's rewrite some param + esd->slConfig->timestampLength = 32; + esd->slConfig->timestampResolution = trak->Media->mediaHeader->timeScale; + //NO OCR from MP4File streams (eg, constant OC Res one) + esd->slConfig->OCRLength = 0; + esd->slConfig->OCRResolution = 0; +// if (OCRTrack) esd->slConfig->OCRResolution = OCRTrack->Media->mediaHeader->timeScale; + + stbl = trak->Media->information->sampleTable; + // a little optimization here: if all our samples are sync, + //set the RAPOnly to true... for external users... + if (! stbl->SyncSample) { + esd->slConfig->hasRandomAccessUnitsOnlyFlag = 1; + esd->slConfig->useRandomAccessPointFlag = 0; + } else { + esd->slConfig->hasRandomAccessUnitsOnlyFlag = 0; + //signal we are NOT using sync points if no info is present in the table + esd->slConfig->useRandomAccessPointFlag = stbl->SyncSample->nb_entries ? 1 : 0; + } + //do we have DegradationPriority ? + if (stbl->DegradationPriority) { + esd->slConfig->degradationPriorityLength = 15; + } else { + esd->slConfig->degradationPriorityLength = 0; + } + //paddingBits + if (stbl->PaddingBits) { + esd->slConfig->usePaddingFlag = 1; + } + //change to support reflecting OD streams + esd->slConfig->useAccessUnitEndFlag = 1; + esd->slConfig->useAccessUnitStartFlag = 1; + + //signal we do have padding flag (since we only use logical SL packet + //the user can decide whether to use the info or not + esd->slConfig->usePaddingFlag = stbl->PaddingBits ? 1 : 0; + + //same with degradation priority + esd->slConfig->degradationPriorityLength = stbl->DegradationPriority ? 32 : 0; + + //this new SL will be OUT OF THE FILE. Let's set its predefined to 0 + esd->slConfig->predefined = 0; + + + *outESD = esd; + return GF_OK; +} + + +//extraction of the ESD from the track for the given time +GF_Err GetESDForTime(GF_MovieBox *moov, u32 trackID, u64 CTS, GF_ESD **outESD) +{ + GF_Err e; + u32 sampleDescIndex; + GF_TrackBox *trak; + + trak = gf_isom_get_track(moov, gf_isom_get_tracknum_from_id(moov, trackID)); + if (!trak) return GF_ISOM_INVALID_FILE; + + e = Media_GetSampleDescIndex(trak->Media, CTS, &sampleDescIndex ); + if (e) return e; + return GetESD(moov, trackID, sampleDescIndex, outESD); +} + + +GF_Err Track_FindRef(GF_TrackBox *trak, u32 ReferenceType, GF_TrackReferenceTypeBox **dpnd) +{ + GF_TrackReferenceBox *ref; + GF_TrackReferenceTypeBox *a; + u32 i; + if (! trak) return GF_BAD_PARAM; + if (! trak->References) { + *dpnd = NULL; + return GF_OK; + } + ref = trak->References; + i=0; + while ((a = (GF_TrackReferenceTypeBox *)gf_list_enum(ref->boxList, &i))) { + if (a->reference_type == ReferenceType) { + *dpnd = a; + return GF_OK; + } + } + *dpnd = NULL; + return GF_OK; +} + +Bool Track_IsMPEG4Stream(u32 HandlerType) +{ + switch (HandlerType) { + case GF_ISOM_MEDIA_VISUAL: + case GF_ISOM_MEDIA_AUDIO: + case GF_ISOM_MEDIA_SUBPIC: + case GF_ISOM_MEDIA_OD: + case GF_ISOM_MEDIA_OCR: + case GF_ISOM_MEDIA_SCENE: + case GF_ISOM_MEDIA_MPEG7: + case GF_ISOM_MEDIA_OCI: + case GF_ISOM_MEDIA_IPMP: + case GF_ISOM_MEDIA_MPEGJ: + case GF_ISOM_MEDIA_ESM: + return 1; + /*Timedtext is NOT an MPEG-4 stream*/ + default: + return 0; + } +} + + +GF_Err SetTrackDuration(GF_TrackBox *trak) +{ + u64 trackDuration; + u32 i; + GF_EdtsEntry *ent; + GF_EditListBox *elst; + GF_Err e; + + //the total duration is the media duration: adjust it in case... + e = Media_SetDuration(trak); + if (e) return e; + + //if we have an edit list, the duration is the sum of all the editList + //entries' duration (always expressed in MovieTimeScale) + if (trak->editBox && trak->editBox->editList) { + trackDuration = 0; + elst = trak->editBox->editList; + i=0; + while ((ent = (GF_EdtsEntry*)gf_list_enum(elst->entryList, &i))) { + trackDuration += ent->segmentDuration; + } + } else { + //assert the timeScales are non-NULL + if (!trak->moov->mvhd->timeScale || !trak->Media->mediaHeader->timeScale) return GF_ISOM_INVALID_FILE; + trackDuration = (trak->Media->mediaHeader->duration * trak->moov->mvhd->timeScale) / trak->Media->mediaHeader->timeScale; + } + trak->Header->duration = trackDuration; + trak->Header->modificationTime = gf_isom_get_mp4time(); + return GF_OK; +} + + +#ifndef GPAC_DISABLE_ISOM_FRAGMENTS + +GF_Err MergeTrack(GF_TrackBox *trak, GF_TrackFragmentBox *traf, u64 moof_offset, Bool is_first_merge) +{ + u32 i, j, chunk_size; + u64 base_offset, data_offset; + u32 def_duration, DescIndex, def_size, def_flags; + u32 duration, size, flags, cts_offset, prev_trun_data_offset; + u8 pad, sync; + u16 degr; + GF_TrackFragmentRunBox *trun; + GF_TrunEntry *ent; + + void stbl_AppendTime(GF_SampleTableBox *stbl, u32 duration); + void stbl_AppendSize(GF_SampleTableBox *stbl, u32 size); + void stbl_AppendChunk(GF_SampleTableBox *stbl, u64 offset); + void stbl_AppendSampleToChunk(GF_SampleTableBox *stbl, u32 DescIndex, u32 samplesInChunk); + void stbl_AppendCTSOffset(GF_SampleTableBox *stbl, u32 CTSOffset); + void stbl_AppendRAP(GF_SampleTableBox *stbl, u8 isRap); + void stbl_AppendPadding(GF_SampleTableBox *stbl, u8 padding); + void stbl_AppendDegradation(GF_SampleTableBox *stbl, u16 DegradationPriority); + + if (trak->Header->trackID != traf->tfhd->trackID) return GF_OK; + + //setup all our defaults + DescIndex = (traf->tfhd->flags & GF_ISOM_TRAF_SAMPLE_DESC) ? traf->tfhd->sample_desc_index : traf->trex->def_sample_desc_index; + def_duration = (traf->tfhd->flags & GF_ISOM_TRAF_SAMPLE_DUR) ? traf->tfhd->def_sample_duration : traf->trex->def_sample_duration; + def_size = (traf->tfhd->flags & GF_ISOM_TRAF_SAMPLE_SIZE) ? traf->tfhd->def_sample_size : traf->trex->def_sample_size; + def_flags = (traf->tfhd->flags & GF_ISOM_TRAF_SAMPLE_FLAGS) ? traf->tfhd->def_sample_flags : traf->trex->def_sample_flags; + + //locate base offset + base_offset = (traf->tfhd->flags & GF_ISOM_TRAF_BASE_OFFSET) ? traf->tfhd->base_data_offset : moof_offset; + + chunk_size = 0; + prev_trun_data_offset = 0; + + /*in playback mode*/ + if (traf->tfdt && is_first_merge) { +#ifndef GPAC_DISABLE_LOG + if (trak->sample_count_at_seg_start && (trak->dts_at_seg_start != traf->tfdt->baseMediaDecodeTime)) { + GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[iso file] Error: TFDT timing "LLD" different from track cumulated timing "LLD" - using tfdt\n", traf->tfdt->baseMediaDecodeTime, trak->dts_at_seg_start )); + } +#endif + trak->dts_at_seg_start = traf->tfdt->baseMediaDecodeTime; + } + + i=0; + while ((trun = (GF_TrackFragmentRunBox *)gf_list_enum(traf->TrackRuns, &i))) { + //merge the run + for (j=0;jsample_count; j++) { + ent = (GF_TrunEntry*)gf_list_get(trun->entries, j); + size = def_size; + duration = def_duration; + flags = def_flags; + + if (ent) { + if (trun->flags & GF_ISOM_TRUN_DURATION) duration = ent->Duration; + if (trun->flags & GF_ISOM_TRUN_SIZE) size = ent->size; + if (trun->flags & GF_ISOM_TRUN_FLAGS) { + flags = ent->flags; + } else if (!j && (trun->flags & GF_ISOM_TRUN_FIRST_FLAG)) { + flags = trun->first_sample_flags; + } + } + //add size first + stbl_AppendSize(trak->Media->information->sampleTable, size); + //then TS + stbl_AppendTime(trak->Media->information->sampleTable, duration); + //add chunk on first sample + if (!j) { + data_offset = base_offset; + //aggregated offset + if (!(traf->tfhd->flags & GF_ISOM_TRAF_BASE_OFFSET)) data_offset += chunk_size; + + if (trun->flags & GF_ISOM_TRUN_DATA_OFFSET) { + data_offset += trun->data_offset; + /*reset chunk size since data is now relative to this trun*/ + chunk_size = 0; + /*remember this data offset for following trun*/ + prev_trun_data_offset = trun->data_offset; + } else { + /*data offset is previous chunk size plus previous offset of the trun*/ + data_offset += prev_trun_data_offset; + } + stbl_AppendChunk(trak->Media->information->sampleTable, data_offset); + //then sampleToChunk + stbl_AppendSampleToChunk(trak->Media->information->sampleTable, + DescIndex, trun->sample_count); + } + chunk_size += size; + + + //CTS + cts_offset = (trun->flags & GF_ISOM_TRUN_CTS_OFFSET) ? ent->CTS_Offset : 0; + stbl_AppendCTSOffset(trak->Media->information->sampleTable, cts_offset); + + //flags + sync = GF_ISOM_GET_FRAG_SYNC(flags); + stbl_AppendRAP(trak->Media->information->sampleTable, sync); + pad = GF_ISOM_GET_FRAG_PAD(flags); + if (pad) stbl_AppendPadding(trak->Media->information->sampleTable, pad); + degr = GF_ISOM_GET_FRAG_DEG(flags); + if (degr) stbl_AppendDegradation(trak->Media->information->sampleTable, degr); + } + } + return GF_OK; +} + +#endif + + +#ifndef GPAC_DISABLE_ISOM_WRITE + +//used to check if a TrackID is available +u8 RequestTrack(GF_MovieBox *moov, u32 TrackID) +{ + u32 i; + GF_TrackBox *trak; + + i=0; + while ((trak = (GF_TrackBox *)gf_list_enum(moov->trackList, &i))) { + if (trak->Header->trackID == TrackID) { + gf_isom_set_last_error(moov->mov, GF_BAD_PARAM); + return 0; + } + } + return 1; +} + +GF_Err Track_RemoveRef(GF_TrackBox *trak, u32 ReferenceType) +{ + GF_TrackReferenceBox *ref; + GF_Box *a; + u32 i; + if (! trak) return GF_BAD_PARAM; + if (! trak->References) return GF_OK; + ref = trak->References; + i=0; + while ((a = (GF_Box *)gf_list_enum(ref->boxList, &i))) { + if (a->type == ReferenceType) { + gf_isom_box_del(a); + gf_list_rem(ref->boxList, i-1); + return GF_OK; + } + } + return GF_OK; +} + +GF_Err NewMedia(GF_MediaBox **mdia, u32 MediaType, u32 TimeScale) +{ + GF_MediaHeaderBox *mdhd; + GF_Box *mediaInfo; + GF_HandlerBox *hdlr; + GF_MediaInformationBox *minf; + GF_DataInformationBox *dinf; + GF_SampleTableBox *stbl; + GF_DataReferenceBox *dref; + char *str; + + GF_Err e; + + if (*mdia || !mdia) return GF_BAD_PARAM; + + e = GF_OK; + minf = NULL; + mdhd = NULL; + hdlr = NULL; + dinf = NULL; + stbl = NULL; + dref = NULL; + mediaInfo = NULL; + + //first create the media + *mdia = (GF_MediaBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_MDIA); + mdhd = (GF_MediaHeaderBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_MDHD); + + //"handler name" is for debugging purposes. Let's stick our name here ;) + switch (MediaType) { + case GF_ISOM_MEDIA_VISUAL: + mediaInfo = gf_isom_box_new(GF_ISOM_BOX_TYPE_VMHD); + str = "GPAC ISO Video Handler"; + break; + case GF_ISOM_MEDIA_AUDIO: + mediaInfo = gf_isom_box_new(GF_ISOM_BOX_TYPE_SMHD); + str = "GPAC ISO Audio Handler"; + break; + case GF_ISOM_MEDIA_HINT: + mediaInfo = gf_isom_box_new(GF_ISOM_BOX_TYPE_HMHD); + str = "GPAC ISO Hint Handler"; + break; + case GF_ISOM_MEDIA_META: + mediaInfo = gf_isom_box_new(GF_ISOM_BOX_TYPE_NMHD); + str = "GPAC Timed MetaData Handler"; + break; + case GF_ISOM_MEDIA_OD: + mediaInfo = gf_isom_box_new(GF_ISOM_BOX_TYPE_NMHD); + str = "GPAC MPEG-4 OD Handler"; + break; + case GF_ISOM_MEDIA_OCR: + mediaInfo = gf_isom_box_new(GF_ISOM_BOX_TYPE_NMHD); + str = "GPAC MPEG-4 OCR Handler"; + break; + case GF_ISOM_MEDIA_SCENE: + mediaInfo = gf_isom_box_new(GF_ISOM_BOX_TYPE_NMHD); + str = "GPAC MPEG-4 Scene Description Handler"; + break; + case GF_ISOM_MEDIA_MPEG7: + mediaInfo = gf_isom_box_new(GF_ISOM_BOX_TYPE_NMHD); + str = "GPAC MPEG-4 MPEG-7 Handler"; + break; + case GF_ISOM_MEDIA_OCI: + mediaInfo = gf_isom_box_new(GF_ISOM_BOX_TYPE_NMHD); + str = "GPAC MPEG-4 OCI Handler"; + break; + case GF_ISOM_MEDIA_IPMP: + mediaInfo = gf_isom_box_new(GF_ISOM_BOX_TYPE_NMHD); + str = "GPAC MPEG-4 IPMP Handler"; + break; + case GF_ISOM_MEDIA_MPEGJ: + mediaInfo = gf_isom_box_new(GF_ISOM_BOX_TYPE_NMHD); + str = "GPAC MPEG-4 MPEG-J Handler"; + break; + case GF_ISOM_MEDIA_TEXT: + case GF_ISOM_MEDIA_SUBT: + mediaInfo = gf_isom_box_new(GF_ISOM_BOX_TYPE_NMHD); + str = "GPAC Streaming Text Handler"; + break; + case GF_ISOM_MEDIA_DIMS: + mediaInfo = gf_isom_box_new(GF_ISOM_BOX_TYPE_VMHD); + MediaType = GF_ISOM_MEDIA_SCENE; + str = "GPAC DIMS Handler"; + break; + default: + mediaInfo = gf_isom_box_new(GF_ISOM_BOX_TYPE_NMHD); + str = "GPAC IsoMedia Handler"; + break; + } + hdlr = (GF_HandlerBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_HDLR); + minf = (GF_MediaInformationBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_MINF); + + mdhd->timeScale = TimeScale; + hdlr->handlerType = MediaType; + hdlr->nameUTF8 = gf_strdup(str); + + //first set-up the sample table... + stbl = (GF_SampleTableBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_STBL); + dinf = (GF_DataInformationBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_DINF); + stbl->SampleDescription = (GF_SampleDescriptionBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_STSD); + stbl->ChunkOffset = gf_isom_box_new(GF_ISOM_BOX_TYPE_STCO); + //by default create a regular table + stbl->SampleSize = (GF_SampleSizeBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_STSZ); + stbl->SampleToChunk = (GF_SampleToChunkBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_STSC); + stbl->TimeToSample = (GF_TimeToSampleBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_STTS); + + //Create a data reference WITHOUT DATA ENTRY (we don't know anything yet about the media Data) + dref = (GF_DataReferenceBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_DREF); + e = dinf_AddBox((GF_Box*)dinf, (GF_Box *)dref); if (e) goto err_exit; + + e = minf_AddBox((GF_Box*)minf, (GF_Box *) mediaInfo); if (e) goto err_exit; + e = minf_AddBox((GF_Box*)minf, (GF_Box *) stbl); if (e) goto err_exit; + e = minf_AddBox((GF_Box*)minf, (GF_Box *) dinf); if (e) goto err_exit; + + e = mdia_AddBox((GF_Box*)*mdia, (GF_Box *) mdhd); if (e) goto err_exit; + e = mdia_AddBox((GF_Box*)*mdia, (GF_Box *) minf); if (e) goto err_exit; + e = mdia_AddBox((GF_Box*)*mdia, (GF_Box *) hdlr); if (e) goto err_exit; + + return GF_OK; + +err_exit: + if (mdhd) gf_isom_box_del((GF_Box *)mdhd); + if (minf) gf_isom_box_del((GF_Box *)minf); + if (hdlr) { + if (hdlr->nameUTF8) gf_free(hdlr->nameUTF8); + gf_isom_box_del((GF_Box *)hdlr); + } + return e; + +} + +GF_Err Track_SetStreamDescriptor(GF_TrackBox *trak, u32 StreamDescriptionIndex, u32 DataReferenceIndex, GF_ESD *esd, u32 *outStreamIndex) +{ + GF_Err e; + GF_MPEGSampleEntryBox *entry; + GF_MPEGVisualSampleEntryBox *entry_v; + GF_MPEGAudioSampleEntryBox *entry_a; + GF_TrackReferenceBox *tref; + GF_TrackReferenceTypeBox *dpnd; + u16 tmpRef; + + entry = NULL; + tref = NULL; + + if (!trak || !esd || (!outStreamIndex && !DataReferenceIndex) ) return GF_BAD_PARAM; + if (!Track_IsMPEG4Stream(trak->Media->handler->handlerType)) return GF_ISOM_INVALID_MEDIA; + + + esd->ESID = 0; + //set SL to predefined if no url + if (esd->URLString == NULL) { + if (!esd->slConfig) esd->slConfig = (GF_SLConfig*) gf_odf_desc_new(GF_ODF_SLC_TAG); + esd->slConfig->predefined = SLPredef_MP4; + esd->slConfig->durationFlag = 0; + esd->slConfig->useTimestampsFlag = 1; + } + + //get the REF box if needed + if (esd->dependsOnESID || esd->OCRESID ) { + if (!trak->References) { + tref = (GF_TrackReferenceBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_TREF); + e = trak_AddBox((GF_Box*)trak, (GF_Box *)tref); + if (e) return e; + } + tref = trak->References; + } + + //Update Stream dependancies + e = Track_FindRef(trak, GF_ISOM_REF_DECODE, &dpnd); + if (e) return e; + if (!dpnd && esd->dependsOnESID) { + dpnd = (GF_TrackReferenceTypeBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_REFT); + dpnd->reference_type = GF_ISOM_BOX_TYPE_DPND; + e = tref_AddBox((GF_Box*)tref, (GF_Box *) dpnd); + if (e) return e; + e = reftype_AddRefTrack(dpnd, esd->dependsOnESID, NULL); + if (e) return e; + } else if (dpnd && !esd->dependsOnESID) { + Track_RemoveRef(trak, GF_ISOM_BOX_TYPE_DPND); + } + esd->dependsOnESID = 0; + + //Update GF_Clock dependancies + e = Track_FindRef(trak, GF_ISOM_REF_OCR, &dpnd); + if (e) return e; + if (!dpnd && esd->OCRESID) { + dpnd = (GF_TrackReferenceTypeBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_REFT); + dpnd->reference_type = GF_ISOM_BOX_TYPE_SYNC; + e = tref_AddBox((GF_Box*)tref, (GF_Box *) dpnd); + if (e) return e; + e = reftype_AddRefTrack(dpnd, esd->OCRESID, NULL); + if (e) return e; + } else if (dpnd && !esd->OCRESID) { + Track_RemoveRef(trak, GF_ISOM_BOX_TYPE_SYNC); + } else if (dpnd && esd->OCRESID) { + if (dpnd->trackIDCount != 1) return GF_ISOM_INVALID_MEDIA; + dpnd->trackIDs[0] = esd->OCRESID; + } + esd->OCRESID = 0; + + //brand new case: we have to change the IPI desc + if (esd->ipiPtr) { + e = Track_FindRef(trak, GF_ISOM_REF_IPI, &dpnd); + if (e) return e; + if (!dpnd) { + tmpRef = 0; + dpnd = (GF_TrackReferenceTypeBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_REFT); + dpnd->reference_type = GF_ISOM_BOX_TYPE_IPIR; + e = tref_AddBox((GF_Box*)tref, (GF_Box *) dpnd); + if (e) return e; + e = reftype_AddRefTrack(dpnd, esd->ipiPtr->IPI_ES_Id, &tmpRef); + if (e) return e; + //and replace the tag and value... + esd->ipiPtr->IPI_ES_Id = tmpRef; + esd->ipiPtr->tag = GF_ODF_ISOM_IPI_PTR_TAG; + } else { + //Watch out! ONLY ONE IPI dependancy is allowed per stream + if (dpnd->trackIDCount != 1) return GF_ISOM_INVALID_MEDIA; + //if an existing one is there, what shall we do ??? + //donno, erase it + dpnd->trackIDs[0] = esd->ipiPtr->IPI_ES_Id; + //and replace the tag and value... + esd->ipiPtr->IPI_ES_Id = 1; + esd->ipiPtr->tag = GF_ODF_ISOM_IPI_PTR_TAG; + } + } + + /*don't store the lang desc in ESD, use the media header language info*/ + if (esd->langDesc) { + trak->Media->mediaHeader->packedLanguage[0] = (esd->langDesc->langCode>>16)&0xFF; + trak->Media->mediaHeader->packedLanguage[1] = (esd->langDesc->langCode>>8)&0xFF; + trak->Media->mediaHeader->packedLanguage[2] = (esd->langDesc->langCode)&0xFF; + gf_odf_desc_del((GF_Descriptor *)esd->langDesc); + esd->langDesc = NULL; + } + + //we have a streamDescritpionIndex, use it + if (StreamDescriptionIndex) { + entry = (GF_MPEGSampleEntryBox*)gf_list_get(trak->Media->information->sampleTable->SampleDescription->boxList, StreamDescriptionIndex - 1); + if (!entry) return GF_ISOM_INVALID_FILE; + + switch (entry->type) { + case GF_ISOM_BOX_TYPE_MP4S: + //OK, delete the previous ESD + gf_odf_desc_del((GF_Descriptor *) entry->esd->desc); + entry->esd->desc = esd; + break; + case GF_ISOM_BOX_TYPE_MP4V: + entry_v = (GF_MPEGVisualSampleEntryBox*) entry; + //OK, delete the previous ESD + gf_odf_desc_del((GF_Descriptor *) entry_v->esd->desc); + entry_v->esd->desc = esd; + break; + case GF_ISOM_BOX_TYPE_MP4A: + entry_a = (GF_MPEGAudioSampleEntryBox*) entry; + //OK, delete the previous ESD + gf_odf_desc_del((GF_Descriptor *) entry_a->esd->desc); + entry_a->esd->desc = esd; + break; + case GF_ISOM_BOX_TYPE_AVC1: + case GF_ISOM_BOX_TYPE_AVC2: + case GF_ISOM_BOX_TYPE_SVC1: + e = AVC_UpdateESD((GF_MPEGVisualSampleEntryBox*)entry, esd); + if (e) return e; + break; + case GF_ISOM_BOX_TYPE_LSR1: + e = LSR_UpdateESD((GF_LASeRSampleEntryBox*)entry, esd); + if (e) return e; + break; + + default: + gf_odf_desc_del((GF_Descriptor *) esd); + break; + } + } else { + //need to check we're not in URL mode where only ONE description is allowed... + StreamDescriptionIndex = gf_list_count(trak->Media->information->sampleTable->SampleDescription->boxList); + if (StreamDescriptionIndex) { + entry = (GF_MPEGSampleEntryBox*)gf_list_get(trak->Media->information->sampleTable->SampleDescription->boxList, StreamDescriptionIndex - 1); + if (!entry) return GF_ISOM_INVALID_FILE; + if (entry->esd && entry->esd->desc->URLString) return GF_BAD_PARAM; + } + + //OK, check the handler and create the entry + switch (trak->Media->handler->handlerType) { + case GF_ISOM_MEDIA_VISUAL: + if (esd->decoderConfig->objectTypeIndication==GPAC_OTI_VIDEO_AVC) { + entry_v = (GF_MPEGVisualSampleEntryBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_AVC1); + if (!entry_v) return GF_OUT_OF_MEM; + e = AVC_UpdateESD((GF_MPEGVisualSampleEntryBox*)entry_v, esd); + } else { + entry_v = (GF_MPEGVisualSampleEntryBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_MP4V); + if (!entry_v) return GF_OUT_OF_MEM; + entry_v->esd = (GF_ESDBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_ESDS); + entry_v->esd->desc = esd; + } + + //type cast possible now + entry = (GF_MPEGSampleEntryBox*) entry_v; + break; + case GF_ISOM_MEDIA_AUDIO: + entry_a = (GF_MPEGAudioSampleEntryBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_MP4A); + entry_a->samplerate_hi = trak->Media->mediaHeader->timeScale; + if (!entry_a) return GF_OUT_OF_MEM; + entry_a->esd = (GF_ESDBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_ESDS); + entry_a->esd->desc = esd; + //type cast possible now + entry = (GF_MPEGSampleEntryBox*) entry_a; + break; + default: + if ((esd->decoderConfig->streamType==0x03) && (esd->decoderConfig->objectTypeIndication==0x09)) { + entry = (GF_MPEGSampleEntryBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_LSR1); + if (!entry) return GF_OUT_OF_MEM; + e = LSR_UpdateESD((GF_LASeRSampleEntryBox*)entry, esd); + } else { + entry = (GF_MPEGSampleEntryBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_MP4S); + entry->esd = (GF_ESDBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_ESDS); + entry->esd->desc = esd; + } + break; + } + entry->dataReferenceIndex = DataReferenceIndex; + + //and add the entry to our table... + e = stsd_AddBox(trak->Media->information->sampleTable->SampleDescription, (GF_Box *) entry); + if (e) return e; + if(outStreamIndex) *outStreamIndex = gf_list_count(trak->Media->information->sampleTable->SampleDescription->boxList); + } + return GF_OK; +} + +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + +#endif /*GPAC_DISABLE_ISOM*/ diff --git a/src/gpacmp4/tx3g.c b/src/gpacmp4/tx3g.c new file mode 100644 index 00000000..097556d8 --- /dev/null +++ b/src/gpacmp4/tx3g.c @@ -0,0 +1,721 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / ISO Media File Format sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ +#include "../disable_warnings.h" +#include +#include + +#ifndef GPAC_DISABLE_ISOM + +#ifndef GPAC_DISABLE_ISOM_WRITE + +GF_Err gf_isom_update_text_description(GF_ISOFile *movie, u32 trackNumber, u32 descriptionIndex, GF_TextSampleDescriptor *desc) +{ + GF_TrackBox *trak; + GF_Err e; + u32 i; + GF_Tx3gSampleEntryBox *txt; + + if (!descriptionIndex || !desc) return GF_BAD_PARAM; + e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE); + if (e) return e; + + trak = gf_isom_get_track_from_file(movie, trackNumber); + if (!trak || !trak->Media || !desc->font_count) return GF_BAD_PARAM; + + switch (trak->Media->handler->handlerType) { + case GF_ISOM_MEDIA_TEXT: + case GF_ISOM_MEDIA_SUBT: + break; + default: + return GF_BAD_PARAM; + } + + txt = (GF_Tx3gSampleEntryBox*)gf_list_get(trak->Media->information->sampleTable->SampleDescription->boxList, descriptionIndex - 1); + if (!txt) return GF_BAD_PARAM; + switch (txt->type) { + case GF_ISOM_BOX_TYPE_TX3G: + case GF_ISOM_BOX_TYPE_TEXT: + break; + default: + return GF_BAD_PARAM; + } + + trak->Media->mediaHeader->modificationTime = gf_isom_get_mp4time(); + + txt->back_color = desc->back_color; + txt->default_box = desc->default_pos; + txt->default_style = desc->default_style; + txt->displayFlags = desc->displayFlags; + txt->vertical_justification = desc->vert_justif; + txt->horizontal_justification = desc->horiz_justif; + if (txt->font_table) gf_isom_box_del((GF_Box*)txt->font_table); + + txt->font_table = (GF_FontTableBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_FTAB); + txt->font_table->entry_count = desc->font_count; + txt->font_table->fonts = (GF_FontRecord *) gf_malloc(sizeof(GF_FontRecord) * desc->font_count); + for (i=0; ifont_count; i++) { + txt->font_table->fonts[i].fontID = desc->fonts[i].fontID; + if (desc->fonts[i].fontName) txt->font_table->fonts[i].fontName = gf_strdup(desc->fonts[i].fontName); + } + return e; +} + +GF_Err gf_isom_new_text_description(GF_ISOFile *movie, u32 trackNumber, GF_TextSampleDescriptor *desc, char *URLname, char *URNname, u32 *outDescriptionIndex) +{ + GF_TrackBox *trak; + GF_Err e; + u32 dataRefIndex, i; + GF_Tx3gSampleEntryBox *txt; + + e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE); + if (e) return e; + + trak = gf_isom_get_track_from_file(movie, trackNumber); + if (!trak || !trak->Media || !desc->font_count) return GF_BAD_PARAM; + + switch (trak->Media->handler->handlerType) { + case GF_ISOM_MEDIA_TEXT: + case GF_ISOM_MEDIA_SUBT: + break; + default: + return GF_BAD_PARAM; + } + + //get or create the data ref + e = Media_FindDataRef(trak->Media->information->dataInformation->dref, URLname, URNname, &dataRefIndex); + if (e) return e; + if (!dataRefIndex) { + e = Media_CreateDataRef(trak->Media->information->dataInformation->dref, URLname, URNname, &dataRefIndex); + if (e) return e; + } + trak->Media->mediaHeader->modificationTime = gf_isom_get_mp4time(); + + txt = (GF_Tx3gSampleEntryBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_TX3G); + txt->dataReferenceIndex = dataRefIndex; + gf_list_add(trak->Media->information->sampleTable->SampleDescription->boxList, txt); + if (outDescriptionIndex) *outDescriptionIndex = gf_list_count(trak->Media->information->sampleTable->SampleDescription->boxList); + + txt->back_color = desc->back_color; + txt->default_box = desc->default_pos; + txt->default_style = desc->default_style; + txt->displayFlags = desc->displayFlags; + txt->vertical_justification = desc->vert_justif; + txt->horizontal_justification = desc->horiz_justif; + txt->font_table = (GF_FontTableBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_FTAB); + txt->font_table->entry_count = desc->font_count; + + txt->font_table->fonts = (GF_FontRecord *) gf_malloc(sizeof(GF_FontRecord) * desc->font_count); + for (i=0; ifont_count; i++) { + txt->font_table->fonts[i].fontID = desc->fonts[i].fontID; + if (desc->fonts[i].fontName) txt->font_table->fonts[i].fontName = gf_strdup(desc->fonts[i].fontName); + } + return e; +} + + +/*blindly adds text - note we don't rely on terminaison characters to handle utf8 and utf16 data +in the same way. It is the user responsability to signal UTF16*/ +GF_Err gf_isom_text_add_text(GF_TextSample *samp, char *text_data, u32 text_len) +{ + if (!samp) return GF_BAD_PARAM; + if (!text_len) return GF_OK; + samp->text = (char*)gf_realloc(samp->text, sizeof(char) * (samp->len + text_len) ); + memcpy(samp->text + samp->len, text_data, sizeof(char) * text_len); + samp->len += text_len; + return GF_OK; +} + +GF_Err gf_isom_text_set_utf16_marker(GF_TextSample *samp) +{ + /*we MUST have an empty sample*/ + if (!samp || samp->text) return GF_BAD_PARAM; + samp->text = (char*)gf_malloc(sizeof(char) * 2); + samp->text[0] = (char) 0xFE; + samp->text[1] = (char) 0xFF; + samp->len = 2; + return GF_OK; +} + +GF_Err gf_isom_text_add_style(GF_TextSample *samp, GF_StyleRecord *rec) +{ + if (!samp || !rec) return GF_BAD_PARAM; + + if (!samp->styles) { + samp->styles = (GF_TextStyleBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_STYL); + if (!samp->styles) return GF_OUT_OF_MEM; + } + samp->styles->styles = (GF_StyleRecord*)gf_realloc(samp->styles->styles, sizeof(GF_StyleRecord)*(samp->styles->entry_count+1)); + if (!samp->styles->styles) return GF_OUT_OF_MEM; + samp->styles->styles[samp->styles->entry_count] = *rec; + samp->styles->entry_count++; + return GF_OK; +} + +GF_Err gf_isom_text_add_highlight(GF_TextSample *samp, u16 start_char, u16 end_char) +{ + GF_TextHighlightBox *a; + if (!samp) return GF_BAD_PARAM; + if (start_char == end_char) return GF_BAD_PARAM; + + a = (GF_TextHighlightBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_HLIT); + if (!a) return GF_OUT_OF_MEM; + a->startcharoffset = start_char; + a->endcharoffset = end_char; + return gf_list_add(samp->others, a); +} + +GF_Err gf_isom_text_set_highlight_color(GF_TextSample *samp, u8 r, u8 g, u8 b, u8 a) +{ + if (!samp) return GF_BAD_PARAM; + + if (!samp->highlight_color) { + samp->highlight_color = (GF_TextHighlightColorBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_HCLR); + if (!samp->highlight_color) return GF_OUT_OF_MEM; + } + samp->highlight_color->hil_color = a; samp->highlight_color->hil_color <<= 8; + samp->highlight_color->hil_color = r; samp->highlight_color->hil_color <<= 8; + samp->highlight_color->hil_color = g; samp->highlight_color->hil_color <<= 8; + samp->highlight_color->hil_color = b; + return GF_OK; +} + +GF_Err gf_isom_text_set_highlight_color_argb(GF_TextSample *samp, u32 argb) +{ + if (!samp) return GF_BAD_PARAM; + + if (!samp->highlight_color) { + samp->highlight_color = (GF_TextHighlightColorBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_HCLR); + if (!samp->highlight_color) return GF_OUT_OF_MEM; + } + samp->highlight_color->hil_color = argb; + return GF_OK; +} + +/*3GPP spec is quite obscur here*/ +GF_Err gf_isom_text_add_karaoke(GF_TextSample *samp, u32 start_time) +{ + if (!samp) return GF_BAD_PARAM; + samp->cur_karaoke = (GF_TextKaraokeBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_KROK); + if (!samp->cur_karaoke) return GF_OUT_OF_MEM; + samp->cur_karaoke->highlight_starttime = start_time; + return gf_list_add(samp->others, samp->cur_karaoke); +} + +GF_Err gf_isom_text_set_karaoke_segment(GF_TextSample *samp, u32 end_time, u16 start_char, u16 end_char) +{ + if (!samp || !samp->cur_karaoke) return GF_BAD_PARAM; + samp->cur_karaoke->records = (KaraokeRecord*)gf_realloc(samp->cur_karaoke->records, sizeof(KaraokeRecord)*(samp->cur_karaoke->nb_entries+1)); + if (!samp->cur_karaoke->records) return GF_OUT_OF_MEM; + samp->cur_karaoke->records[samp->cur_karaoke->nb_entries].end_charoffset = end_char; + samp->cur_karaoke->records[samp->cur_karaoke->nb_entries].start_charoffset = start_char; + samp->cur_karaoke->records[samp->cur_karaoke->nb_entries].highlight_endtime = end_time; + samp->cur_karaoke->nb_entries++; + return GF_OK; +} + + +GF_Err gf_isom_text_set_scroll_delay(GF_TextSample *samp, u32 scroll_delay) +{ + if (!samp) return GF_BAD_PARAM; + if (!samp->scroll_delay) { + samp->scroll_delay = (GF_TextScrollDelayBox*) gf_isom_box_new(GF_ISOM_BOX_TYPE_DLAY); + if (!samp->scroll_delay) return GF_OUT_OF_MEM; + } + samp->scroll_delay->scroll_delay = scroll_delay; + return GF_OK; +} + +GF_Err gf_isom_text_add_hyperlink(GF_TextSample *samp, char *URL, char *altString, u16 start_char, u16 end_char) +{ + GF_TextHyperTextBox*a; + if (!samp) return GF_BAD_PARAM; + a = (GF_TextHyperTextBox*) gf_isom_box_new(GF_ISOM_BOX_TYPE_HREF); + if (!a) return GF_OUT_OF_MEM; + a->startcharoffset = start_char; + a->endcharoffset = end_char; + a->URL = URL ? gf_strdup(URL) : NULL; + a->URL_hint = altString ? gf_strdup(altString) : NULL; + return gf_list_add(samp->others, a); +} + +GF_Err gf_isom_text_set_box(GF_TextSample *samp, s16 top, s16 left, s16 bottom, s16 right) +{ + if (!samp) return GF_BAD_PARAM; + if (!samp->box) { + samp->box = (GF_TextBoxBox*) gf_isom_box_new(GF_ISOM_BOX_TYPE_TBOX); + if (!samp->box) return GF_OUT_OF_MEM; + } + samp->box->box.top = top; + samp->box->box.left = left; + samp->box->box.bottom = bottom; + samp->box->box.right = right; + return GF_OK; +} + +GF_Err gf_isom_text_add_blink(GF_TextSample *samp, u16 start_char, u16 end_char) +{ + GF_TextBlinkBox *a; + if (!samp) return GF_BAD_PARAM; + a = (GF_TextBlinkBox*) gf_isom_box_new(GF_ISOM_BOX_TYPE_BLNK); + if (!a) return GF_OUT_OF_MEM; + a->startcharoffset = start_char; + a->endcharoffset = end_char; + return gf_list_add(samp->others, a); +} + +GF_Err gf_isom_text_set_wrap(GF_TextSample *samp, u8 wrap_flags) +{ + if (!samp) return GF_BAD_PARAM; + if (!samp->wrap) { + samp->wrap = (GF_TextWrapBox*) gf_isom_box_new(GF_ISOM_BOX_TYPE_TWRP); + if (!samp->wrap) return GF_OUT_OF_MEM; + } + samp->wrap->wrap_flag = wrap_flags; + return GF_OK; +} + +static GFINLINE GF_Err gpp_write_modifier(GF_BitStream *bs, GF_Box *a) +{ + GF_Err e; + if (!a) return GF_OK; + e = gf_isom_box_size(a); + if (!e) e = gf_isom_box_write(a, bs); + return e; +} + +GF_ISOSample *gf_isom_text_to_sample(GF_TextSample *samp) +{ + GF_Err e; + GF_ISOSample *res; + GF_BitStream *bs; + u32 i; + if (!samp) return NULL; + + bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); + gf_bs_write_u16(bs, samp->len); + if (samp->len) gf_bs_write_data(bs, samp->text, samp->len); + + e = gpp_write_modifier(bs, (GF_Box *)samp->styles); + if (!e) e = gpp_write_modifier(bs, (GF_Box *)samp->highlight_color); + if (!e) e = gpp_write_modifier(bs, (GF_Box *)samp->scroll_delay); + if (!e) e = gpp_write_modifier(bs, (GF_Box *)samp->box); + if (!e) e = gpp_write_modifier(bs, (GF_Box *)samp->wrap); + + if (!e) { + GF_Box *a; + i=0; + while ((a = (GF_Box*)gf_list_enum(samp->others, &i))) { + e = gpp_write_modifier(bs, a); + if (e) break; + } + } + if (e) { + gf_bs_del(bs); + return NULL; + } + res = gf_isom_sample_new(); + if (!res) { + gf_bs_del(bs); + return NULL; + } + gf_bs_get_content(bs, &res->data, &res->dataLength); + gf_bs_del(bs); + res->IsRAP = 1; + return res; +} + +GF_Err gf_isom_text_has_similar_description(GF_ISOFile *movie, u32 trackNumber, GF_TextSampleDescriptor *desc, u32 *outDescIdx, Bool *same_box, Bool *same_styles) +{ + GF_TrackBox *trak; + GF_Err e; + u32 i, j, count; + GF_Tx3gSampleEntryBox *txt; + + *same_box = *same_styles = 0; + *outDescIdx = 0; + + if (!desc) return GF_BAD_PARAM; + e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE); + if (e) return GF_BAD_PARAM; + + trak = gf_isom_get_track_from_file(movie, trackNumber); + if (!trak || !trak->Media || !desc->font_count) return GF_BAD_PARAM; + + switch (trak->Media->handler->handlerType) { + case GF_ISOM_MEDIA_TEXT: + case GF_ISOM_MEDIA_SUBT: + break; + default: + return GF_BAD_PARAM; + } + + count = gf_list_count(trak->Media->information->sampleTable->SampleDescription->boxList); + for (i=0; iMedia->information->sampleTable->SampleDescription->boxList, i); + if (!txt) continue; + if ((txt->type != GF_ISOM_BOX_TYPE_TX3G) && (txt->type != GF_ISOM_BOX_TYPE_TEXT)) continue; + if (txt->back_color != desc->back_color) continue; + if (txt->displayFlags != desc->displayFlags) continue; + if (txt->vertical_justification != desc->vert_justif) continue; + if (txt->horizontal_justification != desc->horiz_justif) continue; + if (txt->font_table->entry_count != desc->font_count) continue; + + same_fonts = 1; + for (j=0; jfont_count; j++) { + if (txt->font_table->fonts[j].fontID != desc->fonts[j].fontID) same_fonts = 0; + else if (strcmp(desc->fonts[j].fontName, txt->font_table->fonts[j].fontName)) same_fonts = 0; + } + if (same_fonts) { + *outDescIdx = i+1; + if (!memcmp(&txt->default_box, &desc->default_pos, sizeof(GF_BoxRecord))) *same_box = 1; + if (!memcmp(&txt->default_style, &desc->default_style, sizeof(GF_StyleRecord))) *same_styles = 1; + return GF_OK; + } + } + return GF_OK; +} + +#endif /*GPAC_DISABLE_ISOM_WRITE*/ + +GF_TextSample *gf_isom_new_text_sample() +{ + GF_TextSample *res; + GF_SAFEALLOC(res, GF_TextSample); + if (!res) return NULL; + res->others = gf_list_new(); + return res; +} + +GF_Err gf_isom_text_reset_styles(GF_TextSample *samp) +{ + if (!samp) return GF_BAD_PARAM; + if (samp->box) gf_isom_box_del((GF_Box *)samp->box); + samp->box = NULL; + if (samp->highlight_color) gf_isom_box_del((GF_Box *)samp->highlight_color); + samp->highlight_color = NULL; + if (samp->scroll_delay) gf_isom_box_del((GF_Box *)samp->scroll_delay); + samp->scroll_delay = NULL; + if (samp->wrap) gf_isom_box_del((GF_Box *)samp->wrap); + samp->wrap = NULL; + if (samp->styles) gf_isom_box_del((GF_Box *)samp->styles); + samp->styles = NULL; + samp->cur_karaoke = NULL; + while (gf_list_count(samp->others)) { + GF_Box *a = (GF_Box*)gf_list_get(samp->others, 0); + gf_list_rem(samp->others, 0); + gf_isom_box_del(a); + } + return GF_OK; +} + +GF_Err gf_isom_text_reset(GF_TextSample *samp) +{ + if (!samp) return GF_BAD_PARAM; + if (samp->text) gf_free(samp->text); + samp->text = NULL; + samp->len = 0; + return gf_isom_text_reset_styles(samp); +} + +GF_EXPORT +void gf_isom_delete_text_sample(GF_TextSample * tx_samp) +{ + gf_isom_text_reset(tx_samp); + gf_list_del(tx_samp->others); + gf_free(tx_samp); +} + +GF_EXPORT +GF_TextSample *gf_isom_parse_texte_sample(GF_BitStream *bs) +{ + GF_TextSample *s = gf_isom_new_text_sample(); + + /*empty sample*/ + if (!bs || !gf_bs_available(bs)) return s; + + s->len = gf_bs_read_u16(bs); + if (s->len) { + /*2 extra bytes for UTF-16 term char just in case (we don't know if a BOM marker is present or + not since this may be a sample carried over RTP*/ + s->text = (char *) gf_malloc(sizeof(char)*(s->len+2) ); + s->text[s->len] = 0; s->text[s->len+1] = 0; + gf_bs_read_data(bs, s->text, s->len); + } + + while (gf_bs_available(bs)) { + GF_Box *a; + GF_Err e = gf_isom_parse_box(&a, bs); + if (!e) { + switch (a->type) { + case GF_ISOM_BOX_TYPE_STYL: + if (s->styles) { + GF_TextStyleBox *st2 = (GF_TextStyleBox *)a; + if (!s->styles->entry_count) { + gf_isom_box_del((GF_Box*)s->styles); + s->styles = st2; + } else { + s->styles->styles = (GF_StyleRecord*)gf_realloc(s->styles->styles, sizeof(GF_StyleRecord) * (s->styles->entry_count + st2->entry_count)); + memcpy(&s->styles->styles[s->styles->entry_count], st2->styles, sizeof(GF_StyleRecord) * st2->entry_count); + s->styles->entry_count += st2->entry_count; + gf_isom_box_del(a); + } + } else { + s->styles = (GF_TextStyleBox*)a; + } + break; + case GF_ISOM_BOX_TYPE_KROK: + s->cur_karaoke = (GF_TextKaraokeBox*)a; + case GF_ISOM_BOX_TYPE_HLIT: + case GF_ISOM_BOX_TYPE_HREF: + case GF_ISOM_BOX_TYPE_BLNK: + gf_list_add(s->others, a); + break; + case GF_ISOM_BOX_TYPE_HCLR: + if (s->highlight_color) gf_isom_box_del(a); + else s->highlight_color = (GF_TextHighlightColorBox *) a; + break; + case GF_ISOM_BOX_TYPE_DLAY: + if (s->scroll_delay) gf_isom_box_del(a); + else s->scroll_delay= (GF_TextScrollDelayBox*) a; + break; + case GF_ISOM_BOX_TYPE_TBOX: + if (s->box) gf_isom_box_del(a); + else s->box= (GF_TextBoxBox *) a; + break; + case GF_ISOM_BOX_TYPE_TWRP: + if (s->wrap) gf_isom_box_del(a); + else s->wrap= (GF_TextWrapBox*) a; + break; + default: + gf_isom_box_del(a); + break; + } + } + } + return s; +} + +GF_TextSample *gf_isom_parse_texte_sample_from_data(char *data, u32 dataLength) +{ + GF_TextSample *s; + GF_BitStream *bs; + /*empty text sample*/ + if (!data || !dataLength) { + return gf_isom_new_text_sample(); + } + + bs = gf_bs_new(data, dataLength, GF_BITSTREAM_READ); + s = gf_isom_parse_texte_sample(bs); + gf_bs_del(bs); + return s; +} + + +/*out-of-band sample desc (128 and 255 reserved in RFC)*/ +#define SAMPLE_INDEX_OFFSET 129 + + +static void gf_isom_write_tx3g(GF_Tx3gSampleEntryBox *a, GF_BitStream *bs, u32 sidx, u32 sidx_offset) +{ + u32 size, j; + void gpp_write_rgba(GF_BitStream *bs, u32 col); + void gpp_write_box(GF_BitStream *bs, GF_BoxRecord *rec); + void gpp_write_style(GF_BitStream *bs, GF_StyleRecord *rec); + + + if (sidx_offset) gf_bs_write_u8(bs, sidx + sidx_offset); + + /*SINCE WINCE HAS A READONLY VERSION OF MP4 WE MUST DO IT BY HAND*/ + size = 8 + 18 + 8 + 12; + size += 8 + 2; + for (j=0; jfont_table->entry_count; j++) { + size += 3; + if (a->font_table->fonts[j].fontName) size += strlen(a->font_table->fonts[j].fontName); + } + /*write TextSampleEntry box*/ + gf_bs_write_u32(bs, size); + gf_bs_write_u32(bs, a->type); + gf_bs_write_data(bs, a->reserved, 6); + gf_bs_write_u16(bs, a->dataReferenceIndex); + gf_bs_write_u32(bs, a->displayFlags); + gf_bs_write_u8(bs, a->horizontal_justification); + gf_bs_write_u8(bs, a->vertical_justification); + gpp_write_rgba(bs, a->back_color); + gpp_write_box(bs, &a->default_box); + gpp_write_style(bs, &a->default_style); + /*write font table box*/ + size -= (8 + 18 + 8 + 12); + gf_bs_write_u32(bs, size); + gf_bs_write_u32(bs, GF_ISOM_BOX_TYPE_FTAB); + gf_bs_write_u16(bs, a->font_table->entry_count); + for (j=0; jfont_table->entry_count; j++) { + gf_bs_write_u16(bs, a->font_table->fonts[j].fontID); + if (a->font_table->fonts[j].fontName) { + u32 len = strlen(a->font_table->fonts[j].fontName); + gf_bs_write_u8(bs, len); + gf_bs_write_data(bs, a->font_table->fonts[j].fontName, len); + } else { + gf_bs_write_u8(bs, 0); + } + } +} + +GF_Err gf_isom_get_ttxt_esd(GF_MediaBox *mdia, GF_ESD **out_esd) +{ + GF_BitStream *bs; + u32 count, i; + Bool has_v_info; + GF_List *sampleDesc; + GF_ESD *esd; + GF_TrackBox *tk; + + *out_esd = NULL; + sampleDesc = mdia->information->sampleTable->SampleDescription->boxList; + count = gf_list_count(sampleDesc); + if (!count) return GF_ISOM_INVALID_MEDIA; + + esd = gf_odf_desc_esd_new(2); + esd->decoderConfig->streamType = GF_STREAM_TEXT; + esd->decoderConfig->objectTypeIndication = 0x08; + + bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); + + + /*Base3GPPFormat*/ + gf_bs_write_u8(bs, 0x10); + /*MPEGExtendedFormat*/ + gf_bs_write_u8(bs, 0x10); + /*profileLevel*/ + gf_bs_write_u8(bs, 0x10); + gf_bs_write_u24(bs, mdia->mediaHeader->timeScale); + gf_bs_write_int(bs, 0, 1); /*no alt formats*/ + gf_bs_write_int(bs, 2, 2); /*only out-of-band-band sample desc*/ + gf_bs_write_int(bs, 1, 1); /*we will write sample desc*/ + + /*write v info if any visual track in this movie*/ + has_v_info = 0; + i=0; + while ((tk = (GF_TrackBox*)gf_list_enum(mdia->mediaTrack->moov->trackList, &i))) { + if (tk->Media->handler && (tk->Media->handler->handlerType == GF_ISOM_MEDIA_VISUAL)) { + has_v_info = 1; + } + } + gf_bs_write_int(bs, has_v_info, 1); + + gf_bs_write_int(bs, 0, 3); /*reserved, spec doesn't say the values*/ + gf_bs_write_u8(bs, mdia->mediaTrack->Header->layer); + gf_bs_write_u16(bs, mdia->mediaTrack->Header->width>>16); + gf_bs_write_u16(bs, mdia->mediaTrack->Header->height>>16); + + /*write desc*/ + gf_bs_write_u8(bs, count); + for (i=0; itype != GF_ISOM_BOX_TYPE_TX3G) && (a->type != GF_ISOM_BOX_TYPE_TEXT) ) continue; + gf_isom_write_tx3g(a, bs, i+1, SAMPLE_INDEX_OFFSET); + } + if (has_v_info) { + u32 trans; + /*which video shall we pick for MPEG-4, and how is the associations indicated in 3GP ???*/ + gf_bs_write_u16(bs, 0); + gf_bs_write_u16(bs, 0); + trans = mdia->mediaTrack->Header->matrix[6]; trans >>= 16; + gf_bs_write_u16(bs, trans); + trans = mdia->mediaTrack->Header->matrix[7]; trans >>= 16; + gf_bs_write_u16(bs, trans); + } + + gf_bs_get_content(bs, &esd->decoderConfig->decoderSpecificInfo->data, &esd->decoderConfig->decoderSpecificInfo->dataLength); + gf_bs_del(bs); + *out_esd = esd; + return GF_OK; +} + +GF_Err gf_isom_rewrite_text_sample(GF_ISOSample *samp, u32 sampleDescriptionIndex, u32 sample_dur) +{ + GF_BitStream *bs; + u32 pay_start, txt_size; + Bool is_utf_16 = 0; + if (!samp || !samp->data || !samp->dataLength) return GF_OK; + + bs = gf_bs_new(samp->data, samp->dataLength, GF_BITSTREAM_READ); + txt_size = gf_bs_read_u16(bs); + gf_bs_del(bs); + + /*remove BOM*/ + pay_start = 2; + if (txt_size>2) { + /*seems 3GP only accepts BE UTF-16 (no LE, no UTF32)*/ + if (((u8) samp->data[2]==(u8) 0xFE) && ((u8)samp->data[3]==(u8) 0xFF)) { + is_utf_16 = 1; + pay_start = 4; + txt_size -= 2; + } + } + + /*rewrite as TTU(1)*/ + bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); + gf_bs_write_int(bs, is_utf_16, 1); + gf_bs_write_int(bs, 0, 4); + gf_bs_write_int(bs, 1, 3); + gf_bs_write_u16(bs, 8 + samp->dataLength - pay_start); + gf_bs_write_u8(bs, sampleDescriptionIndex + SAMPLE_INDEX_OFFSET); + gf_bs_write_u24(bs, sample_dur); + /*write text size*/ + gf_bs_write_u16(bs, txt_size); + if (txt_size) gf_bs_write_data(bs, samp->data + pay_start, samp->dataLength - pay_start); + + gf_free(samp->data); + samp->data = NULL; + gf_bs_get_content(bs, &samp->data, &samp->dataLength); + gf_bs_del(bs); + return GF_OK; +} + + +GF_Err gf_isom_text_get_encoded_tx3g(GF_ISOFile *file, u32 track, u32 sidx, u32 sidx_offset, char **tx3g, u32 *tx3g_size) +{ + GF_BitStream *bs; + GF_TrackBox *trak; + GF_Tx3gSampleEntryBox *a; + + trak = gf_isom_get_track_from_file(file, track); + if (!trak) return GF_BAD_PARAM; + + a = (GF_Tx3gSampleEntryBox *) gf_list_get(trak->Media->information->sampleTable->SampleDescription->boxList, sidx-1); + if (!a) return GF_BAD_PARAM; + if ((a->type != GF_ISOM_BOX_TYPE_TX3G) && (a->type != GF_ISOM_BOX_TYPE_TEXT)) return GF_BAD_PARAM; + + bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); + gf_isom_write_tx3g(a, bs, sidx, sidx_offset); + *tx3g = NULL; + *tx3g_size = 0; + gf_bs_get_content(bs, tx3g, tx3g_size); + gf_bs_del(bs); + return GF_OK; +} + +#endif /*GPAC_DISABLE_ISOM*/ diff --git a/src/gpacmp4/url.c b/src/gpacmp4/url.c new file mode 100644 index 00000000..da221291 --- /dev/null +++ b/src/gpacmp4/url.c @@ -0,0 +1,302 @@ +/* + * GPAC - Multimedia Framework C SDK + * + * Copyright (c) Jean Le Feuvre 2000-2005 + * All rights reserved + * + * This file is part of GPAC / common tools sub-project + * + * GPAC is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GPAC 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ +#include "../disable_warnings.h" +#include + +/* the length of the URL separator ("://" || "|//") */ +#define URL_SEP_LENGTH 3 + +/* our supported protocol types */ +enum +{ + /*absolute path to file*/ + GF_URL_TYPE_FILE = 0, + /*relative URL*/ + GF_URL_TYPE_RELATIVE , + /*any other URL*/ + GF_URL_TYPE_ANY +}; + +/*resolve the protocol type, for a std URL: http:// or ftp:// ...*/ +static u32 URL_GetProtocolType(const char *pathName) +{ + char *begin; + if (!pathName) return GF_URL_TYPE_ANY; + + if ((pathName[0] == '/') || (pathName[0] == '\\') + || (pathName[1] == ':') + || ((pathName[0] == ':') && (pathName[1] == ':')) + ) return GF_URL_TYPE_FILE; + + begin = (char *) strstr(pathName, "://"); + if (!begin) begin = (char *) strstr(pathName, "|//"); + if (!begin) return GF_URL_TYPE_RELATIVE; + if (!strnicmp(pathName, "file", 4)) return GF_URL_TYPE_FILE; + return GF_URL_TYPE_ANY; +} + +/*gets protocol type*/ +Bool gf_url_is_local(const char *pathName) +{ + u32 mode = URL_GetProtocolType(pathName); + return (mode==GF_URL_TYPE_ANY) ? 0 : 1; +} + +char *gf_url_get_absolute_path(const char *pathName, const char *parentPath) +{ + u32 prot_type = URL_GetProtocolType(pathName); + + /*abs path name*/ + if (prot_type == GF_URL_TYPE_FILE) { + /*abs path*/ + if (!strstr(pathName, "://") && !strstr(pathName, "|//")) return gf_strdup(pathName); + pathName += 6; + /*not sure if "file:///C:\..." is std, but let's handle it anyway*/ + if ((pathName[0]=='/') && (pathName[2]==':')) pathName += 1; + return gf_strdup(pathName); + } + if (prot_type==GF_URL_TYPE_ANY) return NULL; + if (!parentPath) return gf_strdup(pathName); + + /*try with the parent URL*/ + prot_type = URL_GetProtocolType(parentPath); + /*if abs parent path concatenate*/ + if (prot_type == GF_URL_TYPE_FILE) return gf_url_concatenate(parentPath, pathName); + if (prot_type != GF_URL_TYPE_RELATIVE) return NULL; + /*if we are here, parentPath is also relative... return the original PathName*/ + return gf_strdup(pathName); +} + + +char *gf_url_concatenate(const char *parentName, const char *pathName) +{ + u32 pathSepCount, i, prot_type; + char *outPath, *name, *rad; + char tmp[GF_MAX_PATH]; + + if (!pathName || !parentName) return NULL; + + if ( (strlen(parentName) > GF_MAX_PATH) || (strlen(pathName) > GF_MAX_PATH) ) return NULL; + + prot_type = URL_GetProtocolType(pathName); + if (prot_type != GF_URL_TYPE_RELATIVE) { + char *sep = NULL; + if (pathName[0]=='/') sep = (char *) strstr(parentName, "://"); + if (sep) sep = strchr(sep+3, '/'); + if (sep) { + u32 len; + sep[0] = 0; + len = strlen(parentName); + outPath = (char*)gf_malloc(sizeof(char)*(len+1+strlen(pathName))); + strcpy(outPath, parentName); + strcat(outPath, pathName); + sep[0] = '/'; + } else { + outPath = gf_strdup(pathName); + } + goto check_spaces; + } + + /*old upnp addressing a la Platinum*/ + rad = (char *) strstr(parentName, "%3fpath="); + if (!rad) rad = (char *) strstr(parentName, "%3Fpath="); + if (!rad) rad = (char *) strstr(parentName, "?path="); + if (rad) { + char *the_path; + rad = (char *) strchr(rad, '='); + rad[0] = 0; + the_path = gf_strdup(rad+1); + i=0; + while (1) { + if (the_path[i]==0) break; + if (!strnicmp(the_path+i, "%5c", 3) || !strnicmp(the_path+i, "%2f", 3) ) { + the_path[i] = '/'; + memmove(the_path+i+1, the_path+i+3, strlen(the_path+i+3)+1); + } + else if (!strnicmp(the_path+i, "%05c", 4) || !strnicmp(the_path+i, "%02f", 4) ) { + the_path[i] = '/'; + memmove(the_path+i+1, the_path+i+4, strlen(the_path+i+4)+1); + } + i++; + } + name = gf_url_concatenate(the_path, pathName); + outPath = (char *) gf_malloc(strlen(parentName) + strlen(name) + 2); + sprintf(outPath, "%s=%s", parentName, name); + rad[0] = '='; + gf_free(name); + gf_free(the_path); + return outPath; + } + + /*rewrite path to use / not % encoding*/ + rad = (char *) strchr(parentName, '%'); + if (rad && (!strnicmp(rad, "%5c", 3) || !strnicmp(rad, "%05c", 4) || !strnicmp(rad, "%2f", 3) || !strnicmp(rad, "%02f", 4))) { + char *the_path = gf_strdup(parentName); + i=0; + while (1) { + if (the_path[i]==0) break; + if (!strnicmp(the_path+i, "%5c", 3) || !strnicmp(the_path+i, "%2f", 3) ) { + the_path[i] = '/'; + memmove(the_path+i+1, the_path+i+3, strlen(the_path+i+3)+1); + } + else if (!strnicmp(the_path+i, "%05c", 4) || !strnicmp(the_path+i, "%02f", 4) ) { + the_path[i] = '/'; + memmove(the_path+i+1, the_path+i+4, strlen(the_path+i+4)+1); + } + i++; + } + name = gf_url_concatenate(the_path, pathName); + gf_free(the_path); + return name; + } + + + pathSepCount = 0; + name = NULL; + if (pathName[0] == '.') { + if (!strcmp(pathName, "..")) { + pathSepCount = 1; + name = ""; + } + for (i = 0; i< strlen(pathName) - 2; i++) { + /*current dir*/ + if ( (pathName[i] == '.') + && ( (pathName[i+1] == GF_PATH_SEPARATOR) || (pathName[i+1] == '/') ) ) { + i++; + continue; + } + /*parent dir*/ + if ( (pathName[i] == '.') && (pathName[i+1] == '.') + && ( (pathName[i+2] == GF_PATH_SEPARATOR) || (pathName[i+2] == '/') ) + ) { + pathSepCount ++; + i+=2; + name = (char *) &pathName[i+1]; + } else { + name = (char *) &pathName[i]; + break; + } + } + } + if (!name) name = (char *) pathName; + + strcpy(tmp, parentName); + for (i = strlen(parentName); i > 0; i--) { + //break our path at each separator + if ((parentName[i-1] == GF_PATH_SEPARATOR) || (parentName[i-1] == '/')) { + tmp[i-1] = 0; + if (!pathSepCount) break; + pathSepCount--; + } + } + //if i==0, the parent path was relative, just return the pathName + if (!i) { + tmp[i] = 0; + while (pathSepCount) { + strcat(tmp, "../"); + pathSepCount--; + } +/* outPath = gf_strdup(pathName); + goto check_spaces; + */ + } else { + strcat(tmp, "/"); + } + + i = strlen(tmp); + outPath = (char *) gf_malloc(i + strlen(name) + 1); + sprintf(outPath, "%s%s", tmp, name); + + /*cleanup paths sep for win32*/ + for (i = 0; i + + + /* + * Original code from the GNU UTF-8 Library + */ + +GF_EXPORT +size_t gf_utf8_wcstombs(char* dest, size_t len, const unsigned short** srcp) +{ + size_t count; + const unsigned short * src = *srcp; + + if (dest != NULL) { + char* destptr = dest; + for (;; src++) { + unsigned char c; + unsigned short wc = *src; + if (wc < 0x80) { + if (wc == (wchar_t)'\0') { + if (len == 0) { + *srcp = src; + break; + } + *destptr = '\0'; + *srcp = NULL; + break; + } + count = 0; + c = (unsigned char) wc; + } else if (wc < 0x800) { + count = 1; + c = (unsigned char) ((wc >> 6) | 0xC0); + } else { + count = 2; + c = (unsigned char) ((wc >> 12) | 0xE0); + } + if (len <= count) { + *srcp = src; + break; + } + len -= count+1; + *destptr++ = c; + if (count > 0) + do { + *destptr++ = (unsigned char)(((wc >> (6 * --count)) & 0x3F) | 0x80); + } while (count > 0); + } + return destptr - dest; + } else { + /* Ignore dest and len. */ + size_t totalcount = 0; + for (;; src++) { + unsigned short wc = *src; + size_t count; + if (wc < 0x80) { + if (wc == (wchar_t)'\0') { + *srcp = NULL; + break; + } + count = 1; + } else if (wc < 0x800) { + count = 2; + } else { + count = 3; + } + totalcount += count; + } + return totalcount; + } +} + + +typedef struct +{ + u32 count : 16; /* number of bytes remaining to be processed */ + u32 value : 16; /* if count > 0: partial wide character */ +/* + If WCHAR_T_BITS == 16, need 2 bits for count, + 12 bits for value (10 for mbstowcs direction, 12 for wcstombs direction). +*/ +} gf_utf8_mbstate_t; + +static gf_utf8_mbstate_t internal; + +GF_EXPORT +size_t gf_utf8_mbstowcs(unsigned short* dest, size_t len, const char** srcp) +{ + gf_utf8_mbstate_t* ps = &internal; + const char *src = *srcp; + + unsigned short* destptr = dest; + for (; len > 0; destptr++, len--) { + const char* backup_src = src; + unsigned char c; + unsigned short wc; + size_t count; + if (ps->count == 0) { + c = (unsigned char) *src; + if (c < 0x80) { + *destptr = (wchar_t) c; + if (c == 0) { + src = NULL; + break; + } + src++; + continue; + } else if (c < 0xC0) { + /* Spurious 10XXXXXX byte is invalid. */ + goto bad_input; + } + if (c < 0xE0) { + wc = (wchar_t)(c & 0x1F) << 6; + count = 1; + if (c < 0xC2) goto bad_input; + } else if (c < 0xF0) { + wc = (wchar_t)(c & 0x0F) << 12; + count = 2; + } + else goto bad_input; + src++; + } else { + wc = ps->value << 6; + count = ps->count; + } + for (;;) { + c = (unsigned char) *src++ ^ 0x80; + if (!(c < 0x40)) goto bad_input_backup; + wc |= (unsigned short) c << (6 * --count); + if (count == 0) + break; + /* The following test is only necessary once for every character, + but it would be too complicated to perform it once only, on + the first pass through this loop. */ + if ((unsigned short) wc < ((unsigned short) 1 << (5 * count + 6))) + goto bad_input_backup; + } + *destptr = wc; + ps->count = 0; + continue; + +bad_input_backup: + src = backup_src; + goto bad_input; + } + *srcp = src; + return destptr-dest; + +bad_input: + *srcp = src; + return (size_t)(-1); +} + + +GF_EXPORT +size_t gf_utf8_wcslen (const unsigned short *s) +{ + const unsigned short* ptr; + for (ptr = s; *ptr != (unsigned short)'\0'; ptr++) { + } + return ptr - s; +} + + diff --git a/src/hamming.h b/src/hamming.h new file mode 100644 index 00000000..05df794c --- /dev/null +++ b/src/hamming.h @@ -0,0 +1,67 @@ +/*! +(c) 2011-2013 Forers, s. r. o.: telxcc +*/ + +#ifndef hamming_h_included +#define hamming_h_included + +//#include + +const uint8_t PARITY_8[256] = { + 0x00, 0x01, 0x01, 0x00, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x00, 0x01, 0x01, 0x00, + 0x01, 0x00, 0x00, 0x01, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x01, 0x00, 0x00, 0x01, + 0x01, 0x00, 0x00, 0x01, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x01, 0x00, 0x00, 0x01, + 0x00, 0x01, 0x01, 0x00, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x00, 0x01, 0x01, 0x00, + 0x01, 0x00, 0x00, 0x01, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x01, 0x00, 0x00, 0x01, + 0x00, 0x01, 0x01, 0x00, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x00, 0x01, 0x01, 0x00, + 0x00, 0x01, 0x01, 0x00, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x00, 0x01, 0x01, 0x00, + 0x01, 0x00, 0x00, 0x01, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x01, 0x00, 0x00, 0x01, + 0x01, 0x00, 0x00, 0x01, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x01, 0x00, 0x00, 0x01, + 0x00, 0x01, 0x01, 0x00, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x00, 0x01, 0x01, 0x00, + 0x00, 0x01, 0x01, 0x00, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x00, 0x01, 0x01, 0x00, + 0x01, 0x00, 0x00, 0x01, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x01, 0x00, 0x00, 0x01, + 0x00, 0x01, 0x01, 0x00, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x00, 0x01, 0x01, 0x00, + 0x01, 0x00, 0x00, 0x01, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x01, 0x00, 0x00, 0x01, + 0x01, 0x00, 0x00, 0x01, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x01, 0x00, 0x00, 0x01, + 0x00, 0x01, 0x01, 0x00, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x00, 0x01, 0x01, 0x00 +}; + +const uint8_t REVERSE_8[256] = { + 0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0, 0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0, + 0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8, 0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8, + 0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4, 0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4, + 0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec, 0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc, + 0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2, 0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2, + 0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea, 0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa, + 0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6, 0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6, + 0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee, 0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe, + 0x01, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1, 0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1, + 0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9, 0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9, + 0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5, 0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5, + 0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed, 0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd, + 0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3, 0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3, + 0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb, 0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb, + 0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7, 0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7, + 0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef, 0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff +}; + +const uint8_t UNHAM_8_4[256] = { + 0x01, 0xff, 0x01, 0x01, 0xff, 0x00, 0x01, 0xff, 0xff, 0x02, 0x01, 0xff, 0x0a, 0xff, 0xff, 0x07, + 0xff, 0x00, 0x01, 0xff, 0x00, 0x00, 0xff, 0x00, 0x06, 0xff, 0xff, 0x0b, 0xff, 0x00, 0x03, 0xff, + 0xff, 0x0c, 0x01, 0xff, 0x04, 0xff, 0xff, 0x07, 0x06, 0xff, 0xff, 0x07, 0xff, 0x07, 0x07, 0x07, + 0x06, 0xff, 0xff, 0x05, 0xff, 0x00, 0x0d, 0xff, 0x06, 0x06, 0x06, 0xff, 0x06, 0xff, 0xff, 0x07, + 0xff, 0x02, 0x01, 0xff, 0x04, 0xff, 0xff, 0x09, 0x02, 0x02, 0xff, 0x02, 0xff, 0x02, 0x03, 0xff, + 0x08, 0xff, 0xff, 0x05, 0xff, 0x00, 0x03, 0xff, 0xff, 0x02, 0x03, 0xff, 0x03, 0xff, 0x03, 0x03, + 0x04, 0xff, 0xff, 0x05, 0x04, 0x04, 0x04, 0xff, 0xff, 0x02, 0x0f, 0xff, 0x04, 0xff, 0xff, 0x07, + 0xff, 0x05, 0x05, 0x05, 0x04, 0xff, 0xff, 0x05, 0x06, 0xff, 0xff, 0x05, 0xff, 0x0e, 0x03, 0xff, + 0xff, 0x0c, 0x01, 0xff, 0x0a, 0xff, 0xff, 0x09, 0x0a, 0xff, 0xff, 0x0b, 0x0a, 0x0a, 0x0a, 0xff, + 0x08, 0xff, 0xff, 0x0b, 0xff, 0x00, 0x0d, 0xff, 0xff, 0x0b, 0x0b, 0x0b, 0x0a, 0xff, 0xff, 0x0b, + 0x0c, 0x0c, 0xff, 0x0c, 0xff, 0x0c, 0x0d, 0xff, 0xff, 0x0c, 0x0f, 0xff, 0x0a, 0xff, 0xff, 0x07, + 0xff, 0x0c, 0x0d, 0xff, 0x0d, 0xff, 0x0d, 0x0d, 0x06, 0xff, 0xff, 0x0b, 0xff, 0x0e, 0x0d, 0xff, + 0x08, 0xff, 0xff, 0x09, 0xff, 0x09, 0x09, 0x09, 0xff, 0x02, 0x0f, 0xff, 0x0a, 0xff, 0xff, 0x09, + 0x08, 0x08, 0x08, 0xff, 0x08, 0xff, 0xff, 0x09, 0x08, 0xff, 0xff, 0x0b, 0xff, 0x0e, 0x03, 0xff, + 0xff, 0x0c, 0x0f, 0xff, 0x04, 0xff, 0xff, 0x09, 0x0f, 0xff, 0x0f, 0x0f, 0xff, 0x0e, 0x0f, 0xff, + 0x08, 0xff, 0xff, 0x05, 0xff, 0x0e, 0x0d, 0xff, 0xff, 0x0e, 0x0f, 0xff, 0x0e, 0x0e, 0xff, 0x0e +}; + +#endif diff --git a/src/inttypes.h b/src/inttypes.h new file mode 100644 index 00000000..4b3828a2 --- /dev/null +++ b/src/inttypes.h @@ -0,0 +1,305 @@ +// ISO C9x compliant inttypes.h for Microsoft Visual Studio +// Based on ISO/IEC 9899:TC2 Committee draft (May 6, 2005) WG14/N1124 +// +// Copyright (c) 2006 Alexander Chemeris +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// 3. The name of the author may be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED +// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +/////////////////////////////////////////////////////////////////////////////// + +#ifndef _MSC_VER // [ +#error "Use this header only with Microsoft Visual C++ compilers!" +#endif // _MSC_VER ] + +#ifndef _MSC_INTTYPES_H_ // [ +#define _MSC_INTTYPES_H_ + +#if _MSC_VER > 1000 +#pragma once +#endif + +#include "stdint.h" + +// 7.8 Format conversion of integer types + +typedef struct { + intmax_t quot; + intmax_t rem; +} imaxdiv_t; + +// 7.8.1 Macros for format specifiers + +#if !defined(__cplusplus) || defined(__STDC_FORMAT_MACROS) // [ See footnote 185 at page 198 + +// The fprintf macros for signed integers are: +#define PRId8 "d" +#define PRIi8 "i" +#define PRIdLEAST8 "d" +#define PRIiLEAST8 "i" +#define PRIdFAST8 "d" +#define PRIiFAST8 "i" + +#define PRId16 "hd" +#define PRIi16 "hi" +#define PRIdLEAST16 "hd" +#define PRIiLEAST16 "hi" +#define PRIdFAST16 "hd" +#define PRIiFAST16 "hi" + +#define PRId32 "I32d" +#define PRIi32 "I32i" +#define PRIdLEAST32 "I32d" +#define PRIiLEAST32 "I32i" +#define PRIdFAST32 "I32d" +#define PRIiFAST32 "I32i" + +#define PRId64 "I64d" +#define PRIi64 "I64i" +#define PRIdLEAST64 "I64d" +#define PRIiLEAST64 "I64i" +#define PRIdFAST64 "I64d" +#define PRIiFAST64 "I64i" + +#define PRIdMAX "I64d" +#define PRIiMAX "I64i" + +#define PRIdPTR "Id" +#define PRIiPTR "Ii" + +// The fprintf macros for unsigned integers are: +#define PRIo8 "o" +#define PRIu8 "u" +#define PRIx8 "x" +#define PRIX8 "X" +#define PRIoLEAST8 "o" +#define PRIuLEAST8 "u" +#define PRIxLEAST8 "x" +#define PRIXLEAST8 "X" +#define PRIoFAST8 "o" +#define PRIuFAST8 "u" +#define PRIxFAST8 "x" +#define PRIXFAST8 "X" + +#define PRIo16 "ho" +#define PRIu16 "hu" +#define PRIx16 "hx" +#define PRIX16 "hX" +#define PRIoLEAST16 "ho" +#define PRIuLEAST16 "hu" +#define PRIxLEAST16 "hx" +#define PRIXLEAST16 "hX" +#define PRIoFAST16 "ho" +#define PRIuFAST16 "hu" +#define PRIxFAST16 "hx" +#define PRIXFAST16 "hX" + +#define PRIo32 "I32o" +#define PRIu32 "I32u" +#define PRIx32 "I32x" +#define PRIX32 "I32X" +#define PRIoLEAST32 "I32o" +#define PRIuLEAST32 "I32u" +#define PRIxLEAST32 "I32x" +#define PRIXLEAST32 "I32X" +#define PRIoFAST32 "I32o" +#define PRIuFAST32 "I32u" +#define PRIxFAST32 "I32x" +#define PRIXFAST32 "I32X" + +#define PRIo64 "I64o" +#define PRIu64 "I64u" +#define PRIx64 "I64x" +#define PRIX64 "I64X" +#define PRIoLEAST64 "I64o" +#define PRIuLEAST64 "I64u" +#define PRIxLEAST64 "I64x" +#define PRIXLEAST64 "I64X" +#define PRIoFAST64 "I64o" +#define PRIuFAST64 "I64u" +#define PRIxFAST64 "I64x" +#define PRIXFAST64 "I64X" + +#define PRIoMAX "I64o" +#define PRIuMAX "I64u" +#define PRIxMAX "I64x" +#define PRIXMAX "I64X" + +#define PRIoPTR "Io" +#define PRIuPTR "Iu" +#define PRIxPTR "Ix" +#define PRIXPTR "IX" + +// The fscanf macros for signed integers are: +#define SCNd8 "d" +#define SCNi8 "i" +#define SCNdLEAST8 "d" +#define SCNiLEAST8 "i" +#define SCNdFAST8 "d" +#define SCNiFAST8 "i" + +#define SCNd16 "hd" +#define SCNi16 "hi" +#define SCNdLEAST16 "hd" +#define SCNiLEAST16 "hi" +#define SCNdFAST16 "hd" +#define SCNiFAST16 "hi" + +#define SCNd32 "ld" +#define SCNi32 "li" +#define SCNdLEAST32 "ld" +#define SCNiLEAST32 "li" +#define SCNdFAST32 "ld" +#define SCNiFAST32 "li" + +#define SCNd64 "I64d" +#define SCNi64 "I64i" +#define SCNdLEAST64 "I64d" +#define SCNiLEAST64 "I64i" +#define SCNdFAST64 "I64d" +#define SCNiFAST64 "I64i" + +#define SCNdMAX "I64d" +#define SCNiMAX "I64i" + +#ifdef _WIN64 // [ +# define SCNdPTR "I64d" +# define SCNiPTR "I64i" +#else // _WIN64 ][ +# define SCNdPTR "ld" +# define SCNiPTR "li" +#endif // _WIN64 ] + +// The fscanf macros for unsigned integers are: +#define SCNo8 "o" +#define SCNu8 "u" +#define SCNx8 "x" +#define SCNX8 "X" +#define SCNoLEAST8 "o" +#define SCNuLEAST8 "u" +#define SCNxLEAST8 "x" +#define SCNXLEAST8 "X" +#define SCNoFAST8 "o" +#define SCNuFAST8 "u" +#define SCNxFAST8 "x" +#define SCNXFAST8 "X" + +#define SCNo16 "ho" +#define SCNu16 "hu" +#define SCNx16 "hx" +#define SCNX16 "hX" +#define SCNoLEAST16 "ho" +#define SCNuLEAST16 "hu" +#define SCNxLEAST16 "hx" +#define SCNXLEAST16 "hX" +#define SCNoFAST16 "ho" +#define SCNuFAST16 "hu" +#define SCNxFAST16 "hx" +#define SCNXFAST16 "hX" + +#define SCNo32 "lo" +#define SCNu32 "lu" +#define SCNx32 "lx" +#define SCNX32 "lX" +#define SCNoLEAST32 "lo" +#define SCNuLEAST32 "lu" +#define SCNxLEAST32 "lx" +#define SCNXLEAST32 "lX" +#define SCNoFAST32 "lo" +#define SCNuFAST32 "lu" +#define SCNxFAST32 "lx" +#define SCNXFAST32 "lX" + +#define SCNo64 "I64o" +#define SCNu64 "I64u" +#define SCNx64 "I64x" +#define SCNX64 "I64X" +#define SCNoLEAST64 "I64o" +#define SCNuLEAST64 "I64u" +#define SCNxLEAST64 "I64x" +#define SCNXLEAST64 "I64X" +#define SCNoFAST64 "I64o" +#define SCNuFAST64 "I64u" +#define SCNxFAST64 "I64x" +#define SCNXFAST64 "I64X" + +#define SCNoMAX "I64o" +#define SCNuMAX "I64u" +#define SCNxMAX "I64x" +#define SCNXMAX "I64X" + +#ifdef _WIN64 // [ +# define SCNoPTR "I64o" +# define SCNuPTR "I64u" +# define SCNxPTR "I64x" +# define SCNXPTR "I64X" +#else // _WIN64 ][ +# define SCNoPTR "lo" +# define SCNuPTR "lu" +# define SCNxPTR "lx" +# define SCNXPTR "lX" +#endif // _WIN64 ] + +#endif // __STDC_FORMAT_MACROS ] + +// 7.8.2 Functions for greatest-width integer types + +// 7.8.2.1 The imaxabs function +#define imaxabs _abs64 + +// 7.8.2.2 The imaxdiv function + +// This is modified version of div() function from Microsoft's div.c found +// in %MSVC.NET%\crt\src\div.c +#ifdef STATIC_IMAXDIV // [ +static +#else // STATIC_IMAXDIV ][ +_inline +#endif // STATIC_IMAXDIV ] +imaxdiv_t __cdecl imaxdiv(intmax_t numer, intmax_t denom) +{ + imaxdiv_t result; + + result.quot = numer / denom; + result.rem = numer % denom; + + if (numer < 0 && result.rem > 0) { + // did division wrong; must fix up + ++result.quot; + result.rem -= denom; + } + + return result; +} + +// 7.8.2.3 The strtoimax and strtoumax functions +#define strtoimax _strtoi64 +#define strtoumax _strtoui64 + +// 7.8.2.4 The wcstoimax and wcstoumax functions +#define wcstoimax _wcstoi64 +#define wcstoumax _wcstoui64 + + +#endif // _MSC_INTTYPES_H_ ] diff --git a/src/libpng/png.c b/src/libpng/png.c new file mode 100644 index 00000000..3fc488b6 --- /dev/null +++ b/src/libpng/png.c @@ -0,0 +1,1102 @@ +#ifdef _WIN32 +#pragma warning(disable:4996) // Disable messages like this one: '_snprintf': This function or variable may be unsafe. Consider using _snprintf_s instead. To disable deprecation, use _CRT_SECURE_NO_WARNINGS. +#endif +/* png.c - location for general purpose libpng functions + * + * Last changed in libpng 1.2.46 [February 25, 2011] + * Copyright (c) 1998-2011 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + * + * This code is released under the libpng license. + * For conditions of distribution and use, see the disclaimer + * and license in png.h + */ + +#define PNG_INTERNAL +#define PNG_NO_EXTERN +#define PNG_NO_PEDANTIC_WARNINGS +#include "png.h" + +/* Generate a compiler error if there is an old png.h in the search path. */ +typedef version_1_2_50 Your_png_h_is_not_version_1_2_50; + +/* Version information for C files. This had better match the version + * string defined in png.h. + */ + +#ifdef PNG_USE_GLOBAL_ARRAYS +/* png_libpng_ver was changed to a function in version 1.0.5c */ +PNG_CONST char png_libpng_ver[18] = PNG_LIBPNG_VER_STRING; + +#ifdef PNG_READ_SUPPORTED + +/* png_sig was changed to a function in version 1.0.5c */ +/* Place to hold the signature string for a PNG file. */ +PNG_CONST png_byte FARDATA png_sig[8] = {137, 80, 78, 71, 13, 10, 26, 10}; +#endif /* PNG_READ_SUPPORTED */ + +/* Invoke global declarations for constant strings for known chunk types */ +PNG_IHDR; +PNG_IDAT; +PNG_IEND; +PNG_PLTE; +PNG_bKGD; +PNG_cHRM; +PNG_gAMA; +PNG_hIST; +PNG_iCCP; +PNG_iTXt; +PNG_oFFs; +PNG_pCAL; +PNG_sCAL; +PNG_pHYs; +PNG_sBIT; +PNG_sPLT; +PNG_sRGB; +PNG_tEXt; +PNG_tIME; +PNG_tRNS; +PNG_zTXt; + +#ifdef PNG_READ_SUPPORTED +/* Arrays to facilitate easy interlacing - use pass (0 - 6) as index */ + +/* Start of interlace block */ +PNG_CONST int FARDATA png_pass_start[] = {0, 4, 0, 2, 0, 1, 0}; + +/* Offset to next interlace block */ +PNG_CONST int FARDATA png_pass_inc[] = {8, 8, 4, 4, 2, 2, 1}; + +/* Start of interlace block in the y direction */ +PNG_CONST int FARDATA png_pass_ystart[] = {0, 0, 4, 0, 2, 0, 1}; + +/* Offset to next interlace block in the y direction */ +PNG_CONST int FARDATA png_pass_yinc[] = {8, 8, 8, 4, 4, 2, 2}; + +/* Height of interlace block. This is not currently used - if you need + * it, uncomment it here and in png.h +PNG_CONST int FARDATA png_pass_height[] = {8, 8, 4, 4, 2, 2, 1}; +*/ + +/* Mask to determine which pixels are valid in a pass */ +PNG_CONST int FARDATA png_pass_mask[] = + {0x80, 0x08, 0x88, 0x22, 0xaa, 0x55, 0xff}; + +/* Mask to determine which pixels to overwrite while displaying */ +PNG_CONST int FARDATA png_pass_dsp_mask[] + = {0xff, 0x0f, 0xff, 0x33, 0xff, 0x55, 0xff}; + +#endif /* PNG_READ_SUPPORTED */ +#endif /* PNG_USE_GLOBAL_ARRAYS */ + +/* Tells libpng that we have already handled the first "num_bytes" bytes + * of the PNG file signature. If the PNG data is embedded into another + * stream we can set num_bytes = 8 so that libpng will not attempt to read + * or write any of the magic bytes before it starts on the IHDR. + */ + +#ifdef PNG_READ_SUPPORTED +void PNGAPI +png_set_sig_bytes(png_structp png_ptr, int num_bytes) +{ + png_debug(1, "in png_set_sig_bytes"); + + if (png_ptr == NULL) + return; + + if (num_bytes > 8) + png_error(png_ptr, "Too many bytes for PNG signature."); + + png_ptr->sig_bytes = (png_byte)(num_bytes < 0 ? 0 : num_bytes); +} + +/* Checks whether the supplied bytes match the PNG signature. We allow + * checking less than the full 8-byte signature so that those apps that + * already read the first few bytes of a file to determine the file type + * can simply check the remaining bytes for extra assurance. Returns + * an integer less than, equal to, or greater than zero if sig is found, + * respectively, to be less than, to match, or be greater than the correct + * PNG signature (this is the same behaviour as strcmp, memcmp, etc). + */ +int PNGAPI +png_sig_cmp(png_bytep sig, png_size_t start, png_size_t num_to_check) +{ + png_byte png_signature[8] = {137, 80, 78, 71, 13, 10, 26, 10}; + if (num_to_check > 8) + num_to_check = 8; + else if (num_to_check < 1) + return (-1); + + if (start > 7) + return (-1); + + if (start + num_to_check > 8) + num_to_check = 8 - start; + + return ((int)(png_memcmp(&sig[start], &png_signature[start], num_to_check))); +} + +#if defined(PNG_1_0_X) || defined(PNG_1_2_X) +/* (Obsolete) function to check signature bytes. It does not allow one + * to check a partial signature. This function might be removed in the + * future - use png_sig_cmp(). Returns true (nonzero) if the file is PNG. + */ +int PNGAPI +png_check_sig(png_bytep sig, int num) +{ + return ((int)!png_sig_cmp(sig, (png_size_t)0, (png_size_t)num)); +} +#endif +#endif /* PNG_READ_SUPPORTED */ + +#if defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED) +/* Function to allocate memory for zlib and clear it to 0. */ +#ifdef PNG_1_0_X +voidpf PNGAPI +#else +voidpf /* PRIVATE */ +#endif +png_zalloc(voidpf png_ptr, uInt items, uInt size) +{ + png_voidp ptr; + png_structp p=(png_structp)png_ptr; + png_uint_32 save_flags=p->flags; + png_uint_32 num_bytes; + + if (png_ptr == NULL) + return (NULL); + if (items > PNG_UINT_32_MAX/size) + { + png_warning (p, "Potential overflow in png_zalloc()"); + return (NULL); + } + num_bytes = (png_uint_32)items * size; + + p->flags|=PNG_FLAG_MALLOC_NULL_MEM_OK; + ptr = (png_voidp)png_malloc((png_structp)png_ptr, num_bytes); + p->flags=save_flags; + +#if defined(PNG_1_0_X) && !defined(PNG_NO_ZALLOC_ZERO) + if (ptr == NULL) + return ((voidpf)ptr); + + if (num_bytes > (png_uint_32)0x8000L) + { + png_memset(ptr, 0, (png_size_t)0x8000L); + png_memset((png_bytep)ptr + (png_size_t)0x8000L, 0, + (png_size_t)(num_bytes - (png_uint_32)0x8000L)); + } + else + { + png_memset(ptr, 0, (png_size_t)num_bytes); + } +#endif + return ((voidpf)ptr); +} + +/* Function to free memory for zlib */ +#ifdef PNG_1_0_X +void PNGAPI +#else +void /* PRIVATE */ +#endif +png_zfree(voidpf png_ptr, voidpf ptr) +{ + png_free((png_structp)png_ptr, (png_voidp)ptr); +} + +/* Reset the CRC variable to 32 bits of 1's. Care must be taken + * in case CRC is > 32 bits to leave the top bits 0. + */ +void /* PRIVATE */ +png_reset_crc(png_structp png_ptr) +{ + png_ptr->crc = crc32(0, Z_NULL, 0); +} + +/* Calculate the CRC over a section of data. We can only pass as + * much data to this routine as the largest single buffer size. We + * also check that this data will actually be used before going to the + * trouble of calculating it. + */ +void /* PRIVATE */ +png_calculate_crc(png_structp png_ptr, png_bytep ptr, png_size_t length) +{ + int need_crc = 1; + + if (png_ptr->chunk_name[0] & 0x20) /* ancillary */ + { + if ((png_ptr->flags & PNG_FLAG_CRC_ANCILLARY_MASK) == + (PNG_FLAG_CRC_ANCILLARY_USE | PNG_FLAG_CRC_ANCILLARY_NOWARN)) + need_crc = 0; + } + else /* critical */ + { + if (png_ptr->flags & PNG_FLAG_CRC_CRITICAL_IGNORE) + need_crc = 0; + } + + if (need_crc) + png_ptr->crc = crc32(png_ptr->crc, ptr, (uInt)length); +} + +/* Allocate the memory for an info_struct for the application. We don't + * really need the png_ptr, but it could potentially be useful in the + * future. This should be used in favour of malloc(png_sizeof(png_info)) + * and png_info_init() so that applications that want to use a shared + * libpng don't have to be recompiled if png_info changes size. + */ +png_infop PNGAPI +png_create_info_struct(png_structp png_ptr) +{ + png_infop info_ptr; + + png_debug(1, "in png_create_info_struct"); + + if (png_ptr == NULL) + return (NULL); + +#ifdef PNG_USER_MEM_SUPPORTED + info_ptr = (png_infop)png_create_struct_2(PNG_STRUCT_INFO, + png_ptr->malloc_fn, png_ptr->mem_ptr); +#else + info_ptr = (png_infop)png_create_struct(PNG_STRUCT_INFO); +#endif + if (info_ptr != NULL) + png_info_init_3(&info_ptr, png_sizeof(png_info)); + + return (info_ptr); +} + +/* This function frees the memory associated with a single info struct. + * Normally, one would use either png_destroy_read_struct() or + * png_destroy_write_struct() to free an info struct, but this may be + * useful for some applications. + */ +void PNGAPI +png_destroy_info_struct(png_structp png_ptr, png_infopp info_ptr_ptr) +{ + png_infop info_ptr = NULL; + + png_debug(1, "in png_destroy_info_struct"); + + if (png_ptr == NULL) + return; + + if (info_ptr_ptr != NULL) + info_ptr = *info_ptr_ptr; + + if (info_ptr != NULL) + { + png_info_destroy(png_ptr, info_ptr); + +#ifdef PNG_USER_MEM_SUPPORTED + png_destroy_struct_2((png_voidp)info_ptr, png_ptr->free_fn, + png_ptr->mem_ptr); +#else + png_destroy_struct((png_voidp)info_ptr); +#endif + *info_ptr_ptr = NULL; + } +} + +/* Initialize the info structure. This is now an internal function (0.89) + * and applications using it are urged to use png_create_info_struct() + * instead. + */ +#if defined(PNG_1_0_X) || defined(PNG_1_2_X) +#undef png_info_init +void PNGAPI +png_info_init(png_infop info_ptr) +{ + /* We only come here via pre-1.0.12-compiled applications */ + png_info_init_3(&info_ptr, 0); +} +#endif + +void PNGAPI +png_info_init_3(png_infopp ptr_ptr, png_size_t png_info_struct_size) +{ + png_infop info_ptr = *ptr_ptr; + + png_debug(1, "in png_info_init_3"); + + if (info_ptr == NULL) + return; + + if (png_sizeof(png_info) > png_info_struct_size) + { + png_destroy_struct(info_ptr); + info_ptr = (png_infop)png_create_struct(PNG_STRUCT_INFO); + *ptr_ptr = info_ptr; + } + + /* Set everything to 0 */ + png_memset(info_ptr, 0, png_sizeof(png_info)); +} + +#ifdef PNG_FREE_ME_SUPPORTED +void PNGAPI +png_data_freer(png_structp png_ptr, png_infop info_ptr, + int freer, png_uint_32 mask) +{ + png_debug(1, "in png_data_freer"); + + if (png_ptr == NULL || info_ptr == NULL) + return; + + if (freer == PNG_DESTROY_WILL_FREE_DATA) + info_ptr->free_me |= mask; + else if (freer == PNG_USER_WILL_FREE_DATA) + info_ptr->free_me &= ~mask; + else + png_warning(png_ptr, + "Unknown freer parameter in png_data_freer."); +} +#endif + +void PNGAPI +png_free_data(png_structp png_ptr, png_infop info_ptr, png_uint_32 mask, + int num) +{ + png_debug(1, "in png_free_data"); + + if (png_ptr == NULL || info_ptr == NULL) + return; + +#ifdef PNG_TEXT_SUPPORTED + /* Free text item num or (if num == -1) all text items */ +#ifdef PNG_FREE_ME_SUPPORTED + if ((mask & PNG_FREE_TEXT) & info_ptr->free_me) +#else + if (mask & PNG_FREE_TEXT) +#endif + { + if (num != -1) + { + if (info_ptr->text && info_ptr->text[num].key) + { + png_free(png_ptr, info_ptr->text[num].key); + info_ptr->text[num].key = NULL; + } + } + else + { + int i; + for (i = 0; i < info_ptr->num_text; i++) + png_free_data(png_ptr, info_ptr, PNG_FREE_TEXT, i); + png_free(png_ptr, info_ptr->text); + info_ptr->text = NULL; + info_ptr->num_text=0; + } + } +#endif + +#ifdef PNG_tRNS_SUPPORTED + /* Free any tRNS entry */ +#ifdef PNG_FREE_ME_SUPPORTED + if ((mask & PNG_FREE_TRNS) & info_ptr->free_me) +#else + if ((mask & PNG_FREE_TRNS) && (png_ptr->flags & PNG_FLAG_FREE_TRNS)) +#endif + { + png_free(png_ptr, info_ptr->trans); + info_ptr->trans = NULL; + info_ptr->valid &= ~PNG_INFO_tRNS; +#ifndef PNG_FREE_ME_SUPPORTED + png_ptr->flags &= ~PNG_FLAG_FREE_TRNS; +#endif + } +#endif + +#ifdef PNG_sCAL_SUPPORTED + /* Free any sCAL entry */ +#ifdef PNG_FREE_ME_SUPPORTED + if ((mask & PNG_FREE_SCAL) & info_ptr->free_me) +#else + if (mask & PNG_FREE_SCAL) +#endif + { +#if defined(PNG_FIXED_POINT_SUPPORTED) && !defined(PNG_FLOATING_POINT_SUPPORTED) + png_free(png_ptr, info_ptr->scal_s_width); + png_free(png_ptr, info_ptr->scal_s_height); + info_ptr->scal_s_width = NULL; + info_ptr->scal_s_height = NULL; +#endif + info_ptr->valid &= ~PNG_INFO_sCAL; + } +#endif + +#ifdef PNG_pCAL_SUPPORTED + /* Free any pCAL entry */ +#ifdef PNG_FREE_ME_SUPPORTED + if ((mask & PNG_FREE_PCAL) & info_ptr->free_me) +#else + if (mask & PNG_FREE_PCAL) +#endif + { + png_free(png_ptr, info_ptr->pcal_purpose); + png_free(png_ptr, info_ptr->pcal_units); + info_ptr->pcal_purpose = NULL; + info_ptr->pcal_units = NULL; + if (info_ptr->pcal_params != NULL) + { + int i; + for (i = 0; i < (int)info_ptr->pcal_nparams; i++) + { + png_free(png_ptr, info_ptr->pcal_params[i]); + info_ptr->pcal_params[i] = NULL; + } + png_free(png_ptr, info_ptr->pcal_params); + info_ptr->pcal_params = NULL; + } + info_ptr->valid &= ~PNG_INFO_pCAL; + } +#endif + +#ifdef PNG_iCCP_SUPPORTED + /* Free any iCCP entry */ +#ifdef PNG_FREE_ME_SUPPORTED + if ((mask & PNG_FREE_ICCP) & info_ptr->free_me) +#else + if (mask & PNG_FREE_ICCP) +#endif + { + png_free(png_ptr, info_ptr->iccp_name); + png_free(png_ptr, info_ptr->iccp_profile); + info_ptr->iccp_name = NULL; + info_ptr->iccp_profile = NULL; + info_ptr->valid &= ~PNG_INFO_iCCP; + } +#endif + +#ifdef PNG_sPLT_SUPPORTED + /* Free a given sPLT entry, or (if num == -1) all sPLT entries */ +#ifdef PNG_FREE_ME_SUPPORTED + if ((mask & PNG_FREE_SPLT) & info_ptr->free_me) +#else + if (mask & PNG_FREE_SPLT) +#endif + { + if (num != -1) + { + if (info_ptr->splt_palettes) + { + png_free(png_ptr, info_ptr->splt_palettes[num].name); + png_free(png_ptr, info_ptr->splt_palettes[num].entries); + info_ptr->splt_palettes[num].name = NULL; + info_ptr->splt_palettes[num].entries = NULL; + } + } + else + { + if (info_ptr->splt_palettes_num) + { + int i; + for (i = 0; i < (int)info_ptr->splt_palettes_num; i++) + png_free_data(png_ptr, info_ptr, PNG_FREE_SPLT, i); + + png_free(png_ptr, info_ptr->splt_palettes); + info_ptr->splt_palettes = NULL; + info_ptr->splt_palettes_num = 0; + } + info_ptr->valid &= ~PNG_INFO_sPLT; + } + } +#endif + +#ifdef PNG_UNKNOWN_CHUNKS_SUPPORTED + if (png_ptr->unknown_chunk.data) + { + png_free(png_ptr, png_ptr->unknown_chunk.data); + png_ptr->unknown_chunk.data = NULL; + } + +#ifdef PNG_FREE_ME_SUPPORTED + if ((mask & PNG_FREE_UNKN) & info_ptr->free_me) +#else + if (mask & PNG_FREE_UNKN) +#endif + { + if (num != -1) + { + if (info_ptr->unknown_chunks) + { + png_free(png_ptr, info_ptr->unknown_chunks[num].data); + info_ptr->unknown_chunks[num].data = NULL; + } + } + else + { + int i; + + if (info_ptr->unknown_chunks_num) + { + for (i = 0; i < (int)info_ptr->unknown_chunks_num; i++) + png_free_data(png_ptr, info_ptr, PNG_FREE_UNKN, i); + + png_free(png_ptr, info_ptr->unknown_chunks); + info_ptr->unknown_chunks = NULL; + info_ptr->unknown_chunks_num = 0; + } + } + } +#endif + +#ifdef PNG_hIST_SUPPORTED + /* Free any hIST entry */ +#ifdef PNG_FREE_ME_SUPPORTED + if ((mask & PNG_FREE_HIST) & info_ptr->free_me) +#else + if ((mask & PNG_FREE_HIST) && (png_ptr->flags & PNG_FLAG_FREE_HIST)) +#endif + { + png_free(png_ptr, info_ptr->hist); + info_ptr->hist = NULL; + info_ptr->valid &= ~PNG_INFO_hIST; +#ifndef PNG_FREE_ME_SUPPORTED + png_ptr->flags &= ~PNG_FLAG_FREE_HIST; +#endif + } +#endif + + /* Free any PLTE entry that was internally allocated */ +#ifdef PNG_FREE_ME_SUPPORTED + if ((mask & PNG_FREE_PLTE) & info_ptr->free_me) +#else + if ((mask & PNG_FREE_PLTE) && (png_ptr->flags & PNG_FLAG_FREE_PLTE)) +#endif + { + png_zfree(png_ptr, info_ptr->palette); + info_ptr->palette = NULL; + info_ptr->valid &= ~PNG_INFO_PLTE; +#ifndef PNG_FREE_ME_SUPPORTED + png_ptr->flags &= ~PNG_FLAG_FREE_PLTE; +#endif + info_ptr->num_palette = 0; + } + +#ifdef PNG_INFO_IMAGE_SUPPORTED + /* Free any image bits attached to the info structure */ +#ifdef PNG_FREE_ME_SUPPORTED + if ((mask & PNG_FREE_ROWS) & info_ptr->free_me) +#else + if (mask & PNG_FREE_ROWS) +#endif + { + if (info_ptr->row_pointers) + { + int row; + for (row = 0; row < (int)info_ptr->height; row++) + { + png_free(png_ptr, info_ptr->row_pointers[row]); + info_ptr->row_pointers[row] = NULL; + } + png_free(png_ptr, info_ptr->row_pointers); + info_ptr->row_pointers = NULL; + } + info_ptr->valid &= ~PNG_INFO_IDAT; + } +#endif + +#ifdef PNG_FREE_ME_SUPPORTED + if (num == -1) + info_ptr->free_me &= ~mask; + else + info_ptr->free_me &= ~(mask & ~PNG_FREE_MUL); +#endif +} + +/* This is an internal routine to free any memory that the info struct is + * pointing to before re-using it or freeing the struct itself. Recall + * that png_free() checks for NULL pointers for us. + */ +void /* PRIVATE */ +png_info_destroy(png_structp png_ptr, png_infop info_ptr) +{ + png_debug(1, "in png_info_destroy"); + + png_free_data(png_ptr, info_ptr, PNG_FREE_ALL, -1); + +#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED + if (png_ptr->num_chunk_list) + { + png_free(png_ptr, png_ptr->chunk_list); + png_ptr->chunk_list = NULL; + png_ptr->num_chunk_list = 0; + } +#endif + + png_info_init_3(&info_ptr, png_sizeof(png_info)); +} +#endif /* defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED) */ + +/* This function returns a pointer to the io_ptr associated with the user + * functions. The application should free any memory associated with this + * pointer before png_write_destroy() or png_read_destroy() are called. + */ +png_voidp PNGAPI +png_get_io_ptr(png_structp png_ptr) +{ + if (png_ptr == NULL) + return (NULL); + return (png_ptr->io_ptr); +} + +#if defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED) +#ifdef PNG_STDIO_SUPPORTED +/* Initialize the default input/output functions for the PNG file. If you + * use your own read or write routines, you can call either png_set_read_fn() + * or png_set_write_fn() instead of png_init_io(). If you have defined + * PNG_NO_STDIO, you must use a function of your own because "FILE *" isn't + * necessarily available. + */ +void PNGAPI +png_init_io(png_structp png_ptr, png_FILE_p fp) +{ + png_debug(1, "in png_init_io"); + + if (png_ptr == NULL) + return; + + png_ptr->io_ptr = (png_voidp)fp; +} +#endif + +#ifdef PNG_TIME_RFC1123_SUPPORTED +/* Convert the supplied time into an RFC 1123 string suitable for use in + * a "Creation Time" or other text-based time string. + */ +png_charp PNGAPI +png_convert_to_rfc1123(png_structp png_ptr, png_timep ptime) +{ + static PNG_CONST char short_months[12][4] = + {"Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; + + if (png_ptr == NULL) + return (NULL); + if (png_ptr->time_buffer == NULL) + { + png_ptr->time_buffer = (png_charp)png_malloc(png_ptr, (png_uint_32)(29* + png_sizeof(char))); + } + +#ifdef _WIN32_WCE + { + wchar_t time_buf[29]; + wsprintf(time_buf, TEXT("%d %S %d %02d:%02d:%02d +0000"), + ptime->day % 32, short_months[(ptime->month - 1) % 12], + ptime->year, ptime->hour % 24, ptime->minute % 60, + ptime->second % 61); + WideCharToMultiByte(CP_ACP, 0, time_buf, -1, png_ptr->time_buffer, + 29, NULL, NULL); + } +#else +#ifdef USE_FAR_KEYWORD + { + char near_time_buf[29]; + png_snprintf6(near_time_buf, 29, "%d %s %d %02d:%02d:%02d +0000", + ptime->day % 32, short_months[(ptime->month - 1) % 12], + ptime->year, ptime->hour % 24, ptime->minute % 60, + ptime->second % 61); + png_memcpy(png_ptr->time_buffer, near_time_buf, + 29*png_sizeof(char)); + } +#else + png_snprintf6(png_ptr->time_buffer, 29, "%d %s %d %02d:%02d:%02d +0000", + ptime->day % 32, short_months[(ptime->month - 1) % 12], + ptime->year, ptime->hour % 24, ptime->minute % 60, + ptime->second % 61); +#endif +#endif /* _WIN32_WCE */ + return ((png_charp)png_ptr->time_buffer); +} +#endif /* PNG_TIME_RFC1123_SUPPORTED */ + +#endif /* defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED) */ + +png_charp PNGAPI +png_get_copyright(png_structp png_ptr) +{ + png_ptr = png_ptr; /* Silence compiler warning about unused png_ptr */ +#ifdef PNG_STRING_COPYRIGHT + return PNG_STRING_COPYRIGHT +#else +#ifdef __STDC__ + return ((png_charp) PNG_STRING_NEWLINE \ + "libpng version 1.2.50 - July 10, 2012" PNG_STRING_NEWLINE \ + "Copyright (c) 1998-2011 Glenn Randers-Pehrson" PNG_STRING_NEWLINE \ + "Copyright (c) 1996-1997 Andreas Dilger" PNG_STRING_NEWLINE \ + "Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc." \ + PNG_STRING_NEWLINE); +#else + return ((png_charp) "libpng version 1.2.50 - July 10, 2012\ + Copyright (c) 1998-2011 Glenn Randers-Pehrson\ + Copyright (c) 1996-1997 Andreas Dilger\ + Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc."); +#endif +#endif +} + +/* The following return the library version as a short string in the + * format 1.0.0 through 99.99.99zz. To get the version of *.h files + * used with your application, print out PNG_LIBPNG_VER_STRING, which + * is defined in png.h. + * Note: now there is no difference between png_get_libpng_ver() and + * png_get_header_ver(). Due to the version_nn_nn_nn typedef guard, + * it is guaranteed that png.c uses the correct version of png.h. + */ +png_charp PNGAPI +png_get_libpng_ver(png_structp png_ptr) +{ + /* Version of *.c files used when building libpng */ + png_ptr = png_ptr; /* Silence compiler warning about unused png_ptr */ + return ((png_charp) PNG_LIBPNG_VER_STRING); +} + +png_charp PNGAPI +png_get_header_ver(png_structp png_ptr) +{ + /* Version of *.h files used when building libpng */ + png_ptr = png_ptr; /* Silence compiler warning about unused png_ptr */ + return ((png_charp) PNG_LIBPNG_VER_STRING); +} + +png_charp PNGAPI +png_get_header_version(png_structp png_ptr) +{ + /* Returns longer string containing both version and date */ + png_ptr = png_ptr; /* Silence compiler warning about unused png_ptr */ +#ifdef __STDC__ + return ((png_charp) PNG_HEADER_VERSION_STRING +#ifndef PNG_READ_SUPPORTED + " (NO READ SUPPORT)" +#endif + PNG_STRING_NEWLINE); +#else + return ((png_charp) PNG_HEADER_VERSION_STRING); +#endif +} + +#if defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED) +#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED +int PNGAPI +png_handle_as_unknown(png_structp png_ptr, png_bytep chunk_name) +{ + /* Check chunk_name and return "keep" value if it's on the list, else 0 */ + int i; + png_bytep p; + if (png_ptr == NULL || chunk_name == NULL || png_ptr->num_chunk_list<=0) + return 0; + p = png_ptr->chunk_list + png_ptr->num_chunk_list*5 - 5; + for (i = png_ptr->num_chunk_list; i; i--, p -= 5) + if (!png_memcmp(chunk_name, p, 4)) + return ((int)*(p + 4)); + return 0; +} +#endif + +/* This function, added to libpng-1.0.6g, is untested. */ +int PNGAPI +png_reset_zstream(png_structp png_ptr) +{ + if (png_ptr == NULL) + return Z_STREAM_ERROR; + return (inflateReset(&png_ptr->zstream)); +} +#endif /* defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED) */ + +/* This function was added to libpng-1.0.7 */ +png_uint_32 PNGAPI +png_access_version_number(void) +{ + /* Version of *.c files used when building libpng */ + return((png_uint_32) PNG_LIBPNG_VER); +} + + +#if defined(PNG_READ_SUPPORTED) && defined(PNG_ASSEMBLER_CODE_SUPPORTED) +#ifndef PNG_1_0_X +/* This function was added to libpng 1.2.0 */ +int PNGAPI +png_mmx_support(void) +{ + /* Obsolete, to be removed from libpng-1.4.0 */ + return -1; +} +#endif /* PNG_1_0_X */ +#endif /* PNG_READ_SUPPORTED && PNG_ASSEMBLER_CODE_SUPPORTED */ + +#if defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED) +#ifdef PNG_SIZE_T +/* Added at libpng version 1.2.6 */ + PNG_EXTERN png_size_t PNGAPI png_convert_size PNGARG((size_t size)); +png_size_t PNGAPI +png_convert_size(size_t size) +{ + if (size > (png_size_t)-1) + PNG_ABORT(); /* We haven't got access to png_ptr, so no png_error() */ + return ((png_size_t)size); +} +#endif /* PNG_SIZE_T */ + +/* Added at libpng version 1.2.34 and 1.4.0 (moved from pngset.c) */ +#ifdef PNG_cHRM_SUPPORTED +#ifdef PNG_CHECK_cHRM_SUPPORTED + +/* + * Multiply two 32-bit numbers, V1 and V2, using 32-bit + * arithmetic, to produce a 64 bit result in the HI/LO words. + * + * A B + * x C D + * ------ + * AD || BD + * AC || CB || 0 + * + * where A and B are the high and low 16-bit words of V1, + * C and D are the 16-bit words of V2, AD is the product of + * A and D, and X || Y is (X << 16) + Y. +*/ + +void /* PRIVATE */ +png_64bit_product (long v1, long v2, unsigned long *hi_product, + unsigned long *lo_product) +{ + int a, b, c, d; + long lo, hi, x, y; + + a = (v1 >> 16) & 0xffff; + b = v1 & 0xffff; + c = (v2 >> 16) & 0xffff; + d = v2 & 0xffff; + + lo = b * d; /* BD */ + x = a * d + c * b; /* AD + CB */ + y = ((lo >> 16) & 0xffff) + x; + + lo = (lo & 0xffff) | ((y & 0xffff) << 16); + hi = (y >> 16) & 0xffff; + + hi += a * c; /* AC */ + + *hi_product = (unsigned long)hi; + *lo_product = (unsigned long)lo; +} + +int /* PRIVATE */ +png_check_cHRM_fixed(png_structp png_ptr, + png_fixed_point white_x, png_fixed_point white_y, png_fixed_point red_x, + png_fixed_point red_y, png_fixed_point green_x, png_fixed_point green_y, + png_fixed_point blue_x, png_fixed_point blue_y) +{ + int ret = 1; + unsigned long xy_hi,xy_lo,yx_hi,yx_lo; + + png_debug(1, "in function png_check_cHRM_fixed"); + + if (png_ptr == NULL) + return 0; + + if (white_x < 0 || white_y <= 0 || + red_x < 0 || red_y < 0 || + green_x < 0 || green_y < 0 || + blue_x < 0 || blue_y < 0) + { + png_warning(png_ptr, + "Ignoring attempt to set negative chromaticity value"); + ret = 0; + } + if (white_x > (png_fixed_point) PNG_UINT_31_MAX || + white_y > (png_fixed_point) PNG_UINT_31_MAX || + red_x > (png_fixed_point) PNG_UINT_31_MAX || + red_y > (png_fixed_point) PNG_UINT_31_MAX || + green_x > (png_fixed_point) PNG_UINT_31_MAX || + green_y > (png_fixed_point) PNG_UINT_31_MAX || + blue_x > (png_fixed_point) PNG_UINT_31_MAX || + blue_y > (png_fixed_point) PNG_UINT_31_MAX ) + { + png_warning(png_ptr, + "Ignoring attempt to set chromaticity value exceeding 21474.83"); + ret = 0; + } + if (white_x > 100000L - white_y) + { + png_warning(png_ptr, "Invalid cHRM white point"); + ret = 0; + } + if (red_x > 100000L - red_y) + { + png_warning(png_ptr, "Invalid cHRM red point"); + ret = 0; + } + if (green_x > 100000L - green_y) + { + png_warning(png_ptr, "Invalid cHRM green point"); + ret = 0; + } + if (blue_x > 100000L - blue_y) + { + png_warning(png_ptr, "Invalid cHRM blue point"); + ret = 0; + } + + png_64bit_product(green_x - red_x, blue_y - red_y, &xy_hi, &xy_lo); + png_64bit_product(green_y - red_y, blue_x - red_x, &yx_hi, &yx_lo); + + if (xy_hi == yx_hi && xy_lo == yx_lo) + { + png_warning(png_ptr, + "Ignoring attempt to set cHRM RGB triangle with zero area"); + ret = 0; + } + + return ret; +} +#endif /* PNG_CHECK_cHRM_SUPPORTED */ +#endif /* PNG_cHRM_SUPPORTED */ + +void /* PRIVATE */ +png_check_IHDR(png_structp png_ptr, + png_uint_32 width, png_uint_32 height, int bit_depth, + int color_type, int interlace_type, int compression_type, + int filter_type) +{ + int error = 0; + + /* Check for width and height valid values */ + if (width == 0) + { + png_warning(png_ptr, "Image width is zero in IHDR"); + error = 1; + } + + if (height == 0) + { + png_warning(png_ptr, "Image height is zero in IHDR"); + error = 1; + } + +#ifdef PNG_SET_USER_LIMITS_SUPPORTED + if (width > png_ptr->user_width_max || width > PNG_USER_WIDTH_MAX) +#else + if (width > PNG_USER_WIDTH_MAX) +#endif + { + png_warning(png_ptr, "Image width exceeds user limit in IHDR"); + error = 1; + } + +#ifdef PNG_SET_USER_LIMITS_SUPPORTED + if (height > png_ptr->user_height_max || height > PNG_USER_HEIGHT_MAX) +#else + if (height > PNG_USER_HEIGHT_MAX) +#endif + { + png_warning(png_ptr, "Image height exceeds user limit in IHDR"); + error = 1; + } + + if (width > PNG_UINT_31_MAX) + { + png_warning(png_ptr, "Invalid image width in IHDR"); + error = 1; + } + + if ( height > PNG_UINT_31_MAX) + { + png_warning(png_ptr, "Invalid image height in IHDR"); + error = 1; + } + + if ( width > (PNG_UINT_32_MAX + >> 3) /* 8-byte RGBA pixels */ + - 64 /* bigrowbuf hack */ + - 1 /* filter byte */ + - 7*8 /* rounding of width to multiple of 8 pixels */ + - 8) /* extra max_pixel_depth pad */ + png_warning(png_ptr, "Width is too large for libpng to process pixels"); + + /* Check other values */ + if (bit_depth != 1 && bit_depth != 2 && bit_depth != 4 && + bit_depth != 8 && bit_depth != 16) + { + png_warning(png_ptr, "Invalid bit depth in IHDR"); + error = 1; + } + + if (color_type < 0 || color_type == 1 || + color_type == 5 || color_type > 6) + { + png_warning(png_ptr, "Invalid color type in IHDR"); + error = 1; + } + + if (((color_type == PNG_COLOR_TYPE_PALETTE) && bit_depth > 8) || + ((color_type == PNG_COLOR_TYPE_RGB || + color_type == PNG_COLOR_TYPE_GRAY_ALPHA || + color_type == PNG_COLOR_TYPE_RGB_ALPHA) && bit_depth < 8)) + { + png_warning(png_ptr, "Invalid color type/bit depth combination in IHDR"); + error = 1; + } + + if (interlace_type >= PNG_INTERLACE_LAST) + { + png_warning(png_ptr, "Unknown interlace method in IHDR"); + error = 1; + } + + if (compression_type != PNG_COMPRESSION_TYPE_BASE) + { + png_warning(png_ptr, "Unknown compression method in IHDR"); + error = 1; + } + +#ifdef PNG_MNG_FEATURES_SUPPORTED + /* Accept filter_method 64 (intrapixel differencing) only if + * 1. Libpng was compiled with PNG_MNG_FEATURES_SUPPORTED and + * 2. Libpng did not read a PNG signature (this filter_method is only + * used in PNG datastreams that are embedded in MNG datastreams) and + * 3. The application called png_permit_mng_features with a mask that + * included PNG_FLAG_MNG_FILTER_64 and + * 4. The filter_method is 64 and + * 5. The color_type is RGB or RGBA + */ + if ((png_ptr->mode & PNG_HAVE_PNG_SIGNATURE) && + png_ptr->mng_features_permitted) + png_warning(png_ptr, "MNG features are not allowed in a PNG datastream"); + + if (filter_type != PNG_FILTER_TYPE_BASE) + { + if (!((png_ptr->mng_features_permitted & PNG_FLAG_MNG_FILTER_64) && + (filter_type == PNG_INTRAPIXEL_DIFFERENCING) && + ((png_ptr->mode & PNG_HAVE_PNG_SIGNATURE) == 0) && + (color_type == PNG_COLOR_TYPE_RGB || + color_type == PNG_COLOR_TYPE_RGB_ALPHA))) + { + png_warning(png_ptr, "Unknown filter method in IHDR"); + error = 1; + } + + if (png_ptr->mode & PNG_HAVE_PNG_SIGNATURE) + { + png_warning(png_ptr, "Invalid filter method in IHDR"); + error = 1; + } + } + +#else + if (filter_type != PNG_FILTER_TYPE_BASE) + { + png_warning(png_ptr, "Unknown filter method in IHDR"); + error = 1; + } +#endif + + if (error == 1) + png_error(png_ptr, "Invalid IHDR data"); +} +#endif /* defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED) */ diff --git a/src/libpng/png.h b/src/libpng/png.h new file mode 100644 index 00000000..35609691 --- /dev/null +++ b/src/libpng/png.h @@ -0,0 +1,3810 @@ +/* png.h - header file for PNG reference library + * + * libpng version 1.2.50 - July 10, 2012 + * Copyright (c) 1998-2012 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + * + * This code is released under the libpng license (See LICENSE, below) + * + * Authors and maintainers: + * libpng versions 0.71, May 1995, through 0.88, January 1996: Guy Schalnat + * libpng versions 0.89c, June 1996, through 0.96, May 1997: Andreas Dilger + * libpng versions 0.97, January 1998, through 1.2.50 - July 10, 2012: Glenn + * See also "Contributing Authors", below. + * + * Note about libpng version numbers: + * + * Due to various miscommunications, unforeseen code incompatibilities + * and occasional factors outside the authors' control, version numbering + * on the library has not always been consistent and straightforward. + * The following table summarizes matters since version 0.89c, which was + * the first widely used release: + * + * source png.h png.h shared-lib + * version string int version + * ------- ------ ----- ---------- + * 0.89c "1.0 beta 3" 0.89 89 1.0.89 + * 0.90 "1.0 beta 4" 0.90 90 0.90 [should have been 2.0.90] + * 0.95 "1.0 beta 5" 0.95 95 0.95 [should have been 2.0.95] + * 0.96 "1.0 beta 6" 0.96 96 0.96 [should have been 2.0.96] + * 0.97b "1.00.97 beta 7" 1.00.97 97 1.0.1 [should have been 2.0.97] + * 0.97c 0.97 97 2.0.97 + * 0.98 0.98 98 2.0.98 + * 0.99 0.99 98 2.0.99 + * 0.99a-m 0.99 99 2.0.99 + * 1.00 1.00 100 2.1.0 [100 should be 10000] + * 1.0.0 (from here on, the 100 2.1.0 [100 should be 10000] + * 1.0.1 png.h string is 10001 2.1.0 + * 1.0.1a-e identical to the 10002 from here on, the shared library + * 1.0.2 source version) 10002 is 2.V where V is the source code + * 1.0.2a-b 10003 version, except as noted. + * 1.0.3 10003 + * 1.0.3a-d 10004 + * 1.0.4 10004 + * 1.0.4a-f 10005 + * 1.0.5 (+ 2 patches) 10005 + * 1.0.5a-d 10006 + * 1.0.5e-r 10100 (not source compatible) + * 1.0.5s-v 10006 (not binary compatible) + * 1.0.6 (+ 3 patches) 10006 (still binary incompatible) + * 1.0.6d-f 10007 (still binary incompatible) + * 1.0.6g 10007 + * 1.0.6h 10007 10.6h (testing xy.z so-numbering) + * 1.0.6i 10007 10.6i + * 1.0.6j 10007 2.1.0.6j (incompatible with 1.0.0) + * 1.0.7beta11-14 DLLNUM 10007 2.1.0.7beta11-14 (binary compatible) + * 1.0.7beta15-18 1 10007 2.1.0.7beta15-18 (binary compatible) + * 1.0.7rc1-2 1 10007 2.1.0.7rc1-2 (binary compatible) + * 1.0.7 1 10007 (still compatible) + * 1.0.8beta1-4 1 10008 2.1.0.8beta1-4 + * 1.0.8rc1 1 10008 2.1.0.8rc1 + * 1.0.8 1 10008 2.1.0.8 + * 1.0.9beta1-6 1 10009 2.1.0.9beta1-6 + * 1.0.9rc1 1 10009 2.1.0.9rc1 + * 1.0.9beta7-10 1 10009 2.1.0.9beta7-10 + * 1.0.9rc2 1 10009 2.1.0.9rc2 + * 1.0.9 1 10009 2.1.0.9 + * 1.0.10beta1 1 10010 2.1.0.10beta1 + * 1.0.10rc1 1 10010 2.1.0.10rc1 + * 1.0.10 1 10010 2.1.0.10 + * 1.0.11beta1-3 1 10011 2.1.0.11beta1-3 + * 1.0.11rc1 1 10011 2.1.0.11rc1 + * 1.0.11 1 10011 2.1.0.11 + * 1.0.12beta1-2 2 10012 2.1.0.12beta1-2 + * 1.0.12rc1 2 10012 2.1.0.12rc1 + * 1.0.12 2 10012 2.1.0.12 + * 1.1.0a-f - 10100 2.1.1.0a-f (branch abandoned) + * 1.2.0beta1-2 2 10200 2.1.2.0beta1-2 + * 1.2.0beta3-5 3 10200 3.1.2.0beta3-5 + * 1.2.0rc1 3 10200 3.1.2.0rc1 + * 1.2.0 3 10200 3.1.2.0 + * 1.2.1beta1-4 3 10201 3.1.2.1beta1-4 + * 1.2.1rc1-2 3 10201 3.1.2.1rc1-2 + * 1.2.1 3 10201 3.1.2.1 + * 1.2.2beta1-6 12 10202 12.so.0.1.2.2beta1-6 + * 1.0.13beta1 10 10013 10.so.0.1.0.13beta1 + * 1.0.13rc1 10 10013 10.so.0.1.0.13rc1 + * 1.2.2rc1 12 10202 12.so.0.1.2.2rc1 + * 1.0.13 10 10013 10.so.0.1.0.13 + * 1.2.2 12 10202 12.so.0.1.2.2 + * 1.2.3rc1-6 12 10203 12.so.0.1.2.3rc1-6 + * 1.2.3 12 10203 12.so.0.1.2.3 + * 1.2.4beta1-3 13 10204 12.so.0.1.2.4beta1-3 + * 1.0.14rc1 13 10014 10.so.0.1.0.14rc1 + * 1.2.4rc1 13 10204 12.so.0.1.2.4rc1 + * 1.0.14 10 10014 10.so.0.1.0.14 + * 1.2.4 13 10204 12.so.0.1.2.4 + * 1.2.5beta1-2 13 10205 12.so.0.1.2.5beta1-2 + * 1.0.15rc1-3 10 10015 10.so.0.1.0.15rc1-3 + * 1.2.5rc1-3 13 10205 12.so.0.1.2.5rc1-3 + * 1.0.15 10 10015 10.so.0.1.0.15 + * 1.2.5 13 10205 12.so.0.1.2.5 + * 1.2.6beta1-4 13 10206 12.so.0.1.2.6beta1-4 + * 1.0.16 10 10016 10.so.0.1.0.16 + * 1.2.6 13 10206 12.so.0.1.2.6 + * 1.2.7beta1-2 13 10207 12.so.0.1.2.7beta1-2 + * 1.0.17rc1 10 10017 10.so.0.1.0.17rc1 + * 1.2.7rc1 13 10207 12.so.0.1.2.7rc1 + * 1.0.17 10 10017 10.so.0.1.0.17 + * 1.2.7 13 10207 12.so.0.1.2.7 + * 1.2.8beta1-5 13 10208 12.so.0.1.2.8beta1-5 + * 1.0.18rc1-5 10 10018 10.so.0.1.0.18rc1-5 + * 1.2.8rc1-5 13 10208 12.so.0.1.2.8rc1-5 + * 1.0.18 10 10018 10.so.0.1.0.18 + * 1.2.8 13 10208 12.so.0.1.2.8 + * 1.2.9beta1-3 13 10209 12.so.0.1.2.9beta1-3 + * 1.2.9beta4-11 13 10209 12.so.0.9[.0] + * 1.2.9rc1 13 10209 12.so.0.9[.0] + * 1.2.9 13 10209 12.so.0.9[.0] + * 1.2.10beta1-8 13 10210 12.so.0.10[.0] + * 1.2.10rc1-3 13 10210 12.so.0.10[.0] + * 1.2.10 13 10210 12.so.0.10[.0] + * 1.2.11beta1-4 13 10211 12.so.0.11[.0] + * 1.0.19rc1-5 10 10019 10.so.0.19[.0] + * 1.2.11rc1-5 13 10211 12.so.0.11[.0] + * 1.0.19 10 10019 10.so.0.19[.0] + * 1.2.11 13 10211 12.so.0.11[.0] + * 1.0.20 10 10020 10.so.0.20[.0] + * 1.2.12 13 10212 12.so.0.12[.0] + * 1.2.13beta1 13 10213 12.so.0.13[.0] + * 1.0.21 10 10021 10.so.0.21[.0] + * 1.2.13 13 10213 12.so.0.13[.0] + * 1.2.14beta1-2 13 10214 12.so.0.14[.0] + * 1.0.22rc1 10 10022 10.so.0.22[.0] + * 1.2.14rc1 13 10214 12.so.0.14[.0] + * 1.0.22 10 10022 10.so.0.22[.0] + * 1.2.14 13 10214 12.so.0.14[.0] + * 1.2.15beta1-6 13 10215 12.so.0.15[.0] + * 1.0.23rc1-5 10 10023 10.so.0.23[.0] + * 1.2.15rc1-5 13 10215 12.so.0.15[.0] + * 1.0.23 10 10023 10.so.0.23[.0] + * 1.2.15 13 10215 12.so.0.15[.0] + * 1.2.16beta1-2 13 10216 12.so.0.16[.0] + * 1.2.16rc1 13 10216 12.so.0.16[.0] + * 1.0.24 10 10024 10.so.0.24[.0] + * 1.2.16 13 10216 12.so.0.16[.0] + * 1.2.17beta1-2 13 10217 12.so.0.17[.0] + * 1.0.25rc1 10 10025 10.so.0.25[.0] + * 1.2.17rc1-3 13 10217 12.so.0.17[.0] + * 1.0.25 10 10025 10.so.0.25[.0] + * 1.2.17 13 10217 12.so.0.17[.0] + * 1.0.26 10 10026 10.so.0.26[.0] + * 1.2.18 13 10218 12.so.0.18[.0] + * 1.2.19beta1-31 13 10219 12.so.0.19[.0] + * 1.0.27rc1-6 10 10027 10.so.0.27[.0] + * 1.2.19rc1-6 13 10219 12.so.0.19[.0] + * 1.0.27 10 10027 10.so.0.27[.0] + * 1.2.19 13 10219 12.so.0.19[.0] + * 1.2.20beta01-04 13 10220 12.so.0.20[.0] + * 1.0.28rc1-6 10 10028 10.so.0.28[.0] + * 1.2.20rc1-6 13 10220 12.so.0.20[.0] + * 1.0.28 10 10028 10.so.0.28[.0] + * 1.2.20 13 10220 12.so.0.20[.0] + * 1.2.21beta1-2 13 10221 12.so.0.21[.0] + * 1.2.21rc1-3 13 10221 12.so.0.21[.0] + * 1.0.29 10 10029 10.so.0.29[.0] + * 1.2.21 13 10221 12.so.0.21[.0] + * 1.2.22beta1-4 13 10222 12.so.0.22[.0] + * 1.0.30rc1 10 10030 10.so.0.30[.0] + * 1.2.22rc1 13 10222 12.so.0.22[.0] + * 1.0.30 10 10030 10.so.0.30[.0] + * 1.2.22 13 10222 12.so.0.22[.0] + * 1.2.23beta01-05 13 10223 12.so.0.23[.0] + * 1.2.23rc01 13 10223 12.so.0.23[.0] + * 1.2.23 13 10223 12.so.0.23[.0] + * 1.2.24beta01-02 13 10224 12.so.0.24[.0] + * 1.2.24rc01 13 10224 12.so.0.24[.0] + * 1.2.24 13 10224 12.so.0.24[.0] + * 1.2.25beta01-06 13 10225 12.so.0.25[.0] + * 1.2.25rc01-02 13 10225 12.so.0.25[.0] + * 1.0.31 10 10031 10.so.0.31[.0] + * 1.2.25 13 10225 12.so.0.25[.0] + * 1.2.26beta01-06 13 10226 12.so.0.26[.0] + * 1.2.26rc01 13 10226 12.so.0.26[.0] + * 1.2.26 13 10226 12.so.0.26[.0] + * 1.0.32 10 10032 10.so.0.32[.0] + * 1.2.27beta01-06 13 10227 12.so.0.27[.0] + * 1.2.27rc01 13 10227 12.so.0.27[.0] + * 1.0.33 10 10033 10.so.0.33[.0] + * 1.2.27 13 10227 12.so.0.27[.0] + * 1.0.34 10 10034 10.so.0.34[.0] + * 1.2.28 13 10228 12.so.0.28[.0] + * 1.2.29beta01-03 13 10229 12.so.0.29[.0] + * 1.2.29rc01 13 10229 12.so.0.29[.0] + * 1.0.35 10 10035 10.so.0.35[.0] + * 1.2.29 13 10229 12.so.0.29[.0] + * 1.0.37 10 10037 10.so.0.37[.0] + * 1.2.30beta01-04 13 10230 12.so.0.30[.0] + * 1.0.38rc01-08 10 10038 10.so.0.38[.0] + * 1.2.30rc01-08 13 10230 12.so.0.30[.0] + * 1.0.38 10 10038 10.so.0.38[.0] + * 1.2.30 13 10230 12.so.0.30[.0] + * 1.0.39rc01-03 10 10039 10.so.0.39[.0] + * 1.2.31rc01-03 13 10231 12.so.0.31[.0] + * 1.0.39 10 10039 10.so.0.39[.0] + * 1.2.31 13 10231 12.so.0.31[.0] + * 1.2.32beta01-02 13 10232 12.so.0.32[.0] + * 1.0.40rc01 10 10040 10.so.0.40[.0] + * 1.2.32rc01 13 10232 12.so.0.32[.0] + * 1.0.40 10 10040 10.so.0.40[.0] + * 1.2.32 13 10232 12.so.0.32[.0] + * 1.2.33beta01-02 13 10233 12.so.0.33[.0] + * 1.2.33rc01-02 13 10233 12.so.0.33[.0] + * 1.0.41rc01 10 10041 10.so.0.41[.0] + * 1.2.33 13 10233 12.so.0.33[.0] + * 1.0.41 10 10041 10.so.0.41[.0] + * 1.2.34beta01-07 13 10234 12.so.0.34[.0] + * 1.0.42rc01 10 10042 10.so.0.42[.0] + * 1.2.34rc01 13 10234 12.so.0.34[.0] + * 1.0.42 10 10042 10.so.0.42[.0] + * 1.2.34 13 10234 12.so.0.34[.0] + * 1.2.35beta01-03 13 10235 12.so.0.35[.0] + * 1.0.43rc01-02 10 10043 10.so.0.43[.0] + * 1.2.35rc01-02 13 10235 12.so.0.35[.0] + * 1.0.43 10 10043 10.so.0.43[.0] + * 1.2.35 13 10235 12.so.0.35[.0] + * 1.2.36beta01-05 13 10236 12.so.0.36[.0] + * 1.2.36rc01 13 10236 12.so.0.36[.0] + * 1.0.44 10 10044 10.so.0.44[.0] + * 1.2.36 13 10236 12.so.0.36[.0] + * 1.2.37beta01-03 13 10237 12.so.0.37[.0] + * 1.2.37rc01 13 10237 12.so.0.37[.0] + * 1.2.37 13 10237 12.so.0.37[.0] + * 1.0.45 10 10045 12.so.0.45[.0] + * 1.0.46 10 10046 10.so.0.46[.0] + * 1.2.38beta01 13 10238 12.so.0.38[.0] + * 1.2.38rc01-03 13 10238 12.so.0.38[.0] + * 1.0.47 10 10047 10.so.0.47[.0] + * 1.2.38 13 10238 12.so.0.38[.0] + * 1.2.39beta01-05 13 10239 12.so.0.39[.0] + * 1.2.39rc01 13 10239 12.so.0.39[.0] + * 1.0.48 10 10048 10.so.0.48[.0] + * 1.2.39 13 10239 12.so.0.39[.0] + * 1.2.40beta01 13 10240 12.so.0.40[.0] + * 1.2.40rc01 13 10240 12.so.0.40[.0] + * 1.0.49 10 10049 10.so.0.49[.0] + * 1.2.40 13 10240 12.so.0.40[.0] + * 1.2.41beta01-18 13 10241 12.so.0.41[.0] + * 1.0.51rc01 10 10051 10.so.0.51[.0] + * 1.2.41rc01-03 13 10241 12.so.0.41[.0] + * 1.0.51 10 10051 10.so.0.51[.0] + * 1.2.41 13 10241 12.so.0.41[.0] + * 1.2.42beta01-02 13 10242 12.so.0.42[.0] + * 1.2.42rc01-05 13 10242 12.so.0.42[.0] + * 1.0.52 10 10052 10.so.0.52[.0] + * 1.2.42 13 10242 12.so.0.42[.0] + * 1.2.43beta01-05 13 10243 12.so.0.43[.0] + * 1.0.53rc01-02 10 10053 10.so.0.53[.0] + * 1.2.43rc01-02 13 10243 12.so.0.43[.0] + * 1.0.53 10 10053 10.so.0.53[.0] + * 1.2.43 13 10243 12.so.0.43[.0] + * 1.2.44beta01-03 13 10244 12.so.0.44[.0] + * 1.2.44rc01-03 13 10244 12.so.0.44[.0] + * 1.2.44 13 10244 12.so.0.44[.0] + * 1.2.45beta01-03 13 10245 12.so.0.45[.0] + * 1.0.55rc01 10 10055 10.so.0.55[.0] + * 1.2.45rc01 13 10245 12.so.0.45[.0] + * 1.0.55 10 10055 10.so.0.55[.0] + * 1.2.45 13 10245 12.so.0.45[.0] + * 1.2.46rc01-02 13 10246 12.so.0.46[.0] + * 1.0.56 10 10056 10.so.0.56[.0] + * 1.2.46 13 10246 12.so.0.46[.0] + * 1.2.47beta01 13 10247 12.so.0.47[.0] + * 1.2.47rc01 13 10247 12.so.0.47[.0] + * 1.0.57rc01 10 10057 10.so.0.57[.0] + * 1.2.47 13 10247 12.so.0.47[.0] + * 1.0.57 10 10057 10.so.0.57[.0] + * 1.2.48beta01 13 10248 12.so.0.48[.0] + * 1.2.48rc01-02 13 10248 12.so.0.48[.0] + * 1.0.58 10 10058 10.so.0.58[.0] + * 1.2.48 13 10248 12.so.0.48[.0] + * 1.2.49rc01 13 10249 12.so.0.49[.0] + * 1.0.59 10 10059 10.so.0.59[.0] + * 1.2.49 13 10249 12.so.0.49[.0] + * 1.0.60 10 10060 10.so.0.60[.0] + * 1.2.50 13 10250 12.so.0.50[.0] + * + * Henceforth the source version will match the shared-library major + * and minor numbers; the shared-library major version number will be + * used for changes in backward compatibility, as it is intended. The + * PNG_LIBPNG_VER macro, which is not used within libpng but is available + * for applications, is an unsigned integer of the form xyyzz corresponding + * to the source version x.y.z (leading zeros in y and z). Beta versions + * were given the previous public release number plus a letter, until + * version 1.0.6j; from then on they were given the upcoming public + * release number plus "betaNN" or "rcNN". + * + * Binary incompatibility exists only when applications make direct access + * to the info_ptr or png_ptr members through png.h, and the compiled + * application is loaded with a different version of the library. + * + * DLLNUM will change each time there are forward or backward changes + * in binary compatibility (e.g., when a new feature is added). + * + * See libpng.txt or libpng.3 for more information. The PNG specification + * is available as a W3C Recommendation and as an ISO Specification, + * defines should NOT be changed. + */ +#define PNG_INFO_gAMA 0x0001 +#define PNG_INFO_sBIT 0x0002 +#define PNG_INFO_cHRM 0x0004 +#define PNG_INFO_PLTE 0x0008 +#define PNG_INFO_tRNS 0x0010 +#define PNG_INFO_bKGD 0x0020 +#define PNG_INFO_hIST 0x0040 +#define PNG_INFO_pHYs 0x0080 +#define PNG_INFO_oFFs 0x0100 +#define PNG_INFO_tIME 0x0200 +#define PNG_INFO_pCAL 0x0400 +#define PNG_INFO_sRGB 0x0800 /* GR-P, 0.96a */ +#define PNG_INFO_iCCP 0x1000 /* ESR, 1.0.6 */ +#define PNG_INFO_sPLT 0x2000 /* ESR, 1.0.6 */ +#define PNG_INFO_sCAL 0x4000 /* ESR, 1.0.6 */ +#define PNG_INFO_IDAT 0x8000L /* ESR, 1.0.6 */ + +/* This is used for the transformation routines, as some of them + * change these values for the row. It also should enable using + * the routines for other purposes. + */ +typedef struct png_row_info_struct +{ + png_uint_32 width; /* width of row */ + png_uint_32 rowbytes; /* number of bytes in row */ + png_byte color_type; /* color type of row */ + png_byte bit_depth; /* bit depth of row */ + png_byte channels; /* number of channels (1, 2, 3, or 4) */ + png_byte pixel_depth; /* bits per pixel (depth * channels) */ +} png_row_info; + +typedef png_row_info FAR * png_row_infop; +typedef png_row_info FAR * FAR * png_row_infopp; + +/* These are the function types for the I/O functions and for the functions + * that allow the user to override the default I/O functions with his or her + * own. The png_error_ptr type should match that of user-supplied warning + * and error functions, while the png_rw_ptr type should match that of the + * user read/write data functions. + */ +typedef struct png_struct_def png_struct; +typedef png_struct FAR * png_structp; + +typedef void (PNGAPI *png_error_ptr) PNGARG((png_structp, png_const_charp)); +typedef void (PNGAPI *png_rw_ptr) PNGARG((png_structp, png_bytep, png_size_t)); +typedef void (PNGAPI *png_flush_ptr) PNGARG((png_structp)); +typedef void (PNGAPI *png_read_status_ptr) PNGARG((png_structp, png_uint_32, + int)); +typedef void (PNGAPI *png_write_status_ptr) PNGARG((png_structp, png_uint_32, + int)); + +#ifdef PNG_PROGRESSIVE_READ_SUPPORTED +typedef void (PNGAPI *png_progressive_info_ptr) PNGARG((png_structp, png_infop)); +typedef void (PNGAPI *png_progressive_end_ptr) PNGARG((png_structp, png_infop)); +typedef void (PNGAPI *png_progressive_row_ptr) PNGARG((png_structp, png_bytep, + png_uint_32, int)); +#endif + +#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) || \ + defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED) || \ + defined(PNG_LEGACY_SUPPORTED) +typedef void (PNGAPI *png_user_transform_ptr) PNGARG((png_structp, + png_row_infop, png_bytep)); +#endif + +#ifdef PNG_USER_CHUNKS_SUPPORTED +typedef int (PNGAPI *png_user_chunk_ptr) PNGARG((png_structp, png_unknown_chunkp)); +#endif +#ifdef PNG_UNKNOWN_CHUNKS_SUPPORTED +typedef void (PNGAPI *png_unknown_chunk_ptr) PNGARG((png_structp)); +#endif + +/* Transform masks for the high-level interface */ +#define PNG_TRANSFORM_IDENTITY 0x0000 /* read and write */ +#define PNG_TRANSFORM_STRIP_16 0x0001 /* read only */ +#define PNG_TRANSFORM_STRIP_ALPHA 0x0002 /* read only */ +#define PNG_TRANSFORM_PACKING 0x0004 /* read and write */ +#define PNG_TRANSFORM_PACKSWAP 0x0008 /* read and write */ +#define PNG_TRANSFORM_EXPAND 0x0010 /* read only */ +#define PNG_TRANSFORM_INVERT_MONO 0x0020 /* read and write */ +#define PNG_TRANSFORM_SHIFT 0x0040 /* read and write */ +#define PNG_TRANSFORM_BGR 0x0080 /* read and write */ +#define PNG_TRANSFORM_SWAP_ALPHA 0x0100 /* read and write */ +#define PNG_TRANSFORM_SWAP_ENDIAN 0x0200 /* read and write */ +#define PNG_TRANSFORM_INVERT_ALPHA 0x0400 /* read and write */ +#define PNG_TRANSFORM_STRIP_FILLER 0x0800 /* write only, deprecated */ +/* Added to libpng-1.2.34 */ +#define PNG_TRANSFORM_STRIP_FILLER_BEFORE 0x0800 /* write only */ +#define PNG_TRANSFORM_STRIP_FILLER_AFTER 0x1000 /* write only */ +/* Added to libpng-1.2.41 */ +#define PNG_TRANSFORM_GRAY_TO_RGB 0x2000 /* read only */ + +/* Flags for MNG supported features */ +#define PNG_FLAG_MNG_EMPTY_PLTE 0x01 +#define PNG_FLAG_MNG_FILTER_64 0x04 +#define PNG_ALL_MNG_FEATURES 0x05 + +typedef png_voidp (*png_malloc_ptr) PNGARG((png_structp, png_size_t)); +typedef void (*png_free_ptr) PNGARG((png_structp, png_voidp)); + +/* The structure that holds the information to read and write PNG files. + * The only people who need to care about what is inside of this are the + * people who will be modifying the library for their own special needs. + * It should NOT be accessed directly by an application, except to store + * the jmp_buf. + */ + +struct png_struct_def +{ +#ifdef PNG_SETJMP_SUPPORTED + jmp_buf jmpbuf; /* used in png_error */ +#endif + png_error_ptr error_fn PNG_DEPSTRUCT; /* function for printing errors and aborting */ + png_error_ptr warning_fn PNG_DEPSTRUCT; /* function for printing warnings */ + png_voidp error_ptr PNG_DEPSTRUCT; /* user supplied struct for error functions */ + png_rw_ptr write_data_fn PNG_DEPSTRUCT; /* function for writing output data */ + png_rw_ptr read_data_fn PNG_DEPSTRUCT; /* function for reading input data */ + png_voidp io_ptr PNG_DEPSTRUCT; /* ptr to application struct for I/O functions */ + +#ifdef PNG_READ_USER_TRANSFORM_SUPPORTED + png_user_transform_ptr read_user_transform_fn PNG_DEPSTRUCT; /* user read transform */ +#endif + +#ifdef PNG_WRITE_USER_TRANSFORM_SUPPORTED + png_user_transform_ptr write_user_transform_fn PNG_DEPSTRUCT; /* user write transform */ +#endif + +/* These were added in libpng-1.0.2 */ +#ifdef PNG_USER_TRANSFORM_PTR_SUPPORTED +#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) || \ + defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED) + png_voidp user_transform_ptr PNG_DEPSTRUCT; /* user supplied struct for user transform */ + png_byte user_transform_depth PNG_DEPSTRUCT; /* bit depth of user transformed pixels */ + png_byte user_transform_channels PNG_DEPSTRUCT; /* channels in user transformed pixels */ +#endif +#endif + + png_uint_32 mode PNG_DEPSTRUCT; /* tells us where we are in the PNG file */ + png_uint_32 flags PNG_DEPSTRUCT; /* flags indicating various things to libpng */ + png_uint_32 transformations PNG_DEPSTRUCT; /* which transformations to perform */ + + z_stream zstream PNG_DEPSTRUCT; /* pointer to decompression structure (below) */ + png_bytep zbuf PNG_DEPSTRUCT; /* buffer for zlib */ + png_size_t zbuf_size PNG_DEPSTRUCT; /* size of zbuf */ + int zlib_level PNG_DEPSTRUCT; /* holds zlib compression level */ + int zlib_method PNG_DEPSTRUCT; /* holds zlib compression method */ + int zlib_window_bits PNG_DEPSTRUCT; /* holds zlib compression window bits */ + int zlib_mem_level PNG_DEPSTRUCT; /* holds zlib compression memory level */ + int zlib_strategy PNG_DEPSTRUCT; /* holds zlib compression strategy */ + + png_uint_32 width PNG_DEPSTRUCT; /* width of image in pixels */ + png_uint_32 height PNG_DEPSTRUCT; /* height of image in pixels */ + png_uint_32 num_rows PNG_DEPSTRUCT; /* number of rows in current pass */ + png_uint_32 usr_width PNG_DEPSTRUCT; /* width of row at start of write */ + png_uint_32 rowbytes PNG_DEPSTRUCT; /* size of row in bytes */ +#if 0 /* Replaced with the following in libpng-1.2.43 */ + png_size_t irowbytes PNG_DEPSTRUCT; +#endif +/* Added in libpng-1.2.43 */ +#ifdef PNG_USER_LIMITS_SUPPORTED + /* Added in libpng-1.4.0: Total number of sPLT, text, and unknown + * chunks that can be stored (0 means unlimited). + */ + png_uint_32 user_chunk_cache_max PNG_DEPSTRUCT; +#endif + png_uint_32 iwidth PNG_DEPSTRUCT; /* width of current interlaced row in pixels */ + png_uint_32 row_number PNG_DEPSTRUCT; /* current row in interlace pass */ + png_bytep prev_row PNG_DEPSTRUCT; /* buffer to save previous (unfiltered) row */ + png_bytep row_buf PNG_DEPSTRUCT; /* buffer to save current (unfiltered) row */ +#ifndef PNG_NO_WRITE_FILTER + png_bytep sub_row PNG_DEPSTRUCT; /* buffer to save "sub" row when filtering */ + png_bytep up_row PNG_DEPSTRUCT; /* buffer to save "up" row when filtering */ + png_bytep avg_row PNG_DEPSTRUCT; /* buffer to save "avg" row when filtering */ + png_bytep paeth_row PNG_DEPSTRUCT; /* buffer to save "Paeth" row when filtering */ +#endif + png_row_info row_info PNG_DEPSTRUCT; /* used for transformation routines */ + + png_uint_32 idat_size PNG_DEPSTRUCT; /* current IDAT size for read */ + png_uint_32 crc PNG_DEPSTRUCT; /* current chunk CRC value */ + png_colorp palette PNG_DEPSTRUCT; /* palette from the input file */ + png_uint_16 num_palette PNG_DEPSTRUCT; /* number of color entries in palette */ + png_uint_16 num_trans PNG_DEPSTRUCT; /* number of transparency values */ + png_byte chunk_name[5] PNG_DEPSTRUCT; /* null-terminated name of current chunk */ + png_byte compression PNG_DEPSTRUCT; /* file compression type (always 0) */ + png_byte filter PNG_DEPSTRUCT; /* file filter type (always 0) */ + png_byte interlaced PNG_DEPSTRUCT; /* PNG_INTERLACE_NONE, PNG_INTERLACE_ADAM7 */ + png_byte pass PNG_DEPSTRUCT; /* current interlace pass (0 - 6) */ + png_byte do_filter PNG_DEPSTRUCT; /* row filter flags (see PNG_FILTER_ below ) */ + png_byte color_type PNG_DEPSTRUCT; /* color type of file */ + png_byte bit_depth PNG_DEPSTRUCT; /* bit depth of file */ + png_byte usr_bit_depth PNG_DEPSTRUCT; /* bit depth of users row */ + png_byte pixel_depth PNG_DEPSTRUCT; /* number of bits per pixel */ + png_byte channels PNG_DEPSTRUCT; /* number of channels in file */ + png_byte usr_channels PNG_DEPSTRUCT; /* channels at start of write */ + png_byte sig_bytes PNG_DEPSTRUCT; /* magic bytes read/written from start of file */ + +#if defined(PNG_READ_FILLER_SUPPORTED) || defined(PNG_WRITE_FILLER_SUPPORTED) +#ifdef PNG_LEGACY_SUPPORTED + png_byte filler PNG_DEPSTRUCT; /* filler byte for pixel expansion */ +#else + png_uint_16 filler PNG_DEPSTRUCT; /* filler bytes for pixel expansion */ +#endif +#endif + +#ifdef PNG_bKGD_SUPPORTED + png_byte background_gamma_type PNG_DEPSTRUCT; +# ifdef PNG_FLOATING_POINT_SUPPORTED + float background_gamma PNG_DEPSTRUCT; +# endif + png_color_16 background PNG_DEPSTRUCT; /* background color in screen gamma space */ +#ifdef PNG_READ_GAMMA_SUPPORTED + png_color_16 background_1 PNG_DEPSTRUCT; /* background normalized to gamma 1.0 */ +#endif +#endif /* PNG_bKGD_SUPPORTED */ + +#ifdef PNG_WRITE_FLUSH_SUPPORTED + png_flush_ptr output_flush_fn PNG_DEPSTRUCT; /* Function for flushing output */ + png_uint_32 flush_dist PNG_DEPSTRUCT; /* how many rows apart to flush, 0 - no flush */ + png_uint_32 flush_rows PNG_DEPSTRUCT; /* number of rows written since last flush */ +#endif + +#if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) + int gamma_shift PNG_DEPSTRUCT; /* number of "insignificant" bits 16-bit gamma */ +#ifdef PNG_FLOATING_POINT_SUPPORTED + float gamma PNG_DEPSTRUCT; /* file gamma value */ + float screen_gamma PNG_DEPSTRUCT; /* screen gamma value (display_exponent) */ +#endif +#endif + +#if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) + png_bytep gamma_table PNG_DEPSTRUCT; /* gamma table for 8-bit depth files */ + png_bytep gamma_from_1 PNG_DEPSTRUCT; /* converts from 1.0 to screen */ + png_bytep gamma_to_1 PNG_DEPSTRUCT; /* converts from file to 1.0 */ + png_uint_16pp gamma_16_table PNG_DEPSTRUCT; /* gamma table for 16-bit depth files */ + png_uint_16pp gamma_16_from_1 PNG_DEPSTRUCT; /* converts from 1.0 to screen */ + png_uint_16pp gamma_16_to_1 PNG_DEPSTRUCT; /* converts from file to 1.0 */ +#endif + +#if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_sBIT_SUPPORTED) + png_color_8 sig_bit PNG_DEPSTRUCT; /* significant bits in each available channel */ +#endif + +#if defined(PNG_READ_SHIFT_SUPPORTED) || defined(PNG_WRITE_SHIFT_SUPPORTED) + png_color_8 shift PNG_DEPSTRUCT; /* shift for significant bit tranformation */ +#endif + +#if defined(PNG_tRNS_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) \ + || defined(PNG_READ_EXPAND_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) + png_bytep trans PNG_DEPSTRUCT; /* transparency values for paletted files */ + png_color_16 trans_values PNG_DEPSTRUCT; /* transparency values for non-paletted files */ +#endif + + png_read_status_ptr read_row_fn PNG_DEPSTRUCT; /* called after each row is decoded */ + png_write_status_ptr write_row_fn PNG_DEPSTRUCT; /* called after each row is encoded */ +#ifdef PNG_PROGRESSIVE_READ_SUPPORTED + png_progressive_info_ptr info_fn PNG_DEPSTRUCT; /* called after header data fully read */ + png_progressive_row_ptr row_fn PNG_DEPSTRUCT; /* called after each prog. row is decoded */ + png_progressive_end_ptr end_fn PNG_DEPSTRUCT; /* called after image is complete */ + png_bytep save_buffer_ptr PNG_DEPSTRUCT; /* current location in save_buffer */ + png_bytep save_buffer PNG_DEPSTRUCT; /* buffer for previously read data */ + png_bytep current_buffer_ptr PNG_DEPSTRUCT; /* current location in current_buffer */ + png_bytep current_buffer PNG_DEPSTRUCT; /* buffer for recently used data */ + png_uint_32 push_length PNG_DEPSTRUCT; /* size of current input chunk */ + png_uint_32 skip_length PNG_DEPSTRUCT; /* bytes to skip in input data */ + png_size_t save_buffer_size PNG_DEPSTRUCT; /* amount of data now in save_buffer */ + png_size_t save_buffer_max PNG_DEPSTRUCT; /* total size of save_buffer */ + png_size_t buffer_size PNG_DEPSTRUCT; /* total amount of available input data */ + png_size_t current_buffer_size PNG_DEPSTRUCT; /* amount of data now in current_buffer */ + int process_mode PNG_DEPSTRUCT; /* what push library is currently doing */ + int cur_palette PNG_DEPSTRUCT; /* current push library palette index */ + +# ifdef PNG_TEXT_SUPPORTED + png_size_t current_text_size PNG_DEPSTRUCT; /* current size of text input data */ + png_size_t current_text_left PNG_DEPSTRUCT; /* how much text left to read in input */ + png_charp current_text PNG_DEPSTRUCT; /* current text chunk buffer */ + png_charp current_text_ptr PNG_DEPSTRUCT; /* current location in current_text */ +# endif /* PNG_TEXT_SUPPORTED */ +#endif /* PNG_PROGRESSIVE_READ_SUPPORTED */ + +#if defined(__TURBOC__) && !defined(_Windows) && !defined(__FLAT__) +/* for the Borland special 64K segment handler */ + png_bytepp offset_table_ptr PNG_DEPSTRUCT; + png_bytep offset_table PNG_DEPSTRUCT; + png_uint_16 offset_table_number PNG_DEPSTRUCT; + png_uint_16 offset_table_count PNG_DEPSTRUCT; + png_uint_16 offset_table_count_free PNG_DEPSTRUCT; +#endif + +#ifdef PNG_READ_DITHER_SUPPORTED + png_bytep palette_lookup PNG_DEPSTRUCT; /* lookup table for dithering */ + png_bytep dither_index PNG_DEPSTRUCT; /* index translation for palette files */ +#endif + +#if defined(PNG_READ_DITHER_SUPPORTED) || defined(PNG_hIST_SUPPORTED) + png_uint_16p hist PNG_DEPSTRUCT; /* histogram */ +#endif + +#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED + png_byte heuristic_method PNG_DEPSTRUCT; /* heuristic for row filter selection */ + png_byte num_prev_filters PNG_DEPSTRUCT; /* number of weights for previous rows */ + png_bytep prev_filters PNG_DEPSTRUCT; /* filter type(s) of previous row(s) */ + png_uint_16p filter_weights PNG_DEPSTRUCT; /* weight(s) for previous line(s) */ + png_uint_16p inv_filter_weights PNG_DEPSTRUCT; /* 1/weight(s) for previous line(s) */ + png_uint_16p filter_costs PNG_DEPSTRUCT; /* relative filter calculation cost */ + png_uint_16p inv_filter_costs PNG_DEPSTRUCT; /* 1/relative filter calculation cost */ +#endif + +#ifdef PNG_TIME_RFC1123_SUPPORTED + png_charp time_buffer PNG_DEPSTRUCT; /* String to hold RFC 1123 time text */ +#endif + +/* New members added in libpng-1.0.6 */ + +#ifdef PNG_FREE_ME_SUPPORTED + png_uint_32 free_me PNG_DEPSTRUCT; /* flags items libpng is responsible for freeing */ +#endif + +#ifdef PNG_USER_CHUNKS_SUPPORTED + png_voidp user_chunk_ptr PNG_DEPSTRUCT; + png_user_chunk_ptr read_user_chunk_fn PNG_DEPSTRUCT; /* user read chunk handler */ +#endif + +#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED + int num_chunk_list PNG_DEPSTRUCT; + png_bytep chunk_list PNG_DEPSTRUCT; +#endif + +/* New members added in libpng-1.0.3 */ +#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED + png_byte rgb_to_gray_status PNG_DEPSTRUCT; + /* These were changed from png_byte in libpng-1.0.6 */ + png_uint_16 rgb_to_gray_red_coeff PNG_DEPSTRUCT; + png_uint_16 rgb_to_gray_green_coeff PNG_DEPSTRUCT; + png_uint_16 rgb_to_gray_blue_coeff PNG_DEPSTRUCT; +#endif + +/* New member added in libpng-1.0.4 (renamed in 1.0.9) */ +#if defined(PNG_MNG_FEATURES_SUPPORTED) || \ + defined(PNG_READ_EMPTY_PLTE_SUPPORTED) || \ + defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) +/* Changed from png_byte to png_uint_32 at version 1.2.0 */ +#ifdef PNG_1_0_X + png_byte mng_features_permitted PNG_DEPSTRUCT; +#else + png_uint_32 mng_features_permitted PNG_DEPSTRUCT; +#endif /* PNG_1_0_X */ +#endif + +/* New member added in libpng-1.0.7 */ +#if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) + png_fixed_point int_gamma PNG_DEPSTRUCT; +#endif + +/* New member added in libpng-1.0.9, ifdef'ed out in 1.0.12, enabled in 1.2.0 */ +#ifdef PNG_MNG_FEATURES_SUPPORTED + png_byte filter_type PNG_DEPSTRUCT; +#endif + +#ifdef PNG_1_0_X +/* New member added in libpng-1.0.10, ifdef'ed out in 1.2.0 */ + png_uint_32 row_buf_size PNG_DEPSTRUCT; +#endif + +/* New members added in libpng-1.2.0 */ +#ifdef PNG_ASSEMBLER_CODE_SUPPORTED +# ifndef PNG_1_0_X +# ifdef PNG_MMX_CODE_SUPPORTED + png_byte mmx_bitdepth_threshold PNG_DEPSTRUCT; + png_uint_32 mmx_rowbytes_threshold PNG_DEPSTRUCT; +# endif + png_uint_32 asm_flags PNG_DEPSTRUCT; +# endif +#endif + +/* New members added in libpng-1.0.2 but first enabled by default in 1.2.0 */ +#ifdef PNG_USER_MEM_SUPPORTED + png_voidp mem_ptr PNG_DEPSTRUCT; /* user supplied struct for mem functions */ + png_malloc_ptr malloc_fn PNG_DEPSTRUCT; /* function for allocating memory */ + png_free_ptr free_fn PNG_DEPSTRUCT; /* function for freeing memory */ +#endif + +/* New member added in libpng-1.0.13 and 1.2.0 */ + png_bytep big_row_buf PNG_DEPSTRUCT; /* buffer to save current (unfiltered) row */ + +#ifdef PNG_READ_DITHER_SUPPORTED +/* The following three members were added at version 1.0.14 and 1.2.4 */ + png_bytep dither_sort PNG_DEPSTRUCT; /* working sort array */ + png_bytep index_to_palette PNG_DEPSTRUCT; /* where the original index currently is */ + /* in the palette */ + png_bytep palette_to_index PNG_DEPSTRUCT; /* which original index points to this */ + /* palette color */ +#endif + +/* New members added in libpng-1.0.16 and 1.2.6 */ + png_byte compression_type PNG_DEPSTRUCT; + +#ifdef PNG_USER_LIMITS_SUPPORTED + png_uint_32 user_width_max PNG_DEPSTRUCT; + png_uint_32 user_height_max PNG_DEPSTRUCT; +#endif + +/* New member added in libpng-1.0.25 and 1.2.17 */ +#ifdef PNG_UNKNOWN_CHUNKS_SUPPORTED + /* Storage for unknown chunk that the library doesn't recognize. */ + png_unknown_chunk unknown_chunk PNG_DEPSTRUCT; +#endif + +/* New members added in libpng-1.2.26 */ + png_uint_32 old_big_row_buf_size PNG_DEPSTRUCT; + png_uint_32 old_prev_row_size PNG_DEPSTRUCT; + +/* New member added in libpng-1.2.30 */ + png_charp chunkdata PNG_DEPSTRUCT; /* buffer for reading chunk data */ + + +}; + + +/* This triggers a compiler error in png.c, if png.c and png.h + * do not agree upon the version number. + */ +typedef png_structp version_1_2_50; + +typedef png_struct FAR * FAR * png_structpp; + +/* Here are the function definitions most commonly used. This is not + * the place to find out how to use libpng. See libpng.txt for the + * full explanation, see example.c for the summary. This just provides + * a simple one line description of the use of each function. + */ + +/* Returns the version number of the library */ +extern PNG_EXPORT(png_uint_32,png_access_version_number) PNGARG((void)); + +/* Tell lib we have already handled the first magic bytes. + * Handling more than 8 bytes from the beginning of the file is an error. + */ +extern PNG_EXPORT(void,png_set_sig_bytes) PNGARG((png_structp png_ptr, + int num_bytes)); + +/* Check sig[start] through sig[start + num_to_check - 1] to see if it's a + * PNG file. Returns zero if the supplied bytes match the 8-byte PNG + * signature, and non-zero otherwise. Having num_to_check == 0 or + * start > 7 will always fail (ie return non-zero). + */ +extern PNG_EXPORT(int,png_sig_cmp) PNGARG((png_bytep sig, png_size_t start, + png_size_t num_to_check)); + +/* Simple signature checking function. This is the same as calling + * png_check_sig(sig, n) := !png_sig_cmp(sig, 0, n). + */ +extern PNG_EXPORT(int,png_check_sig) PNGARG((png_bytep sig, int num)) PNG_DEPRECATED; + +/* Allocate and initialize png_ptr struct for reading, and any other memory. */ +extern PNG_EXPORT(png_structp,png_create_read_struct) + PNGARG((png_const_charp user_png_ver, png_voidp error_ptr, + png_error_ptr error_fn, png_error_ptr warn_fn)) PNG_ALLOCATED; + +/* Allocate and initialize png_ptr struct for writing, and any other memory */ +extern PNG_EXPORT(png_structp,png_create_write_struct) + PNGARG((png_const_charp user_png_ver, png_voidp error_ptr, + png_error_ptr error_fn, png_error_ptr warn_fn)) PNG_ALLOCATED; + +#ifdef PNG_WRITE_SUPPORTED +extern PNG_EXPORT(png_uint_32,png_get_compression_buffer_size) + PNGARG((png_structp png_ptr)); +#endif + +#ifdef PNG_WRITE_SUPPORTED +extern PNG_EXPORT(void,png_set_compression_buffer_size) + PNGARG((png_structp png_ptr, png_uint_32 size)); +#endif + +/* Reset the compression stream */ +extern PNG_EXPORT(int,png_reset_zstream) PNGARG((png_structp png_ptr)); + +/* New functions added in libpng-1.0.2 (not enabled by default until 1.2.0) */ +#ifdef PNG_USER_MEM_SUPPORTED +extern PNG_EXPORT(png_structp,png_create_read_struct_2) + PNGARG((png_const_charp user_png_ver, png_voidp error_ptr, + png_error_ptr error_fn, png_error_ptr warn_fn, png_voidp mem_ptr, + png_malloc_ptr malloc_fn, png_free_ptr free_fn)) PNG_ALLOCATED; +extern PNG_EXPORT(png_structp,png_create_write_struct_2) + PNGARG((png_const_charp user_png_ver, png_voidp error_ptr, + png_error_ptr error_fn, png_error_ptr warn_fn, png_voidp mem_ptr, + png_malloc_ptr malloc_fn, png_free_ptr free_fn)) PNG_ALLOCATED; +#endif + +/* Write a PNG chunk - size, type, (optional) data, CRC. */ +extern PNG_EXPORT(void,png_write_chunk) PNGARG((png_structp png_ptr, + png_bytep chunk_name, png_bytep data, png_size_t length)); + +/* Write the start of a PNG chunk - length and chunk name. */ +extern PNG_EXPORT(void,png_write_chunk_start) PNGARG((png_structp png_ptr, + png_bytep chunk_name, png_uint_32 length)); + +/* Write the data of a PNG chunk started with png_write_chunk_start(). */ +extern PNG_EXPORT(void,png_write_chunk_data) PNGARG((png_structp png_ptr, + png_bytep data, png_size_t length)); + +/* Finish a chunk started with png_write_chunk_start() (includes CRC). */ +extern PNG_EXPORT(void,png_write_chunk_end) PNGARG((png_structp png_ptr)); + +/* Allocate and initialize the info structure */ +extern PNG_EXPORT(png_infop,png_create_info_struct) + PNGARG((png_structp png_ptr)) PNG_ALLOCATED; + +#if defined(PNG_1_0_X) || defined (PNG_1_2_X) +/* Initialize the info structure (old interface - DEPRECATED) */ +extern PNG_EXPORT(void,png_info_init) PNGARG((png_infop info_ptr)) + PNG_DEPRECATED; +#undef png_info_init +#define png_info_init(info_ptr) png_info_init_3(&info_ptr,\ + png_sizeof(png_info)); +#endif + +extern PNG_EXPORT(void,png_info_init_3) PNGARG((png_infopp info_ptr, + png_size_t png_info_struct_size)); + +/* Writes all the PNG information before the image. */ +extern PNG_EXPORT(void,png_write_info_before_PLTE) PNGARG((png_structp png_ptr, + png_infop info_ptr)); +extern PNG_EXPORT(void,png_write_info) PNGARG((png_structp png_ptr, + png_infop info_ptr)); + +#ifdef PNG_SEQUENTIAL_READ_SUPPORTED +/* Read the information before the actual image data. */ +extern PNG_EXPORT(void,png_read_info) PNGARG((png_structp png_ptr, + png_infop info_ptr)); +#endif + +#ifdef PNG_TIME_RFC1123_SUPPORTED +extern PNG_EXPORT(png_charp,png_convert_to_rfc1123) + PNGARG((png_structp png_ptr, png_timep ptime)); +#endif + +#ifdef PNG_CONVERT_tIME_SUPPORTED +/* Convert from a struct tm to png_time */ +extern PNG_EXPORT(void,png_convert_from_struct_tm) PNGARG((png_timep ptime, + struct tm FAR * ttime)); + +/* Convert from time_t to png_time. Uses gmtime() */ +extern PNG_EXPORT(void,png_convert_from_time_t) PNGARG((png_timep ptime, + time_t ttime)); +#endif /* PNG_CONVERT_tIME_SUPPORTED */ + +#ifdef PNG_READ_EXPAND_SUPPORTED +/* Expand data to 24-bit RGB, or 8-bit grayscale, with alpha if available. */ +extern PNG_EXPORT(void,png_set_expand) PNGARG((png_structp png_ptr)); +#ifndef PNG_1_0_X +extern PNG_EXPORT(void,png_set_expand_gray_1_2_4_to_8) PNGARG((png_structp + png_ptr)); +#endif +extern PNG_EXPORT(void,png_set_palette_to_rgb) PNGARG((png_structp png_ptr)); +extern PNG_EXPORT(void,png_set_tRNS_to_alpha) PNGARG((png_structp png_ptr)); +#if defined(PNG_1_0_X) || defined (PNG_1_2_X) +/* Deprecated */ +extern PNG_EXPORT(void,png_set_gray_1_2_4_to_8) PNGARG((png_structp + png_ptr)) PNG_DEPRECATED; +#endif +#endif + +#if defined(PNG_READ_BGR_SUPPORTED) || defined(PNG_WRITE_BGR_SUPPORTED) +/* Use blue, green, red order for pixels. */ +extern PNG_EXPORT(void,png_set_bgr) PNGARG((png_structp png_ptr)); +#endif + +#ifdef PNG_READ_GRAY_TO_RGB_SUPPORTED +/* Expand the grayscale to 24-bit RGB if necessary. */ +extern PNG_EXPORT(void,png_set_gray_to_rgb) PNGARG((png_structp png_ptr)); +#endif + +#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED +/* Reduce RGB to grayscale. */ +#ifdef PNG_FLOATING_POINT_SUPPORTED +extern PNG_EXPORT(void,png_set_rgb_to_gray) PNGARG((png_structp png_ptr, + int error_action, double red, double green )); +#endif +extern PNG_EXPORT(void,png_set_rgb_to_gray_fixed) PNGARG((png_structp png_ptr, + int error_action, png_fixed_point red, png_fixed_point green )); +extern PNG_EXPORT(png_byte,png_get_rgb_to_gray_status) PNGARG((png_structp + png_ptr)); +#endif + +extern PNG_EXPORT(void,png_build_grayscale_palette) PNGARG((int bit_depth, + png_colorp palette)); + +#ifdef PNG_READ_STRIP_ALPHA_SUPPORTED +extern PNG_EXPORT(void,png_set_strip_alpha) PNGARG((png_structp png_ptr)); +#endif + +#if defined(PNG_READ_SWAP_ALPHA_SUPPORTED) || \ + defined(PNG_WRITE_SWAP_ALPHA_SUPPORTED) +extern PNG_EXPORT(void,png_set_swap_alpha) PNGARG((png_structp png_ptr)); +#endif + +#if defined(PNG_READ_INVERT_ALPHA_SUPPORTED) || \ + defined(PNG_WRITE_INVERT_ALPHA_SUPPORTED) +extern PNG_EXPORT(void,png_set_invert_alpha) PNGARG((png_structp png_ptr)); +#endif + +#if defined(PNG_READ_FILLER_SUPPORTED) || defined(PNG_WRITE_FILLER_SUPPORTED) +/* Add a filler byte to 8-bit Gray or 24-bit RGB images. */ +extern PNG_EXPORT(void,png_set_filler) PNGARG((png_structp png_ptr, + png_uint_32 filler, int flags)); +/* The values of the PNG_FILLER_ defines should NOT be changed */ +#define PNG_FILLER_BEFORE 0 +#define PNG_FILLER_AFTER 1 +/* Add an alpha byte to 8-bit Gray or 24-bit RGB images. */ +#ifndef PNG_1_0_X +extern PNG_EXPORT(void,png_set_add_alpha) PNGARG((png_structp png_ptr, + png_uint_32 filler, int flags)); +#endif +#endif /* PNG_READ_FILLER_SUPPORTED || PNG_WRITE_FILLER_SUPPORTED */ + +#if defined(PNG_READ_SWAP_SUPPORTED) || defined(PNG_WRITE_SWAP_SUPPORTED) +/* Swap bytes in 16-bit depth files. */ +extern PNG_EXPORT(void,png_set_swap) PNGARG((png_structp png_ptr)); +#endif + +#if defined(PNG_READ_PACK_SUPPORTED) || defined(PNG_WRITE_PACK_SUPPORTED) +/* Use 1 byte per pixel in 1, 2, or 4-bit depth files. */ +extern PNG_EXPORT(void,png_set_packing) PNGARG((png_structp png_ptr)); +#endif + +#if defined(PNG_READ_PACKSWAP_SUPPORTED) || defined(PNG_WRITE_PACKSWAP_SUPPORTED) +/* Swap packing order of pixels in bytes. */ +extern PNG_EXPORT(void,png_set_packswap) PNGARG((png_structp png_ptr)); +#endif + +#if defined(PNG_READ_SHIFT_SUPPORTED) || defined(PNG_WRITE_SHIFT_SUPPORTED) +/* Converts files to legal bit depths. */ +extern PNG_EXPORT(void,png_set_shift) PNGARG((png_structp png_ptr, + png_color_8p true_bits)); +#endif + +#if defined(PNG_READ_INTERLACING_SUPPORTED) || \ + defined(PNG_WRITE_INTERLACING_SUPPORTED) +/* Have the code handle the interlacing. Returns the number of passes. */ +extern PNG_EXPORT(int,png_set_interlace_handling) PNGARG((png_structp png_ptr)); +#endif + +#if defined(PNG_READ_INVERT_SUPPORTED) || defined(PNG_WRITE_INVERT_SUPPORTED) +/* Invert monochrome files */ +extern PNG_EXPORT(void,png_set_invert_mono) PNGARG((png_structp png_ptr)); +#endif + +#ifdef PNG_READ_BACKGROUND_SUPPORTED +/* Handle alpha and tRNS by replacing with a background color. */ +#ifdef PNG_FLOATING_POINT_SUPPORTED +extern PNG_EXPORT(void,png_set_background) PNGARG((png_structp png_ptr, + png_color_16p background_color, int background_gamma_code, + int need_expand, double background_gamma)); +#endif +#define PNG_BACKGROUND_GAMMA_UNKNOWN 0 +#define PNG_BACKGROUND_GAMMA_SCREEN 1 +#define PNG_BACKGROUND_GAMMA_FILE 2 +#define PNG_BACKGROUND_GAMMA_UNIQUE 3 +#endif + +#ifdef PNG_READ_16_TO_8_SUPPORTED +/* Strip the second byte of information from a 16-bit depth file. */ +extern PNG_EXPORT(void,png_set_strip_16) PNGARG((png_structp png_ptr)); +#endif + +#ifdef PNG_READ_DITHER_SUPPORTED +/* Turn on dithering, and reduce the palette to the number of colors available. */ +extern PNG_EXPORT(void,png_set_dither) PNGARG((png_structp png_ptr, + png_colorp palette, int num_palette, int maximum_colors, + png_uint_16p histogram, int full_dither)); +#endif + +#ifdef PNG_READ_GAMMA_SUPPORTED +/* Handle gamma correction. Screen_gamma=(display_exponent) */ +#ifdef PNG_FLOATING_POINT_SUPPORTED +extern PNG_EXPORT(void,png_set_gamma) PNGARG((png_structp png_ptr, + double screen_gamma, double default_file_gamma)); +#endif +#endif + +#if defined(PNG_1_0_X) || defined (PNG_1_2_X) +#if defined(PNG_READ_EMPTY_PLTE_SUPPORTED) || \ + defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) +/* Permit or disallow empty PLTE (0: not permitted, 1: permitted) */ +/* Deprecated and will be removed. Use png_permit_mng_features() instead. */ +extern PNG_EXPORT(void,png_permit_empty_plte) PNGARG((png_structp png_ptr, + int empty_plte_permitted)) PNG_DEPRECATED; +#endif +#endif + +#ifdef PNG_WRITE_FLUSH_SUPPORTED +/* Set how many lines between output flushes - 0 for no flushing */ +extern PNG_EXPORT(void,png_set_flush) PNGARG((png_structp png_ptr, int nrows)); +/* Flush the current PNG output buffer */ +extern PNG_EXPORT(void,png_write_flush) PNGARG((png_structp png_ptr)); +#endif + +/* Optional update palette with requested transformations */ +extern PNG_EXPORT(void,png_start_read_image) PNGARG((png_structp png_ptr)); + +/* Optional call to update the users info structure */ +extern PNG_EXPORT(void,png_read_update_info) PNGARG((png_structp png_ptr, + png_infop info_ptr)); + +#ifndef PNG_NO_SEQUENTIAL_READ_SUPPORTED +/* Read one or more rows of image data. */ +extern PNG_EXPORT(void,png_read_rows) PNGARG((png_structp png_ptr, + png_bytepp row, png_bytepp display_row, png_uint_32 num_rows)); +#endif + +#ifndef PNG_NO_SEQUENTIAL_READ_SUPPORTED +/* Read a row of data. */ +extern PNG_EXPORT(void,png_read_row) PNGARG((png_structp png_ptr, + png_bytep row, + png_bytep display_row)); +#endif + +#ifndef PNG_NO_SEQUENTIAL_READ_SUPPORTED +/* Read the whole image into memory at once. */ +extern PNG_EXPORT(void,png_read_image) PNGARG((png_structp png_ptr, + png_bytepp image)); +#endif + +/* Write a row of image data */ +extern PNG_EXPORT(void,png_write_row) PNGARG((png_structp png_ptr, + png_bytep row)); + +/* Write a few rows of image data */ +extern PNG_EXPORT(void,png_write_rows) PNGARG((png_structp png_ptr, + png_bytepp row, png_uint_32 num_rows)); + +/* Write the image data */ +extern PNG_EXPORT(void,png_write_image) PNGARG((png_structp png_ptr, + png_bytepp image)); + +/* Writes the end of the PNG file. */ +extern PNG_EXPORT(void,png_write_end) PNGARG((png_structp png_ptr, + png_infop info_ptr)); + +#ifndef PNG_NO_SEQUENTIAL_READ_SUPPORTED +/* Read the end of the PNG file. */ +extern PNG_EXPORT(void,png_read_end) PNGARG((png_structp png_ptr, + png_infop info_ptr)); +#endif + +/* Free any memory associated with the png_info_struct */ +extern PNG_EXPORT(void,png_destroy_info_struct) PNGARG((png_structp png_ptr, + png_infopp info_ptr_ptr)); + +/* Free any memory associated with the png_struct and the png_info_structs */ +extern PNG_EXPORT(void,png_destroy_read_struct) PNGARG((png_structpp + png_ptr_ptr, png_infopp info_ptr_ptr, png_infopp end_info_ptr_ptr)); + +/* Free all memory used by the read (old method - NOT DLL EXPORTED) */ +extern void png_read_destroy PNGARG((png_structp png_ptr, png_infop info_ptr, + png_infop end_info_ptr)) PNG_DEPRECATED; + +/* Free any memory associated with the png_struct and the png_info_structs */ +extern PNG_EXPORT(void,png_destroy_write_struct) + PNGARG((png_structpp png_ptr_ptr, png_infopp info_ptr_ptr)); + +/* Free any memory used in png_ptr struct (old method - NOT DLL EXPORTED) */ +extern void png_write_destroy PNGARG((png_structp png_ptr)) PNG_DEPRECATED; + +/* Set the libpng method of handling chunk CRC errors */ +extern PNG_EXPORT(void,png_set_crc_action) PNGARG((png_structp png_ptr, + int crit_action, int ancil_action)); + +/* Values for png_set_crc_action() to say how to handle CRC errors in + * ancillary and critical chunks, and whether to use the data contained + * therein. Note that it is impossible to "discard" data in a critical + * chunk. For versions prior to 0.90, the action was always error/quit, + * whereas in version 0.90 and later, the action for CRC errors in ancillary + * chunks is warn/discard. These values should NOT be changed. + * + * value action:critical action:ancillary + */ +#define PNG_CRC_DEFAULT 0 /* error/quit warn/discard data */ +#define PNG_CRC_ERROR_QUIT 1 /* error/quit error/quit */ +#define PNG_CRC_WARN_DISCARD 2 /* (INVALID) warn/discard data */ +#define PNG_CRC_WARN_USE 3 /* warn/use data warn/use data */ +#define PNG_CRC_QUIET_USE 4 /* quiet/use data quiet/use data */ +#define PNG_CRC_NO_CHANGE 5 /* use current value use current value */ + +/* These functions give the user control over the scan-line filtering in + * libpng and the compression methods used by zlib. These functions are + * mainly useful for testing, as the defaults should work with most users. + * Those users who are tight on memory or want faster performance at the + * expense of compression can modify them. See the compression library + * header file (zlib.h) for an explination of the compression functions. + */ + +/* Set the filtering method(s) used by libpng. Currently, the only valid + * value for "method" is 0. + */ +extern PNG_EXPORT(void,png_set_filter) PNGARG((png_structp png_ptr, int method, + int filters)); + +/* Flags for png_set_filter() to say which filters to use. The flags + * are chosen so that they don't conflict with real filter types + * below, in case they are supplied instead of the #defined constants. + * These values should NOT be changed. + */ +#define PNG_NO_FILTERS 0x00 +#define PNG_FILTER_NONE 0x08 +#define PNG_FILTER_SUB 0x10 +#define PNG_FILTER_UP 0x20 +#define PNG_FILTER_AVG 0x40 +#define PNG_FILTER_PAETH 0x80 +#define PNG_ALL_FILTERS (PNG_FILTER_NONE | PNG_FILTER_SUB | PNG_FILTER_UP | \ + PNG_FILTER_AVG | PNG_FILTER_PAETH) + +/* Filter values (not flags) - used in pngwrite.c, pngwutil.c for now. + * These defines should NOT be changed. + */ +#define PNG_FILTER_VALUE_NONE 0 +#define PNG_FILTER_VALUE_SUB 1 +#define PNG_FILTER_VALUE_UP 2 +#define PNG_FILTER_VALUE_AVG 3 +#define PNG_FILTER_VALUE_PAETH 4 +#define PNG_FILTER_VALUE_LAST 5 + +#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED) /* EXPERIMENTAL */ +/* The "heuristic_method" is given by one of the PNG_FILTER_HEURISTIC_ + * defines, either the default (minimum-sum-of-absolute-differences), or + * the experimental method (weighted-minimum-sum-of-absolute-differences). + * + * Weights are factors >= 1.0, indicating how important it is to keep the + * filter type consistent between rows. Larger numbers mean the current + * filter is that many times as likely to be the same as the "num_weights" + * previous filters. This is cumulative for each previous row with a weight. + * There needs to be "num_weights" values in "filter_weights", or it can be + * NULL if the weights aren't being specified. Weights have no influence on + * the selection of the first row filter. Well chosen weights can (in theory) + * improve the compression for a given image. + * + * Costs are factors >= 1.0 indicating the relative decoding costs of a + * filter type. Higher costs indicate more decoding expense, and are + * therefore less likely to be selected over a filter with lower computational + * costs. There needs to be a value in "filter_costs" for each valid filter + * type (given by PNG_FILTER_VALUE_LAST), or it can be NULL if you aren't + * setting the costs. Costs try to improve the speed of decompression without + * unduly increasing the compressed image size. + * + * A negative weight or cost indicates the default value is to be used, and + * values in the range [0.0, 1.0) indicate the value is to remain unchanged. + * The default values for both weights and costs are currently 1.0, but may + * change if good general weighting/cost heuristics can be found. If both + * the weights and costs are set to 1.0, this degenerates the WEIGHTED method + * to the UNWEIGHTED method, but with added encoding time/computation. + */ +#ifdef PNG_FLOATING_POINT_SUPPORTED +extern PNG_EXPORT(void,png_set_filter_heuristics) PNGARG((png_structp png_ptr, + int heuristic_method, int num_weights, png_doublep filter_weights, + png_doublep filter_costs)); +#endif +#endif /* PNG_WRITE_WEIGHTED_FILTER_SUPPORTED */ + +/* Heuristic used for row filter selection. These defines should NOT be + * changed. + */ +#define PNG_FILTER_HEURISTIC_DEFAULT 0 /* Currently "UNWEIGHTED" */ +#define PNG_FILTER_HEURISTIC_UNWEIGHTED 1 /* Used by libpng < 0.95 */ +#define PNG_FILTER_HEURISTIC_WEIGHTED 2 /* Experimental feature */ +#define PNG_FILTER_HEURISTIC_LAST 3 /* Not a valid value */ + +/* Set the library compression level. Currently, valid values range from + * 0 - 9, corresponding directly to the zlib compression levels 0 - 9 + * (0 - no compression, 9 - "maximal" compression). Note that tests have + * shown that zlib compression levels 3-6 usually perform as well as level 9 + * for PNG images, and do considerably fewer caclulations. In the future, + * these values may not correspond directly to the zlib compression levels. + */ +extern PNG_EXPORT(void,png_set_compression_level) PNGARG((png_structp png_ptr, + int level)); + +extern PNG_EXPORT(void,png_set_compression_mem_level) + PNGARG((png_structp png_ptr, int mem_level)); + +extern PNG_EXPORT(void,png_set_compression_strategy) + PNGARG((png_structp png_ptr, int strategy)); + +extern PNG_EXPORT(void,png_set_compression_window_bits) + PNGARG((png_structp png_ptr, int window_bits)); + +extern PNG_EXPORT(void,png_set_compression_method) PNGARG((png_structp png_ptr, + int method)); + +/* These next functions are called for input/output, memory, and error + * handling. They are in the file pngrio.c, pngwio.c, and pngerror.c, + * and call standard C I/O routines such as fread(), fwrite(), and + * fprintf(). These functions can be made to use other I/O routines + * at run time for those applications that need to handle I/O in a + * different manner by calling png_set_???_fn(). See libpng.txt for + * more information. + */ + +#ifdef PNG_STDIO_SUPPORTED +/* Initialize the input/output for the PNG file to the default functions. */ +extern PNG_EXPORT(void,png_init_io) PNGARG((png_structp png_ptr, png_FILE_p fp)); +#endif + +/* Replace the (error and abort), and warning functions with user + * supplied functions. If no messages are to be printed you must still + * write and use replacement functions. The replacement error_fn should + * still do a longjmp to the last setjmp location if you are using this + * method of error handling. If error_fn or warning_fn is NULL, the + * default function will be used. + */ + +extern PNG_EXPORT(void,png_set_error_fn) PNGARG((png_structp png_ptr, + png_voidp error_ptr, png_error_ptr error_fn, png_error_ptr warning_fn)); + +/* Return the user pointer associated with the error functions */ +extern PNG_EXPORT(png_voidp,png_get_error_ptr) PNGARG((png_structp png_ptr)); + +/* Replace the default data output functions with a user supplied one(s). + * If buffered output is not used, then output_flush_fn can be set to NULL. + * If PNG_WRITE_FLUSH_SUPPORTED is not defined at libpng compile time + * output_flush_fn will be ignored (and thus can be NULL). + * It is probably a mistake to use NULL for output_flush_fn if + * write_data_fn is not also NULL unless you have built libpng with + * PNG_WRITE_FLUSH_SUPPORTED undefined, because in this case libpng's + * default flush function, which uses the standard *FILE structure, will + * be used. + */ +extern PNG_EXPORT(void,png_set_write_fn) PNGARG((png_structp png_ptr, + png_voidp io_ptr, png_rw_ptr write_data_fn, png_flush_ptr output_flush_fn)); + +/* Replace the default data input function with a user supplied one. */ +extern PNG_EXPORT(void,png_set_read_fn) PNGARG((png_structp png_ptr, + png_voidp io_ptr, png_rw_ptr read_data_fn)); + +/* Return the user pointer associated with the I/O functions */ +extern PNG_EXPORT(png_voidp,png_get_io_ptr) PNGARG((png_structp png_ptr)); + +extern PNG_EXPORT(void,png_set_read_status_fn) PNGARG((png_structp png_ptr, + png_read_status_ptr read_row_fn)); + +extern PNG_EXPORT(void,png_set_write_status_fn) PNGARG((png_structp png_ptr, + png_write_status_ptr write_row_fn)); + +#ifdef PNG_USER_MEM_SUPPORTED +/* Replace the default memory allocation functions with user supplied one(s). */ +extern PNG_EXPORT(void,png_set_mem_fn) PNGARG((png_structp png_ptr, + png_voidp mem_ptr, png_malloc_ptr malloc_fn, png_free_ptr free_fn)); +/* Return the user pointer associated with the memory functions */ +extern PNG_EXPORT(png_voidp,png_get_mem_ptr) PNGARG((png_structp png_ptr)); +#endif + +#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) || \ + defined(PNG_LEGACY_SUPPORTED) +extern PNG_EXPORT(void,png_set_read_user_transform_fn) PNGARG((png_structp + png_ptr, png_user_transform_ptr read_user_transform_fn)); +#endif + +#if defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED) || \ + defined(PNG_LEGACY_SUPPORTED) +extern PNG_EXPORT(void,png_set_write_user_transform_fn) PNGARG((png_structp + png_ptr, png_user_transform_ptr write_user_transform_fn)); +#endif + +#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) || \ + defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED) || \ + defined(PNG_LEGACY_SUPPORTED) +extern PNG_EXPORT(void,png_set_user_transform_info) PNGARG((png_structp + png_ptr, png_voidp user_transform_ptr, int user_transform_depth, + int user_transform_channels)); +/* Return the user pointer associated with the user transform functions */ +extern PNG_EXPORT(png_voidp,png_get_user_transform_ptr) + PNGARG((png_structp png_ptr)); +#endif + +#ifdef PNG_USER_CHUNKS_SUPPORTED +extern PNG_EXPORT(void,png_set_read_user_chunk_fn) PNGARG((png_structp png_ptr, + png_voidp user_chunk_ptr, png_user_chunk_ptr read_user_chunk_fn)); +extern PNG_EXPORT(png_voidp,png_get_user_chunk_ptr) PNGARG((png_structp + png_ptr)); +#endif + +#ifdef PNG_PROGRESSIVE_READ_SUPPORTED +/* Sets the function callbacks for the push reader, and a pointer to a + * user-defined structure available to the callback functions. + */ +extern PNG_EXPORT(void,png_set_progressive_read_fn) PNGARG((png_structp png_ptr, + png_voidp progressive_ptr, + png_progressive_info_ptr info_fn, png_progressive_row_ptr row_fn, + png_progressive_end_ptr end_fn)); + +/* Returns the user pointer associated with the push read functions */ +extern PNG_EXPORT(png_voidp,png_get_progressive_ptr) + PNGARG((png_structp png_ptr)); + +/* Function to be called when data becomes available */ +extern PNG_EXPORT(void,png_process_data) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_bytep buffer, png_size_t buffer_size)); + +/* Function that combines rows. Not very much different than the + * png_combine_row() call. Is this even used????? + */ +extern PNG_EXPORT(void,png_progressive_combine_row) PNGARG((png_structp png_ptr, + png_bytep old_row, png_bytep new_row)); +#endif /* PNG_PROGRESSIVE_READ_SUPPORTED */ + +extern PNG_EXPORT(png_voidp,png_malloc) PNGARG((png_structp png_ptr, + png_uint_32 size)) PNG_ALLOCATED; + +#ifdef PNG_1_0_X +# define png_malloc_warn png_malloc +#else +/* Added at libpng version 1.2.4 */ +extern PNG_EXPORT(png_voidp,png_malloc_warn) PNGARG((png_structp png_ptr, + png_uint_32 size)) PNG_ALLOCATED; +#endif + +/* Frees a pointer allocated by png_malloc() */ +extern PNG_EXPORT(void,png_free) PNGARG((png_structp png_ptr, png_voidp ptr)); + +#ifdef PNG_1_0_X +/* Function to allocate memory for zlib. */ +extern PNG_EXPORT(voidpf,png_zalloc) PNGARG((voidpf png_ptr, uInt items, + uInt size)); + +/* Function to free memory for zlib */ +extern PNG_EXPORT(void,png_zfree) PNGARG((voidpf png_ptr, voidpf ptr)); +#endif + +/* Free data that was allocated internally */ +extern PNG_EXPORT(void,png_free_data) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_uint_32 free_me, int num)); +#ifdef PNG_FREE_ME_SUPPORTED +/* Reassign responsibility for freeing existing data, whether allocated + * by libpng or by the application + */ +extern PNG_EXPORT(void,png_data_freer) PNGARG((png_structp png_ptr, + png_infop info_ptr, int freer, png_uint_32 mask)); +#endif +/* Assignments for png_data_freer */ +#define PNG_DESTROY_WILL_FREE_DATA 1 +#define PNG_SET_WILL_FREE_DATA 1 +#define PNG_USER_WILL_FREE_DATA 2 +/* Flags for png_ptr->free_me and info_ptr->free_me */ +#define PNG_FREE_HIST 0x0008 +#define PNG_FREE_ICCP 0x0010 +#define PNG_FREE_SPLT 0x0020 +#define PNG_FREE_ROWS 0x0040 +#define PNG_FREE_PCAL 0x0080 +#define PNG_FREE_SCAL 0x0100 +#define PNG_FREE_UNKN 0x0200 +#define PNG_FREE_LIST 0x0400 +#define PNG_FREE_PLTE 0x1000 +#define PNG_FREE_TRNS 0x2000 +#define PNG_FREE_TEXT 0x4000 +#define PNG_FREE_ALL 0x7fff +#define PNG_FREE_MUL 0x4220 /* PNG_FREE_SPLT|PNG_FREE_TEXT|PNG_FREE_UNKN */ + +#ifdef PNG_USER_MEM_SUPPORTED +extern PNG_EXPORT(png_voidp,png_malloc_default) PNGARG((png_structp png_ptr, + png_uint_32 size)) PNG_ALLOCATED; +extern PNG_EXPORT(void,png_free_default) PNGARG((png_structp png_ptr, + png_voidp ptr)); +#endif + +extern PNG_EXPORT(png_voidp,png_memcpy_check) PNGARG((png_structp png_ptr, + png_voidp s1, png_voidp s2, png_uint_32 size)) PNG_DEPRECATED; + +extern PNG_EXPORT(png_voidp,png_memset_check) PNGARG((png_structp png_ptr, + png_voidp s1, int value, png_uint_32 size)) PNG_DEPRECATED; + +#if defined(USE_FAR_KEYWORD) /* memory model conversion function */ +extern void *png_far_to_near PNGARG((png_structp png_ptr,png_voidp ptr, + int check)); +#endif /* USE_FAR_KEYWORD */ + +#ifndef PNG_NO_ERROR_TEXT +/* Fatal error in PNG image of libpng - can't continue */ +extern PNG_EXPORT(void,png_error) PNGARG((png_structp png_ptr, + png_const_charp error_message)) PNG_NORETURN; + +/* The same, but the chunk name is prepended to the error string. */ +extern PNG_EXPORT(void,png_chunk_error) PNGARG((png_structp png_ptr, + png_const_charp error_message)) PNG_NORETURN; +#else +/* Fatal error in PNG image of libpng - can't continue */ +extern PNG_EXPORT(void,png_err) PNGARG((png_structp png_ptr)) PNG_NORETURN; +#endif + +#ifndef PNG_NO_WARNINGS +/* Non-fatal error in libpng. Can continue, but may have a problem. */ +extern PNG_EXPORT(void,png_warning) PNGARG((png_structp png_ptr, + png_const_charp warning_message)); + +#ifdef PNG_READ_SUPPORTED +/* Non-fatal error in libpng, chunk name is prepended to message. */ +extern PNG_EXPORT(void,png_chunk_warning) PNGARG((png_structp png_ptr, + png_const_charp warning_message)); +#endif /* PNG_READ_SUPPORTED */ +#endif /* PNG_NO_WARNINGS */ + +/* The png_set_ functions are for storing values in the png_info_struct. + * Similarly, the png_get_ calls are used to read values from the + * png_info_struct, either storing the parameters in the passed variables, or + * setting pointers into the png_info_struct where the data is stored. The + * png_get_ functions return a non-zero value if the data was available + * in info_ptr, or return zero and do not change any of the parameters if the + * data was not available. + * + * These functions should be used instead of directly accessing png_info + * to avoid problems with future changes in the size and internal layout of + * png_info_struct. + */ +/* Returns "flag" if chunk data is valid in info_ptr. */ +extern PNG_EXPORT(png_uint_32,png_get_valid) PNGARG((png_structp png_ptr, +png_infop info_ptr, png_uint_32 flag)); + +/* Returns number of bytes needed to hold a transformed row. */ +extern PNG_EXPORT(png_uint_32,png_get_rowbytes) PNGARG((png_structp png_ptr, +png_infop info_ptr)); + +#ifdef PNG_INFO_IMAGE_SUPPORTED +/* Returns row_pointers, which is an array of pointers to scanlines that was + * returned from png_read_png(). + */ +extern PNG_EXPORT(png_bytepp,png_get_rows) PNGARG((png_structp png_ptr, +png_infop info_ptr)); +/* Set row_pointers, which is an array of pointers to scanlines for use + * by png_write_png(). + */ +extern PNG_EXPORT(void,png_set_rows) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_bytepp row_pointers)); +#endif + +/* Returns number of color channels in image. */ +extern PNG_EXPORT(png_byte,png_get_channels) PNGARG((png_structp png_ptr, +png_infop info_ptr)); + +#ifdef PNG_EASY_ACCESS_SUPPORTED +/* Returns image width in pixels. */ +extern PNG_EXPORT(png_uint_32, png_get_image_width) PNGARG((png_structp +png_ptr, png_infop info_ptr)); + +/* Returns image height in pixels. */ +extern PNG_EXPORT(png_uint_32, png_get_image_height) PNGARG((png_structp +png_ptr, png_infop info_ptr)); + +/* Returns image bit_depth. */ +extern PNG_EXPORT(png_byte, png_get_bit_depth) PNGARG((png_structp +png_ptr, png_infop info_ptr)); + +/* Returns image color_type. */ +extern PNG_EXPORT(png_byte, png_get_color_type) PNGARG((png_structp +png_ptr, png_infop info_ptr)); + +/* Returns image filter_type. */ +extern PNG_EXPORT(png_byte, png_get_filter_type) PNGARG((png_structp +png_ptr, png_infop info_ptr)); + +/* Returns image interlace_type. */ +extern PNG_EXPORT(png_byte, png_get_interlace_type) PNGARG((png_structp +png_ptr, png_infop info_ptr)); + +/* Returns image compression_type. */ +extern PNG_EXPORT(png_byte, png_get_compression_type) PNGARG((png_structp +png_ptr, png_infop info_ptr)); + +/* Returns image resolution in pixels per meter, from pHYs chunk data. */ +extern PNG_EXPORT(png_uint_32, png_get_pixels_per_meter) PNGARG((png_structp +png_ptr, png_infop info_ptr)); +extern PNG_EXPORT(png_uint_32, png_get_x_pixels_per_meter) PNGARG((png_structp +png_ptr, png_infop info_ptr)); +extern PNG_EXPORT(png_uint_32, png_get_y_pixels_per_meter) PNGARG((png_structp +png_ptr, png_infop info_ptr)); + +/* Returns pixel aspect ratio, computed from pHYs chunk data. */ +#ifdef PNG_FLOATING_POINT_SUPPORTED +extern PNG_EXPORT(float, png_get_pixel_aspect_ratio) PNGARG((png_structp +png_ptr, png_infop info_ptr)); +#endif + +/* Returns image x, y offset in pixels or microns, from oFFs chunk data. */ +extern PNG_EXPORT(png_int_32, png_get_x_offset_pixels) PNGARG((png_structp +png_ptr, png_infop info_ptr)); +extern PNG_EXPORT(png_int_32, png_get_y_offset_pixels) PNGARG((png_structp +png_ptr, png_infop info_ptr)); +extern PNG_EXPORT(png_int_32, png_get_x_offset_microns) PNGARG((png_structp +png_ptr, png_infop info_ptr)); +extern PNG_EXPORT(png_int_32, png_get_y_offset_microns) PNGARG((png_structp +png_ptr, png_infop info_ptr)); + +#endif /* PNG_EASY_ACCESS_SUPPORTED */ + +/* Returns pointer to signature string read from PNG header */ +extern PNG_EXPORT(png_bytep,png_get_signature) PNGARG((png_structp png_ptr, +png_infop info_ptr)); + +#ifdef PNG_bKGD_SUPPORTED +extern PNG_EXPORT(png_uint_32,png_get_bKGD) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_color_16p *background)); +#endif + +#ifdef PNG_bKGD_SUPPORTED +extern PNG_EXPORT(void,png_set_bKGD) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_color_16p background)); +#endif + +#ifdef PNG_cHRM_SUPPORTED +#ifdef PNG_FLOATING_POINT_SUPPORTED +extern PNG_EXPORT(png_uint_32,png_get_cHRM) PNGARG((png_structp png_ptr, + png_infop info_ptr, double *white_x, double *white_y, double *red_x, + double *red_y, double *green_x, double *green_y, double *blue_x, + double *blue_y)); +#endif +#ifdef PNG_FIXED_POINT_SUPPORTED +extern PNG_EXPORT(png_uint_32,png_get_cHRM_fixed) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_fixed_point *int_white_x, png_fixed_point + *int_white_y, png_fixed_point *int_red_x, png_fixed_point *int_red_y, + png_fixed_point *int_green_x, png_fixed_point *int_green_y, png_fixed_point + *int_blue_x, png_fixed_point *int_blue_y)); +#endif +#endif + +#ifdef PNG_cHRM_SUPPORTED +#ifdef PNG_FLOATING_POINT_SUPPORTED +extern PNG_EXPORT(void,png_set_cHRM) PNGARG((png_structp png_ptr, + png_infop info_ptr, double white_x, double white_y, double red_x, + double red_y, double green_x, double green_y, double blue_x, double blue_y)); +#endif +#ifdef PNG_FIXED_POINT_SUPPORTED +extern PNG_EXPORT(void,png_set_cHRM_fixed) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_fixed_point int_white_x, png_fixed_point int_white_y, + png_fixed_point int_red_x, png_fixed_point int_red_y, png_fixed_point + int_green_x, png_fixed_point int_green_y, png_fixed_point int_blue_x, + png_fixed_point int_blue_y)); +#endif +#endif + +#ifdef PNG_gAMA_SUPPORTED +#ifdef PNG_FLOATING_POINT_SUPPORTED +extern PNG_EXPORT(png_uint_32,png_get_gAMA) PNGARG((png_structp png_ptr, + png_infop info_ptr, double *file_gamma)); +#endif +extern PNG_EXPORT(png_uint_32,png_get_gAMA_fixed) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_fixed_point *int_file_gamma)); +#endif + +#ifdef PNG_gAMA_SUPPORTED +#ifdef PNG_FLOATING_POINT_SUPPORTED +extern PNG_EXPORT(void,png_set_gAMA) PNGARG((png_structp png_ptr, + png_infop info_ptr, double file_gamma)); +#endif +extern PNG_EXPORT(void,png_set_gAMA_fixed) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_fixed_point int_file_gamma)); +#endif + +#ifdef PNG_hIST_SUPPORTED +extern PNG_EXPORT(png_uint_32,png_get_hIST) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_uint_16p *hist)); +#endif + +#ifdef PNG_hIST_SUPPORTED +extern PNG_EXPORT(void,png_set_hIST) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_uint_16p hist)); +#endif + +extern PNG_EXPORT(png_uint_32,png_get_IHDR) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_uint_32 *width, png_uint_32 *height, + int *bit_depth, int *color_type, int *interlace_method, + int *compression_method, int *filter_method)); + +extern PNG_EXPORT(void,png_set_IHDR) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_uint_32 width, png_uint_32 height, int bit_depth, + int color_type, int interlace_method, int compression_method, + int filter_method)); + +#ifdef PNG_oFFs_SUPPORTED +extern PNG_EXPORT(png_uint_32,png_get_oFFs) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_int_32 *offset_x, png_int_32 *offset_y, + int *unit_type)); +#endif + +#ifdef PNG_oFFs_SUPPORTED +extern PNG_EXPORT(void,png_set_oFFs) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_int_32 offset_x, png_int_32 offset_y, + int unit_type)); +#endif + +#ifdef PNG_pCAL_SUPPORTED +extern PNG_EXPORT(png_uint_32,png_get_pCAL) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_charp *purpose, png_int_32 *X0, png_int_32 *X1, + int *type, int *nparams, png_charp *units, png_charpp *params)); +#endif + +#ifdef PNG_pCAL_SUPPORTED +extern PNG_EXPORT(void,png_set_pCAL) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_charp purpose, png_int_32 X0, png_int_32 X1, + int type, int nparams, png_charp units, png_charpp params)); +#endif + +#ifdef PNG_pHYs_SUPPORTED +extern PNG_EXPORT(png_uint_32,png_get_pHYs) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_uint_32 *res_x, png_uint_32 *res_y, int *unit_type)); +#endif + +#ifdef PNG_pHYs_SUPPORTED +extern PNG_EXPORT(void,png_set_pHYs) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_uint_32 res_x, png_uint_32 res_y, int unit_type)); +#endif + +extern PNG_EXPORT(png_uint_32,png_get_PLTE) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_colorp *palette, int *num_palette)); + +extern PNG_EXPORT(void,png_set_PLTE) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_colorp palette, int num_palette)); + +#ifdef PNG_sBIT_SUPPORTED +extern PNG_EXPORT(png_uint_32,png_get_sBIT) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_color_8p *sig_bit)); +#endif + +#ifdef PNG_sBIT_SUPPORTED +extern PNG_EXPORT(void,png_set_sBIT) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_color_8p sig_bit)); +#endif + +#ifdef PNG_sRGB_SUPPORTED +extern PNG_EXPORT(png_uint_32,png_get_sRGB) PNGARG((png_structp png_ptr, + png_infop info_ptr, int *intent)); +#endif + +#ifdef PNG_sRGB_SUPPORTED +extern PNG_EXPORT(void,png_set_sRGB) PNGARG((png_structp png_ptr, + png_infop info_ptr, int intent)); +extern PNG_EXPORT(void,png_set_sRGB_gAMA_and_cHRM) PNGARG((png_structp png_ptr, + png_infop info_ptr, int intent)); +#endif + +#ifdef PNG_iCCP_SUPPORTED +extern PNG_EXPORT(png_uint_32,png_get_iCCP) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_charpp name, int *compression_type, + png_charpp profile, png_uint_32 *proflen)); + /* Note to maintainer: profile should be png_bytepp */ +#endif + +#ifdef PNG_iCCP_SUPPORTED +extern PNG_EXPORT(void,png_set_iCCP) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_charp name, int compression_type, + png_charp profile, png_uint_32 proflen)); + /* Note to maintainer: profile should be png_bytep */ +#endif + +#ifdef PNG_sPLT_SUPPORTED +extern PNG_EXPORT(png_uint_32,png_get_sPLT) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_sPLT_tpp entries)); +#endif + +#ifdef PNG_sPLT_SUPPORTED +extern PNG_EXPORT(void,png_set_sPLT) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_sPLT_tp entries, int nentries)); +#endif + +#ifdef PNG_TEXT_SUPPORTED +/* png_get_text also returns the number of text chunks in *num_text */ +extern PNG_EXPORT(png_uint_32,png_get_text) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_textp *text_ptr, int *num_text)); +#endif + +/* + * Note while png_set_text() will accept a structure whose text, + * language, and translated keywords are NULL pointers, the structure + * returned by png_get_text will always contain regular + * zero-terminated C strings. They might be empty strings but + * they will never be NULL pointers. + */ + +#ifdef PNG_TEXT_SUPPORTED +extern PNG_EXPORT(void,png_set_text) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_textp text_ptr, int num_text)); +#endif + +#ifdef PNG_tIME_SUPPORTED +extern PNG_EXPORT(png_uint_32,png_get_tIME) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_timep *mod_time)); +#endif + +#ifdef PNG_tIME_SUPPORTED +extern PNG_EXPORT(void,png_set_tIME) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_timep mod_time)); +#endif + +#ifdef PNG_tRNS_SUPPORTED +extern PNG_EXPORT(png_uint_32,png_get_tRNS) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_bytep *trans, int *num_trans, + png_color_16p *trans_values)); +#endif + +#ifdef PNG_tRNS_SUPPORTED +extern PNG_EXPORT(void,png_set_tRNS) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_bytep trans, int num_trans, + png_color_16p trans_values)); +#endif + +#ifdef PNG_tRNS_SUPPORTED +#endif + +#ifdef PNG_sCAL_SUPPORTED +#ifdef PNG_FLOATING_POINT_SUPPORTED +extern PNG_EXPORT(png_uint_32,png_get_sCAL) PNGARG((png_structp png_ptr, + png_infop info_ptr, int *unit, double *width, double *height)); +#else +#ifdef PNG_FIXED_POINT_SUPPORTED +extern PNG_EXPORT(png_uint_32,png_get_sCAL_s) PNGARG((png_structp png_ptr, + png_infop info_ptr, int *unit, png_charpp swidth, png_charpp sheight)); +#endif +#endif +#endif /* PNG_sCAL_SUPPORTED */ + +#ifdef PNG_sCAL_SUPPORTED +#ifdef PNG_FLOATING_POINT_SUPPORTED +extern PNG_EXPORT(void,png_set_sCAL) PNGARG((png_structp png_ptr, + png_infop info_ptr, int unit, double width, double height)); +#else +#ifdef PNG_FIXED_POINT_SUPPORTED +extern PNG_EXPORT(void,png_set_sCAL_s) PNGARG((png_structp png_ptr, + png_infop info_ptr, int unit, png_charp swidth, png_charp sheight)); +#endif +#endif +#endif /* PNG_sCAL_SUPPORTED || PNG_WRITE_sCAL_SUPPORTED */ + +#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED +/* Provide a list of chunks and how they are to be handled, if the built-in + handling or default unknown chunk handling is not desired. Any chunks not + listed will be handled in the default manner. The IHDR and IEND chunks + must not be listed. + keep = 0: follow default behaviour + = 1: do not keep + = 2: keep only if safe-to-copy + = 3: keep even if unsafe-to-copy +*/ +extern PNG_EXPORT(void, png_set_keep_unknown_chunks) PNGARG((png_structp + png_ptr, int keep, png_bytep chunk_list, int num_chunks)); +PNG_EXPORT(int,png_handle_as_unknown) PNGARG((png_structp png_ptr, png_bytep + chunk_name)); +#endif +#ifdef PNG_UNKNOWN_CHUNKS_SUPPORTED +extern PNG_EXPORT(void, png_set_unknown_chunks) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_unknown_chunkp unknowns, int num_unknowns)); +extern PNG_EXPORT(void, png_set_unknown_chunk_location) + PNGARG((png_structp png_ptr, png_infop info_ptr, int chunk, int location)); +extern PNG_EXPORT(png_uint_32,png_get_unknown_chunks) PNGARG((png_structp + png_ptr, png_infop info_ptr, png_unknown_chunkpp entries)); +#endif + +/* Png_free_data() will turn off the "valid" flag for anything it frees. + * If you need to turn it off for a chunk that your application has freed, + * you can use png_set_invalid(png_ptr, info_ptr, PNG_INFO_CHNK); + */ +extern PNG_EXPORT(void, png_set_invalid) PNGARG((png_structp png_ptr, + png_infop info_ptr, int mask)); + +#ifdef PNG_INFO_IMAGE_SUPPORTED +/* The "params" pointer is currently not used and is for future expansion. */ +extern PNG_EXPORT(void, png_read_png) PNGARG((png_structp png_ptr, + png_infop info_ptr, + int transforms, + png_voidp params)); +extern PNG_EXPORT(void, png_write_png) PNGARG((png_structp png_ptr, + png_infop info_ptr, + int transforms, + png_voidp params)); +#endif + +/* Define PNG_DEBUG at compile time for debugging information. Higher + * numbers for PNG_DEBUG mean more debugging information. This has + * only been added since version 0.95 so it is not implemented throughout + * libpng yet, but more support will be added as needed. + */ +#ifdef PNG_DEBUG +#if (PNG_DEBUG > 0) +#if !defined(PNG_DEBUG_FILE) && defined(_MSC_VER) +#include +#if (PNG_DEBUG > 1) +#ifndef _DEBUG +# define _DEBUG +#endif +#ifndef png_debug +#define png_debug(l,m) _RPT0(_CRT_WARN,m PNG_STRING_NEWLINE) +#endif +#ifndef png_debug1 +#define png_debug1(l,m,p1) _RPT1(_CRT_WARN,m PNG_STRING_NEWLINE,p1) +#endif +#ifndef png_debug2 +#define png_debug2(l,m,p1,p2) _RPT2(_CRT_WARN,m PNG_STRING_NEWLINE,p1,p2) +#endif +#endif +#else /* PNG_DEBUG_FILE || !_MSC_VER */ +#ifndef PNG_DEBUG_FILE +#define PNG_DEBUG_FILE stderr +#endif /* PNG_DEBUG_FILE */ + +#if (PNG_DEBUG > 1) +/* Note: ["%s"m PNG_STRING_NEWLINE] probably does not work on non-ISO + * compilers. + */ +# ifdef __STDC__ +# ifndef png_debug +# define png_debug(l,m) \ + { \ + int num_tabs=l; \ + fprintf(PNG_DEBUG_FILE,"%s"m PNG_STRING_NEWLINE,(num_tabs==1 ? "\t" : \ + (num_tabs==2 ? "\t\t":(num_tabs>2 ? "\t\t\t":"")))); \ + } +# endif +# ifndef png_debug1 +# define png_debug1(l,m,p1) \ + { \ + int num_tabs=l; \ + fprintf(PNG_DEBUG_FILE,"%s"m PNG_STRING_NEWLINE,(num_tabs==1 ? "\t" : \ + (num_tabs==2 ? "\t\t":(num_tabs>2 ? "\t\t\t":""))),p1); \ + } +# endif +# ifndef png_debug2 +# define png_debug2(l,m,p1,p2) \ + { \ + int num_tabs=l; \ + fprintf(PNG_DEBUG_FILE,"%s"m PNG_STRING_NEWLINE,(num_tabs==1 ? "\t" : \ + (num_tabs==2 ? "\t\t":(num_tabs>2 ? "\t\t\t":""))),p1,p2); \ + } +# endif +# else /* __STDC __ */ +# ifndef png_debug +# define png_debug(l,m) \ + { \ + int num_tabs=l; \ + char format[256]; \ + snprintf(format,256,"%s%s%s",(num_tabs==1 ? "\t" : \ + (num_tabs==2 ? "\t\t":(num_tabs>2 ? "\t\t\t":""))), \ + m,PNG_STRING_NEWLINE); \ + fprintf(PNG_DEBUG_FILE,format); \ + } +# endif +# ifndef png_debug1 +# define png_debug1(l,m,p1) \ + { \ + int num_tabs=l; \ + char format[256]; \ + snprintf(format,256,"%s%s%s",(num_tabs==1 ? "\t" : \ + (num_tabs==2 ? "\t\t":(num_tabs>2 ? "\t\t\t":""))), \ + m,PNG_STRING_NEWLINE); \ + fprintf(PNG_DEBUG_FILE,format,p1); \ + } +# endif +# ifndef png_debug2 +# define png_debug2(l,m,p1,p2) \ + { \ + int num_tabs=l; \ + char format[256]; \ + snprintf(format,256,"%s%s%s",(num_tabs==1 ? "\t" : \ + (num_tabs==2 ? "\t\t":(num_tabs>2 ? "\t\t\t":""))), \ + m,PNG_STRING_NEWLINE); \ + fprintf(PNG_DEBUG_FILE,format,p1,p2); \ + } +# endif +# endif /* __STDC __ */ +#endif /* (PNG_DEBUG > 1) */ + +#endif /* _MSC_VER */ +#endif /* (PNG_DEBUG > 0) */ +#endif /* PNG_DEBUG */ +#ifndef png_debug +#define png_debug(l, m) +#endif +#ifndef png_debug1 +#define png_debug1(l, m, p1) +#endif +#ifndef png_debug2 +#define png_debug2(l, m, p1, p2) +#endif + +extern PNG_EXPORT(png_charp,png_get_copyright) PNGARG((png_structp png_ptr)); +extern PNG_EXPORT(png_charp,png_get_header_ver) PNGARG((png_structp png_ptr)); +extern PNG_EXPORT(png_charp,png_get_header_version) PNGARG((png_structp png_ptr)); +extern PNG_EXPORT(png_charp,png_get_libpng_ver) PNGARG((png_structp png_ptr)); + +#ifdef PNG_MNG_FEATURES_SUPPORTED +extern PNG_EXPORT(png_uint_32,png_permit_mng_features) PNGARG((png_structp + png_ptr, png_uint_32 mng_features_permitted)); +#endif + +/* For use in png_set_keep_unknown, added to version 1.2.6 */ +#define PNG_HANDLE_CHUNK_AS_DEFAULT 0 +#define PNG_HANDLE_CHUNK_NEVER 1 +#define PNG_HANDLE_CHUNK_IF_SAFE 2 +#define PNG_HANDLE_CHUNK_ALWAYS 3 + +/* Added to version 1.2.0 */ +#ifdef PNG_ASSEMBLER_CODE_SUPPORTED +#ifdef PNG_MMX_CODE_SUPPORTED +#define PNG_ASM_FLAG_MMX_SUPPORT_COMPILED 0x01 /* not user-settable */ +#define PNG_ASM_FLAG_MMX_SUPPORT_IN_CPU 0x02 /* not user-settable */ +#define PNG_ASM_FLAG_MMX_READ_COMBINE_ROW 0x04 +#define PNG_ASM_FLAG_MMX_READ_INTERLACE 0x08 +#define PNG_ASM_FLAG_MMX_READ_FILTER_SUB 0x10 +#define PNG_ASM_FLAG_MMX_READ_FILTER_UP 0x20 +#define PNG_ASM_FLAG_MMX_READ_FILTER_AVG 0x40 +#define PNG_ASM_FLAG_MMX_READ_FILTER_PAETH 0x80 +#define PNG_ASM_FLAGS_INITIALIZED 0x80000000 /* not user-settable */ + +#define PNG_MMX_READ_FLAGS ( PNG_ASM_FLAG_MMX_READ_COMBINE_ROW \ + | PNG_ASM_FLAG_MMX_READ_INTERLACE \ + | PNG_ASM_FLAG_MMX_READ_FILTER_SUB \ + | PNG_ASM_FLAG_MMX_READ_FILTER_UP \ + | PNG_ASM_FLAG_MMX_READ_FILTER_AVG \ + | PNG_ASM_FLAG_MMX_READ_FILTER_PAETH ) +#define PNG_MMX_WRITE_FLAGS ( 0 ) + +#define PNG_MMX_FLAGS ( PNG_ASM_FLAG_MMX_SUPPORT_COMPILED \ + | PNG_ASM_FLAG_MMX_SUPPORT_IN_CPU \ + | PNG_MMX_READ_FLAGS \ + | PNG_MMX_WRITE_FLAGS ) + +#define PNG_SELECT_READ 1 +#define PNG_SELECT_WRITE 2 +#endif /* PNG_MMX_CODE_SUPPORTED */ + +#ifndef PNG_1_0_X +/* pngget.c */ +extern PNG_EXPORT(png_uint_32,png_get_mmx_flagmask) + PNGARG((int flag_select, int *compilerID)); + +/* pngget.c */ +extern PNG_EXPORT(png_uint_32,png_get_asm_flagmask) + PNGARG((int flag_select)); + +/* pngget.c */ +extern PNG_EXPORT(png_uint_32,png_get_asm_flags) + PNGARG((png_structp png_ptr)); + +/* pngget.c */ +extern PNG_EXPORT(png_byte,png_get_mmx_bitdepth_threshold) + PNGARG((png_structp png_ptr)); + +/* pngget.c */ +extern PNG_EXPORT(png_uint_32,png_get_mmx_rowbytes_threshold) + PNGARG((png_structp png_ptr)); + +/* pngset.c */ +extern PNG_EXPORT(void,png_set_asm_flags) + PNGARG((png_structp png_ptr, png_uint_32 asm_flags)); + +/* pngset.c */ +extern PNG_EXPORT(void,png_set_mmx_thresholds) + PNGARG((png_structp png_ptr, png_byte mmx_bitdepth_threshold, + png_uint_32 mmx_rowbytes_threshold)); + +#endif /* PNG_1_0_X */ + +#ifndef PNG_1_0_X +/* png.c, pnggccrd.c, or pngvcrd.c */ +extern PNG_EXPORT(int,png_mmx_support) PNGARG((void)); +#endif /* PNG_1_0_X */ +#endif /* PNG_ASSEMBLER_CODE_SUPPORTED */ + +/* Strip the prepended error numbers ("#nnn ") from error and warning + * messages before passing them to the error or warning handler. + */ +#ifdef PNG_ERROR_NUMBERS_SUPPORTED +extern PNG_EXPORT(void,png_set_strip_error_numbers) PNGARG((png_structp + png_ptr, png_uint_32 strip_mode)); +#endif + +/* Added at libpng-1.2.6 */ +#ifdef PNG_SET_USER_LIMITS_SUPPORTED +extern PNG_EXPORT(void,png_set_user_limits) PNGARG((png_structp + png_ptr, png_uint_32 user_width_max, png_uint_32 user_height_max)); +extern PNG_EXPORT(png_uint_32,png_get_user_width_max) PNGARG((png_structp + png_ptr)); +extern PNG_EXPORT(png_uint_32,png_get_user_height_max) PNGARG((png_structp + png_ptr)); +#endif +/* Maintainer: Put new public prototypes here ^, in libpng.3, and in + * project defs + */ + +#ifdef PNG_READ_COMPOSITE_NODIV_SUPPORTED +/* With these routines we avoid an integer divide, which will be slower on + * most machines. However, it does take more operations than the corresponding + * divide method, so it may be slower on a few RISC systems. There are two + * shifts (by 8 or 16 bits) and an addition, versus a single integer divide. + * + * Note that the rounding factors are NOT supposed to be the same! 128 and + * 32768 are correct for the NODIV code; 127 and 32767 are correct for the + * standard method. + * + * [Optimized code by Greg Roelofs and Mark Adler...blame us for bugs. :-) ] + */ + + /* fg and bg should be in `gamma 1.0' space; alpha is the opacity */ + +# define png_composite(composite, fg, alpha, bg) \ + { png_uint_16 temp = (png_uint_16)((png_uint_16)(fg) * (png_uint_16)(alpha) \ + + (png_uint_16)(bg)*(png_uint_16)(255 - \ + (png_uint_16)(alpha)) + (png_uint_16)128); \ + (composite) = (png_byte)((temp + (temp >> 8)) >> 8); } + +# define png_composite_16(composite, fg, alpha, bg) \ + { png_uint_32 temp = (png_uint_32)((png_uint_32)(fg) * (png_uint_32)(alpha) \ + + (png_uint_32)(bg)*(png_uint_32)(65535L - \ + (png_uint_32)(alpha)) + (png_uint_32)32768L); \ + (composite) = (png_uint_16)((temp + (temp >> 16)) >> 16); } + +#else /* Standard method using integer division */ + +# define png_composite(composite, fg, alpha, bg) \ + (composite) = (png_byte)(((png_uint_16)(fg) * (png_uint_16)(alpha) + \ + (png_uint_16)(bg) * (png_uint_16)(255 - (png_uint_16)(alpha)) + \ + (png_uint_16)127) / 255) + +# define png_composite_16(composite, fg, alpha, bg) \ + (composite) = (png_uint_16)(((png_uint_32)(fg) * (png_uint_32)(alpha) + \ + (png_uint_32)(bg)*(png_uint_32)(65535L - (png_uint_32)(alpha)) + \ + (png_uint_32)32767) / (png_uint_32)65535L) + +#endif /* PNG_READ_COMPOSITE_NODIV_SUPPORTED */ + +/* Inline macros to do direct reads of bytes from the input buffer. These + * require that you are using an architecture that uses PNG byte ordering + * (MSB first) and supports unaligned data storage. I think that PowerPC + * in big-endian mode and 680x0 are the only ones that will support this. + * The x86 line of processors definitely do not. The png_get_int_32() + * routine also assumes we are using two's complement format for negative + * values, which is almost certainly true. + */ +#ifdef PNG_READ_BIG_ENDIAN_SUPPORTED +# define png_get_uint_32(buf) ( *((png_uint_32p) (buf))) +# define png_get_uint_16(buf) ( *((png_uint_16p) (buf))) +# define png_get_int_32(buf) ( *((png_int_32p) (buf))) +#else +extern PNG_EXPORT(png_uint_32,png_get_uint_32) PNGARG((png_bytep buf)); +extern PNG_EXPORT(png_uint_16,png_get_uint_16) PNGARG((png_bytep buf)); +extern PNG_EXPORT(png_int_32,png_get_int_32) PNGARG((png_bytep buf)); +#endif /* !PNG_READ_BIG_ENDIAN_SUPPORTED */ +extern PNG_EXPORT(png_uint_32,png_get_uint_31) + PNGARG((png_structp png_ptr, png_bytep buf)); +/* No png_get_int_16 -- may be added if there's a real need for it. */ + +/* Place a 32-bit number into a buffer in PNG byte order (big-endian). + */ +extern PNG_EXPORT(void,png_save_uint_32) + PNGARG((png_bytep buf, png_uint_32 i)); +extern PNG_EXPORT(void,png_save_int_32) + PNGARG((png_bytep buf, png_int_32 i)); + +/* Place a 16-bit number into a buffer in PNG byte order. + * The parameter is declared unsigned int, not png_uint_16, + * just to avoid potential problems on pre-ANSI C compilers. + */ +extern PNG_EXPORT(void,png_save_uint_16) + PNGARG((png_bytep buf, unsigned int i)); +/* No png_save_int_16 -- may be added if there's a real need for it. */ + +/* ************************************************************************* */ + +/* These next functions are used internally in the code. They generally + * shouldn't be used unless you are writing code to add or replace some + * functionality in libpng. More information about most functions can + * be found in the files where the functions are located. + */ + + +/* Various modes of operation, that are visible to applications because + * they are used for unknown chunk location. + */ +#define PNG_HAVE_IHDR 0x01 +#define PNG_HAVE_PLTE 0x02 +#define PNG_HAVE_IDAT 0x04 +#define PNG_AFTER_IDAT 0x08 /* Have complete zlib datastream */ +#define PNG_HAVE_IEND 0x10 + +#ifdef PNG_INTERNAL + +/* More modes of operation. Note that after an init, mode is set to + * zero automatically when the structure is created. + */ +#define PNG_HAVE_gAMA 0x20 +#define PNG_HAVE_cHRM 0x40 +#define PNG_HAVE_sRGB 0x80 +#define PNG_HAVE_CHUNK_HEADER 0x100 +#define PNG_WROTE_tIME 0x200 +#define PNG_WROTE_INFO_BEFORE_PLTE 0x400 +#define PNG_BACKGROUND_IS_GRAY 0x800 +#define PNG_HAVE_PNG_SIGNATURE 0x1000 +#define PNG_HAVE_CHUNK_AFTER_IDAT 0x2000 /* Have another chunk after IDAT */ + +/* Flags for the transformations the PNG library does on the image data */ +#define PNG_BGR 0x0001 +#define PNG_INTERLACE 0x0002 +#define PNG_PACK 0x0004 +#define PNG_SHIFT 0x0008 +#define PNG_SWAP_BYTES 0x0010 +#define PNG_INVERT_MONO 0x0020 +#define PNG_DITHER 0x0040 +#define PNG_BACKGROUND 0x0080 +#define PNG_BACKGROUND_EXPAND 0x0100 + /* 0x0200 unused */ +#define PNG_16_TO_8 0x0400 +#define PNG_RGBA 0x0800 +#define PNG_EXPAND 0x1000 +#define PNG_GAMMA 0x2000 +#define PNG_GRAY_TO_RGB 0x4000 +#define PNG_FILLER 0x8000L +#define PNG_PACKSWAP 0x10000L +#define PNG_SWAP_ALPHA 0x20000L +#define PNG_STRIP_ALPHA 0x40000L +#define PNG_INVERT_ALPHA 0x80000L +#define PNG_USER_TRANSFORM 0x100000L +#define PNG_RGB_TO_GRAY_ERR 0x200000L +#define PNG_RGB_TO_GRAY_WARN 0x400000L +#define PNG_RGB_TO_GRAY 0x600000L /* two bits, RGB_TO_GRAY_ERR|WARN */ + /* 0x800000L Unused */ +#define PNG_ADD_ALPHA 0x1000000L /* Added to libpng-1.2.7 */ +#define PNG_EXPAND_tRNS 0x2000000L /* Added to libpng-1.2.9 */ +#define PNG_PREMULTIPLY_ALPHA 0x4000000L /* Added to libpng-1.2.41 */ + /* by volker */ + /* 0x8000000L unused */ + /* 0x10000000L unused */ + /* 0x20000000L unused */ + /* 0x40000000L unused */ + +/* Flags for png_create_struct */ +#define PNG_STRUCT_PNG 0x0001 +#define PNG_STRUCT_INFO 0x0002 + +/* Scaling factor for filter heuristic weighting calculations */ +#define PNG_WEIGHT_SHIFT 8 +#define PNG_WEIGHT_FACTOR (1<<(PNG_WEIGHT_SHIFT)) +#define PNG_COST_SHIFT 3 +#define PNG_COST_FACTOR (1<<(PNG_COST_SHIFT)) + +/* Flags for the png_ptr->flags rather than declaring a byte for each one */ +#define PNG_FLAG_ZLIB_CUSTOM_STRATEGY 0x0001 +#define PNG_FLAG_ZLIB_CUSTOM_LEVEL 0x0002 +#define PNG_FLAG_ZLIB_CUSTOM_MEM_LEVEL 0x0004 +#define PNG_FLAG_ZLIB_CUSTOM_WINDOW_BITS 0x0008 +#define PNG_FLAG_ZLIB_CUSTOM_METHOD 0x0010 +#define PNG_FLAG_ZLIB_FINISHED 0x0020 +#define PNG_FLAG_ROW_INIT 0x0040 +#define PNG_FLAG_FILLER_AFTER 0x0080 +#define PNG_FLAG_CRC_ANCILLARY_USE 0x0100 +#define PNG_FLAG_CRC_ANCILLARY_NOWARN 0x0200 +#define PNG_FLAG_CRC_CRITICAL_USE 0x0400 +#define PNG_FLAG_CRC_CRITICAL_IGNORE 0x0800 +#define PNG_FLAG_FREE_PLTE 0x1000 +#define PNG_FLAG_FREE_TRNS 0x2000 +#define PNG_FLAG_FREE_HIST 0x4000 +#define PNG_FLAG_KEEP_UNKNOWN_CHUNKS 0x8000L +#define PNG_FLAG_KEEP_UNSAFE_CHUNKS 0x10000L +#define PNG_FLAG_LIBRARY_MISMATCH 0x20000L +#define PNG_FLAG_STRIP_ERROR_NUMBERS 0x40000L +#define PNG_FLAG_STRIP_ERROR_TEXT 0x80000L +#define PNG_FLAG_MALLOC_NULL_MEM_OK 0x100000L +#define PNG_FLAG_ADD_ALPHA 0x200000L /* Added to libpng-1.2.8 */ +#define PNG_FLAG_STRIP_ALPHA 0x400000L /* Added to libpng-1.2.8 */ + /* 0x800000L unused */ + /* 0x1000000L unused */ + /* 0x2000000L unused */ + /* 0x4000000L unused */ + /* 0x8000000L unused */ + /* 0x10000000L unused */ + /* 0x20000000L unused */ + /* 0x40000000L unused */ + +#define PNG_FLAG_CRC_ANCILLARY_MASK (PNG_FLAG_CRC_ANCILLARY_USE | \ + PNG_FLAG_CRC_ANCILLARY_NOWARN) + +#define PNG_FLAG_CRC_CRITICAL_MASK (PNG_FLAG_CRC_CRITICAL_USE | \ + PNG_FLAG_CRC_CRITICAL_IGNORE) + +#define PNG_FLAG_CRC_MASK (PNG_FLAG_CRC_ANCILLARY_MASK | \ + PNG_FLAG_CRC_CRITICAL_MASK) + +/* Save typing and make code easier to understand */ + +#define PNG_COLOR_DIST(c1, c2) (abs((int)((c1).red) - (int)((c2).red)) + \ + abs((int)((c1).green) - (int)((c2).green)) + \ + abs((int)((c1).blue) - (int)((c2).blue))) + +/* Added to libpng-1.2.6 JB */ +#define PNG_ROWBYTES(pixel_bits, width) \ + ((pixel_bits) >= 8 ? \ + ((width) * (((png_uint_32)(pixel_bits)) >> 3)) : \ + (( ((width) * ((png_uint_32)(pixel_bits))) + 7) >> 3) ) + +/* PNG_OUT_OF_RANGE returns true if value is outside the range + * ideal-delta..ideal+delta. Each argument is evaluated twice. + * "ideal" and "delta" should be constants, normally simple + * integers, "value" a variable. Added to libpng-1.2.6 JB + */ +#define PNG_OUT_OF_RANGE(value, ideal, delta) \ + ( (value) < (ideal)-(delta) || (value) > (ideal)+(delta) ) + +/* Variables declared in png.c - only it needs to define PNG_NO_EXTERN */ +#if !defined(PNG_NO_EXTERN) || defined(PNG_ALWAYS_EXTERN) +/* Place to hold the signature string for a PNG file. */ +#ifdef PNG_USE_GLOBAL_ARRAYS + PNG_EXPORT_VAR (PNG_CONST png_byte FARDATA) png_sig[8]; +#else +#endif +#endif /* PNG_NO_EXTERN */ + +/* Constant strings for known chunk types. If you need to add a chunk, + * define the name here, and add an invocation of the macro in png.c and + * wherever it's needed. + */ +#define PNG_IHDR png_byte png_IHDR[5] = { 73, 72, 68, 82, '\0'} +#define PNG_IDAT png_byte png_IDAT[5] = { 73, 68, 65, 84, '\0'} +#define PNG_IEND png_byte png_IEND[5] = { 73, 69, 78, 68, '\0'} +#define PNG_PLTE png_byte png_PLTE[5] = { 80, 76, 84, 69, '\0'} +#define PNG_bKGD png_byte png_bKGD[5] = { 98, 75, 71, 68, '\0'} +#define PNG_cHRM png_byte png_cHRM[5] = { 99, 72, 82, 77, '\0'} +#define PNG_gAMA png_byte png_gAMA[5] = {103, 65, 77, 65, '\0'} +#define PNG_hIST png_byte png_hIST[5] = {104, 73, 83, 84, '\0'} +#define PNG_iCCP png_byte png_iCCP[5] = {105, 67, 67, 80, '\0'} +#define PNG_iTXt png_byte png_iTXt[5] = {105, 84, 88, 116, '\0'} +#define PNG_oFFs png_byte png_oFFs[5] = {111, 70, 70, 115, '\0'} +#define PNG_pCAL png_byte png_pCAL[5] = {112, 67, 65, 76, '\0'} +#define PNG_sCAL png_byte png_sCAL[5] = {115, 67, 65, 76, '\0'} +#define PNG_pHYs png_byte png_pHYs[5] = {112, 72, 89, 115, '\0'} +#define PNG_sBIT png_byte png_sBIT[5] = {115, 66, 73, 84, '\0'} +#define PNG_sPLT png_byte png_sPLT[5] = {115, 80, 76, 84, '\0'} +#define PNG_sRGB png_byte png_sRGB[5] = {115, 82, 71, 66, '\0'} +#define PNG_tEXt png_byte png_tEXt[5] = {116, 69, 88, 116, '\0'} +#define PNG_tIME png_byte png_tIME[5] = {116, 73, 77, 69, '\0'} +#define PNG_tRNS png_byte png_tRNS[5] = {116, 82, 78, 83, '\0'} +#define PNG_zTXt png_byte png_zTXt[5] = {122, 84, 88, 116, '\0'} + +#ifdef PNG_USE_GLOBAL_ARRAYS +PNG_EXPORT_VAR (png_byte FARDATA) png_IHDR[5]; +PNG_EXPORT_VAR (png_byte FARDATA) png_IDAT[5]; +PNG_EXPORT_VAR (png_byte FARDATA) png_IEND[5]; +PNG_EXPORT_VAR (png_byte FARDATA) png_PLTE[5]; +PNG_EXPORT_VAR (png_byte FARDATA) png_bKGD[5]; +PNG_EXPORT_VAR (png_byte FARDATA) png_cHRM[5]; +PNG_EXPORT_VAR (png_byte FARDATA) png_gAMA[5]; +PNG_EXPORT_VAR (png_byte FARDATA) png_hIST[5]; +PNG_EXPORT_VAR (png_byte FARDATA) png_iCCP[5]; +PNG_EXPORT_VAR (png_byte FARDATA) png_iTXt[5]; +PNG_EXPORT_VAR (png_byte FARDATA) png_oFFs[5]; +PNG_EXPORT_VAR (png_byte FARDATA) png_pCAL[5]; +PNG_EXPORT_VAR (png_byte FARDATA) png_sCAL[5]; +PNG_EXPORT_VAR (png_byte FARDATA) png_pHYs[5]; +PNG_EXPORT_VAR (png_byte FARDATA) png_sBIT[5]; +PNG_EXPORT_VAR (png_byte FARDATA) png_sPLT[5]; +PNG_EXPORT_VAR (png_byte FARDATA) png_sRGB[5]; +PNG_EXPORT_VAR (png_byte FARDATA) png_tEXt[5]; +PNG_EXPORT_VAR (png_byte FARDATA) png_tIME[5]; +PNG_EXPORT_VAR (png_byte FARDATA) png_tRNS[5]; +PNG_EXPORT_VAR (png_byte FARDATA) png_zTXt[5]; +#endif /* PNG_USE_GLOBAL_ARRAYS */ + +#if defined(PNG_1_0_X) || defined (PNG_1_2_X) +/* Initialize png_ptr struct for reading, and allocate any other memory. + * (old interface - DEPRECATED - use png_create_read_struct instead). + */ +extern PNG_EXPORT(void,png_read_init) PNGARG((png_structp png_ptr)) + PNG_DEPRECATED; +#undef png_read_init +#define png_read_init(png_ptr) png_read_init_3(&png_ptr, \ + PNG_LIBPNG_VER_STRING, png_sizeof(png_struct)); +#endif + +extern PNG_EXPORT(void,png_read_init_3) PNGARG((png_structpp ptr_ptr, + png_const_charp user_png_ver, png_size_t png_struct_size)); +#if defined(PNG_1_0_X) || defined (PNG_1_2_X) +extern PNG_EXPORT(void,png_read_init_2) PNGARG((png_structp png_ptr, + png_const_charp user_png_ver, png_size_t png_struct_size, png_size_t + png_info_size)); +#endif + +#if defined(PNG_1_0_X) || defined (PNG_1_2_X) +/* Initialize png_ptr struct for writing, and allocate any other memory. + * (old interface - DEPRECATED - use png_create_write_struct instead). + */ +extern PNG_EXPORT(void,png_write_init) PNGARG((png_structp png_ptr)) + PNG_DEPRECATED; +#undef png_write_init +#define png_write_init(png_ptr) png_write_init_3(&png_ptr, \ + PNG_LIBPNG_VER_STRING, png_sizeof(png_struct)); +#endif + +extern PNG_EXPORT(void,png_write_init_3) PNGARG((png_structpp ptr_ptr, + png_const_charp user_png_ver, png_size_t png_struct_size)); +extern PNG_EXPORT(void,png_write_init_2) PNGARG((png_structp png_ptr, + png_const_charp user_png_ver, png_size_t png_struct_size, png_size_t + png_info_size)); + +/* Allocate memory for an internal libpng struct */ +PNG_EXTERN png_voidp png_create_struct PNGARG((int type)) PNG_PRIVATE; + +/* Free memory from internal libpng struct */ +PNG_EXTERN void png_destroy_struct PNGARG((png_voidp struct_ptr)) PNG_PRIVATE; + +PNG_EXTERN png_voidp png_create_struct_2 PNGARG((int type, png_malloc_ptr + malloc_fn, png_voidp mem_ptr)) PNG_PRIVATE; +PNG_EXTERN void png_destroy_struct_2 PNGARG((png_voidp struct_ptr, + png_free_ptr free_fn, png_voidp mem_ptr)) PNG_PRIVATE; + +/* Free any memory that info_ptr points to and reset struct. */ +PNG_EXTERN void png_info_destroy PNGARG((png_structp png_ptr, + png_infop info_ptr)) PNG_PRIVATE; + +#ifndef PNG_1_0_X +/* Function to allocate memory for zlib. */ +PNG_EXTERN voidpf png_zalloc PNGARG((voidpf png_ptr, uInt items, + uInt size)) PNG_PRIVATE; + +/* Function to free memory for zlib */ +PNG_EXTERN void png_zfree PNGARG((voidpf png_ptr, voidpf ptr)) PNG_PRIVATE; + +#ifdef PNG_SIZE_T +/* Function to convert a sizeof an item to png_sizeof item */ + PNG_EXTERN png_size_t PNGAPI png_convert_size PNGARG((size_t size)) + PNG_PRIVATE; +#endif + +/* Next four functions are used internally as callbacks. PNGAPI is required + * but not PNG_EXPORT. PNGAPI added at libpng version 1.2.3. + */ + +PNG_EXTERN void PNGAPI png_default_read_data PNGARG((png_structp png_ptr, + png_bytep data, png_size_t length)) PNG_PRIVATE; + +#ifdef PNG_PROGRESSIVE_READ_SUPPORTED +PNG_EXTERN void PNGAPI png_push_fill_buffer PNGARG((png_structp png_ptr, + png_bytep buffer, png_size_t length)) PNG_PRIVATE; +#endif + +PNG_EXTERN void PNGAPI png_default_write_data PNGARG((png_structp png_ptr, + png_bytep data, png_size_t length)) PNG_PRIVATE; + +#ifdef PNG_WRITE_FLUSH_SUPPORTED +#ifdef PNG_STDIO_SUPPORTED +PNG_EXTERN void PNGAPI png_default_flush PNGARG((png_structp png_ptr)) + PNG_PRIVATE; +#endif +#endif +#else /* PNG_1_0_X */ +#ifdef PNG_PROGRESSIVE_READ_SUPPORTED +PNG_EXTERN void png_push_fill_buffer PNGARG((png_structp png_ptr, + png_bytep buffer, png_size_t length)) PNG_PRIVATE; +#endif +#endif /* PNG_1_0_X */ + +/* Reset the CRC variable */ +PNG_EXTERN void png_reset_crc PNGARG((png_structp png_ptr)) PNG_PRIVATE; + +/* Write the "data" buffer to whatever output you are using. */ +PNG_EXTERN void png_write_data PNGARG((png_structp png_ptr, png_bytep data, + png_size_t length)) PNG_PRIVATE; + +/* Read data from whatever input you are using into the "data" buffer */ +PNG_EXTERN void png_read_data PNGARG((png_structp png_ptr, png_bytep data, + png_size_t length)) PNG_PRIVATE; + +/* Read bytes into buf, and update png_ptr->crc */ +PNG_EXTERN void png_crc_read PNGARG((png_structp png_ptr, png_bytep buf, + png_size_t length)) PNG_PRIVATE; + +/* Decompress data in a chunk that uses compression */ +#if defined(PNG_zTXt_SUPPORTED) || defined(PNG_iTXt_SUPPORTED) || \ + defined(PNG_iCCP_SUPPORTED) || defined(PNG_sPLT_SUPPORTED) +PNG_EXTERN void png_decompress_chunk PNGARG((png_structp png_ptr, + int comp_type, png_size_t chunklength, + png_size_t prefix_length, png_size_t *data_length)) PNG_PRIVATE; +#endif + +/* Read "skip" bytes, read the file crc, and (optionally) verify png_ptr->crc */ +PNG_EXTERN int png_crc_finish PNGARG((png_structp png_ptr, png_uint_32 skip) + PNG_PRIVATE); + +/* Read the CRC from the file and compare it to the libpng calculated CRC */ +PNG_EXTERN int png_crc_error PNGARG((png_structp png_ptr)) PNG_PRIVATE; + +/* Calculate the CRC over a section of data. Note that we are only + * passing a maximum of 64K on systems that have this as a memory limit, + * since this is the maximum buffer size we can specify. + */ +PNG_EXTERN void png_calculate_crc PNGARG((png_structp png_ptr, png_bytep ptr, + png_size_t length)) PNG_PRIVATE; + +#ifdef PNG_WRITE_FLUSH_SUPPORTED +PNG_EXTERN void png_flush PNGARG((png_structp png_ptr)) PNG_PRIVATE; +#endif + +/* Simple function to write the signature */ +PNG_EXTERN void png_write_sig PNGARG((png_structp png_ptr)) PNG_PRIVATE; + +/* Write various chunks */ + +/* Write the IHDR chunk, and update the png_struct with the necessary + * information. + */ +PNG_EXTERN void png_write_IHDR PNGARG((png_structp png_ptr, png_uint_32 width, + png_uint_32 height, + int bit_depth, int color_type, int compression_method, int filter_method, + int interlace_method)) PNG_PRIVATE; + +PNG_EXTERN void png_write_PLTE PNGARG((png_structp png_ptr, png_colorp palette, + png_uint_32 num_pal)) PNG_PRIVATE; + +PNG_EXTERN void png_write_IDAT PNGARG((png_structp png_ptr, png_bytep data, + png_size_t length)) PNG_PRIVATE; + +PNG_EXTERN void png_write_IEND PNGARG((png_structp png_ptr)) PNG_PRIVATE; + +#ifdef PNG_WRITE_gAMA_SUPPORTED +#ifdef PNG_FLOATING_POINT_SUPPORTED +PNG_EXTERN void png_write_gAMA PNGARG((png_structp png_ptr, double file_gamma)) + PNG_PRIVATE; +#endif +#ifdef PNG_FIXED_POINT_SUPPORTED +PNG_EXTERN void png_write_gAMA_fixed PNGARG((png_structp png_ptr, + png_fixed_point file_gamma)) PNG_PRIVATE; +#endif +#endif + +#ifdef PNG_WRITE_sBIT_SUPPORTED +PNG_EXTERN void png_write_sBIT PNGARG((png_structp png_ptr, png_color_8p sbit, + int color_type)) PNG_PRIVATE; +#endif + +#ifdef PNG_WRITE_cHRM_SUPPORTED +#ifdef PNG_FLOATING_POINT_SUPPORTED +PNG_EXTERN void png_write_cHRM PNGARG((png_structp png_ptr, + double white_x, double white_y, + double red_x, double red_y, double green_x, double green_y, + double blue_x, double blue_y)) PNG_PRIVATE; +#endif +#ifdef PNG_FIXED_POINT_SUPPORTED +PNG_EXTERN void png_write_cHRM_fixed PNGARG((png_structp png_ptr, + png_fixed_point int_white_x, png_fixed_point int_white_y, + png_fixed_point int_red_x, png_fixed_point int_red_y, png_fixed_point + int_green_x, png_fixed_point int_green_y, png_fixed_point int_blue_x, + png_fixed_point int_blue_y)) PNG_PRIVATE; +#endif +#endif + +#ifdef PNG_WRITE_sRGB_SUPPORTED +PNG_EXTERN void png_write_sRGB PNGARG((png_structp png_ptr, + int intent)) PNG_PRIVATE; +#endif + +#ifdef PNG_WRITE_iCCP_SUPPORTED +PNG_EXTERN void png_write_iCCP PNGARG((png_structp png_ptr, + png_charp name, int compression_type, + png_charp profile, int proflen)) PNG_PRIVATE; + /* Note to maintainer: profile should be png_bytep */ +#endif + +#ifdef PNG_WRITE_sPLT_SUPPORTED +PNG_EXTERN void png_write_sPLT PNGARG((png_structp png_ptr, + png_sPLT_tp palette)) PNG_PRIVATE; +#endif + +#ifdef PNG_WRITE_tRNS_SUPPORTED +PNG_EXTERN void png_write_tRNS PNGARG((png_structp png_ptr, png_bytep trans, + png_color_16p values, int number, int color_type)) PNG_PRIVATE; +#endif + +#ifdef PNG_WRITE_bKGD_SUPPORTED +PNG_EXTERN void png_write_bKGD PNGARG((png_structp png_ptr, + png_color_16p values, int color_type)) PNG_PRIVATE; +#endif + +#ifdef PNG_WRITE_hIST_SUPPORTED +PNG_EXTERN void png_write_hIST PNGARG((png_structp png_ptr, png_uint_16p hist, + int num_hist)) PNG_PRIVATE; +#endif + +#if defined(PNG_WRITE_TEXT_SUPPORTED) || defined(PNG_WRITE_pCAL_SUPPORTED) || \ + defined(PNG_WRITE_iCCP_SUPPORTED) || defined(PNG_WRITE_sPLT_SUPPORTED) +PNG_EXTERN png_size_t png_check_keyword PNGARG((png_structp png_ptr, + png_charp key, png_charpp new_key)) PNG_PRIVATE; +#endif + +#ifdef PNG_WRITE_tEXt_SUPPORTED +PNG_EXTERN void png_write_tEXt PNGARG((png_structp png_ptr, png_charp key, + png_charp text, png_size_t text_len)) PNG_PRIVATE; +#endif + +#ifdef PNG_WRITE_zTXt_SUPPORTED +PNG_EXTERN void png_write_zTXt PNGARG((png_structp png_ptr, png_charp key, + png_charp text, png_size_t text_len, int compression)) PNG_PRIVATE; +#endif + +#ifdef PNG_WRITE_iTXt_SUPPORTED +PNG_EXTERN void png_write_iTXt PNGARG((png_structp png_ptr, + int compression, png_charp key, png_charp lang, png_charp lang_key, + png_charp text)) PNG_PRIVATE; +#endif + +#ifdef PNG_TEXT_SUPPORTED /* Added at version 1.0.14 and 1.2.4 */ +PNG_EXTERN int png_set_text_2 PNGARG((png_structp png_ptr, + png_infop info_ptr, png_textp text_ptr, int num_text)) PNG_PRIVATE; +#endif + +#ifdef PNG_WRITE_oFFs_SUPPORTED +PNG_EXTERN void png_write_oFFs PNGARG((png_structp png_ptr, + png_int_32 x_offset, png_int_32 y_offset, int unit_type)) PNG_PRIVATE; +#endif + +#ifdef PNG_WRITE_pCAL_SUPPORTED +PNG_EXTERN void png_write_pCAL PNGARG((png_structp png_ptr, png_charp purpose, + png_int_32 X0, png_int_32 X1, int type, int nparams, + png_charp units, png_charpp params)) PNG_PRIVATE; +#endif + +#ifdef PNG_WRITE_pHYs_SUPPORTED +PNG_EXTERN void png_write_pHYs PNGARG((png_structp png_ptr, + png_uint_32 x_pixels_per_unit, png_uint_32 y_pixels_per_unit, + int unit_type)) PNG_PRIVATE; +#endif + +#ifdef PNG_WRITE_tIME_SUPPORTED +PNG_EXTERN void png_write_tIME PNGARG((png_structp png_ptr, + png_timep mod_time)) PNG_PRIVATE; +#endif + +#ifdef PNG_WRITE_sCAL_SUPPORTED +#if defined(PNG_FLOATING_POINT_SUPPORTED) && !defined(PNG_NO_STDIO) +PNG_EXTERN void png_write_sCAL PNGARG((png_structp png_ptr, + int unit, double width, double height)) PNG_PRIVATE; +#else +#ifdef PNG_FIXED_POINT_SUPPORTED +PNG_EXTERN void png_write_sCAL_s PNGARG((png_structp png_ptr, + int unit, png_charp width, png_charp height)) PNG_PRIVATE; +#endif +#endif +#endif + +/* Called when finished processing a row of data */ +PNG_EXTERN void png_write_finish_row PNGARG((png_structp png_ptr)) PNG_PRIVATE; + +/* Internal use only. Called before first row of data */ +PNG_EXTERN void png_write_start_row PNGARG((png_structp png_ptr)) PNG_PRIVATE; + +#ifdef PNG_READ_GAMMA_SUPPORTED +PNG_EXTERN void png_build_gamma_table PNGARG((png_structp png_ptr)) PNG_PRIVATE; +#endif + +/* Combine a row of data, dealing with alpha, etc. if requested */ +PNG_EXTERN void png_combine_row PNGARG((png_structp png_ptr, png_bytep row, + int mask)) PNG_PRIVATE; + +#ifdef PNG_READ_INTERLACING_SUPPORTED +/* Expand an interlaced row */ +/* OLD pre-1.0.9 interface: +PNG_EXTERN void png_do_read_interlace PNGARG((png_row_infop row_info, + png_bytep row, int pass, png_uint_32 transformations)) PNG_PRIVATE; + */ +PNG_EXTERN void png_do_read_interlace PNGARG((png_structp png_ptr)) PNG_PRIVATE; +#endif + +/* GRR TO DO (2.0 or whenever): simplify other internal calling interfaces */ + +#ifdef PNG_WRITE_INTERLACING_SUPPORTED +/* Grab pixels out of a row for an interlaced pass */ +PNG_EXTERN void png_do_write_interlace PNGARG((png_row_infop row_info, + png_bytep row, int pass)) PNG_PRIVATE; +#endif + +/* Unfilter a row */ +PNG_EXTERN void png_read_filter_row PNGARG((png_structp png_ptr, + png_row_infop row_info, png_bytep row, png_bytep prev_row, + int filter)) PNG_PRIVATE; + +/* Choose the best filter to use and filter the row data */ +PNG_EXTERN void png_write_find_filter PNGARG((png_structp png_ptr, + png_row_infop row_info)) PNG_PRIVATE; + +/* Write out the filtered row. */ +PNG_EXTERN void png_write_filtered_row PNGARG((png_structp png_ptr, + png_bytep filtered_row)) PNG_PRIVATE; +/* Finish a row while reading, dealing with interlacing passes, etc. */ +PNG_EXTERN void png_read_finish_row PNGARG((png_structp png_ptr)); + +/* Initialize the row buffers, etc. */ +PNG_EXTERN void png_read_start_row PNGARG((png_structp png_ptr)) PNG_PRIVATE; +/* Optional call to update the users info structure */ +PNG_EXTERN void png_read_transform_info PNGARG((png_structp png_ptr, + png_infop info_ptr)) PNG_PRIVATE; + +/* These are the functions that do the transformations */ +#ifdef PNG_READ_FILLER_SUPPORTED +PNG_EXTERN void png_do_read_filler PNGARG((png_row_infop row_info, + png_bytep row, png_uint_32 filler, png_uint_32 flags)) PNG_PRIVATE; +#endif + +#ifdef PNG_READ_SWAP_ALPHA_SUPPORTED +PNG_EXTERN void png_do_read_swap_alpha PNGARG((png_row_infop row_info, + png_bytep row)) PNG_PRIVATE; +#endif + +#ifdef PNG_WRITE_SWAP_ALPHA_SUPPORTED +PNG_EXTERN void png_do_write_swap_alpha PNGARG((png_row_infop row_info, + png_bytep row)) PNG_PRIVATE; +#endif + +#ifdef PNG_READ_INVERT_ALPHA_SUPPORTED +PNG_EXTERN void png_do_read_invert_alpha PNGARG((png_row_infop row_info, + png_bytep row)) PNG_PRIVATE; +#endif + +#ifdef PNG_WRITE_INVERT_ALPHA_SUPPORTED +PNG_EXTERN void png_do_write_invert_alpha PNGARG((png_row_infop row_info, + png_bytep row)) PNG_PRIVATE; +#endif + +#if defined(PNG_WRITE_FILLER_SUPPORTED) || \ + defined(PNG_READ_STRIP_ALPHA_SUPPORTED) +PNG_EXTERN void png_do_strip_filler PNGARG((png_row_infop row_info, + png_bytep row, png_uint_32 flags)) PNG_PRIVATE; +#endif + +#if defined(PNG_READ_SWAP_SUPPORTED) || defined(PNG_WRITE_SWAP_SUPPORTED) +PNG_EXTERN void png_do_swap PNGARG((png_row_infop row_info, + png_bytep row)) PNG_PRIVATE; +#endif + +#if defined(PNG_READ_PACKSWAP_SUPPORTED) || defined(PNG_WRITE_PACKSWAP_SUPPORTED) +PNG_EXTERN void png_do_packswap PNGARG((png_row_infop row_info, + png_bytep row)) PNG_PRIVATE; +#endif + +#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED +PNG_EXTERN int png_do_rgb_to_gray PNGARG((png_structp png_ptr, png_row_infop + row_info, png_bytep row)) PNG_PRIVATE; +#endif + +#ifdef PNG_READ_GRAY_TO_RGB_SUPPORTED +PNG_EXTERN void png_do_gray_to_rgb PNGARG((png_row_infop row_info, + png_bytep row)) PNG_PRIVATE; +#endif + +#ifdef PNG_READ_PACK_SUPPORTED +PNG_EXTERN void png_do_unpack PNGARG((png_row_infop row_info, + png_bytep row)) PNG_PRIVATE; +#endif + +#ifdef PNG_READ_SHIFT_SUPPORTED +PNG_EXTERN void png_do_unshift PNGARG((png_row_infop row_info, png_bytep row, + png_color_8p sig_bits)) PNG_PRIVATE; +#endif + +#if defined(PNG_READ_INVERT_SUPPORTED) || defined(PNG_WRITE_INVERT_SUPPORTED) +PNG_EXTERN void png_do_invert PNGARG((png_row_infop row_info, + png_bytep row)) PNG_PRIVATE; +#endif + +#ifdef PNG_READ_16_TO_8_SUPPORTED +PNG_EXTERN void png_do_chop PNGARG((png_row_infop row_info, + png_bytep row)) PNG_PRIVATE; +#endif + +#ifdef PNG_READ_DITHER_SUPPORTED +PNG_EXTERN void png_do_dither PNGARG((png_row_infop row_info, + png_bytep row, png_bytep palette_lookup, + png_bytep dither_lookup)) PNG_PRIVATE; + +# ifdef PNG_CORRECT_PALETTE_SUPPORTED +PNG_EXTERN void png_correct_palette PNGARG((png_structp png_ptr, + png_colorp palette, int num_palette)) PNG_PRIVATE; +# endif +#endif + +#if defined(PNG_READ_BGR_SUPPORTED) || defined(PNG_WRITE_BGR_SUPPORTED) +PNG_EXTERN void png_do_bgr PNGARG((png_row_infop row_info, + png_bytep row)) PNG_PRIVATE; +#endif + +#ifdef PNG_WRITE_PACK_SUPPORTED +PNG_EXTERN void png_do_pack PNGARG((png_row_infop row_info, + png_bytep row, png_uint_32 bit_depth)) PNG_PRIVATE; +#endif + +#ifdef PNG_WRITE_SHIFT_SUPPORTED +PNG_EXTERN void png_do_shift PNGARG((png_row_infop row_info, png_bytep row, + png_color_8p bit_depth)) PNG_PRIVATE; +#endif + +#ifdef PNG_READ_BACKGROUND_SUPPORTED +#ifdef PNG_READ_GAMMA_SUPPORTED +PNG_EXTERN void png_do_background PNGARG((png_row_infop row_info, png_bytep row, + png_color_16p trans_values, png_color_16p background, + png_color_16p background_1, + png_bytep gamma_table, png_bytep gamma_from_1, png_bytep gamma_to_1, + png_uint_16pp gamma_16, png_uint_16pp gamma_16_from_1, + png_uint_16pp gamma_16_to_1, int gamma_shift)) PNG_PRIVATE; +#else +PNG_EXTERN void png_do_background PNGARG((png_row_infop row_info, png_bytep row, + png_color_16p trans_values, png_color_16p background)) PNG_PRIVATE; +#endif +#endif + +#ifdef PNG_READ_GAMMA_SUPPORTED +PNG_EXTERN void png_do_gamma PNGARG((png_row_infop row_info, png_bytep row, + png_bytep gamma_table, png_uint_16pp gamma_16_table, + int gamma_shift)) PNG_PRIVATE; +#endif + +#ifdef PNG_READ_EXPAND_SUPPORTED +PNG_EXTERN void png_do_expand_palette PNGARG((png_row_infop row_info, + png_bytep row, png_colorp palette, png_bytep trans, + int num_trans)) PNG_PRIVATE; +PNG_EXTERN void png_do_expand PNGARG((png_row_infop row_info, + png_bytep row, png_color_16p trans_value)) PNG_PRIVATE; +#endif + +/* The following decodes the appropriate chunks, and does error correction, + * then calls the appropriate callback for the chunk if it is valid. + */ + +/* Decode the IHDR chunk */ +PNG_EXTERN void png_handle_IHDR PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)) PNG_PRIVATE; +PNG_EXTERN void png_handle_PLTE PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)); +PNG_EXTERN void png_handle_IEND PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)); + +#ifdef PNG_READ_bKGD_SUPPORTED +PNG_EXTERN void png_handle_bKGD PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)) PNG_PRIVATE; +#endif + +#ifdef PNG_READ_cHRM_SUPPORTED +PNG_EXTERN void png_handle_cHRM PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)) PNG_PRIVATE; +#endif + +#ifdef PNG_READ_gAMA_SUPPORTED +PNG_EXTERN void png_handle_gAMA PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)) PNG_PRIVATE; +#endif + +#ifdef PNG_READ_hIST_SUPPORTED +PNG_EXTERN void png_handle_hIST PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)) PNG_PRIVATE; +#endif + +#ifdef PNG_READ_iCCP_SUPPORTED +extern void png_handle_iCCP PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)); +#endif /* PNG_READ_iCCP_SUPPORTED */ + +#ifdef PNG_READ_iTXt_SUPPORTED +PNG_EXTERN void png_handle_iTXt PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)) PNG_PRIVATE; +#endif + +#ifdef PNG_READ_oFFs_SUPPORTED +PNG_EXTERN void png_handle_oFFs PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)) PNG_PRIVATE; +#endif + +#ifdef PNG_READ_pCAL_SUPPORTED +PNG_EXTERN void png_handle_pCAL PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)) PNG_PRIVATE; +#endif + +#ifdef PNG_READ_pHYs_SUPPORTED +PNG_EXTERN void png_handle_pHYs PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)) PNG_PRIVATE; +#endif + +#ifdef PNG_READ_sBIT_SUPPORTED +PNG_EXTERN void png_handle_sBIT PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)) PNG_PRIVATE; +#endif + +#ifdef PNG_READ_sCAL_SUPPORTED +PNG_EXTERN void png_handle_sCAL PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)) PNG_PRIVATE; +#endif + +#ifdef PNG_READ_sPLT_SUPPORTED +extern void png_handle_sPLT PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)) PNG_PRIVATE; +#endif /* PNG_READ_sPLT_SUPPORTED */ + +#ifdef PNG_READ_sRGB_SUPPORTED +PNG_EXTERN void png_handle_sRGB PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)) PNG_PRIVATE; +#endif + +#ifdef PNG_READ_tEXt_SUPPORTED +PNG_EXTERN void png_handle_tEXt PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)) PNG_PRIVATE; +#endif + +#ifdef PNG_READ_tIME_SUPPORTED +PNG_EXTERN void png_handle_tIME PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)) PNG_PRIVATE; +#endif + +#ifdef PNG_READ_tRNS_SUPPORTED +PNG_EXTERN void png_handle_tRNS PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)) PNG_PRIVATE; +#endif + +#ifdef PNG_READ_zTXt_SUPPORTED +PNG_EXTERN void png_handle_zTXt PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)) PNG_PRIVATE; +#endif + +PNG_EXTERN void png_handle_unknown PNGARG((png_structp png_ptr, + png_infop info_ptr, png_uint_32 length)) PNG_PRIVATE; + +PNG_EXTERN void png_check_chunk_name PNGARG((png_structp png_ptr, + png_bytep chunk_name)) PNG_PRIVATE; + +/* Handle the transformations for reading and writing */ +PNG_EXTERN void png_do_read_transformations + PNGARG((png_structp png_ptr)) PNG_PRIVATE; +PNG_EXTERN void png_do_write_transformations + PNGARG((png_structp png_ptr)) PNG_PRIVATE; + +PNG_EXTERN void png_init_read_transformations + PNGARG((png_structp png_ptr)) PNG_PRIVATE; + +#ifdef PNG_PROGRESSIVE_READ_SUPPORTED +PNG_EXTERN void png_push_read_chunk PNGARG((png_structp png_ptr, + png_infop info_ptr)) PNG_PRIVATE; +PNG_EXTERN void png_push_read_sig PNGARG((png_structp png_ptr, + png_infop info_ptr)) PNG_PRIVATE; +PNG_EXTERN void png_push_check_crc PNGARG((png_structp png_ptr)) PNG_PRIVATE; +PNG_EXTERN void png_push_crc_skip PNGARG((png_structp png_ptr, + png_uint_32 length)) PNG_PRIVATE; +PNG_EXTERN void png_push_crc_finish PNGARG((png_structp png_ptr)) PNG_PRIVATE; +PNG_EXTERN void png_push_save_buffer PNGARG((png_structp png_ptr)) PNG_PRIVATE; +PNG_EXTERN void png_push_restore_buffer PNGARG((png_structp png_ptr, + png_bytep buffer, png_size_t buffer_length)) PNG_PRIVATE; +PNG_EXTERN void png_push_read_IDAT PNGARG((png_structp png_ptr)) PNG_PRIVATE; +PNG_EXTERN void png_process_IDAT_data PNGARG((png_structp png_ptr, + png_bytep buffer, png_size_t buffer_length)) PNG_PRIVATE; +PNG_EXTERN void png_push_process_row PNGARG((png_structp png_ptr)) PNG_PRIVATE; +PNG_EXTERN void png_push_handle_unknown PNGARG((png_structp png_ptr, + png_infop info_ptr, png_uint_32 length)) PNG_PRIVATE; +PNG_EXTERN void png_push_have_info PNGARG((png_structp png_ptr, + png_infop info_ptr)) PNG_PRIVATE; +PNG_EXTERN void png_push_have_end PNGARG((png_structp png_ptr, + png_infop info_ptr)) PNG_PRIVATE; +PNG_EXTERN void png_push_have_row PNGARG((png_structp png_ptr, + png_bytep row)) PNG_PRIVATE; +PNG_EXTERN void png_push_read_end PNGARG((png_structp png_ptr, + png_infop info_ptr)) PNG_PRIVATE; +PNG_EXTERN void png_process_some_data PNGARG((png_structp png_ptr, + png_infop info_ptr)) PNG_PRIVATE; +PNG_EXTERN void png_read_push_finish_row + PNGARG((png_structp png_ptr)) PNG_PRIVATE; +#ifdef PNG_READ_tEXt_SUPPORTED +PNG_EXTERN void png_push_handle_tEXt PNGARG((png_structp png_ptr, + png_infop info_ptr, png_uint_32 length)) PNG_PRIVATE; +PNG_EXTERN void png_push_read_tEXt PNGARG((png_structp png_ptr, + png_infop info_ptr)) PNG_PRIVATE; +#endif +#ifdef PNG_READ_zTXt_SUPPORTED +PNG_EXTERN void png_push_handle_zTXt PNGARG((png_structp png_ptr, + png_infop info_ptr, png_uint_32 length)) PNG_PRIVATE; +PNG_EXTERN void png_push_read_zTXt PNGARG((png_structp png_ptr, + png_infop info_ptr)) PNG_PRIVATE; +#endif +#ifdef PNG_READ_iTXt_SUPPORTED +PNG_EXTERN void png_push_handle_iTXt PNGARG((png_structp png_ptr, + png_infop info_ptr, png_uint_32 length)) PNG_PRIVATE; +PNG_EXTERN void png_push_read_iTXt PNGARG((png_structp png_ptr, + png_infop info_ptr)) PNG_PRIVATE; +#endif + +#endif /* PNG_PROGRESSIVE_READ_SUPPORTED */ + +#ifdef PNG_MNG_FEATURES_SUPPORTED +PNG_EXTERN void png_do_read_intrapixel PNGARG((png_row_infop row_info, + png_bytep row)) PNG_PRIVATE; +PNG_EXTERN void png_do_write_intrapixel PNGARG((png_row_infop row_info, + png_bytep row)) PNG_PRIVATE; +#endif + +#ifdef PNG_ASSEMBLER_CODE_SUPPORTED +#ifdef PNG_MMX_CODE_SUPPORTED +/* png.c */ /* PRIVATE */ +PNG_EXTERN void png_init_mmx_flags PNGARG((png_structp png_ptr)) PNG_PRIVATE; +#endif +#endif + + +/* The following six functions will be exported in libpng-1.4.0. */ +#if defined(PNG_INCH_CONVERSIONS) && defined(PNG_FLOATING_POINT_SUPPORTED) +PNG_EXTERN png_uint_32 png_get_pixels_per_inch PNGARG((png_structp png_ptr, +png_infop info_ptr)); + +PNG_EXTERN png_uint_32 png_get_x_pixels_per_inch PNGARG((png_structp png_ptr, +png_infop info_ptr)); + +PNG_EXTERN png_uint_32 png_get_y_pixels_per_inch PNGARG((png_structp png_ptr, +png_infop info_ptr)); + +PNG_EXTERN float png_get_x_offset_inches PNGARG((png_structp png_ptr, +png_infop info_ptr)); + +PNG_EXTERN float png_get_y_offset_inches PNGARG((png_structp png_ptr, +png_infop info_ptr)); + +#ifdef PNG_pHYs_SUPPORTED +PNG_EXTERN png_uint_32 png_get_pHYs_dpi PNGARG((png_structp png_ptr, +png_infop info_ptr, png_uint_32 *res_x, png_uint_32 *res_y, int *unit_type)); +#endif /* PNG_pHYs_SUPPORTED */ +#endif /* PNG_INCH_CONVERSIONS && PNG_FLOATING_POINT_SUPPORTED */ + +/* Read the chunk header (length + type name) */ +PNG_EXTERN png_uint_32 png_read_chunk_header + PNGARG((png_structp png_ptr)) PNG_PRIVATE; + +/* Added at libpng version 1.2.34 */ +#ifdef PNG_cHRM_SUPPORTED +PNG_EXTERN int png_check_cHRM_fixed PNGARG((png_structp png_ptr, + png_fixed_point int_white_x, png_fixed_point int_white_y, + png_fixed_point int_red_x, png_fixed_point int_red_y, png_fixed_point + int_green_x, png_fixed_point int_green_y, png_fixed_point int_blue_x, + png_fixed_point int_blue_y)) PNG_PRIVATE; +#endif + +#ifdef PNG_cHRM_SUPPORTED +#ifdef PNG_CHECK_cHRM_SUPPORTED +/* Added at libpng version 1.2.34 */ +PNG_EXTERN void png_64bit_product PNGARG((long v1, long v2, + unsigned long *hi_product, unsigned long *lo_product)) PNG_PRIVATE; +#endif +#endif + +/* Added at libpng version 1.2.41 */ +PNG_EXTERN void png_check_IHDR PNGARG((png_structp png_ptr, + png_uint_32 width, png_uint_32 height, int bit_depth, + int color_type, int interlace_type, int compression_type, + int filter_type)) PNG_PRIVATE; + +/* Added at libpng version 1.2.41 */ +PNG_EXTERN png_voidp png_calloc PNGARG((png_structp png_ptr, + png_uint_32 size)); + +/* Maintainer: Put new private prototypes here ^ and in libpngpf.3 */ + +#endif /* PNG_INTERNAL */ + +#ifdef __cplusplus +} +#endif + +#endif /* PNG_VERSION_INFO_ONLY */ +/* Do not put anything past this line */ +#endif /* PNG_H */ diff --git a/src/libpng/pngconf.h b/src/libpng/pngconf.h new file mode 100644 index 00000000..042a5e98 --- /dev/null +++ b/src/libpng/pngconf.h @@ -0,0 +1,1665 @@ + +/* pngconf.h - machine configurable file for libpng + * + * libpng version 1.2.50 - July 10, 2012 + * Copyright (c) 1998-2012 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + * + * This code is released under the libpng license. + * For conditions of distribution and use, see the disclaimer + * and license in png.h + */ + +/* Any machine specific code is near the front of this file, so if you + * are configuring libpng for a machine, you may want to read the section + * starting here down to where it starts to typedef png_color, png_text, + * and png_info. + */ + +#ifndef PNGCONF_H +#define PNGCONF_H + +#define PNG_1_2_X + +/* + * PNG_USER_CONFIG has to be defined on the compiler command line. This + * includes the resource compiler for Windows DLL configurations. + */ +#ifdef PNG_USER_CONFIG +# ifndef PNG_USER_PRIVATEBUILD +# define PNG_USER_PRIVATEBUILD +# endif +#include "pngusr.h" +#endif + +/* PNG_CONFIGURE_LIBPNG is set by the "configure" script. */ +#ifdef PNG_CONFIGURE_LIBPNG +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#endif + +/* + * Added at libpng-1.2.8 + * + * If you create a private DLL you need to define in "pngusr.h" the followings: + * #define PNG_USER_PRIVATEBUILD + * e.g. #define PNG_USER_PRIVATEBUILD "Build by MyCompany for xyz reasons." + * #define PNG_USER_DLLFNAME_POSTFIX + * e.g. // private DLL "libpng13gx.dll" + * #define PNG_USER_DLLFNAME_POSTFIX "gx" + * + * The following macros are also at your disposal if you want to complete the + * DLL VERSIONINFO structure. + * - PNG_USER_VERSIONINFO_COMMENTS + * - PNG_USER_VERSIONINFO_COMPANYNAME + * - PNG_USER_VERSIONINFO_LEGALTRADEMARKS + */ + +#ifdef __STDC__ +#ifdef SPECIALBUILD +# pragma message("PNG_LIBPNG_SPECIALBUILD (and deprecated SPECIALBUILD)\ + are now LIBPNG reserved macros. Use PNG_USER_PRIVATEBUILD instead.") +#endif + +#ifdef PRIVATEBUILD +# pragma message("PRIVATEBUILD is deprecated.\ + Use PNG_USER_PRIVATEBUILD instead.") +# define PNG_USER_PRIVATEBUILD PRIVATEBUILD +#endif +#endif /* __STDC__ */ + +#ifndef PNG_VERSION_INFO_ONLY + +/* End of material added to libpng-1.2.8 */ + +/* Added at libpng-1.2.19, removed at libpng-1.2.20 because it caused trouble + Restored at libpng-1.2.21 */ +#if !defined(PNG_NO_WARN_UNINITIALIZED_ROW) && \ + !defined(PNG_WARN_UNINITIALIZED_ROW) +# define PNG_WARN_UNINITIALIZED_ROW 1 +#endif +/* End of material added at libpng-1.2.19/1.2.21 */ + +/* This is the size of the compression buffer, and thus the size of + * an IDAT chunk. Make this whatever size you feel is best for your + * machine. One of these will be allocated per png_struct. When this + * is full, it writes the data to the disk, and does some other + * calculations. Making this an extremely small size will slow + * the library down, but you may want to experiment to determine + * where it becomes significant, if you are concerned with memory + * usage. Note that zlib allocates at least 32Kb also. For readers, + * this describes the size of the buffer available to read the data in. + * Unless this gets smaller than the size of a row (compressed), + * it should not make much difference how big this is. + */ + +#ifndef PNG_ZBUF_SIZE +# define PNG_ZBUF_SIZE 8192 +#endif + +/* Enable if you want a write-only libpng */ + +#ifndef PNG_NO_READ_SUPPORTED +# define PNG_READ_SUPPORTED +#endif + +/* Enable if you want a read-only libpng */ + +#ifndef PNG_NO_WRITE_SUPPORTED +# define PNG_WRITE_SUPPORTED +#endif + +/* Enabled in 1.2.41. */ +#ifdef PNG_ALLOW_BENIGN_ERRORS +# define png_benign_error png_warning +# define png_chunk_benign_error png_chunk_warning +#else +# ifndef PNG_BENIGN_ERRORS_SUPPORTED +# define png_benign_error png_error +# define png_chunk_benign_error png_chunk_error +# endif +#endif + +/* Added in libpng-1.2.41 */ +#if !defined(PNG_NO_WARNINGS) && !defined(PNG_WARNINGS_SUPPORTED) +# define PNG_WARNINGS_SUPPORTED +#endif + +#if !defined(PNG_NO_ERROR_TEXT) && !defined(PNG_ERROR_TEXT_SUPPORTED) +# define PNG_ERROR_TEXT_SUPPORTED +#endif + +#if !defined(PNG_NO_CHECK_cHRM) && !defined(PNG_CHECK_cHRM_SUPPORTED) +# define PNG_CHECK_cHRM_SUPPORTED +#endif + +/* Enabled by default in 1.2.0. You can disable this if you don't need to + * support PNGs that are embedded in MNG datastreams + */ +#if !defined(PNG_1_0_X) && !defined(PNG_NO_MNG_FEATURES) +# ifndef PNG_MNG_FEATURES_SUPPORTED +# define PNG_MNG_FEATURES_SUPPORTED +# endif +#endif + +#ifndef PNG_NO_FLOATING_POINT_SUPPORTED +# ifndef PNG_FLOATING_POINT_SUPPORTED +# define PNG_FLOATING_POINT_SUPPORTED +# endif +#endif + +/* If you are running on a machine where you cannot allocate more + * than 64K of memory at once, uncomment this. While libpng will not + * normally need that much memory in a chunk (unless you load up a very + * large file), zlib needs to know how big of a chunk it can use, and + * libpng thus makes sure to check any memory allocation to verify it + * will fit into memory. +#define PNG_MAX_MALLOC_64K + */ +#if defined(MAXSEG_64K) && !defined(PNG_MAX_MALLOC_64K) +# define PNG_MAX_MALLOC_64K +#endif + +/* Special munging to support doing things the 'cygwin' way: + * 'Normal' png-on-win32 defines/defaults: + * PNG_BUILD_DLL -- building dll + * PNG_USE_DLL -- building an application, linking to dll + * (no define) -- building static library, or building an + * application and linking to the static lib + * 'Cygwin' defines/defaults: + * PNG_BUILD_DLL -- (ignored) building the dll + * (no define) -- (ignored) building an application, linking to the dll + * PNG_STATIC -- (ignored) building the static lib, or building an + * application that links to the static lib. + * ALL_STATIC -- (ignored) building various static libs, or building an + * application that links to the static libs. + * Thus, + * a cygwin user should define either PNG_BUILD_DLL or PNG_STATIC, and + * this bit of #ifdefs will define the 'correct' config variables based on + * that. If a cygwin user *wants* to define 'PNG_USE_DLL' that's okay, but + * unnecessary. + * + * Also, the precedence order is: + * ALL_STATIC (since we can't #undef something outside our namespace) + * PNG_BUILD_DLL + * PNG_STATIC + * (nothing) == PNG_USE_DLL + * + * CYGWIN (2002-01-20): The preceding is now obsolete. With the advent + * of auto-import in binutils, we no longer need to worry about + * __declspec(dllexport) / __declspec(dllimport) and friends. Therefore, + * we don't need to worry about PNG_STATIC or ALL_STATIC when it comes + * to __declspec() stuff. However, we DO need to worry about + * PNG_BUILD_DLL and PNG_STATIC because those change some defaults + * such as CONSOLE_IO and whether GLOBAL_ARRAYS are allowed. + */ +#ifdef __CYGWIN__ +# ifdef ALL_STATIC +# ifdef PNG_BUILD_DLL +# undef PNG_BUILD_DLL +# endif +# ifdef PNG_USE_DLL +# undef PNG_USE_DLL +# endif +# ifdef PNG_DLL +# undef PNG_DLL +# endif +# ifndef PNG_STATIC +# define PNG_STATIC +# endif +# else +# ifdef PNG_BUILD_DLL +# ifdef PNG_STATIC +# undef PNG_STATIC +# endif +# ifdef PNG_USE_DLL +# undef PNG_USE_DLL +# endif +# ifndef PNG_DLL +# define PNG_DLL +# endif +# else +# ifdef PNG_STATIC +# ifdef PNG_USE_DLL +# undef PNG_USE_DLL +# endif +# ifdef PNG_DLL +# undef PNG_DLL +# endif +# else +# ifndef PNG_USE_DLL +# define PNG_USE_DLL +# endif +# ifndef PNG_DLL +# define PNG_DLL +# endif +# endif +# endif +# endif +#endif + +/* This protects us against compilers that run on a windowing system + * and thus don't have or would rather us not use the stdio types: + * stdin, stdout, and stderr. The only one currently used is stderr + * in png_error() and png_warning(). #defining PNG_NO_CONSOLE_IO will + * prevent these from being compiled and used. #defining PNG_NO_STDIO + * will also prevent these, plus will prevent the entire set of stdio + * macros and functions (FILE *, printf, etc.) from being compiled and used, + * unless (PNG_DEBUG > 0) has been #defined. + * + * #define PNG_NO_CONSOLE_IO + * #define PNG_NO_STDIO + */ + +#if !defined(PNG_NO_STDIO) && !defined(PNG_STDIO_SUPPORTED) +# define PNG_STDIO_SUPPORTED +#endif + +#ifdef _WIN32_WCE +# include + /* Console I/O functions are not supported on WindowsCE */ +# define PNG_NO_CONSOLE_IO + /* abort() may not be supported on some/all Windows CE platforms */ +# define PNG_ABORT() exit(-1) +# ifdef PNG_DEBUG +# undef PNG_DEBUG +# endif +#endif + +#ifdef PNG_BUILD_DLL +# ifndef PNG_CONSOLE_IO_SUPPORTED +# ifndef PNG_NO_CONSOLE_IO +# define PNG_NO_CONSOLE_IO +# endif +# endif +#endif + +# ifdef PNG_NO_STDIO +# ifndef PNG_NO_CONSOLE_IO +# define PNG_NO_CONSOLE_IO +# endif +# ifdef PNG_DEBUG +# if (PNG_DEBUG > 0) +# include +# endif +# endif +# else +# ifndef _WIN32_WCE +/* "stdio.h" functions are not supported on WindowsCE */ +# include +# endif +# endif + +#if !(defined PNG_NO_CONSOLE_IO) && !defined(PNG_CONSOLE_IO_SUPPORTED) +# define PNG_CONSOLE_IO_SUPPORTED +#endif + +/* This macro protects us against machines that don't have function + * prototypes (ie K&R style headers). If your compiler does not handle + * function prototypes, define this macro and use the included ansi2knr. + * I've always been able to use _NO_PROTO as the indicator, but you may + * need to drag the empty declaration out in front of here, or change the + * ifdef to suit your own needs. + */ +#ifndef PNGARG + +#ifdef OF /* zlib prototype munger */ +# define PNGARG(arglist) OF(arglist) +#else + +#ifdef _NO_PROTO +# define PNGARG(arglist) () +# ifndef PNG_TYPECAST_NULL +# define PNG_TYPECAST_NULL +# endif +#else +# define PNGARG(arglist) arglist +#endif /* _NO_PROTO */ + + +#endif /* OF */ + +#endif /* PNGARG */ + +/* Try to determine if we are compiling on a Mac. Note that testing for + * just __MWERKS__ is not good enough, because the Codewarrior is now used + * on non-Mac platforms. + */ +#ifndef MACOS +# if (defined(__MWERKS__) && defined(macintosh)) || defined(applec) || \ + defined(THINK_C) || defined(__SC__) || defined(TARGET_OS_MAC) +# define MACOS +# endif +#endif + +/* enough people need this for various reasons to include it here */ +#if !defined(MACOS) && !defined(RISCOS) && !defined(_WIN32_WCE) +# include +#endif + +#if !defined(PNG_SETJMP_NOT_SUPPORTED) && !defined(PNG_NO_SETJMP_SUPPORTED) +# define PNG_SETJMP_SUPPORTED +#endif + +#ifdef PNG_SETJMP_SUPPORTED +/* This is an attempt to force a single setjmp behaviour on Linux. If + * the X config stuff didn't define _BSD_SOURCE we wouldn't need this. + * + * You can bypass this test if you know that your application uses exactly + * the same setjmp.h that was included when libpng was built. Only define + * PNG_SKIP_SETJMP_CHECK while building your application, prior to the + * application's '#include "png.h"'. Don't define PNG_SKIP_SETJMP_CHECK + * while building a separate libpng library for general use. + */ + +# ifndef PNG_SKIP_SETJMP_CHECK +# ifdef __linux__ +# ifdef _BSD_SOURCE +# define PNG_SAVE_BSD_SOURCE +# undef _BSD_SOURCE +# endif +# ifdef _SETJMP_H + /* If you encounter a compiler error here, see the explanation + * near the end of INSTALL. + */ + __pngconf.h__ in libpng already includes setjmp.h; + __dont__ include it again.; +# endif +# endif /* __linux__ */ +# endif /* PNG_SKIP_SETJMP_CHECK */ + + /* include setjmp.h for error handling */ +# include + +# ifdef __linux__ +# ifdef PNG_SAVE_BSD_SOURCE +# ifndef _BSD_SOURCE +# define _BSD_SOURCE +# endif +# undef PNG_SAVE_BSD_SOURCE +# endif +# endif /* __linux__ */ +#endif /* PNG_SETJMP_SUPPORTED */ + +#ifdef BSD +# include +#else +# include +#endif + +/* Other defines for things like memory and the like can go here. */ +#ifdef PNG_INTERNAL + +#include + +/* The functions exported by PNG_EXTERN are PNG_INTERNAL functions, which + * aren't usually used outside the library (as far as I know), so it is + * debatable if they should be exported at all. In the future, when it is + * possible to have run-time registry of chunk-handling functions, some of + * these will be made available again. +#define PNG_EXTERN extern + */ +#define PNG_EXTERN + +/* Other defines specific to compilers can go here. Try to keep + * them inside an appropriate ifdef/endif pair for portability. + */ + +#ifdef PNG_FLOATING_POINT_SUPPORTED +# ifdef MACOS + /* We need to check that hasn't already been included earlier + * as it seems it doesn't agree with , yet we should really use + * if possible. + */ +# if !defined(__MATH_H__) && !defined(__MATH_H) && !defined(__cmath__) +# include +# endif +# else +# include +# endif +# if defined(_AMIGA) && defined(__SASC) && defined(_M68881) + /* Amiga SAS/C: We must include builtin FPU functions when compiling using + * MATH=68881 + */ +# include +# endif +#endif + +/* Codewarrior on NT has linking problems without this. */ +#if (defined(__MWERKS__) && defined(WIN32)) || defined(__STDC__) +# define PNG_ALWAYS_EXTERN +#endif + +/* This provides the non-ANSI (far) memory allocation routines. */ +#if defined(__TURBOC__) && defined(__MSDOS__) +# include +# include +#endif + +/* I have no idea why is this necessary... */ +#if defined(_MSC_VER) && (defined(WIN32) || defined(_Windows) || \ + defined(_WINDOWS) || defined(_WIN32) || defined(__WIN32__)) +# include +#endif + +/* This controls how fine the dithering gets. As this allocates + * a largish chunk of memory (32K), those who are not as concerned + * with dithering quality can decrease some or all of these. + */ +#ifndef PNG_DITHER_RED_BITS +# define PNG_DITHER_RED_BITS 5 +#endif +#ifndef PNG_DITHER_GREEN_BITS +# define PNG_DITHER_GREEN_BITS 5 +#endif +#ifndef PNG_DITHER_BLUE_BITS +# define PNG_DITHER_BLUE_BITS 5 +#endif + +/* This controls how fine the gamma correction becomes when you + * are only interested in 8 bits anyway. Increasing this value + * results in more memory being used, and more pow() functions + * being called to fill in the gamma tables. Don't set this value + * less then 8, and even that may not work (I haven't tested it). + */ + +#ifndef PNG_MAX_GAMMA_8 +# define PNG_MAX_GAMMA_8 11 +#endif + +/* This controls how much a difference in gamma we can tolerate before + * we actually start doing gamma conversion. + */ +#ifndef PNG_GAMMA_THRESHOLD +# define PNG_GAMMA_THRESHOLD 0.05 +#endif + +#endif /* PNG_INTERNAL */ + +/* The following uses const char * instead of char * for error + * and warning message functions, so some compilers won't complain. + * If you do not want to use const, define PNG_NO_CONST here. + */ + +#ifndef PNG_NO_CONST +# define PNG_CONST const +#else +# define PNG_CONST +#endif + +/* The following defines give you the ability to remove code from the + * library that you will not be using. I wish I could figure out how to + * automate this, but I can't do that without making it seriously hard + * on the users. So if you are not using an ability, change the #define + * to and #undef, and that part of the library will not be compiled. If + * your linker can't find a function, you may want to make sure the + * ability is defined here. Some of these depend upon some others being + * defined. I haven't figured out all the interactions here, so you may + * have to experiment awhile to get everything to compile. If you are + * creating or using a shared library, you probably shouldn't touch this, + * as it will affect the size of the structures, and this will cause bad + * things to happen if the library and/or application ever change. + */ + +/* Any features you will not be using can be undef'ed here */ + +/* GR-P, 0.96a: Set "*TRANSFORMS_SUPPORTED as default but allow user + * to turn it off with "*TRANSFORMS_NOT_SUPPORTED" or *PNG_NO_*_TRANSFORMS + * on the compile line, then pick and choose which ones to define without + * having to edit this file. It is safe to use the *TRANSFORMS_NOT_SUPPORTED + * if you only want to have a png-compliant reader/writer but don't need + * any of the extra transformations. This saves about 80 kbytes in a + * typical installation of the library. (PNG_NO_* form added in version + * 1.0.1c, for consistency) + */ + +/* The size of the png_text structure changed in libpng-1.0.6 when + * iTXt support was added. iTXt support was turned off by default through + * libpng-1.2.x, to support old apps that malloc the png_text structure + * instead of calling png_set_text() and letting libpng malloc it. It + * will be turned on by default in libpng-1.4.0. + */ + +#if defined(PNG_1_0_X) || defined (PNG_1_2_X) +# ifndef PNG_NO_iTXt_SUPPORTED +# define PNG_NO_iTXt_SUPPORTED +# endif +# ifndef PNG_NO_READ_iTXt +# define PNG_NO_READ_iTXt +# endif +# ifndef PNG_NO_WRITE_iTXt +# define PNG_NO_WRITE_iTXt +# endif +#endif + +#if !defined(PNG_NO_iTXt_SUPPORTED) +# if !defined(PNG_READ_iTXt_SUPPORTED) && !defined(PNG_NO_READ_iTXt) +# define PNG_READ_iTXt +# endif +# if !defined(PNG_WRITE_iTXt_SUPPORTED) && !defined(PNG_NO_WRITE_iTXt) +# define PNG_WRITE_iTXt +# endif +#endif + +/* The following support, added after version 1.0.0, can be turned off here en + * masse by defining PNG_LEGACY_SUPPORTED in case you need binary compatibility + * with old applications that require the length of png_struct and png_info + * to remain unchanged. + */ + +#ifdef PNG_LEGACY_SUPPORTED +# define PNG_NO_FREE_ME +# define PNG_NO_READ_UNKNOWN_CHUNKS +# define PNG_NO_WRITE_UNKNOWN_CHUNKS +# define PNG_NO_HANDLE_AS_UNKNOWN +# define PNG_NO_READ_USER_CHUNKS +# define PNG_NO_READ_iCCP +# define PNG_NO_WRITE_iCCP +# define PNG_NO_READ_iTXt +# define PNG_NO_WRITE_iTXt +# define PNG_NO_READ_sCAL +# define PNG_NO_WRITE_sCAL +# define PNG_NO_READ_sPLT +# define PNG_NO_WRITE_sPLT +# define PNG_NO_INFO_IMAGE +# define PNG_NO_READ_RGB_TO_GRAY +# define PNG_NO_READ_USER_TRANSFORM +# define PNG_NO_WRITE_USER_TRANSFORM +# define PNG_NO_USER_MEM +# define PNG_NO_READ_EMPTY_PLTE +# define PNG_NO_MNG_FEATURES +# define PNG_NO_FIXED_POINT_SUPPORTED +#endif + +/* Ignore attempt to turn off both floating and fixed point support */ +#if !defined(PNG_FLOATING_POINT_SUPPORTED) || \ + !defined(PNG_NO_FIXED_POINT_SUPPORTED) +# define PNG_FIXED_POINT_SUPPORTED +#endif + +#ifndef PNG_NO_FREE_ME +# define PNG_FREE_ME_SUPPORTED +#endif + +#ifdef PNG_READ_SUPPORTED + +#if !defined(PNG_READ_TRANSFORMS_NOT_SUPPORTED) && \ + !defined(PNG_NO_READ_TRANSFORMS) +# define PNG_READ_TRANSFORMS_SUPPORTED +#endif + +#ifdef PNG_READ_TRANSFORMS_SUPPORTED +# ifndef PNG_NO_READ_EXPAND +# define PNG_READ_EXPAND_SUPPORTED +# endif +# ifndef PNG_NO_READ_SHIFT +# define PNG_READ_SHIFT_SUPPORTED +# endif +# ifndef PNG_NO_READ_PACK +# define PNG_READ_PACK_SUPPORTED +# endif +# ifndef PNG_NO_READ_BGR +# define PNG_READ_BGR_SUPPORTED +# endif +# ifndef PNG_NO_READ_SWAP +# define PNG_READ_SWAP_SUPPORTED +# endif +# ifndef PNG_NO_READ_PACKSWAP +# define PNG_READ_PACKSWAP_SUPPORTED +# endif +# ifndef PNG_NO_READ_INVERT +# define PNG_READ_INVERT_SUPPORTED +# endif +# ifndef PNG_NO_READ_DITHER +# define PNG_READ_DITHER_SUPPORTED +# endif +# ifndef PNG_NO_READ_BACKGROUND +# define PNG_READ_BACKGROUND_SUPPORTED +# endif +# ifndef PNG_NO_READ_16_TO_8 +# define PNG_READ_16_TO_8_SUPPORTED +# endif +# ifndef PNG_NO_READ_FILLER +# define PNG_READ_FILLER_SUPPORTED +# endif +# ifndef PNG_NO_READ_GAMMA +# define PNG_READ_GAMMA_SUPPORTED +# endif +# ifndef PNG_NO_READ_GRAY_TO_RGB +# define PNG_READ_GRAY_TO_RGB_SUPPORTED +# endif +# ifndef PNG_NO_READ_SWAP_ALPHA +# define PNG_READ_SWAP_ALPHA_SUPPORTED +# endif +# ifndef PNG_NO_READ_INVERT_ALPHA +# define PNG_READ_INVERT_ALPHA_SUPPORTED +# endif +# ifndef PNG_NO_READ_STRIP_ALPHA +# define PNG_READ_STRIP_ALPHA_SUPPORTED +# endif +# ifndef PNG_NO_READ_USER_TRANSFORM +# define PNG_READ_USER_TRANSFORM_SUPPORTED +# endif +# ifndef PNG_NO_READ_RGB_TO_GRAY +# define PNG_READ_RGB_TO_GRAY_SUPPORTED +# endif +#endif /* PNG_READ_TRANSFORMS_SUPPORTED */ + +/* PNG_PROGRESSIVE_READ_NOT_SUPPORTED is deprecated. */ +#if !defined(PNG_NO_PROGRESSIVE_READ) && \ + !defined(PNG_PROGRESSIVE_READ_NOT_SUPPORTED) /* if you don't do progressive */ +# define PNG_PROGRESSIVE_READ_SUPPORTED /* reading. This is not talking */ +#endif /* about interlacing capability! You'll */ + /* still have interlacing unless you change the following define: */ +#define PNG_READ_INTERLACING_SUPPORTED /* required for PNG-compliant decoders */ + +/* PNG_NO_SEQUENTIAL_READ_SUPPORTED is deprecated. */ +#if !defined(PNG_NO_SEQUENTIAL_READ) && \ + !defined(PNG_SEQUENTIAL_READ_SUPPORTED) && \ + !defined(PNG_NO_SEQUENTIAL_READ_SUPPORTED) +# define PNG_SEQUENTIAL_READ_SUPPORTED +#endif + +#define PNG_READ_INTERLACING_SUPPORTED /* required in PNG-compliant decoders */ + +#ifndef PNG_NO_READ_COMPOSITE_NODIV +# ifndef PNG_NO_READ_COMPOSITED_NODIV /* libpng-1.0.x misspelling */ +# define PNG_READ_COMPOSITE_NODIV_SUPPORTED /* well tested on Intel, SGI */ +# endif +#endif + +#if defined(PNG_1_0_X) || defined (PNG_1_2_X) +/* Deprecated, will be removed from version 2.0.0. + Use PNG_MNG_FEATURES_SUPPORTED instead. */ +#ifndef PNG_NO_READ_EMPTY_PLTE +# define PNG_READ_EMPTY_PLTE_SUPPORTED +#endif +#endif + +#endif /* PNG_READ_SUPPORTED */ + +#ifdef PNG_WRITE_SUPPORTED + +# if !defined(PNG_WRITE_TRANSFORMS_NOT_SUPPORTED) && \ + !defined(PNG_NO_WRITE_TRANSFORMS) +# define PNG_WRITE_TRANSFORMS_SUPPORTED +#endif + +#ifdef PNG_WRITE_TRANSFORMS_SUPPORTED +# ifndef PNG_NO_WRITE_SHIFT +# define PNG_WRITE_SHIFT_SUPPORTED +# endif +# ifndef PNG_NO_WRITE_PACK +# define PNG_WRITE_PACK_SUPPORTED +# endif +# ifndef PNG_NO_WRITE_BGR +# define PNG_WRITE_BGR_SUPPORTED +# endif +# ifndef PNG_NO_WRITE_SWAP +# define PNG_WRITE_SWAP_SUPPORTED +# endif +# ifndef PNG_NO_WRITE_PACKSWAP +# define PNG_WRITE_PACKSWAP_SUPPORTED +# endif +# ifndef PNG_NO_WRITE_INVERT +# define PNG_WRITE_INVERT_SUPPORTED +# endif +# ifndef PNG_NO_WRITE_FILLER +# define PNG_WRITE_FILLER_SUPPORTED /* same as WRITE_STRIP_ALPHA */ +# endif +# ifndef PNG_NO_WRITE_SWAP_ALPHA +# define PNG_WRITE_SWAP_ALPHA_SUPPORTED +# endif +#ifndef PNG_1_0_X +# ifndef PNG_NO_WRITE_INVERT_ALPHA +# define PNG_WRITE_INVERT_ALPHA_SUPPORTED +# endif +#endif +# ifndef PNG_NO_WRITE_USER_TRANSFORM +# define PNG_WRITE_USER_TRANSFORM_SUPPORTED +# endif +#endif /* PNG_WRITE_TRANSFORMS_SUPPORTED */ + +#if !defined(PNG_NO_WRITE_INTERLACING_SUPPORTED) && \ + !defined(PNG_WRITE_INTERLACING_SUPPORTED) +#define PNG_WRITE_INTERLACING_SUPPORTED /* not required for PNG-compliant + encoders, but can cause trouble + if left undefined */ +#endif + +#if !defined(PNG_NO_WRITE_WEIGHTED_FILTER) && \ + !defined(PNG_WRITE_WEIGHTED_FILTER) && \ + defined(PNG_FLOATING_POINT_SUPPORTED) +# define PNG_WRITE_WEIGHTED_FILTER_SUPPORTED +#endif + +#ifndef PNG_NO_WRITE_FLUSH +# define PNG_WRITE_FLUSH_SUPPORTED +#endif + +#if defined(PNG_1_0_X) || defined (PNG_1_2_X) +/* Deprecated, see PNG_MNG_FEATURES_SUPPORTED, above */ +#ifndef PNG_NO_WRITE_EMPTY_PLTE +# define PNG_WRITE_EMPTY_PLTE_SUPPORTED +#endif +#endif + +#endif /* PNG_WRITE_SUPPORTED */ + +#ifndef PNG_1_0_X +# ifndef PNG_NO_ERROR_NUMBERS +# define PNG_ERROR_NUMBERS_SUPPORTED +# endif +#endif /* PNG_1_0_X */ + +#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) || \ + defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED) +# ifndef PNG_NO_USER_TRANSFORM_PTR +# define PNG_USER_TRANSFORM_PTR_SUPPORTED +# endif +#endif + +#ifndef PNG_NO_STDIO +# define PNG_TIME_RFC1123_SUPPORTED +#endif + +/* This adds extra functions in pngget.c for accessing data from the + * info pointer (added in version 0.99) + * png_get_image_width() + * png_get_image_height() + * png_get_bit_depth() + * png_get_color_type() + * png_get_compression_type() + * png_get_filter_type() + * png_get_interlace_type() + * png_get_pixel_aspect_ratio() + * png_get_pixels_per_meter() + * png_get_x_offset_pixels() + * png_get_y_offset_pixels() + * png_get_x_offset_microns() + * png_get_y_offset_microns() + */ +#if !defined(PNG_NO_EASY_ACCESS) && !defined(PNG_EASY_ACCESS_SUPPORTED) +# define PNG_EASY_ACCESS_SUPPORTED +#endif + +/* PNG_ASSEMBLER_CODE was enabled by default in version 1.2.0 + * and removed from version 1.2.20. The following will be removed + * from libpng-1.4.0 +*/ + +#if defined(PNG_READ_SUPPORTED) && !defined(PNG_NO_OPTIMIZED_CODE) +# ifndef PNG_OPTIMIZED_CODE_SUPPORTED +# define PNG_OPTIMIZED_CODE_SUPPORTED +# endif +#endif + +#if defined(PNG_READ_SUPPORTED) && !defined(PNG_NO_ASSEMBLER_CODE) +# ifndef PNG_ASSEMBLER_CODE_SUPPORTED +# define PNG_ASSEMBLER_CODE_SUPPORTED +# endif + +# if defined(__GNUC__) && defined(__x86_64__) && (__GNUC__ < 4) + /* work around 64-bit gcc compiler bugs in gcc-3.x */ +# if !defined(PNG_MMX_CODE_SUPPORTED) && !defined(PNG_NO_MMX_CODE) +# define PNG_NO_MMX_CODE +# endif +# endif + +# ifdef __APPLE__ +# if !defined(PNG_MMX_CODE_SUPPORTED) && !defined(PNG_NO_MMX_CODE) +# define PNG_NO_MMX_CODE +# endif +# endif + +# if (defined(__MWERKS__) && ((__MWERKS__ < 0x0900) || macintosh)) +# if !defined(PNG_MMX_CODE_SUPPORTED) && !defined(PNG_NO_MMX_CODE) +# define PNG_NO_MMX_CODE +# endif +# endif + +# if !defined(PNG_MMX_CODE_SUPPORTED) && !defined(PNG_NO_MMX_CODE) +# define PNG_MMX_CODE_SUPPORTED +# endif + +#endif +/* end of obsolete code to be removed from libpng-1.4.0 */ + +/* Added at libpng-1.2.0 */ +#ifndef PNG_1_0_X +#if !defined(PNG_NO_USER_MEM) && !defined(PNG_USER_MEM_SUPPORTED) +# define PNG_USER_MEM_SUPPORTED +#endif +#endif /* PNG_1_0_X */ + +/* Added at libpng-1.2.6 */ +#ifndef PNG_1_0_X +# ifndef PNG_SET_USER_LIMITS_SUPPORTED +# ifndef PNG_NO_SET_USER_LIMITS +# define PNG_SET_USER_LIMITS_SUPPORTED +# endif +# endif +#endif /* PNG_1_0_X */ + +/* Added at libpng-1.0.53 and 1.2.43 */ +#ifndef PNG_USER_LIMITS_SUPPORTED +# ifndef PNG_NO_USER_LIMITS +# define PNG_USER_LIMITS_SUPPORTED +# endif +#endif + +/* Added at libpng-1.0.16 and 1.2.6. To accept all valid PNGS no matter + * how large, set these limits to 0x7fffffffL + */ +#ifndef PNG_USER_WIDTH_MAX +# define PNG_USER_WIDTH_MAX 1000000L +#endif +#ifndef PNG_USER_HEIGHT_MAX +# define PNG_USER_HEIGHT_MAX 1000000L +#endif + +/* Added at libpng-1.2.43. To accept all valid PNGs no matter + * how large, set these two limits to 0. + */ +#ifndef PNG_USER_CHUNK_CACHE_MAX +# define PNG_USER_CHUNK_CACHE_MAX 0 +#endif + +/* Added at libpng-1.2.43 */ +#ifndef PNG_USER_CHUNK_MALLOC_MAX +# define PNG_USER_CHUNK_MALLOC_MAX 0 +#endif + +#ifndef PNG_LITERAL_SHARP +# define PNG_LITERAL_SHARP 0x23 +#endif +#ifndef PNG_LITERAL_LEFT_SQUARE_BRACKET +# define PNG_LITERAL_LEFT_SQUARE_BRACKET 0x5b +#endif +#ifndef PNG_LITERAL_RIGHT_SQUARE_BRACKET +# define PNG_LITERAL_RIGHT_SQUARE_BRACKET 0x5d +#endif + +/* Added at libpng-1.2.34 */ +#ifndef PNG_STRING_NEWLINE +#define PNG_STRING_NEWLINE "\n" +#endif + +/* These are currently experimental features, define them if you want */ + +/* very little testing */ +/* +#ifdef PNG_READ_SUPPORTED +# ifndef PNG_READ_16_TO_8_ACCURATE_SCALE_SUPPORTED +# define PNG_READ_16_TO_8_ACCURATE_SCALE_SUPPORTED +# endif +#endif +*/ + +/* This is only for PowerPC big-endian and 680x0 systems */ +/* some testing */ +/* +#ifndef PNG_READ_BIG_ENDIAN_SUPPORTED +# define PNG_READ_BIG_ENDIAN_SUPPORTED +#endif +*/ + +/* Buggy compilers (e.g., gcc 2.7.2.2) need this */ +/* +#define PNG_NO_POINTER_INDEXING +*/ + +#if !defined(PNG_NO_POINTER_INDEXING) && \ + !defined(PNG_POINTER_INDEXING_SUPPORTED) +# define PNG_POINTER_INDEXING_SUPPORTED +#endif + +/* These functions are turned off by default, as they will be phased out. */ +/* +#define PNG_USELESS_TESTS_SUPPORTED +#define PNG_CORRECT_PALETTE_SUPPORTED +*/ + +/* Any chunks you are not interested in, you can undef here. The + * ones that allocate memory may be expecially important (hIST, + * tEXt, zTXt, tRNS, pCAL). Others will just save time and make png_info + * a bit smaller. + */ + +#if defined(PNG_READ_SUPPORTED) && \ + !defined(PNG_READ_ANCILLARY_CHUNKS_NOT_SUPPORTED) && \ + !defined(PNG_NO_READ_ANCILLARY_CHUNKS) +# define PNG_READ_ANCILLARY_CHUNKS_SUPPORTED +#endif + +#if defined(PNG_WRITE_SUPPORTED) && \ + !defined(PNG_WRITE_ANCILLARY_CHUNKS_NOT_SUPPORTED) && \ + !defined(PNG_NO_WRITE_ANCILLARY_CHUNKS) +# define PNG_WRITE_ANCILLARY_CHUNKS_SUPPORTED +#endif + +#ifdef PNG_READ_ANCILLARY_CHUNKS_SUPPORTED + +#ifdef PNG_NO_READ_TEXT +# define PNG_NO_READ_iTXt +# define PNG_NO_READ_tEXt +# define PNG_NO_READ_zTXt +#endif +#ifndef PNG_NO_READ_bKGD +# define PNG_READ_bKGD_SUPPORTED +# define PNG_bKGD_SUPPORTED +#endif +#ifndef PNG_NO_READ_cHRM +# define PNG_READ_cHRM_SUPPORTED +# define PNG_cHRM_SUPPORTED +#endif +#ifndef PNG_NO_READ_gAMA +# define PNG_READ_gAMA_SUPPORTED +# define PNG_gAMA_SUPPORTED +#endif +#ifndef PNG_NO_READ_hIST +# define PNG_READ_hIST_SUPPORTED +# define PNG_hIST_SUPPORTED +#endif +#ifndef PNG_NO_READ_iCCP +# define PNG_READ_iCCP_SUPPORTED +# define PNG_iCCP_SUPPORTED +#endif +#ifndef PNG_NO_READ_iTXt +# ifndef PNG_READ_iTXt_SUPPORTED +# define PNG_READ_iTXt_SUPPORTED +# endif +# ifndef PNG_iTXt_SUPPORTED +# define PNG_iTXt_SUPPORTED +# endif +#endif +#ifndef PNG_NO_READ_oFFs +# define PNG_READ_oFFs_SUPPORTED +# define PNG_oFFs_SUPPORTED +#endif +#ifndef PNG_NO_READ_pCAL +# define PNG_READ_pCAL_SUPPORTED +# define PNG_pCAL_SUPPORTED +#endif +#ifndef PNG_NO_READ_sCAL +# define PNG_READ_sCAL_SUPPORTED +# define PNG_sCAL_SUPPORTED +#endif +#ifndef PNG_NO_READ_pHYs +# define PNG_READ_pHYs_SUPPORTED +# define PNG_pHYs_SUPPORTED +#endif +#ifndef PNG_NO_READ_sBIT +# define PNG_READ_sBIT_SUPPORTED +# define PNG_sBIT_SUPPORTED +#endif +#ifndef PNG_NO_READ_sPLT +# define PNG_READ_sPLT_SUPPORTED +# define PNG_sPLT_SUPPORTED +#endif +#ifndef PNG_NO_READ_sRGB +# define PNG_READ_sRGB_SUPPORTED +# define PNG_sRGB_SUPPORTED +#endif +#ifndef PNG_NO_READ_tEXt +# define PNG_READ_tEXt_SUPPORTED +# define PNG_tEXt_SUPPORTED +#endif +#ifndef PNG_NO_READ_tIME +# define PNG_READ_tIME_SUPPORTED +# define PNG_tIME_SUPPORTED +#endif +#ifndef PNG_NO_READ_tRNS +# define PNG_READ_tRNS_SUPPORTED +# define PNG_tRNS_SUPPORTED +#endif +#ifndef PNG_NO_READ_zTXt +# define PNG_READ_zTXt_SUPPORTED +# define PNG_zTXt_SUPPORTED +#endif +#ifndef PNG_NO_READ_OPT_PLTE +# define PNG_READ_OPT_PLTE_SUPPORTED /* only affects support of the */ +#endif /* optional PLTE chunk in RGB and RGBA images */ +#if defined(PNG_READ_iTXt_SUPPORTED) || defined(PNG_READ_tEXt_SUPPORTED) || \ + defined(PNG_READ_zTXt_SUPPORTED) +# define PNG_READ_TEXT_SUPPORTED +# define PNG_TEXT_SUPPORTED +#endif + +#endif /* PNG_READ_ANCILLARY_CHUNKS_SUPPORTED */ + +#ifndef PNG_NO_READ_UNKNOWN_CHUNKS +# define PNG_READ_UNKNOWN_CHUNKS_SUPPORTED +# ifndef PNG_UNKNOWN_CHUNKS_SUPPORTED +# define PNG_UNKNOWN_CHUNKS_SUPPORTED +# endif +#endif +#if !defined(PNG_NO_READ_USER_CHUNKS) && \ + defined(PNG_READ_UNKNOWN_CHUNKS_SUPPORTED) +# define PNG_READ_USER_CHUNKS_SUPPORTED +# define PNG_USER_CHUNKS_SUPPORTED +# ifdef PNG_NO_READ_UNKNOWN_CHUNKS +# undef PNG_NO_READ_UNKNOWN_CHUNKS +# endif +# ifdef PNG_NO_HANDLE_AS_UNKNOWN +# undef PNG_NO_HANDLE_AS_UNKNOWN +# endif +#endif + +#ifndef PNG_NO_HANDLE_AS_UNKNOWN +# ifndef PNG_HANDLE_AS_UNKNOWN_SUPPORTED +# define PNG_HANDLE_AS_UNKNOWN_SUPPORTED +# endif +#endif + +#ifdef PNG_WRITE_SUPPORTED +#ifdef PNG_WRITE_ANCILLARY_CHUNKS_SUPPORTED + +#ifdef PNG_NO_WRITE_TEXT +# define PNG_NO_WRITE_iTXt +# define PNG_NO_WRITE_tEXt +# define PNG_NO_WRITE_zTXt +#endif +#ifndef PNG_NO_WRITE_bKGD +# define PNG_WRITE_bKGD_SUPPORTED +# ifndef PNG_bKGD_SUPPORTED +# define PNG_bKGD_SUPPORTED +# endif +#endif +#ifndef PNG_NO_WRITE_cHRM +# define PNG_WRITE_cHRM_SUPPORTED +# ifndef PNG_cHRM_SUPPORTED +# define PNG_cHRM_SUPPORTED +# endif +#endif +#ifndef PNG_NO_WRITE_gAMA +# define PNG_WRITE_gAMA_SUPPORTED +# ifndef PNG_gAMA_SUPPORTED +# define PNG_gAMA_SUPPORTED +# endif +#endif +#ifndef PNG_NO_WRITE_hIST +# define PNG_WRITE_hIST_SUPPORTED +# ifndef PNG_hIST_SUPPORTED +# define PNG_hIST_SUPPORTED +# endif +#endif +#ifndef PNG_NO_WRITE_iCCP +# define PNG_WRITE_iCCP_SUPPORTED +# ifndef PNG_iCCP_SUPPORTED +# define PNG_iCCP_SUPPORTED +# endif +#endif +#ifndef PNG_NO_WRITE_iTXt +# ifndef PNG_WRITE_iTXt_SUPPORTED +# define PNG_WRITE_iTXt_SUPPORTED +# endif +# ifndef PNG_iTXt_SUPPORTED +# define PNG_iTXt_SUPPORTED +# endif +#endif +#ifndef PNG_NO_WRITE_oFFs +# define PNG_WRITE_oFFs_SUPPORTED +# ifndef PNG_oFFs_SUPPORTED +# define PNG_oFFs_SUPPORTED +# endif +#endif +#ifndef PNG_NO_WRITE_pCAL +# define PNG_WRITE_pCAL_SUPPORTED +# ifndef PNG_pCAL_SUPPORTED +# define PNG_pCAL_SUPPORTED +# endif +#endif +#ifndef PNG_NO_WRITE_sCAL +# define PNG_WRITE_sCAL_SUPPORTED +# ifndef PNG_sCAL_SUPPORTED +# define PNG_sCAL_SUPPORTED +# endif +#endif +#ifndef PNG_NO_WRITE_pHYs +# define PNG_WRITE_pHYs_SUPPORTED +# ifndef PNG_pHYs_SUPPORTED +# define PNG_pHYs_SUPPORTED +# endif +#endif +#ifndef PNG_NO_WRITE_sBIT +# define PNG_WRITE_sBIT_SUPPORTED +# ifndef PNG_sBIT_SUPPORTED +# define PNG_sBIT_SUPPORTED +# endif +#endif +#ifndef PNG_NO_WRITE_sPLT +# define PNG_WRITE_sPLT_SUPPORTED +# ifndef PNG_sPLT_SUPPORTED +# define PNG_sPLT_SUPPORTED +# endif +#endif +#ifndef PNG_NO_WRITE_sRGB +# define PNG_WRITE_sRGB_SUPPORTED +# ifndef PNG_sRGB_SUPPORTED +# define PNG_sRGB_SUPPORTED +# endif +#endif +#ifndef PNG_NO_WRITE_tEXt +# define PNG_WRITE_tEXt_SUPPORTED +# ifndef PNG_tEXt_SUPPORTED +# define PNG_tEXt_SUPPORTED +# endif +#endif +#ifndef PNG_NO_WRITE_tIME +# define PNG_WRITE_tIME_SUPPORTED +# ifndef PNG_tIME_SUPPORTED +# define PNG_tIME_SUPPORTED +# endif +#endif +#ifndef PNG_NO_WRITE_tRNS +# define PNG_WRITE_tRNS_SUPPORTED +# ifndef PNG_tRNS_SUPPORTED +# define PNG_tRNS_SUPPORTED +# endif +#endif +#ifndef PNG_NO_WRITE_zTXt +# define PNG_WRITE_zTXt_SUPPORTED +# ifndef PNG_zTXt_SUPPORTED +# define PNG_zTXt_SUPPORTED +# endif +#endif +#if defined(PNG_WRITE_iTXt_SUPPORTED) || defined(PNG_WRITE_tEXt_SUPPORTED) || \ + defined(PNG_WRITE_zTXt_SUPPORTED) +# define PNG_WRITE_TEXT_SUPPORTED +# ifndef PNG_TEXT_SUPPORTED +# define PNG_TEXT_SUPPORTED +# endif +#endif + +#ifdef PNG_WRITE_tIME_SUPPORTED +# ifndef PNG_NO_CONVERT_tIME +# ifndef _WIN32_WCE +/* The "tm" structure is not supported on WindowsCE */ +# ifndef PNG_CONVERT_tIME_SUPPORTED +# define PNG_CONVERT_tIME_SUPPORTED +# endif +# endif +# endif +#endif + +#endif /* PNG_WRITE_ANCILLARY_CHUNKS_SUPPORTED */ + +#if !defined(PNG_NO_WRITE_FILTER) && !defined(PNG_WRITE_FILTER_SUPPORTED) +# define PNG_WRITE_FILTER_SUPPORTED +#endif + +#ifndef PNG_NO_WRITE_UNKNOWN_CHUNKS +# define PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED +# ifndef PNG_UNKNOWN_CHUNKS_SUPPORTED +# define PNG_UNKNOWN_CHUNKS_SUPPORTED +# endif +#endif + +#ifndef PNG_NO_HANDLE_AS_UNKNOWN +# ifndef PNG_HANDLE_AS_UNKNOWN_SUPPORTED +# define PNG_HANDLE_AS_UNKNOWN_SUPPORTED +# endif +#endif +#endif /* PNG_WRITE_SUPPORTED */ + +/* Turn this off to disable png_read_png() and + * png_write_png() and leave the row_pointers member + * out of the info structure. + */ +#ifndef PNG_NO_INFO_IMAGE +# define PNG_INFO_IMAGE_SUPPORTED +#endif + +/* Need the time information for converting tIME chunks */ +#ifdef PNG_CONVERT_tIME_SUPPORTED + /* "time.h" functions are not supported on WindowsCE */ +# include +#endif + +/* Some typedefs to get us started. These should be safe on most of the + * common platforms. The typedefs should be at least as large as the + * numbers suggest (a png_uint_32 must be at least 32 bits long), but they + * don't have to be exactly that size. Some compilers dislike passing + * unsigned shorts as function parameters, so you may be better off using + * unsigned int for png_uint_16. Likewise, for 64-bit systems, you may + * want to have unsigned int for png_uint_32 instead of unsigned long. + */ + +typedef unsigned long png_uint_32; +typedef long png_int_32; +typedef unsigned short png_uint_16; +typedef short png_int_16; +typedef unsigned char png_byte; + +/* This is usually size_t. It is typedef'ed just in case you need it to + change (I'm not sure if you will or not, so I thought I'd be safe) */ +#ifdef PNG_SIZE_T + typedef PNG_SIZE_T png_size_t; +# define png_sizeof(x) png_convert_size(sizeof(x)) +#else + typedef size_t png_size_t; +# define png_sizeof(x) sizeof(x) +#endif + +/* The following is needed for medium model support. It cannot be in the + * PNG_INTERNAL section. Needs modification for other compilers besides + * MSC. Model independent support declares all arrays and pointers to be + * large using the far keyword. The zlib version used must also support + * model independent data. As of version zlib 1.0.4, the necessary changes + * have been made in zlib. The USE_FAR_KEYWORD define triggers other + * changes that are needed. (Tim Wegner) + */ + +/* Separate compiler dependencies (problem here is that zlib.h always + defines FAR. (SJT) */ +#ifdef __BORLANDC__ +# if defined(__LARGE__) || defined(__HUGE__) || defined(__COMPACT__) +# define LDATA 1 +# else +# define LDATA 0 +# endif + /* GRR: why is Cygwin in here? Cygwin is not Borland C... */ +# if !defined(__WIN32__) && !defined(__FLAT__) && !defined(__CYGWIN__) +# define PNG_MAX_MALLOC_64K +# if (LDATA != 1) +# ifndef FAR +# define FAR __far +# endif +# define USE_FAR_KEYWORD +# endif /* LDATA != 1 */ + /* Possibly useful for moving data out of default segment. + * Uncomment it if you want. Could also define FARDATA as + * const if your compiler supports it. (SJT) +# define FARDATA FAR + */ +# endif /* __WIN32__, __FLAT__, __CYGWIN__ */ +#endif /* __BORLANDC__ */ + + +/* Suggest testing for specific compiler first before testing for + * FAR. The Watcom compiler defines both __MEDIUM__ and M_I86MM, + * making reliance oncertain keywords suspect. (SJT) + */ + +/* MSC Medium model */ +#ifdef FAR +# ifdef M_I86MM +# define USE_FAR_KEYWORD +# define FARDATA FAR +# include +# endif +#endif + +/* SJT: default case */ +#ifndef FAR +# define FAR +#endif + +/* At this point FAR is always defined */ +#ifndef FARDATA +# define FARDATA +#endif + +/* Typedef for floating-point numbers that are converted + to fixed-point with a multiple of 100,000, e.g., int_gamma */ +typedef png_int_32 png_fixed_point; + +/* Add typedefs for pointers */ +typedef void FAR * png_voidp; +typedef png_byte FAR * png_bytep; +typedef png_uint_32 FAR * png_uint_32p; +typedef png_int_32 FAR * png_int_32p; +typedef png_uint_16 FAR * png_uint_16p; +typedef png_int_16 FAR * png_int_16p; +typedef PNG_CONST char FAR * png_const_charp; +typedef char FAR * png_charp; +typedef png_fixed_point FAR * png_fixed_point_p; + +#ifndef PNG_NO_STDIO +#ifdef _WIN32_WCE +typedef HANDLE png_FILE_p; +#else +typedef FILE * png_FILE_p; +#endif +#endif + +#ifdef PNG_FLOATING_POINT_SUPPORTED +typedef double FAR * png_doublep; +#endif + +/* Pointers to pointers; i.e. arrays */ +typedef png_byte FAR * FAR * png_bytepp; +typedef png_uint_32 FAR * FAR * png_uint_32pp; +typedef png_int_32 FAR * FAR * png_int_32pp; +typedef png_uint_16 FAR * FAR * png_uint_16pp; +typedef png_int_16 FAR * FAR * png_int_16pp; +typedef PNG_CONST char FAR * FAR * png_const_charpp; +typedef char FAR * FAR * png_charpp; +typedef png_fixed_point FAR * FAR * png_fixed_point_pp; +#ifdef PNG_FLOATING_POINT_SUPPORTED +typedef double FAR * FAR * png_doublepp; +#endif + +/* Pointers to pointers to pointers; i.e., pointer to array */ +typedef char FAR * FAR * FAR * png_charppp; + +#if defined(PNG_1_0_X) || defined(PNG_1_2_X) +/* SPC - Is this stuff deprecated? */ +/* It'll be removed as of libpng-1.4.0 - GR-P */ +/* libpng typedefs for types in zlib. If zlib changes + * or another compression library is used, then change these. + * Eliminates need to change all the source files. + */ +typedef charf * png_zcharp; +typedef charf * FAR * png_zcharpp; +typedef z_stream FAR * png_zstreamp; +#endif /* (PNG_1_0_X) || defined(PNG_1_2_X) */ + +/* + * Define PNG_BUILD_DLL if the module being built is a Windows + * LIBPNG DLL. + * + * Define PNG_USE_DLL if you want to *link* to the Windows LIBPNG DLL. + * It is equivalent to Microsoft predefined macro _DLL that is + * automatically defined when you compile using the share + * version of the CRT (C Run-Time library) + * + * The cygwin mods make this behavior a little different: + * Define PNG_BUILD_DLL if you are building a dll for use with cygwin + * Define PNG_STATIC if you are building a static library for use with cygwin, + * -or- if you are building an application that you want to link to the + * static library. + * PNG_USE_DLL is defined by default (no user action needed) unless one of + * the other flags is defined. + */ + +#if !defined(PNG_DLL) && (defined(PNG_BUILD_DLL) || defined(PNG_USE_DLL)) +# define PNG_DLL +#endif +/* If CYGWIN, then disallow GLOBAL ARRAYS unless building a static lib. + * When building a static lib, default to no GLOBAL ARRAYS, but allow + * command-line override + */ +#ifdef __CYGWIN__ +# ifndef PNG_STATIC +# ifdef PNG_USE_GLOBAL_ARRAYS +# undef PNG_USE_GLOBAL_ARRAYS +# endif +# ifndef PNG_USE_LOCAL_ARRAYS +# define PNG_USE_LOCAL_ARRAYS +# endif +# else +# if defined(PNG_USE_LOCAL_ARRAYS) || defined(PNG_NO_GLOBAL_ARRAYS) +# ifdef PNG_USE_GLOBAL_ARRAYS +# undef PNG_USE_GLOBAL_ARRAYS +# endif +# endif +# endif +# if !defined(PNG_USE_LOCAL_ARRAYS) && !defined(PNG_USE_GLOBAL_ARRAYS) +# define PNG_USE_LOCAL_ARRAYS +# endif +#endif + +/* Do not use global arrays (helps with building DLL's) + * They are no longer used in libpng itself, since version 1.0.5c, + * but might be required for some pre-1.0.5c applications. + */ +#if !defined(PNG_USE_LOCAL_ARRAYS) && !defined(PNG_USE_GLOBAL_ARRAYS) +# if defined(PNG_NO_GLOBAL_ARRAYS) || \ + (defined(__GNUC__) && defined(PNG_DLL)) || defined(_MSC_VER) +# define PNG_USE_LOCAL_ARRAYS +# else +# define PNG_USE_GLOBAL_ARRAYS +# endif +#endif + +#ifdef __CYGWIN__ +# undef PNGAPI +# define PNGAPI __cdecl +# undef PNG_IMPEXP +# define PNG_IMPEXP +#endif + +/* If you define PNGAPI, e.g., with compiler option "-DPNGAPI=__stdcall", + * you may get warnings regarding the linkage of png_zalloc and png_zfree. + * Don't ignore those warnings; you must also reset the default calling + * convention in your compiler to match your PNGAPI, and you must build + * zlib and your applications the same way you build libpng. + */ + +#if defined(__MINGW32__) && !defined(PNG_MODULEDEF) +# ifndef PNG_NO_MODULEDEF +# define PNG_NO_MODULEDEF +# endif +#endif + +#if !defined(PNG_IMPEXP) && defined(PNG_BUILD_DLL) && !defined(PNG_NO_MODULEDEF) +# define PNG_IMPEXP +#endif + +#if defined(PNG_DLL) || defined(_DLL) || defined(__DLL__ ) || \ + (( defined(_Windows) || defined(_WINDOWS) || \ + defined(WIN32) || defined(_WIN32) || defined(__WIN32__) )) + +# ifndef PNGAPI +# if defined(__GNUC__) || (defined (_MSC_VER) && (_MSC_VER >= 800)) +# define PNGAPI __cdecl +# else +# define PNGAPI _cdecl +# endif +# endif + +# if !defined(PNG_IMPEXP) && (!defined(PNG_DLL) || \ + 0 /* WINCOMPILER_WITH_NO_SUPPORT_FOR_DECLIMPEXP */) +# define PNG_IMPEXP +# endif + +# ifndef PNG_IMPEXP + +# define PNG_EXPORT_TYPE1(type,symbol) PNG_IMPEXP type PNGAPI symbol +# define PNG_EXPORT_TYPE2(type,symbol) type PNG_IMPEXP PNGAPI symbol + + /* Borland/Microsoft */ +# if defined(_MSC_VER) || defined(__BORLANDC__) +# if (_MSC_VER >= 800) || (__BORLANDC__ >= 0x500) +# define PNG_EXPORT PNG_EXPORT_TYPE1 +# else +# define PNG_EXPORT PNG_EXPORT_TYPE2 +# ifdef PNG_BUILD_DLL +# define PNG_IMPEXP __export +# else +# define PNG_IMPEXP /*__import */ /* doesn't exist AFAIK in + VC++ */ +# endif /* Exists in Borland C++ for + C++ classes (== huge) */ +# endif +# endif + +# ifndef PNG_IMPEXP +# ifdef PNG_BUILD_DLL +# define PNG_IMPEXP __declspec(dllexport) +# else +# define PNG_IMPEXP __declspec(dllimport) +# endif +# endif +# endif /* PNG_IMPEXP */ +#else /* !(DLL || non-cygwin WINDOWS) */ +# if (defined(__IBMC__) || defined(__IBMCPP__)) && defined(__OS2__) +# ifndef PNGAPI +# define PNGAPI _System +# endif +# else +# if 0 /* ... other platforms, with other meanings */ +# endif +# endif +#endif + +#ifndef PNGAPI +# define PNGAPI +#endif +#ifndef PNG_IMPEXP +# define PNG_IMPEXP +#endif + +#ifdef PNG_BUILDSYMS +# ifndef PNG_EXPORT +# define PNG_EXPORT(type,symbol) PNG_FUNCTION_EXPORT symbol END +# endif +# ifdef PNG_USE_GLOBAL_ARRAYS +# ifndef PNG_EXPORT_VAR +# define PNG_EXPORT_VAR(type) PNG_DATA_EXPORT +# endif +# endif +#endif + +#ifndef PNG_EXPORT +# define PNG_EXPORT(type,symbol) PNG_IMPEXP type PNGAPI symbol +#endif + +#ifdef PNG_USE_GLOBAL_ARRAYS +# ifndef PNG_EXPORT_VAR +# define PNG_EXPORT_VAR(type) extern PNG_IMPEXP type +# endif +#endif + +#ifdef PNG_PEDANTIC_WARNINGS +# ifndef PNG_PEDANTIC_WARNINGS_SUPPORTED +# define PNG_PEDANTIC_WARNINGS_SUPPORTED +# endif +#endif + +#ifdef PNG_PEDANTIC_WARNINGS_SUPPORTED +/* Support for compiler specific function attributes. These are used + * so that where compiler support is available incorrect use of API + * functions in png.h will generate compiler warnings. Added at libpng + * version 1.2.41. + */ +# ifdef __GNUC__ +# ifndef PNG_USE_RESULT +# define PNG_USE_RESULT __attribute__((__warn_unused_result__)) +# endif +# ifndef PNG_NORETURN +# define PNG_NORETURN __attribute__((__noreturn__)) +# endif +# ifndef PNG_ALLOCATED +# define PNG_ALLOCATED __attribute__((__malloc__)) +# endif + + /* This specifically protects structure members that should only be + * accessed from within the library, therefore should be empty during + * a library build. + */ +# ifndef PNG_DEPRECATED +# define PNG_DEPRECATED __attribute__((__deprecated__)) +# endif +# ifndef PNG_DEPSTRUCT +# define PNG_DEPSTRUCT __attribute__((__deprecated__)) +# endif +# ifndef PNG_PRIVATE +# if 0 /* Doesn't work so we use deprecated instead*/ +# define PNG_PRIVATE \ + __attribute__((warning("This function is not exported by libpng."))) +# else +# define PNG_PRIVATE \ + __attribute__((__deprecated__)) +# endif +# endif /* PNG_PRIVATE */ +# endif /* __GNUC__ */ +#endif /* PNG_PEDANTIC_WARNINGS */ + +#ifndef PNG_DEPRECATED +# define PNG_DEPRECATED /* Use of this function is deprecated */ +#endif +#ifndef PNG_USE_RESULT +# define PNG_USE_RESULT /* The result of this function must be checked */ +#endif +#ifndef PNG_NORETURN +# define PNG_NORETURN /* This function does not return */ +#endif +#ifndef PNG_ALLOCATED +# define PNG_ALLOCATED /* The result of the function is new memory */ +#endif +#ifndef PNG_DEPSTRUCT +# define PNG_DEPSTRUCT /* Access to this struct member is deprecated */ +#endif +#ifndef PNG_PRIVATE +# define PNG_PRIVATE /* This is a private libpng function */ +#endif + +/* User may want to use these so they are not in PNG_INTERNAL. Any library + * functions that are passed far data must be model independent. + */ + +#ifndef PNG_ABORT +# define PNG_ABORT() abort() +#endif + +#ifdef PNG_SETJMP_SUPPORTED +# define png_jmpbuf(png_ptr) ((png_ptr)->jmpbuf) +#else +# define png_jmpbuf(png_ptr) \ + (LIBPNG_WAS_COMPILED_WITH__PNG_SETJMP_NOT_SUPPORTED) +#endif + +#ifdef USE_FAR_KEYWORD /* memory model independent fns */ +/* Use this to make far-to-near assignments */ +# define CHECK 1 +# define NOCHECK 0 +# define CVT_PTR(ptr) (png_far_to_near(png_ptr,ptr,CHECK)) +# define CVT_PTR_NOCHECK(ptr) (png_far_to_near(png_ptr,ptr,NOCHECK)) +# define png_snprintf _fsnprintf /* Added to v 1.2.19 */ +# define png_strlen _fstrlen +# define png_memcmp _fmemcmp /* SJT: added */ +# define png_memcpy _fmemcpy +# define png_memset _fmemset +#else /* Use the usual functions */ +# define CVT_PTR(ptr) (ptr) +# define CVT_PTR_NOCHECK(ptr) (ptr) +# ifndef PNG_NO_SNPRINTF +# ifdef _MSC_VER +# define png_snprintf _snprintf /* Added to v 1.2.19 */ +# define png_snprintf2 _snprintf +# define png_snprintf6 _snprintf +# else +# define png_snprintf snprintf /* Added to v 1.2.19 */ +# define png_snprintf2 snprintf +# define png_snprintf6 snprintf +# endif +# else + /* You don't have or don't want to use snprintf(). Caution: Using + * sprintf instead of snprintf exposes your application to accidental + * or malevolent buffer overflows. If you don't have snprintf() + * as a general rule you should provide one (you can get one from + * Portable OpenSSH). + */ +# define png_snprintf(s1,n,fmt,x1) sprintf(s1,fmt,x1) +# define png_snprintf2(s1,n,fmt,x1,x2) sprintf(s1,fmt,x1,x2) +# define png_snprintf6(s1,n,fmt,x1,x2,x3,x4,x5,x6) \ + sprintf(s1,fmt,x1,x2,x3,x4,x5,x6) +# endif +# define png_strlen strlen +# define png_memcmp memcmp /* SJT: added */ +# define png_memcpy memcpy +# define png_memset memset +#endif +/* End of memory model independent support */ + +/* Just a little check that someone hasn't tried to define something + * contradictory. + */ +#if (PNG_ZBUF_SIZE > 65536L) && defined(PNG_MAX_MALLOC_64K) +# undef PNG_ZBUF_SIZE +# define PNG_ZBUF_SIZE 65536L +#endif + +/* Added at libpng-1.2.8 */ +#endif /* PNG_VERSION_INFO_ONLY */ + +#endif /* PNGCONF_H */ diff --git a/src/libpng/pngerror.c b/src/libpng/pngerror.c new file mode 100644 index 00000000..025d52e7 --- /dev/null +++ b/src/libpng/pngerror.c @@ -0,0 +1,396 @@ + +/* pngerror.c - stub functions for i/o and memory allocation + * + * Last changed in libpng 1.2.45 [July 7, 2011] + * Copyright (c) 1998-2011 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + * + * This code is released under the libpng license. + * For conditions of distribution and use, see the disclaimer + * and license in png.h + * + * This file provides a location for all error handling. Users who + * need special error handling are expected to write replacement functions + * and use png_set_error_fn() to use those functions. See the instructions + * at each function. + */ + +#define PNG_INTERNAL +#define PNG_NO_PEDANTIC_WARNINGS +#include "png.h" +#if defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED) + +static void /* PRIVATE */ +png_default_error PNGARG((png_structp png_ptr, + png_const_charp error_message)) PNG_NORETURN; +#ifdef PNG_WARNINGS_SUPPORTED +static void /* PRIVATE */ +png_default_warning PNGARG((png_structp png_ptr, + png_const_charp warning_message)); +#endif /* PNG_WARNINGS_SUPPORTED */ + +/* This function is called whenever there is a fatal error. This function + * should not be changed. If there is a need to handle errors differently, + * you should supply a replacement error function and use png_set_error_fn() + * to replace the error function at run-time. + */ +#ifdef PNG_ERROR_TEXT_SUPPORTED +void PNGAPI +png_error(png_structp png_ptr, png_const_charp error_message) +{ +#ifdef PNG_ERROR_NUMBERS_SUPPORTED + char msg[16]; + if (png_ptr != NULL) + { + if (png_ptr->flags& + (PNG_FLAG_STRIP_ERROR_NUMBERS|PNG_FLAG_STRIP_ERROR_TEXT)) + { + if (*error_message == PNG_LITERAL_SHARP) + { + /* Strip "#nnnn " from beginning of error message. */ + int offset; + for (offset = 1; offset<15; offset++) + if (error_message[offset] == ' ') + break; + if (png_ptr->flags&PNG_FLAG_STRIP_ERROR_TEXT) + { + int i; + for (i = 0; i < offset - 1; i++) + msg[i] = error_message[i + 1]; + msg[i - 1] = '\0'; + error_message = msg; + } + else + error_message += offset; + } + else + { + if (png_ptr->flags&PNG_FLAG_STRIP_ERROR_TEXT) + { + msg[0] = '0'; + msg[1] = '\0'; + error_message = msg; + } + } + } + } +#endif + if (png_ptr != NULL && png_ptr->error_fn != NULL) + (*(png_ptr->error_fn))(png_ptr, error_message); + + /* If the custom handler doesn't exist, or if it returns, + use the default handler, which will not return. */ + png_default_error(png_ptr, error_message); +} +#else +void PNGAPI +png_err(png_structp png_ptr) +{ + /* Prior to 1.2.45 the error_fn received a NULL pointer, expressed + * erroneously as '\0', instead of the empty string "". This was + * apparently an error, introduced in libpng-1.2.20, and png_default_error + * will crash in this case. + */ + if (png_ptr != NULL && png_ptr->error_fn != NULL) + (*(png_ptr->error_fn))(png_ptr, ""); + + /* If the custom handler doesn't exist, or if it returns, + use the default handler, which will not return. */ + png_default_error(png_ptr, ""); +} +#endif /* PNG_ERROR_TEXT_SUPPORTED */ + +#ifdef PNG_WARNINGS_SUPPORTED +/* This function is called whenever there is a non-fatal error. This function + * should not be changed. If there is a need to handle warnings differently, + * you should supply a replacement warning function and use + * png_set_error_fn() to replace the warning function at run-time. + */ +void PNGAPI +png_warning(png_structp png_ptr, png_const_charp warning_message) +{ + int offset = 0; + if (png_ptr != NULL) + { +#ifdef PNG_ERROR_NUMBERS_SUPPORTED + if (png_ptr->flags& + (PNG_FLAG_STRIP_ERROR_NUMBERS|PNG_FLAG_STRIP_ERROR_TEXT)) +#endif + { + if (*warning_message == PNG_LITERAL_SHARP) + { + for (offset = 1; offset < 15; offset++) + if (warning_message[offset] == ' ') + break; + } + } + } + if (png_ptr != NULL && png_ptr->warning_fn != NULL) + (*(png_ptr->warning_fn))(png_ptr, warning_message + offset); + else + png_default_warning(png_ptr, warning_message + offset); +} +#endif /* PNG_WARNINGS_SUPPORTED */ + +#ifdef PNG_BENIGN_ERRORS_SUPPORTED +void PNGAPI +png_benign_error(png_structp png_ptr, png_const_charp error_message) +{ + if (png_ptr->flags & PNG_FLAG_BENIGN_ERRORS_WARN) + png_warning(png_ptr, error_message); + else + png_error(png_ptr, error_message); +} +#endif + +/* These utilities are used internally to build an error message that relates + * to the current chunk. The chunk name comes from png_ptr->chunk_name, + * this is used to prefix the message. The message is limited in length + * to 63 bytes, the name characters are output as hex digits wrapped in [] + * if the character is invalid. + */ +#define isnonalpha(c) ((c) < 65 || (c) > 122 || ((c) > 90 && (c) < 97)) +static PNG_CONST char png_digit[16] = { + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', + 'A', 'B', 'C', 'D', 'E', 'F' +}; + +#define PNG_MAX_ERROR_TEXT 64 +#if defined(PNG_WARNINGS_SUPPORTED) || defined(PNG_ERROR_TEXT_SUPPORTED) +static void /* PRIVATE */ +png_format_buffer(png_structp png_ptr, png_charp buffer, png_const_charp + error_message) +{ + int iout = 0, iin = 0; + + while (iin < 4) + { + int c = png_ptr->chunk_name[iin++]; + if (isnonalpha(c)) + { + buffer[iout++] = PNG_LITERAL_LEFT_SQUARE_BRACKET; + buffer[iout++] = png_digit[(c & 0xf0) >> 4]; + buffer[iout++] = png_digit[c & 0x0f]; + buffer[iout++] = PNG_LITERAL_RIGHT_SQUARE_BRACKET; + } + else + { + buffer[iout++] = (png_byte)c; + } + } + + if (error_message == NULL) + buffer[iout] = '\0'; + else + { + buffer[iout++] = ':'; + buffer[iout++] = ' '; + + iin = 0; + while (iin < PNG_MAX_ERROR_TEXT-1 && error_message[iin] != '\0') + buffer[iout++] = error_message[iin++]; + + /* iin < PNG_MAX_ERROR_TEXT, so the following is safe: */ + buffer[iout] = '\0'; + } +} + +#ifdef PNG_READ_SUPPORTED +void PNGAPI +png_chunk_error(png_structp png_ptr, png_const_charp error_message) +{ + char msg[18+PNG_MAX_ERROR_TEXT]; + if (png_ptr == NULL) + png_error(png_ptr, error_message); + else + { + png_format_buffer(png_ptr, msg, error_message); + png_error(png_ptr, msg); + } +} +#endif /* PNG_READ_SUPPORTED */ +#endif /* PNG_WARNINGS_SUPPORTED || PNG_ERROR_TEXT_SUPPORTED */ + +#ifdef PNG_WARNINGS_SUPPORTED +void PNGAPI +png_chunk_warning(png_structp png_ptr, png_const_charp warning_message) +{ + char msg[18+PNG_MAX_ERROR_TEXT]; + if (png_ptr == NULL) + png_warning(png_ptr, warning_message); + else + { + png_format_buffer(png_ptr, msg, warning_message); + png_warning(png_ptr, msg); + } +} +#endif /* PNG_WARNINGS_SUPPORTED */ + +#ifdef PNG_READ_SUPPORTED +#ifdef PNG_BENIGN_ERRORS_SUPPORTED +void PNGAPI +png_chunk_benign_error(png_structp png_ptr, png_const_charp error_message) +{ + if (png_ptr->flags & PNG_FLAG_BENIGN_ERRORS_WARN) + png_chunk_warning(png_ptr, error_message); + else + png_chunk_error(png_ptr, error_message); +} +#endif +#endif /* PNG_READ_SUPPORTED */ + +/* This is the default error handling function. Note that replacements for + * this function MUST NOT RETURN, or the program will likely crash. This + * function is used by default, or if the program supplies NULL for the + * error function pointer in png_set_error_fn(). + */ +static void /* PRIVATE */ +png_default_error(png_structp png_ptr, png_const_charp error_message) +{ +#ifdef PNG_CONSOLE_IO_SUPPORTED +#ifdef PNG_ERROR_NUMBERS_SUPPORTED + if (*error_message == PNG_LITERAL_SHARP) + { + /* Strip "#nnnn " from beginning of error message. */ + int offset; + char error_number[16]; + for (offset = 0; offset<15; offset++) + { + error_number[offset] = error_message[offset + 1]; + if (error_message[offset] == ' ') + break; + } + if ((offset > 1) && (offset < 15)) + { + error_number[offset - 1] = '\0'; + fprintf(stderr, "libpng error no. %s: %s", + error_number, error_message + offset + 1); + fprintf(stderr, PNG_STRING_NEWLINE); + } + else + { + fprintf(stderr, "libpng error: %s, offset=%d", + error_message, offset); + fprintf(stderr, PNG_STRING_NEWLINE); + } + } + else +#endif + { + fprintf(stderr, "libpng error: %s", error_message); + fprintf(stderr, PNG_STRING_NEWLINE); + } +#endif + +#ifdef PNG_SETJMP_SUPPORTED + if (png_ptr) + { +# ifdef USE_FAR_KEYWORD + { + jmp_buf jmpbuf; + png_memcpy(jmpbuf, png_ptr->jmpbuf, png_sizeof(jmp_buf)); + longjmp(jmpbuf,1); + } +# else + longjmp(png_ptr->jmpbuf, 1); +# endif + } +#endif + /* Here if not setjmp support or if png_ptr is null. */ + PNG_ABORT(); +#ifndef PNG_CONSOLE_IO_SUPPORTED + error_message = error_message; /* Make compiler happy */ +#endif +} + +#ifdef PNG_WARNINGS_SUPPORTED +/* This function is called when there is a warning, but the library thinks + * it can continue anyway. Replacement functions don't have to do anything + * here if you don't want them to. In the default configuration, png_ptr is + * not used, but it is passed in case it may be useful. + */ +static void /* PRIVATE */ +png_default_warning(png_structp png_ptr, png_const_charp warning_message) +{ +#ifdef PNG_CONSOLE_IO_SUPPORTED +# ifdef PNG_ERROR_NUMBERS_SUPPORTED + if (*warning_message == PNG_LITERAL_SHARP) + { + int offset; + char warning_number[16]; + for (offset = 0; offset < 15; offset++) + { + warning_number[offset] = warning_message[offset + 1]; + if (warning_message[offset] == ' ') + break; + } + if ((offset > 1) && (offset < 15)) + { + warning_number[offset + 1] = '\0'; + fprintf(stderr, "libpng warning no. %s: %s", + warning_number, warning_message + offset); + fprintf(stderr, PNG_STRING_NEWLINE); + } + else + { + fprintf(stderr, "libpng warning: %s", + warning_message); + fprintf(stderr, PNG_STRING_NEWLINE); + } + } + else +# endif + { + fprintf(stderr, "libpng warning: %s", warning_message); + fprintf(stderr, PNG_STRING_NEWLINE); + } +#else + warning_message = warning_message; /* Make compiler happy */ +#endif + png_ptr = png_ptr; /* Make compiler happy */ +} +#endif /* PNG_WARNINGS_SUPPORTED */ + +/* This function is called when the application wants to use another method + * of handling errors and warnings. Note that the error function MUST NOT + * return to the calling routine or serious problems will occur. The return + * method used in the default routine calls longjmp(png_ptr->jmpbuf, 1) + */ +void PNGAPI +png_set_error_fn(png_structp png_ptr, png_voidp error_ptr, + png_error_ptr error_fn, png_error_ptr warning_fn) +{ + if (png_ptr == NULL) + return; + png_ptr->error_ptr = error_ptr; + png_ptr->error_fn = error_fn; + png_ptr->warning_fn = warning_fn; +} + + +/* This function returns a pointer to the error_ptr associated with the user + * functions. The application should free any memory associated with this + * pointer before png_write_destroy and png_read_destroy are called. + */ +png_voidp PNGAPI +png_get_error_ptr(png_structp png_ptr) +{ + if (png_ptr == NULL) + return NULL; + return ((png_voidp)png_ptr->error_ptr); +} + + +#ifdef PNG_ERROR_NUMBERS_SUPPORTED +void PNGAPI +png_set_strip_error_numbers(png_structp png_ptr, png_uint_32 strip_mode) +{ + if (png_ptr != NULL) + { + png_ptr->flags &= + ((~(PNG_FLAG_STRIP_ERROR_NUMBERS|PNG_FLAG_STRIP_ERROR_TEXT))&strip_mode); + } +} +#endif +#endif /* PNG_READ_SUPPORTED || PNG_WRITE_SUPPORTED */ diff --git a/src/libpng/pngmem.c b/src/libpng/pngmem.c new file mode 100644 index 00000000..a18719b8 --- /dev/null +++ b/src/libpng/pngmem.c @@ -0,0 +1,641 @@ + +/* pngmem.c - stub functions for memory allocation + * + * Last changed in libpng 1.2.41 [February 25, 2010] + * Copyright (c) 1998-2010 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + * + * This code is released under the libpng license. + * For conditions of distribution and use, see the disclaimer + * and license in png.h + * + * This file provides a location for all memory allocation. Users who + * need special memory handling are expected to supply replacement + * functions for png_malloc() and png_free(), and to use + * png_create_read_struct_2() and png_create_write_struct_2() to + * identify the replacement functions. + */ + +#define PNG_INTERNAL +#define PNG_NO_PEDANTIC_WARNINGS +#include "png.h" +#if defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED) + +/* Borland DOS special memory handler */ +#if defined(__TURBOC__) && !defined(_Windows) && !defined(__FLAT__) +/* If you change this, be sure to change the one in png.h also */ + +/* Allocate memory for a png_struct. The malloc and memset can be replaced + by a single call to calloc() if this is thought to improve performance. */ +png_voidp /* PRIVATE */ +png_create_struct(int type) +{ +#ifdef PNG_USER_MEM_SUPPORTED + return (png_create_struct_2(type, png_malloc_ptr_NULL, png_voidp_NULL)); +} + +/* Alternate version of png_create_struct, for use with user-defined malloc. */ +png_voidp /* PRIVATE */ +png_create_struct_2(int type, png_malloc_ptr malloc_fn, png_voidp mem_ptr) +{ +#endif /* PNG_USER_MEM_SUPPORTED */ + png_size_t size; + png_voidp struct_ptr; + + if (type == PNG_STRUCT_INFO) + size = png_sizeof(png_info); + else if (type == PNG_STRUCT_PNG) + size = png_sizeof(png_struct); + else + return (png_get_copyright(NULL)); + +#ifdef PNG_USER_MEM_SUPPORTED + if (malloc_fn != NULL) + { + png_struct dummy_struct; + png_structp png_ptr = &dummy_struct; + png_ptr->mem_ptr=mem_ptr; + struct_ptr = (*(malloc_fn))(png_ptr, (png_uint_32)size); + } + else +#endif /* PNG_USER_MEM_SUPPORTED */ + struct_ptr = (png_voidp)farmalloc(size); + if (struct_ptr != NULL) + png_memset(struct_ptr, 0, size); + return (struct_ptr); +} + +/* Free memory allocated by a png_create_struct() call */ +void /* PRIVATE */ +png_destroy_struct(png_voidp struct_ptr) +{ +#ifdef PNG_USER_MEM_SUPPORTED + png_destroy_struct_2(struct_ptr, png_free_ptr_NULL, png_voidp_NULL); +} + +/* Free memory allocated by a png_create_struct() call */ +void /* PRIVATE */ +png_destroy_struct_2(png_voidp struct_ptr, png_free_ptr free_fn, + png_voidp mem_ptr) +{ +#endif + if (struct_ptr != NULL) + { +#ifdef PNG_USER_MEM_SUPPORTED + if (free_fn != NULL) + { + png_struct dummy_struct; + png_structp png_ptr = &dummy_struct; + png_ptr->mem_ptr=mem_ptr; + (*(free_fn))(png_ptr, struct_ptr); + return; + } +#endif /* PNG_USER_MEM_SUPPORTED */ + farfree (struct_ptr); + } +} + +/* Allocate memory. For reasonable files, size should never exceed + * 64K. However, zlib may allocate more then 64K if you don't tell + * it not to. See zconf.h and png.h for more information. zlib does + * need to allocate exactly 64K, so whatever you call here must + * have the ability to do that. + * + * Borland seems to have a problem in DOS mode for exactly 64K. + * It gives you a segment with an offset of 8 (perhaps to store its + * memory stuff). zlib doesn't like this at all, so we have to + * detect and deal with it. This code should not be needed in + * Windows or OS/2 modes, and only in 16 bit mode. This code has + * been updated by Alexander Lehmann for version 0.89 to waste less + * memory. + * + * Note that we can't use png_size_t for the "size" declaration, + * since on some systems a png_size_t is a 16-bit quantity, and as a + * result, we would be truncating potentially larger memory requests + * (which should cause a fatal error) and introducing major problems. + */ +png_voidp /* PRIVATE */ +png_calloc(png_structp png_ptr, png_uint_32 size) +{ + png_voidp ret; + + ret = (png_malloc(png_ptr, size)); + if (ret != NULL) + png_memset(ret,0,(png_size_t)size); + return (ret); +} + +png_voidp PNGAPI +png_malloc(png_structp png_ptr, png_uint_32 size) +{ + png_voidp ret; + + if (png_ptr == NULL || size == 0) + return (NULL); + +#ifdef PNG_USER_MEM_SUPPORTED + if (png_ptr->malloc_fn != NULL) + ret = ((png_voidp)(*(png_ptr->malloc_fn))(png_ptr, (png_size_t)size)); + else + ret = (png_malloc_default(png_ptr, size)); + if (ret == NULL && (png_ptr->flags&PNG_FLAG_MALLOC_NULL_MEM_OK) == 0) + png_error(png_ptr, "Out of memory!"); + return (ret); +} + +png_voidp PNGAPI +png_malloc_default(png_structp png_ptr, png_uint_32 size) +{ + png_voidp ret; +#endif /* PNG_USER_MEM_SUPPORTED */ + + if (png_ptr == NULL || size == 0) + return (NULL); + +#ifdef PNG_MAX_MALLOC_64K + if (size > (png_uint_32)65536L) + { + png_warning(png_ptr, "Cannot Allocate > 64K"); + ret = NULL; + } + else +#endif + + if (size != (size_t)size) + ret = NULL; + else if (size == (png_uint_32)65536L) + { + if (png_ptr->offset_table == NULL) + { + /* Try to see if we need to do any of this fancy stuff */ + ret = farmalloc(size); + if (ret == NULL || ((png_size_t)ret & 0xffff)) + { + int num_blocks; + png_uint_32 total_size; + png_bytep table; + int i; + png_byte huge * hptr; + + if (ret != NULL) + { + farfree(ret); + ret = NULL; + } + + if (png_ptr->zlib_window_bits > 14) + num_blocks = (int)(1 << (png_ptr->zlib_window_bits - 14)); + else + num_blocks = 1; + if (png_ptr->zlib_mem_level >= 7) + num_blocks += (int)(1 << (png_ptr->zlib_mem_level - 7)); + else + num_blocks++; + + total_size = ((png_uint_32)65536L) * (png_uint_32)num_blocks+16; + + table = farmalloc(total_size); + + if (table == NULL) + { +#ifndef PNG_USER_MEM_SUPPORTED + if ((png_ptr->flags&PNG_FLAG_MALLOC_NULL_MEM_OK) == 0) + png_error(png_ptr, "Out Of Memory."); /* Note "O", "M" */ + else + png_warning(png_ptr, "Out Of Memory."); +#endif + return (NULL); + } + + if ((png_size_t)table & 0xfff0) + { +#ifndef PNG_USER_MEM_SUPPORTED + if ((png_ptr->flags&PNG_FLAG_MALLOC_NULL_MEM_OK) == 0) + png_error(png_ptr, + "Farmalloc didn't return normalized pointer"); + else + png_warning(png_ptr, + "Farmalloc didn't return normalized pointer"); +#endif + return (NULL); + } + + png_ptr->offset_table = table; + png_ptr->offset_table_ptr = farmalloc(num_blocks * + png_sizeof(png_bytep)); + + if (png_ptr->offset_table_ptr == NULL) + { +#ifndef PNG_USER_MEM_SUPPORTED + if ((png_ptr->flags&PNG_FLAG_MALLOC_NULL_MEM_OK) == 0) + png_error(png_ptr, "Out Of memory."); /* Note "O", "m" */ + else + png_warning(png_ptr, "Out Of memory."); +#endif + return (NULL); + } + + hptr = (png_byte huge *)table; + if ((png_size_t)hptr & 0xf) + { + hptr = (png_byte huge *)((long)(hptr) & 0xfffffff0L); + hptr = hptr + 16L; /* "hptr += 16L" fails on Turbo C++ 3.0 */ + } + for (i = 0; i < num_blocks; i++) + { + png_ptr->offset_table_ptr[i] = (png_bytep)hptr; + hptr = hptr + (png_uint_32)65536L; /* "+=" fails on TC++3.0 */ + } + + png_ptr->offset_table_number = num_blocks; + png_ptr->offset_table_count = 0; + png_ptr->offset_table_count_free = 0; + } + } + + if (png_ptr->offset_table_count >= png_ptr->offset_table_number) + { +#ifndef PNG_USER_MEM_SUPPORTED + if ((png_ptr->flags&PNG_FLAG_MALLOC_NULL_MEM_OK) == 0) + png_error(png_ptr, "Out of Memory."); /* Note "o" and "M" */ + else + png_warning(png_ptr, "Out of Memory."); +#endif + return (NULL); + } + + ret = png_ptr->offset_table_ptr[png_ptr->offset_table_count++]; + } + else + ret = farmalloc(size); + +#ifndef PNG_USER_MEM_SUPPORTED + if (ret == NULL) + { + if ((png_ptr->flags&PNG_FLAG_MALLOC_NULL_MEM_OK) == 0) + png_error(png_ptr, "Out of memory."); /* Note "o" and "m" */ + else + png_warning(png_ptr, "Out of memory."); /* Note "o" and "m" */ + } +#endif + + return (ret); +} + +/* Free a pointer allocated by png_malloc(). In the default + * configuration, png_ptr is not used, but is passed in case it + * is needed. If ptr is NULL, return without taking any action. + */ +void PNGAPI +png_free(png_structp png_ptr, png_voidp ptr) +{ + if (png_ptr == NULL || ptr == NULL) + return; + +#ifdef PNG_USER_MEM_SUPPORTED + if (png_ptr->free_fn != NULL) + { + (*(png_ptr->free_fn))(png_ptr, ptr); + return; + } + else + png_free_default(png_ptr, ptr); +} + +void PNGAPI +png_free_default(png_structp png_ptr, png_voidp ptr) +{ +#endif /* PNG_USER_MEM_SUPPORTED */ + + if (png_ptr == NULL || ptr == NULL) + return; + + if (png_ptr->offset_table != NULL) + { + int i; + + for (i = 0; i < png_ptr->offset_table_count; i++) + { + if (ptr == png_ptr->offset_table_ptr[i]) + { + ptr = NULL; + png_ptr->offset_table_count_free++; + break; + } + } + if (png_ptr->offset_table_count_free == png_ptr->offset_table_count) + { + farfree(png_ptr->offset_table); + farfree(png_ptr->offset_table_ptr); + png_ptr->offset_table = NULL; + png_ptr->offset_table_ptr = NULL; + } + } + + if (ptr != NULL) + { + farfree(ptr); + } +} + +#else /* Not the Borland DOS special memory handler */ + +/* Allocate memory for a png_struct or a png_info. The malloc and + memset can be replaced by a single call to calloc() if this is thought + to improve performance noticably. */ +png_voidp /* PRIVATE */ +png_create_struct(int type) +{ +#ifdef PNG_USER_MEM_SUPPORTED + return (png_create_struct_2(type, png_malloc_ptr_NULL, png_voidp_NULL)); +} + +/* Allocate memory for a png_struct or a png_info. The malloc and + memset can be replaced by a single call to calloc() if this is thought + to improve performance noticably. */ +png_voidp /* PRIVATE */ +png_create_struct_2(int type, png_malloc_ptr malloc_fn, png_voidp mem_ptr) +{ +#endif /* PNG_USER_MEM_SUPPORTED */ + png_size_t size; + png_voidp struct_ptr; + + if (type == PNG_STRUCT_INFO) + size = png_sizeof(png_info); + else if (type == PNG_STRUCT_PNG) + size = png_sizeof(png_struct); + else + return (NULL); + +#ifdef PNG_USER_MEM_SUPPORTED + if (malloc_fn != NULL) + { + png_struct dummy_struct; + png_structp png_ptr = &dummy_struct; + png_ptr->mem_ptr=mem_ptr; + struct_ptr = (*(malloc_fn))(png_ptr, size); + if (struct_ptr != NULL) + png_memset(struct_ptr, 0, size); + return (struct_ptr); + } +#endif /* PNG_USER_MEM_SUPPORTED */ + +#if defined(__TURBOC__) && !defined(__FLAT__) + struct_ptr = (png_voidp)farmalloc(size); +#else +# if defined(_MSC_VER) && defined(MAXSEG_64K) + struct_ptr = (png_voidp)halloc(size, 1); +# else + struct_ptr = (png_voidp)malloc(size); +# endif +#endif + if (struct_ptr != NULL) + png_memset(struct_ptr, 0, size); + + return (struct_ptr); +} + + +/* Free memory allocated by a png_create_struct() call */ +void /* PRIVATE */ +png_destroy_struct(png_voidp struct_ptr) +{ +#ifdef PNG_USER_MEM_SUPPORTED + png_destroy_struct_2(struct_ptr, png_free_ptr_NULL, png_voidp_NULL); +} + +/* Free memory allocated by a png_create_struct() call */ +void /* PRIVATE */ +png_destroy_struct_2(png_voidp struct_ptr, png_free_ptr free_fn, + png_voidp mem_ptr) +{ +#endif /* PNG_USER_MEM_SUPPORTED */ + if (struct_ptr != NULL) + { +#ifdef PNG_USER_MEM_SUPPORTED + if (free_fn != NULL) + { + png_struct dummy_struct; + png_structp png_ptr = &dummy_struct; + png_ptr->mem_ptr=mem_ptr; + (*(free_fn))(png_ptr, struct_ptr); + return; + } +#endif /* PNG_USER_MEM_SUPPORTED */ +#if defined(__TURBOC__) && !defined(__FLAT__) + farfree(struct_ptr); +#else +# if defined(_MSC_VER) && defined(MAXSEG_64K) + hfree(struct_ptr); +# else + free(struct_ptr); +# endif +#endif + } +} + +/* Allocate memory. For reasonable files, size should never exceed + * 64K. However, zlib may allocate more then 64K if you don't tell + * it not to. See zconf.h and png.h for more information. zlib does + * need to allocate exactly 64K, so whatever you call here must + * have the ability to do that. + */ + +png_voidp /* PRIVATE */ +png_calloc(png_structp png_ptr, png_uint_32 size) +{ + png_voidp ret; + + ret = (png_malloc(png_ptr, size)); + if (ret != NULL) + png_memset(ret,0,(png_size_t)size); + return (ret); +} + +png_voidp PNGAPI +png_malloc(png_structp png_ptr, png_uint_32 size) +{ + png_voidp ret; + +#ifdef PNG_USER_MEM_SUPPORTED + if (png_ptr == NULL || size == 0) + return (NULL); + + if (png_ptr->malloc_fn != NULL) + ret = ((png_voidp)(*(png_ptr->malloc_fn))(png_ptr, (png_size_t)size)); + else + ret = (png_malloc_default(png_ptr, size)); + if (ret == NULL && (png_ptr->flags&PNG_FLAG_MALLOC_NULL_MEM_OK) == 0) + png_error(png_ptr, "Out of Memory!"); + return (ret); +} + +png_voidp PNGAPI +png_malloc_default(png_structp png_ptr, png_uint_32 size) +{ + png_voidp ret; +#endif /* PNG_USER_MEM_SUPPORTED */ + + if (png_ptr == NULL || size == 0) + return (NULL); + +#ifdef PNG_MAX_MALLOC_64K + if (size > (png_uint_32)65536L) + { +#ifndef PNG_USER_MEM_SUPPORTED + if ((png_ptr->flags&PNG_FLAG_MALLOC_NULL_MEM_OK) == 0) + png_error(png_ptr, "Cannot Allocate > 64K"); + else +#endif + return NULL; + } +#endif + + /* Check for overflow */ +#if defined(__TURBOC__) && !defined(__FLAT__) + if (size != (unsigned long)size) + ret = NULL; + else + ret = farmalloc(size); +#else +# if defined(_MSC_VER) && defined(MAXSEG_64K) + if (size != (unsigned long)size) + ret = NULL; + else + ret = halloc(size, 1); +# else + if (size != (size_t)size) + ret = NULL; + else + ret = malloc((size_t)size); +# endif +#endif + +#ifndef PNG_USER_MEM_SUPPORTED + if (ret == NULL && (png_ptr->flags&PNG_FLAG_MALLOC_NULL_MEM_OK) == 0) + png_error(png_ptr, "Out of Memory"); +#endif + + return (ret); +} + +/* Free a pointer allocated by png_malloc(). If ptr is NULL, return + * without taking any action. + */ +void PNGAPI +png_free(png_structp png_ptr, png_voidp ptr) +{ + if (png_ptr == NULL || ptr == NULL) + return; + +#ifdef PNG_USER_MEM_SUPPORTED + if (png_ptr->free_fn != NULL) + { + (*(png_ptr->free_fn))(png_ptr, ptr); + return; + } + else + png_free_default(png_ptr, ptr); +} +void PNGAPI +png_free_default(png_structp png_ptr, png_voidp ptr) +{ + if (png_ptr == NULL || ptr == NULL) + return; + +#endif /* PNG_USER_MEM_SUPPORTED */ + +#if defined(__TURBOC__) && !defined(__FLAT__) + farfree(ptr); +#else +# if defined(_MSC_VER) && defined(MAXSEG_64K) + hfree(ptr); +# else + free(ptr); +# endif +#endif +} + +#endif /* Not Borland DOS special memory handler */ + +#ifdef PNG_1_0_X +# define png_malloc_warn png_malloc +#else +/* This function was added at libpng version 1.2.3. The png_malloc_warn() + * function will set up png_malloc() to issue a png_warning and return NULL + * instead of issuing a png_error, if it fails to allocate the requested + * memory. + */ +png_voidp PNGAPI +png_malloc_warn(png_structp png_ptr, png_uint_32 size) +{ + png_voidp ptr; + png_uint_32 save_flags; + if (png_ptr == NULL) + return (NULL); + + save_flags = png_ptr->flags; + png_ptr->flags|=PNG_FLAG_MALLOC_NULL_MEM_OK; + ptr = (png_voidp)png_malloc((png_structp)png_ptr, size); + png_ptr->flags=save_flags; + return(ptr); +} +#endif + +png_voidp PNGAPI +png_memcpy_check (png_structp png_ptr, png_voidp s1, png_voidp s2, + png_uint_32 length) +{ + png_size_t size; + + size = (png_size_t)length; + if ((png_uint_32)size != length) + png_error(png_ptr, "Overflow in png_memcpy_check."); + + return(png_memcpy (s1, s2, size)); +} + +png_voidp PNGAPI +png_memset_check (png_structp png_ptr, png_voidp s1, int value, + png_uint_32 length) +{ + png_size_t size; + + size = (png_size_t)length; + if ((png_uint_32)size != length) + png_error(png_ptr, "Overflow in png_memset_check."); + + return (png_memset (s1, value, size)); + +} + +#ifdef PNG_USER_MEM_SUPPORTED +/* This function is called when the application wants to use another method + * of allocating and freeing memory. + */ +void PNGAPI +png_set_mem_fn(png_structp png_ptr, png_voidp mem_ptr, png_malloc_ptr + malloc_fn, png_free_ptr free_fn) +{ + if (png_ptr != NULL) + { + png_ptr->mem_ptr = mem_ptr; + png_ptr->malloc_fn = malloc_fn; + png_ptr->free_fn = free_fn; + } +} + +/* This function returns a pointer to the mem_ptr associated with the user + * functions. The application should free any memory associated with this + * pointer before png_write_destroy and png_read_destroy are called. + */ +png_voidp PNGAPI +png_get_mem_ptr(png_structp png_ptr) +{ + if (png_ptr == NULL) + return (NULL); + return ((png_voidp)png_ptr->mem_ptr); +} +#endif /* PNG_USER_MEM_SUPPORTED */ +#endif /* PNG_READ_SUPPORTED || PNG_WRITE_SUPPORTED */ diff --git a/src/libpng/pngset.c b/src/libpng/pngset.c new file mode 100644 index 00000000..72d89fcf --- /dev/null +++ b/src/libpng/pngset.c @@ -0,0 +1,1236 @@ + +/* pngset.c - storage of image information into info struct + * + * Last changed in libpng 1.2.49 [March 29, 2012] + * Copyright (c) 1998-2012 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + * + * This code is released under the libpng license. + * For conditions of distribution and use, see the disclaimer + * and license in png.h + * + * The functions here are used during reads to store data from the file + * into the info struct, and during writes to store application data + * into the info struct for writing into the file. This abstracts the + * info struct and allows us to change the structure in the future. + */ + +#define PNG_INTERNAL +#define PNG_NO_PEDANTIC_WARNINGS +#include "png.h" +#if defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED) + +#ifdef PNG_bKGD_SUPPORTED +void PNGAPI +png_set_bKGD(png_structp png_ptr, png_infop info_ptr, png_color_16p background) +{ + png_debug1(1, "in %s storage function", "bKGD"); + + if (png_ptr == NULL || info_ptr == NULL) + return; + + png_memcpy(&(info_ptr->background), background, png_sizeof(png_color_16)); + info_ptr->valid |= PNG_INFO_bKGD; +} +#endif + +#ifdef PNG_cHRM_SUPPORTED +#ifdef PNG_FLOATING_POINT_SUPPORTED +void PNGAPI +png_set_cHRM(png_structp png_ptr, png_infop info_ptr, + double white_x, double white_y, double red_x, double red_y, + double green_x, double green_y, double blue_x, double blue_y) +{ + png_debug1(1, "in %s storage function", "cHRM"); + + if (png_ptr == NULL || info_ptr == NULL) + return; + + info_ptr->x_white = (float)white_x; + info_ptr->y_white = (float)white_y; + info_ptr->x_red = (float)red_x; + info_ptr->y_red = (float)red_y; + info_ptr->x_green = (float)green_x; + info_ptr->y_green = (float)green_y; + info_ptr->x_blue = (float)blue_x; + info_ptr->y_blue = (float)blue_y; +#ifdef PNG_FIXED_POINT_SUPPORTED + info_ptr->int_x_white = (png_fixed_point)(white_x*100000.+0.5); + info_ptr->int_y_white = (png_fixed_point)(white_y*100000.+0.5); + info_ptr->int_x_red = (png_fixed_point)( red_x*100000.+0.5); + info_ptr->int_y_red = (png_fixed_point)( red_y*100000.+0.5); + info_ptr->int_x_green = (png_fixed_point)(green_x*100000.+0.5); + info_ptr->int_y_green = (png_fixed_point)(green_y*100000.+0.5); + info_ptr->int_x_blue = (png_fixed_point)( blue_x*100000.+0.5); + info_ptr->int_y_blue = (png_fixed_point)( blue_y*100000.+0.5); +#endif + info_ptr->valid |= PNG_INFO_cHRM; +} +#endif /* PNG_FLOATING_POINT_SUPPORTED */ + +#ifdef PNG_FIXED_POINT_SUPPORTED +void PNGAPI +png_set_cHRM_fixed(png_structp png_ptr, png_infop info_ptr, + png_fixed_point white_x, png_fixed_point white_y, png_fixed_point red_x, + png_fixed_point red_y, png_fixed_point green_x, png_fixed_point green_y, + png_fixed_point blue_x, png_fixed_point blue_y) +{ + png_debug1(1, "in %s storage function", "cHRM fixed"); + + if (png_ptr == NULL || info_ptr == NULL) + return; + +#ifdef PNG_CHECK_cHRM_SUPPORTED + if (png_check_cHRM_fixed(png_ptr, + white_x, white_y, red_x, red_y, green_x, green_y, blue_x, blue_y)) +#endif + { + info_ptr->int_x_white = white_x; + info_ptr->int_y_white = white_y; + info_ptr->int_x_red = red_x; + info_ptr->int_y_red = red_y; + info_ptr->int_x_green = green_x; + info_ptr->int_y_green = green_y; + info_ptr->int_x_blue = blue_x; + info_ptr->int_y_blue = blue_y; +#ifdef PNG_FLOATING_POINT_SUPPORTED + info_ptr->x_white = (float)(white_x/100000.); + info_ptr->y_white = (float)(white_y/100000.); + info_ptr->x_red = (float)( red_x/100000.); + info_ptr->y_red = (float)( red_y/100000.); + info_ptr->x_green = (float)(green_x/100000.); + info_ptr->y_green = (float)(green_y/100000.); + info_ptr->x_blue = (float)( blue_x/100000.); + info_ptr->y_blue = (float)( blue_y/100000.); +#endif + info_ptr->valid |= PNG_INFO_cHRM; + } +} +#endif /* PNG_FIXED_POINT_SUPPORTED */ +#endif /* PNG_cHRM_SUPPORTED */ + +#ifdef PNG_gAMA_SUPPORTED +#ifdef PNG_FLOATING_POINT_SUPPORTED +void PNGAPI +png_set_gAMA(png_structp png_ptr, png_infop info_ptr, double file_gamma) +{ + double png_gamma; + + png_debug1(1, "in %s storage function", "gAMA"); + + if (png_ptr == NULL || info_ptr == NULL) + return; + + /* Check for overflow */ + if (file_gamma > 21474.83) + { + png_warning(png_ptr, "Limiting gamma to 21474.83"); + png_gamma=21474.83; + } + else + png_gamma = file_gamma; + info_ptr->gamma = (float)png_gamma; +#ifdef PNG_FIXED_POINT_SUPPORTED + info_ptr->int_gamma = (int)(png_gamma*100000.+.5); +#endif + info_ptr->valid |= PNG_INFO_gAMA; + if (png_gamma == 0.0) + png_warning(png_ptr, "Setting gamma=0"); +} +#endif +void PNGAPI +png_set_gAMA_fixed(png_structp png_ptr, png_infop info_ptr, png_fixed_point + int_gamma) +{ + png_fixed_point png_gamma; + + png_debug1(1, "in %s storage function", "gAMA"); + + if (png_ptr == NULL || info_ptr == NULL) + return; + + if (int_gamma > (png_fixed_point)PNG_UINT_31_MAX) + { + png_warning(png_ptr, "Limiting gamma to 21474.83"); + png_gamma=PNG_UINT_31_MAX; + } + else + { + if (int_gamma < 0) + { + png_warning(png_ptr, "Setting negative gamma to zero"); + png_gamma = 0; + } + else + png_gamma = int_gamma; + } +#ifdef PNG_FLOATING_POINT_SUPPORTED + info_ptr->gamma = (float)(png_gamma/100000.); +#endif +#ifdef PNG_FIXED_POINT_SUPPORTED + info_ptr->int_gamma = png_gamma; +#endif + info_ptr->valid |= PNG_INFO_gAMA; + if (png_gamma == 0) + png_warning(png_ptr, "Setting gamma=0"); +} +#endif + +#ifdef PNG_hIST_SUPPORTED +void PNGAPI +png_set_hIST(png_structp png_ptr, png_infop info_ptr, png_uint_16p hist) +{ + int i; + + png_debug1(1, "in %s storage function", "hIST"); + + if (png_ptr == NULL || info_ptr == NULL) + return; + + if (info_ptr->num_palette == 0 || info_ptr->num_palette + > PNG_MAX_PALETTE_LENGTH) + { + png_warning(png_ptr, + "Invalid palette size, hIST allocation skipped."); + return; + } + +#ifdef PNG_FREE_ME_SUPPORTED + png_free_data(png_ptr, info_ptr, PNG_FREE_HIST, 0); +#endif + /* Changed from info->num_palette to PNG_MAX_PALETTE_LENGTH in + * version 1.2.1 + */ + png_ptr->hist = (png_uint_16p)png_malloc_warn(png_ptr, + (png_uint_32)(PNG_MAX_PALETTE_LENGTH * png_sizeof(png_uint_16))); + if (png_ptr->hist == NULL) + { + png_warning(png_ptr, "Insufficient memory for hIST chunk data."); + return; + } + + for (i = 0; i < info_ptr->num_palette; i++) + png_ptr->hist[i] = hist[i]; + info_ptr->hist = png_ptr->hist; + info_ptr->valid |= PNG_INFO_hIST; + +#ifdef PNG_FREE_ME_SUPPORTED + info_ptr->free_me |= PNG_FREE_HIST; +#else + png_ptr->flags |= PNG_FLAG_FREE_HIST; +#endif +} +#endif + +void PNGAPI +png_set_IHDR(png_structp png_ptr, png_infop info_ptr, + png_uint_32 width, png_uint_32 height, int bit_depth, + int color_type, int interlace_type, int compression_type, + int filter_type) +{ + png_debug1(1, "in %s storage function", "IHDR"); + + if (png_ptr == NULL || info_ptr == NULL) + return; + + info_ptr->width = width; + info_ptr->height = height; + info_ptr->bit_depth = (png_byte)bit_depth; + info_ptr->color_type = (png_byte)color_type; + info_ptr->compression_type = (png_byte)compression_type; + info_ptr->filter_type = (png_byte)filter_type; + info_ptr->interlace_type = (png_byte)interlace_type; + + png_check_IHDR (png_ptr, info_ptr->width, info_ptr->height, + info_ptr->bit_depth, info_ptr->color_type, info_ptr->interlace_type, + info_ptr->compression_type, info_ptr->filter_type); + + if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + info_ptr->channels = 1; + else if (info_ptr->color_type & PNG_COLOR_MASK_COLOR) + info_ptr->channels = 3; + else + info_ptr->channels = 1; + if (info_ptr->color_type & PNG_COLOR_MASK_ALPHA) + info_ptr->channels++; + info_ptr->pixel_depth = (png_byte)(info_ptr->channels * info_ptr->bit_depth); + + /* Check for potential overflow */ + if (width > (PNG_UINT_32_MAX + >> 3) /* 8-byte RGBA pixels */ + - 64 /* bigrowbuf hack */ + - 1 /* filter byte */ + - 7*8 /* rounding of width to multiple of 8 pixels */ + - 8) /* extra max_pixel_depth pad */ + info_ptr->rowbytes = (png_size_t)0; + else + info_ptr->rowbytes = PNG_ROWBYTES(info_ptr->pixel_depth, width); +} + +#ifdef PNG_oFFs_SUPPORTED +void PNGAPI +png_set_oFFs(png_structp png_ptr, png_infop info_ptr, + png_int_32 offset_x, png_int_32 offset_y, int unit_type) +{ + png_debug1(1, "in %s storage function", "oFFs"); + + if (png_ptr == NULL || info_ptr == NULL) + return; + + info_ptr->x_offset = offset_x; + info_ptr->y_offset = offset_y; + info_ptr->offset_unit_type = (png_byte)unit_type; + info_ptr->valid |= PNG_INFO_oFFs; +} +#endif + +#ifdef PNG_pCAL_SUPPORTED +void PNGAPI +png_set_pCAL(png_structp png_ptr, png_infop info_ptr, + png_charp purpose, png_int_32 X0, png_int_32 X1, int type, int nparams, + png_charp units, png_charpp params) +{ + png_uint_32 length; + int i; + + png_debug1(1, "in %s storage function", "pCAL"); + + if (png_ptr == NULL || info_ptr == NULL) + return; + + length = png_strlen(purpose) + 1; + png_debug1(3, "allocating purpose for info (%lu bytes)", + (unsigned long)length); + info_ptr->pcal_purpose = (png_charp)png_malloc_warn(png_ptr, length); + if (info_ptr->pcal_purpose == NULL) + { + png_warning(png_ptr, "Insufficient memory for pCAL purpose."); + return; + } + png_memcpy(info_ptr->pcal_purpose, purpose, (png_size_t)length); + + png_debug(3, "storing X0, X1, type, and nparams in info"); + info_ptr->pcal_X0 = X0; + info_ptr->pcal_X1 = X1; + info_ptr->pcal_type = (png_byte)type; + info_ptr->pcal_nparams = (png_byte)nparams; + + length = png_strlen(units) + 1; + png_debug1(3, "allocating units for info (%lu bytes)", + (unsigned long)length); + info_ptr->pcal_units = (png_charp)png_malloc_warn(png_ptr, length); + if (info_ptr->pcal_units == NULL) + { + png_warning(png_ptr, "Insufficient memory for pCAL units."); + return; + } + png_memcpy(info_ptr->pcal_units, units, (png_size_t)length); + + info_ptr->pcal_params = (png_charpp)png_malloc_warn(png_ptr, + (png_uint_32)((nparams + 1) * png_sizeof(png_charp))); + if (info_ptr->pcal_params == NULL) + { + png_warning(png_ptr, "Insufficient memory for pCAL params."); + return; + } + + png_memset(info_ptr->pcal_params, 0, (nparams + 1) * png_sizeof(png_charp)); + + for (i = 0; i < nparams; i++) + { + length = png_strlen(params[i]) + 1; + png_debug2(3, "allocating parameter %d for info (%lu bytes)", i, + (unsigned long)length); + info_ptr->pcal_params[i] = (png_charp)png_malloc_warn(png_ptr, length); + if (info_ptr->pcal_params[i] == NULL) + { + png_warning(png_ptr, "Insufficient memory for pCAL parameter."); + return; + } + png_memcpy(info_ptr->pcal_params[i], params[i], (png_size_t)length); + } + + info_ptr->valid |= PNG_INFO_pCAL; +#ifdef PNG_FREE_ME_SUPPORTED + info_ptr->free_me |= PNG_FREE_PCAL; +#endif +} +#endif + +#if defined(PNG_READ_sCAL_SUPPORTED) || defined(PNG_WRITE_sCAL_SUPPORTED) +#ifdef PNG_FLOATING_POINT_SUPPORTED +void PNGAPI +png_set_sCAL(png_structp png_ptr, png_infop info_ptr, + int unit, double width, double height) +{ + png_debug1(1, "in %s storage function", "sCAL"); + + if (png_ptr == NULL || info_ptr == NULL) + return; + + info_ptr->scal_unit = (png_byte)unit; + info_ptr->scal_pixel_width = width; + info_ptr->scal_pixel_height = height; + + info_ptr->valid |= PNG_INFO_sCAL; +} +#else +#ifdef PNG_FIXED_POINT_SUPPORTED +void PNGAPI +png_set_sCAL_s(png_structp png_ptr, png_infop info_ptr, + int unit, png_charp swidth, png_charp sheight) +{ + png_uint_32 length; + + png_debug1(1, "in %s storage function", "sCAL"); + + if (png_ptr == NULL || info_ptr == NULL) + return; + + info_ptr->scal_unit = (png_byte)unit; + + length = png_strlen(swidth) + 1; + png_debug1(3, "allocating unit for info (%u bytes)", + (unsigned int)length); + info_ptr->scal_s_width = (png_charp)png_malloc_warn(png_ptr, length); + if (info_ptr->scal_s_width == NULL) + { + png_warning(png_ptr, + "Memory allocation failed while processing sCAL."); + return; + } + png_memcpy(info_ptr->scal_s_width, swidth, (png_size_t)length); + + length = png_strlen(sheight) + 1; + png_debug1(3, "allocating unit for info (%u bytes)", + (unsigned int)length); + info_ptr->scal_s_height = (png_charp)png_malloc_warn(png_ptr, length); + if (info_ptr->scal_s_height == NULL) + { + png_free (png_ptr, info_ptr->scal_s_width); + info_ptr->scal_s_width = NULL; + png_warning(png_ptr, + "Memory allocation failed while processing sCAL."); + return; + } + png_memcpy(info_ptr->scal_s_height, sheight, (png_size_t)length); + info_ptr->valid |= PNG_INFO_sCAL; +#ifdef PNG_FREE_ME_SUPPORTED + info_ptr->free_me |= PNG_FREE_SCAL; +#endif +} +#endif +#endif +#endif + +#ifdef PNG_pHYs_SUPPORTED +void PNGAPI +png_set_pHYs(png_structp png_ptr, png_infop info_ptr, + png_uint_32 res_x, png_uint_32 res_y, int unit_type) +{ + png_debug1(1, "in %s storage function", "pHYs"); + + if (png_ptr == NULL || info_ptr == NULL) + return; + + info_ptr->x_pixels_per_unit = res_x; + info_ptr->y_pixels_per_unit = res_y; + info_ptr->phys_unit_type = (png_byte)unit_type; + info_ptr->valid |= PNG_INFO_pHYs; +} +#endif + +void PNGAPI +png_set_PLTE(png_structp png_ptr, png_infop info_ptr, + png_colorp palette, int num_palette) +{ + + png_debug1(1, "in %s storage function", "PLTE"); + + if (png_ptr == NULL || info_ptr == NULL) + return; + + if (num_palette < 0 || num_palette > PNG_MAX_PALETTE_LENGTH) + { + if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + png_error(png_ptr, "Invalid palette length"); + else + { + png_warning(png_ptr, "Invalid palette length"); + return; + } + } + + /* It may not actually be necessary to set png_ptr->palette here; + * we do it for backward compatibility with the way the png_handle_tRNS + * function used to do the allocation. + */ +#ifdef PNG_FREE_ME_SUPPORTED + png_free_data(png_ptr, info_ptr, PNG_FREE_PLTE, 0); +#endif + + /* Changed in libpng-1.2.1 to allocate PNG_MAX_PALETTE_LENGTH instead + * of num_palette entries, in case of an invalid PNG file that has + * too-large sample values. + */ + png_ptr->palette = (png_colorp)png_calloc(png_ptr, + PNG_MAX_PALETTE_LENGTH * png_sizeof(png_color)); + png_memcpy(png_ptr->palette, palette, num_palette * png_sizeof(png_color)); + info_ptr->palette = png_ptr->palette; + info_ptr->num_palette = png_ptr->num_palette = (png_uint_16)num_palette; + +#ifdef PNG_FREE_ME_SUPPORTED + info_ptr->free_me |= PNG_FREE_PLTE; +#else + png_ptr->flags |= PNG_FLAG_FREE_PLTE; +#endif + + info_ptr->valid |= PNG_INFO_PLTE; +} + +#ifdef PNG_sBIT_SUPPORTED +void PNGAPI +png_set_sBIT(png_structp png_ptr, png_infop info_ptr, + png_color_8p sig_bit) +{ + png_debug1(1, "in %s storage function", "sBIT"); + + if (png_ptr == NULL || info_ptr == NULL) + return; + + png_memcpy(&(info_ptr->sig_bit), sig_bit, png_sizeof(png_color_8)); + info_ptr->valid |= PNG_INFO_sBIT; +} +#endif + +#ifdef PNG_sRGB_SUPPORTED +void PNGAPI +png_set_sRGB(png_structp png_ptr, png_infop info_ptr, int intent) +{ + png_debug1(1, "in %s storage function", "sRGB"); + + if (png_ptr == NULL || info_ptr == NULL) + return; + + info_ptr->srgb_intent = (png_byte)intent; + info_ptr->valid |= PNG_INFO_sRGB; +} + +void PNGAPI +png_set_sRGB_gAMA_and_cHRM(png_structp png_ptr, png_infop info_ptr, + int intent) +{ +#ifdef PNG_gAMA_SUPPORTED +#ifdef PNG_FLOATING_POINT_SUPPORTED + float file_gamma; +#endif +#ifdef PNG_FIXED_POINT_SUPPORTED + png_fixed_point int_file_gamma; +#endif +#endif +#ifdef PNG_cHRM_SUPPORTED +#ifdef PNG_FLOATING_POINT_SUPPORTED + float white_x, white_y, red_x, red_y, green_x, green_y, blue_x, blue_y; +#endif + png_fixed_point int_white_x, int_white_y, int_red_x, int_red_y, int_green_x, + int_green_y, int_blue_x, int_blue_y; +#endif + png_debug1(1, "in %s storage function", "sRGB_gAMA_and_cHRM"); + + if (png_ptr == NULL || info_ptr == NULL) + return; + + png_set_sRGB(png_ptr, info_ptr, intent); + +#ifdef PNG_gAMA_SUPPORTED +#ifdef PNG_FLOATING_POINT_SUPPORTED + file_gamma = (float).45455; + png_set_gAMA(png_ptr, info_ptr, file_gamma); +#endif +#ifdef PNG_FIXED_POINT_SUPPORTED + int_file_gamma = 45455L; + png_set_gAMA_fixed(png_ptr, info_ptr, int_file_gamma); +#endif +#endif + +#ifdef PNG_cHRM_SUPPORTED + int_white_x = 31270L; + int_white_y = 32900L; + int_red_x = 64000L; + int_red_y = 33000L; + int_green_x = 30000L; + int_green_y = 60000L; + int_blue_x = 15000L; + int_blue_y = 6000L; + +#ifdef PNG_FLOATING_POINT_SUPPORTED + white_x = (float).3127; + white_y = (float).3290; + red_x = (float).64; + red_y = (float).33; + green_x = (float).30; + green_y = (float).60; + blue_x = (float).15; + blue_y = (float).06; +#endif + +#ifdef PNG_FIXED_POINT_SUPPORTED + png_set_cHRM_fixed(png_ptr, info_ptr, + int_white_x, int_white_y, int_red_x, int_red_y, int_green_x, + int_green_y, int_blue_x, int_blue_y); +#endif +#ifdef PNG_FLOATING_POINT_SUPPORTED + png_set_cHRM(png_ptr, info_ptr, + white_x, white_y, red_x, red_y, green_x, green_y, blue_x, blue_y); +#endif +#endif /* cHRM */ +} +#endif /* sRGB */ + + +#ifdef PNG_iCCP_SUPPORTED +void PNGAPI +png_set_iCCP(png_structp png_ptr, png_infop info_ptr, + png_charp name, int compression_type, + png_charp profile, png_uint_32 proflen) +{ + png_charp new_iccp_name; + png_charp new_iccp_profile; + png_uint_32 length; + + png_debug1(1, "in %s storage function", "iCCP"); + + if (png_ptr == NULL || info_ptr == NULL || name == NULL || profile == NULL) + return; + + length = png_strlen(name)+1; + new_iccp_name = (png_charp)png_malloc_warn(png_ptr, length); + if (new_iccp_name == NULL) + { + png_warning(png_ptr, "Insufficient memory to process iCCP chunk."); + return; + } + png_memcpy(new_iccp_name, name, length); + new_iccp_profile = (png_charp)png_malloc_warn(png_ptr, proflen); + if (new_iccp_profile == NULL) + { + png_free (png_ptr, new_iccp_name); + png_warning(png_ptr, + "Insufficient memory to process iCCP profile."); + return; + } + png_memcpy(new_iccp_profile, profile, (png_size_t)proflen); + + png_free_data(png_ptr, info_ptr, PNG_FREE_ICCP, 0); + + info_ptr->iccp_proflen = proflen; + info_ptr->iccp_name = new_iccp_name; + info_ptr->iccp_profile = new_iccp_profile; + /* Compression is always zero but is here so the API and info structure + * does not have to change if we introduce multiple compression types + */ + info_ptr->iccp_compression = (png_byte)compression_type; +#ifdef PNG_FREE_ME_SUPPORTED + info_ptr->free_me |= PNG_FREE_ICCP; +#endif + info_ptr->valid |= PNG_INFO_iCCP; +} +#endif + +#ifdef PNG_TEXT_SUPPORTED +void PNGAPI +png_set_text(png_structp png_ptr, png_infop info_ptr, png_textp text_ptr, + int num_text) +{ + int ret; + ret = png_set_text_2(png_ptr, info_ptr, text_ptr, num_text); + if (ret) + png_error(png_ptr, "Insufficient memory to store text"); +} + +int /* PRIVATE */ +png_set_text_2(png_structp png_ptr, png_infop info_ptr, png_textp text_ptr, + int num_text) +{ + int i; + + png_debug1(1, "in %s storage function", ((png_ptr == NULL || + png_ptr->chunk_name[0] == '\0') ? + "text" : (png_const_charp)png_ptr->chunk_name)); + + if (png_ptr == NULL || info_ptr == NULL || num_text == 0) + return(0); + + /* Make sure we have enough space in the "text" array in info_struct + * to hold all of the incoming text_ptr objects. + */ + if (info_ptr->num_text + num_text > info_ptr->max_text) + { + int old_max_text = info_ptr->max_text; + int old_num_text = info_ptr->num_text; + + if (info_ptr->text != NULL) + { + png_textp old_text; + + info_ptr->max_text = info_ptr->num_text + num_text + 8; + old_text = info_ptr->text; + + info_ptr->text = (png_textp)png_malloc_warn(png_ptr, + (png_uint_32)(info_ptr->max_text * png_sizeof(png_text))); + if (info_ptr->text == NULL) + { + /* Restore to previous condition */ + info_ptr->max_text = old_max_text; + info_ptr->text = old_text; + return(1); + } + png_memcpy(info_ptr->text, old_text, (png_size_t)(old_max_text * + png_sizeof(png_text))); + png_free(png_ptr, old_text); + } + else + { + info_ptr->max_text = num_text + 8; + info_ptr->num_text = 0; + info_ptr->text = (png_textp)png_malloc_warn(png_ptr, + (png_uint_32)(info_ptr->max_text * png_sizeof(png_text))); + if (info_ptr->text == NULL) + { + /* Restore to previous condition */ + info_ptr->num_text = old_num_text; + info_ptr->max_text = old_max_text; + return(1); + } +#ifdef PNG_FREE_ME_SUPPORTED + info_ptr->free_me |= PNG_FREE_TEXT; +#endif + } + png_debug1(3, "allocated %d entries for info_ptr->text", + info_ptr->max_text); + } + + for (i = 0; i < num_text; i++) + { + png_size_t text_length, key_len; + png_size_t lang_len, lang_key_len; + png_textp textp = &(info_ptr->text[info_ptr->num_text]); + + if (text_ptr[i].key == NULL) + continue; + + key_len = png_strlen(text_ptr[i].key); + + if (text_ptr[i].compression <= 0) + { + lang_len = 0; + lang_key_len = 0; + } + + else +#ifdef PNG_iTXt_SUPPORTED + { + /* Set iTXt data */ + + if (text_ptr[i].lang != NULL) + lang_len = png_strlen(text_ptr[i].lang); + else + lang_len = 0; + if (text_ptr[i].lang_key != NULL) + lang_key_len = png_strlen(text_ptr[i].lang_key); + else + lang_key_len = 0; + } +#else /* PNG_iTXt_SUPPORTED */ + { + png_warning(png_ptr, "iTXt chunk not supported."); + continue; + } +#endif + + if (text_ptr[i].text == NULL || text_ptr[i].text[0] == '\0') + { + text_length = 0; +#ifdef PNG_iTXt_SUPPORTED + if (text_ptr[i].compression > 0) + textp->compression = PNG_ITXT_COMPRESSION_NONE; + else +#endif + textp->compression = PNG_TEXT_COMPRESSION_NONE; + } + + else + { + text_length = png_strlen(text_ptr[i].text); + textp->compression = text_ptr[i].compression; + } + + textp->key = (png_charp)png_malloc_warn(png_ptr, + (png_uint_32) + (key_len + text_length + lang_len + lang_key_len + 4)); + if (textp->key == NULL) + return(1); + png_debug2(2, "Allocated %lu bytes at %x in png_set_text", + (png_uint_32) + (key_len + lang_len + lang_key_len + text_length + 4), + (int)textp->key); + + png_memcpy(textp->key, text_ptr[i].key,(png_size_t)(key_len)); + *(textp->key + key_len) = '\0'; +#ifdef PNG_iTXt_SUPPORTED + if (text_ptr[i].compression > 0) + { + textp->lang = textp->key + key_len + 1; + png_memcpy(textp->lang, text_ptr[i].lang, lang_len); + *(textp->lang + lang_len) = '\0'; + textp->lang_key = textp->lang + lang_len + 1; + png_memcpy(textp->lang_key, text_ptr[i].lang_key, lang_key_len); + *(textp->lang_key + lang_key_len) = '\0'; + textp->text = textp->lang_key + lang_key_len + 1; + } + else +#endif + { +#ifdef PNG_iTXt_SUPPORTED + textp->lang=NULL; + textp->lang_key=NULL; +#endif + textp->text = textp->key + key_len + 1; + } + if (text_length) + png_memcpy(textp->text, text_ptr[i].text, + (png_size_t)(text_length)); + *(textp->text + text_length) = '\0'; + +#ifdef PNG_iTXt_SUPPORTED + if (textp->compression > 0) + { + textp->text_length = 0; + textp->itxt_length = text_length; + } + else +#endif + + { + textp->text_length = text_length; +#ifdef PNG_iTXt_SUPPORTED + textp->itxt_length = 0; +#endif + } + info_ptr->num_text++; + png_debug1(3, "transferred text chunk %d", info_ptr->num_text); + } + return(0); +} +#endif + +#ifdef PNG_tIME_SUPPORTED +void PNGAPI +png_set_tIME(png_structp png_ptr, png_infop info_ptr, png_timep mod_time) +{ + png_debug1(1, "in %s storage function", "tIME"); + + if (png_ptr == NULL || info_ptr == NULL || + (png_ptr->mode & PNG_WROTE_tIME)) + return; + + png_memcpy(&(info_ptr->mod_time), mod_time, png_sizeof(png_time)); + info_ptr->valid |= PNG_INFO_tIME; +} +#endif + +#ifdef PNG_tRNS_SUPPORTED +void PNGAPI +png_set_tRNS(png_structp png_ptr, png_infop info_ptr, + png_bytep trans, int num_trans, png_color_16p trans_values) +{ + png_debug1(1, "in %s storage function", "tRNS"); + + if (png_ptr == NULL || info_ptr == NULL) + return; + + if (trans != NULL) + { + /* It may not actually be necessary to set png_ptr->trans here; + * we do it for backward compatibility with the way the png_handle_tRNS + * function used to do the allocation. + */ + +#ifdef PNG_FREE_ME_SUPPORTED + png_free_data(png_ptr, info_ptr, PNG_FREE_TRNS, 0); +#endif + + /* Changed from num_trans to PNG_MAX_PALETTE_LENGTH in version 1.2.1 */ + png_ptr->trans = info_ptr->trans = (png_bytep)png_malloc(png_ptr, + (png_uint_32)PNG_MAX_PALETTE_LENGTH); + if (num_trans > 0 && num_trans <= PNG_MAX_PALETTE_LENGTH) + png_memcpy(info_ptr->trans, trans, (png_size_t)num_trans); + } + + if (trans_values != NULL) + { + int sample_max = (1 << info_ptr->bit_depth); + if ((info_ptr->color_type == PNG_COLOR_TYPE_GRAY && + (int)trans_values->gray > sample_max) || + (info_ptr->color_type == PNG_COLOR_TYPE_RGB && + ((int)trans_values->red > sample_max || + (int)trans_values->green > sample_max || + (int)trans_values->blue > sample_max))) + png_warning(png_ptr, + "tRNS chunk has out-of-range samples for bit_depth"); + png_memcpy(&(info_ptr->trans_values), trans_values, + png_sizeof(png_color_16)); + if (num_trans == 0) + num_trans = 1; + } + + info_ptr->num_trans = (png_uint_16)num_trans; + if (num_trans != 0) + { + info_ptr->valid |= PNG_INFO_tRNS; +#ifdef PNG_FREE_ME_SUPPORTED + info_ptr->free_me |= PNG_FREE_TRNS; +#else + png_ptr->flags |= PNG_FLAG_FREE_TRNS; +#endif + } +} +#endif + +#ifdef PNG_sPLT_SUPPORTED +void PNGAPI +png_set_sPLT(png_structp png_ptr, + png_infop info_ptr, png_sPLT_tp entries, int nentries) +/* + * entries - array of png_sPLT_t structures + * to be added to the list of palettes + * in the info structure. + * nentries - number of palette structures to be + * added. + */ +{ + png_sPLT_tp np; + int i; + + if (png_ptr == NULL || info_ptr == NULL) + return; + + np = (png_sPLT_tp)png_malloc_warn(png_ptr, + (info_ptr->splt_palettes_num + nentries) * + (png_uint_32)png_sizeof(png_sPLT_t)); + if (np == NULL) + { + png_warning(png_ptr, "No memory for sPLT palettes."); + return; + } + + png_memcpy(np, info_ptr->splt_palettes, + info_ptr->splt_palettes_num * png_sizeof(png_sPLT_t)); + png_free(png_ptr, info_ptr->splt_palettes); + info_ptr->splt_palettes=NULL; + + for (i = 0; i < nentries; i++) + { + png_sPLT_tp to = np + info_ptr->splt_palettes_num + i; + png_sPLT_tp from = entries + i; + png_uint_32 length; + + length = png_strlen(from->name) + 1; + to->name = (png_charp)png_malloc_warn(png_ptr, length); + if (to->name == NULL) + { + png_warning(png_ptr, + "Out of memory while processing sPLT chunk"); + continue; + } + png_memcpy(to->name, from->name, length); + to->entries = (png_sPLT_entryp)png_malloc_warn(png_ptr, + (png_uint_32)(from->nentries * png_sizeof(png_sPLT_entry))); + if (to->entries == NULL) + { + png_warning(png_ptr, + "Out of memory while processing sPLT chunk"); + png_free(png_ptr, to->name); + to->name = NULL; + continue; + } + png_memcpy(to->entries, from->entries, + from->nentries * png_sizeof(png_sPLT_entry)); + to->nentries = from->nentries; + to->depth = from->depth; + } + + info_ptr->splt_palettes = np; + info_ptr->splt_palettes_num += nentries; + info_ptr->valid |= PNG_INFO_sPLT; +#ifdef PNG_FREE_ME_SUPPORTED + info_ptr->free_me |= PNG_FREE_SPLT; +#endif +} +#endif /* PNG_sPLT_SUPPORTED */ + +#ifdef PNG_UNKNOWN_CHUNKS_SUPPORTED +void PNGAPI +png_set_unknown_chunks(png_structp png_ptr, + png_infop info_ptr, png_unknown_chunkp unknowns, int num_unknowns) +{ + png_unknown_chunkp np; + int i; + + if (png_ptr == NULL || info_ptr == NULL || num_unknowns == 0) + return; + + np = (png_unknown_chunkp)png_malloc_warn(png_ptr, + (png_uint_32)((info_ptr->unknown_chunks_num + num_unknowns) * + png_sizeof(png_unknown_chunk))); + if (np == NULL) + { + png_warning(png_ptr, + "Out of memory while processing unknown chunk."); + return; + } + + png_memcpy(np, info_ptr->unknown_chunks, + info_ptr->unknown_chunks_num * png_sizeof(png_unknown_chunk)); + png_free(png_ptr, info_ptr->unknown_chunks); + info_ptr->unknown_chunks = NULL; + + for (i = 0; i < num_unknowns; i++) + { + png_unknown_chunkp to = np + info_ptr->unknown_chunks_num + i; + png_unknown_chunkp from = unknowns + i; + + png_memcpy((png_charp)to->name, (png_charp)from->name, + png_sizeof(from->name)); + to->name[png_sizeof(to->name)-1] = '\0'; + to->size = from->size; + /* Note our location in the read or write sequence */ + to->location = (png_byte)(png_ptr->mode & 0xff); + + if (from->size == 0) + to->data=NULL; + else + { + to->data = (png_bytep)png_malloc_warn(png_ptr, + (png_uint_32)from->size); + if (to->data == NULL) + { + png_warning(png_ptr, + "Out of memory while processing unknown chunk."); + to->size = 0; + } + else + png_memcpy(to->data, from->data, from->size); + } + } + + info_ptr->unknown_chunks = np; + info_ptr->unknown_chunks_num += num_unknowns; +#ifdef PNG_FREE_ME_SUPPORTED + info_ptr->free_me |= PNG_FREE_UNKN; +#endif +} +void PNGAPI +png_set_unknown_chunk_location(png_structp png_ptr, png_infop info_ptr, + int chunk, int location) +{ + if (png_ptr != NULL && info_ptr != NULL && chunk >= 0 && chunk < + (int)info_ptr->unknown_chunks_num) + info_ptr->unknown_chunks[chunk].location = (png_byte)location; +} +#endif + +#if defined(PNG_1_0_X) || defined(PNG_1_2_X) +#if defined(PNG_READ_EMPTY_PLTE_SUPPORTED) || \ + defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) +void PNGAPI +png_permit_empty_plte (png_structp png_ptr, int empty_plte_permitted) +{ + /* This function is deprecated in favor of png_permit_mng_features() + and will be removed from libpng-1.3.0 */ + + png_debug(1, "in png_permit_empty_plte, DEPRECATED."); + + if (png_ptr == NULL) + return; + png_ptr->mng_features_permitted = (png_byte) + ((png_ptr->mng_features_permitted & (~PNG_FLAG_MNG_EMPTY_PLTE)) | + ((empty_plte_permitted & PNG_FLAG_MNG_EMPTY_PLTE))); +} +#endif +#endif + +#ifdef PNG_MNG_FEATURES_SUPPORTED +png_uint_32 PNGAPI +png_permit_mng_features (png_structp png_ptr, png_uint_32 mng_features) +{ + png_debug(1, "in png_permit_mng_features"); + + if (png_ptr == NULL) + return (png_uint_32)0; + png_ptr->mng_features_permitted = + (png_byte)(mng_features & PNG_ALL_MNG_FEATURES); + return (png_uint_32)png_ptr->mng_features_permitted; +} +#endif + +#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED +void PNGAPI +png_set_keep_unknown_chunks(png_structp png_ptr, int keep, png_bytep + chunk_list, int num_chunks) +{ + png_bytep new_list, p; + int i, old_num_chunks; + if (png_ptr == NULL) + return; + if (num_chunks == 0) + { + if (keep == PNG_HANDLE_CHUNK_ALWAYS || keep == PNG_HANDLE_CHUNK_IF_SAFE) + png_ptr->flags |= PNG_FLAG_KEEP_UNKNOWN_CHUNKS; + else + png_ptr->flags &= ~PNG_FLAG_KEEP_UNKNOWN_CHUNKS; + + if (keep == PNG_HANDLE_CHUNK_ALWAYS) + png_ptr->flags |= PNG_FLAG_KEEP_UNSAFE_CHUNKS; + else + png_ptr->flags &= ~PNG_FLAG_KEEP_UNSAFE_CHUNKS; + return; + } + if (chunk_list == NULL) + return; + old_num_chunks = png_ptr->num_chunk_list; + new_list=(png_bytep)png_malloc(png_ptr, + (png_uint_32) + (5*(num_chunks + old_num_chunks))); + if (png_ptr->chunk_list != NULL) + { + png_memcpy(new_list, png_ptr->chunk_list, + (png_size_t)(5*old_num_chunks)); + png_free(png_ptr, png_ptr->chunk_list); + png_ptr->chunk_list=NULL; + } + png_memcpy(new_list + 5*old_num_chunks, chunk_list, + (png_size_t)(5*num_chunks)); + for (p = new_list + 5*old_num_chunks + 4, i = 0; inum_chunk_list = old_num_chunks + num_chunks; + png_ptr->chunk_list = new_list; +#ifdef PNG_FREE_ME_SUPPORTED + png_ptr->free_me |= PNG_FREE_LIST; +#endif +} +#endif + +#ifdef PNG_READ_USER_CHUNKS_SUPPORTED +void PNGAPI +png_set_read_user_chunk_fn(png_structp png_ptr, png_voidp user_chunk_ptr, + png_user_chunk_ptr read_user_chunk_fn) +{ + png_debug(1, "in png_set_read_user_chunk_fn"); + + if (png_ptr == NULL) + return; + + png_ptr->read_user_chunk_fn = read_user_chunk_fn; + png_ptr->user_chunk_ptr = user_chunk_ptr; +} +#endif + +#ifdef PNG_INFO_IMAGE_SUPPORTED +void PNGAPI +png_set_rows(png_structp png_ptr, png_infop info_ptr, png_bytepp row_pointers) +{ + png_debug1(1, "in %s storage function", "rows"); + + if (png_ptr == NULL || info_ptr == NULL) + return; + + if (info_ptr->row_pointers && (info_ptr->row_pointers != row_pointers)) + png_free_data(png_ptr, info_ptr, PNG_FREE_ROWS, 0); + info_ptr->row_pointers = row_pointers; + if (row_pointers) + info_ptr->valid |= PNG_INFO_IDAT; +} +#endif + +void PNGAPI +png_set_compression_buffer_size(png_structp png_ptr, + png_uint_32 size) +{ + if (png_ptr == NULL) + return; + png_free(png_ptr, png_ptr->zbuf); + png_ptr->zbuf_size = (png_size_t)size; + png_ptr->zbuf = (png_bytep)png_malloc(png_ptr, size); + png_ptr->zstream.next_out = png_ptr->zbuf; + png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size; +} + +void PNGAPI +png_set_invalid(png_structp png_ptr, png_infop info_ptr, int mask) +{ + if (png_ptr && info_ptr) + info_ptr->valid &= ~mask; +} + + +#ifndef PNG_1_0_X +#ifdef PNG_ASSEMBLER_CODE_SUPPORTED +/* Function was added to libpng 1.2.0 and should always exist by default */ +void PNGAPI +png_set_asm_flags (png_structp png_ptr, png_uint_32 asm_flags) +{ +/* Obsolete as of libpng-1.2.20 and will be removed from libpng-1.4.0 */ + if (png_ptr != NULL) + png_ptr->asm_flags = 0; + asm_flags = asm_flags; /* Quiet the compiler */ +} + +/* This function was added to libpng 1.2.0 */ +void PNGAPI +png_set_mmx_thresholds (png_structp png_ptr, + png_byte mmx_bitdepth_threshold, + png_uint_32 mmx_rowbytes_threshold) +{ +/* Obsolete as of libpng-1.2.20 and will be removed from libpng-1.4.0 */ + if (png_ptr == NULL) + return; + /* Quiet the compiler */ + mmx_bitdepth_threshold = mmx_bitdepth_threshold; + mmx_rowbytes_threshold = mmx_rowbytes_threshold; +} +#endif /* ?PNG_ASSEMBLER_CODE_SUPPORTED */ + +#ifdef PNG_SET_USER_LIMITS_SUPPORTED +/* This function was added to libpng 1.2.6 */ +void PNGAPI +png_set_user_limits (png_structp png_ptr, png_uint_32 user_width_max, + png_uint_32 user_height_max) +{ + /* Images with dimensions larger than these limits will be + * rejected by png_set_IHDR(). To accept any PNG datastream + * regardless of dimensions, set both limits to 0x7ffffffL. + */ + if (png_ptr == NULL) + return; + png_ptr->user_width_max = user_width_max; + png_ptr->user_height_max = user_height_max; +} +#endif /* ?PNG_SET_USER_LIMITS_SUPPORTED */ + + +#ifdef PNG_BENIGN_ERRORS_SUPPORTED +void PNGAPI +png_set_benign_errors(png_structp png_ptr, int allowed) +{ + png_debug(1, "in png_set_benign_errors"); + + if (allowed) + png_ptr->flags |= PNG_FLAG_BENIGN_ERRORS_WARN; + else + png_ptr->flags &= ~PNG_FLAG_BENIGN_ERRORS_WARN; +} +#endif /* PNG_BENIGN_ERRORS_SUPPORTED */ +#endif /* ?PNG_1_0_X */ +#endif /* PNG_READ_SUPPORTED || PNG_WRITE_SUPPORTED */ diff --git a/src/libpng/pngtrans.c b/src/libpng/pngtrans.c new file mode 100644 index 00000000..6ad9dcf6 --- /dev/null +++ b/src/libpng/pngtrans.c @@ -0,0 +1,699 @@ + +/* pngtrans.c - transforms the data in a row (used by both readers and writers) + * + * Last changed in libpng 1.2.41 [December 3, 2009] + * Copyright (c) 1998-2009 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + * + * This code is released under the libpng license. + * For conditions of distribution and use, see the disclaimer + * and license in png.h + */ + +#define PNG_INTERNAL +#define PNG_NO_PEDANTIC_WARNINGS +#include "png.h" +#if defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED) + +#if defined(PNG_READ_BGR_SUPPORTED) || defined(PNG_WRITE_BGR_SUPPORTED) +/* Turn on BGR-to-RGB mapping */ +void PNGAPI +png_set_bgr(png_structp png_ptr) +{ + png_debug(1, "in png_set_bgr"); + + if (png_ptr == NULL) + return; + png_ptr->transformations |= PNG_BGR; +} +#endif + +#if defined(PNG_READ_SWAP_SUPPORTED) || defined(PNG_WRITE_SWAP_SUPPORTED) +/* Turn on 16 bit byte swapping */ +void PNGAPI +png_set_swap(png_structp png_ptr) +{ + png_debug(1, "in png_set_swap"); + + if (png_ptr == NULL) + return; + if (png_ptr->bit_depth == 16) + png_ptr->transformations |= PNG_SWAP_BYTES; +} +#endif + +#if defined(PNG_READ_PACK_SUPPORTED) || defined(PNG_WRITE_PACK_SUPPORTED) +/* Turn on pixel packing */ +void PNGAPI +png_set_packing(png_structp png_ptr) +{ + png_debug(1, "in png_set_packing"); + + if (png_ptr == NULL) + return; + if (png_ptr->bit_depth < 8) + { + png_ptr->transformations |= PNG_PACK; + png_ptr->usr_bit_depth = 8; + } +} +#endif + +#if defined(PNG_READ_PACKSWAP_SUPPORTED)||defined(PNG_WRITE_PACKSWAP_SUPPORTED) +/* Turn on packed pixel swapping */ +void PNGAPI +png_set_packswap(png_structp png_ptr) +{ + png_debug(1, "in png_set_packswap"); + + if (png_ptr == NULL) + return; + if (png_ptr->bit_depth < 8) + png_ptr->transformations |= PNG_PACKSWAP; +} +#endif + +#if defined(PNG_READ_SHIFT_SUPPORTED) || defined(PNG_WRITE_SHIFT_SUPPORTED) +void PNGAPI +png_set_shift(png_structp png_ptr, png_color_8p true_bits) +{ + png_debug(1, "in png_set_shift"); + + if (png_ptr == NULL) + return; + png_ptr->transformations |= PNG_SHIFT; + png_ptr->shift = *true_bits; +} +#endif + +#if defined(PNG_READ_INTERLACING_SUPPORTED) || \ + defined(PNG_WRITE_INTERLACING_SUPPORTED) +int PNGAPI +png_set_interlace_handling(png_structp png_ptr) +{ + png_debug(1, "in png_set_interlace handling"); + + if (png_ptr && png_ptr->interlaced) + { + png_ptr->transformations |= PNG_INTERLACE; + return (7); + } + + return (1); +} +#endif + +#if defined(PNG_READ_FILLER_SUPPORTED) || defined(PNG_WRITE_FILLER_SUPPORTED) +/* Add a filler byte on read, or remove a filler or alpha byte on write. + * The filler type has changed in v0.95 to allow future 2-byte fillers + * for 48-bit input data, as well as to avoid problems with some compilers + * that don't like bytes as parameters. + */ +void PNGAPI +png_set_filler(png_structp png_ptr, png_uint_32 filler, int filler_loc) +{ + png_debug(1, "in png_set_filler"); + + if (png_ptr == NULL) + return; + png_ptr->transformations |= PNG_FILLER; +#ifdef PNG_LEGACY_SUPPORTED + png_ptr->filler = (png_byte)filler; +#else + png_ptr->filler = (png_uint_16)filler; +#endif + if (filler_loc == PNG_FILLER_AFTER) + png_ptr->flags |= PNG_FLAG_FILLER_AFTER; + else + png_ptr->flags &= ~PNG_FLAG_FILLER_AFTER; + + /* This should probably go in the "do_read_filler" routine. + * I attempted to do that in libpng-1.0.1a but that caused problems + * so I restored it in libpng-1.0.2a + */ + + if (png_ptr->color_type == PNG_COLOR_TYPE_RGB) + { + png_ptr->usr_channels = 4; + } + + /* Also I added this in libpng-1.0.2a (what happens when we expand + * a less-than-8-bit grayscale to GA? */ + + if (png_ptr->color_type == PNG_COLOR_TYPE_GRAY && png_ptr->bit_depth >= 8) + { + png_ptr->usr_channels = 2; + } +} + +#ifndef PNG_1_0_X +/* Added to libpng-1.2.7 */ +void PNGAPI +png_set_add_alpha(png_structp png_ptr, png_uint_32 filler, int filler_loc) +{ + png_debug(1, "in png_set_add_alpha"); + + if (png_ptr == NULL) + return; + png_set_filler(png_ptr, filler, filler_loc); + png_ptr->transformations |= PNG_ADD_ALPHA; +} +#endif + +#endif + +#if defined(PNG_READ_SWAP_ALPHA_SUPPORTED) || \ + defined(PNG_WRITE_SWAP_ALPHA_SUPPORTED) +void PNGAPI +png_set_swap_alpha(png_structp png_ptr) +{ + png_debug(1, "in png_set_swap_alpha"); + + if (png_ptr == NULL) + return; + png_ptr->transformations |= PNG_SWAP_ALPHA; +} +#endif + +#if defined(PNG_READ_INVERT_ALPHA_SUPPORTED) || \ + defined(PNG_WRITE_INVERT_ALPHA_SUPPORTED) +void PNGAPI +png_set_invert_alpha(png_structp png_ptr) +{ + png_debug(1, "in png_set_invert_alpha"); + + if (png_ptr == NULL) + return; + png_ptr->transformations |= PNG_INVERT_ALPHA; +} +#endif + +#if defined(PNG_READ_INVERT_SUPPORTED) || defined(PNG_WRITE_INVERT_SUPPORTED) +void PNGAPI +png_set_invert_mono(png_structp png_ptr) +{ + png_debug(1, "in png_set_invert_mono"); + + if (png_ptr == NULL) + return; + png_ptr->transformations |= PNG_INVERT_MONO; +} + +/* Invert monochrome grayscale data */ +void /* PRIVATE */ +png_do_invert(png_row_infop row_info, png_bytep row) +{ + png_debug(1, "in png_do_invert"); + + /* This test removed from libpng version 1.0.13 and 1.2.0: + * if (row_info->bit_depth == 1 && + */ +#ifdef PNG_USELESS_TESTS_SUPPORTED + if (row == NULL || row_info == NULL) + return; +#endif + if (row_info->color_type == PNG_COLOR_TYPE_GRAY) + { + png_bytep rp = row; + png_uint_32 i; + png_uint_32 istop = row_info->rowbytes; + + for (i = 0; i < istop; i++) + { + *rp = (png_byte)(~(*rp)); + rp++; + } + } + else if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA && + row_info->bit_depth == 8) + { + png_bytep rp = row; + png_uint_32 i; + png_uint_32 istop = row_info->rowbytes; + + for (i = 0; i < istop; i+=2) + { + *rp = (png_byte)(~(*rp)); + rp+=2; + } + } + else if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA && + row_info->bit_depth == 16) + { + png_bytep rp = row; + png_uint_32 i; + png_uint_32 istop = row_info->rowbytes; + + for (i = 0; i < istop; i+=4) + { + *rp = (png_byte)(~(*rp)); + *(rp+1) = (png_byte)(~(*(rp+1))); + rp+=4; + } + } +} +#endif + +#if defined(PNG_READ_SWAP_SUPPORTED) || defined(PNG_WRITE_SWAP_SUPPORTED) +/* Swaps byte order on 16 bit depth images */ +void /* PRIVATE */ +png_do_swap(png_row_infop row_info, png_bytep row) +{ + png_debug(1, "in png_do_swap"); + + if ( +#ifdef PNG_USELESS_TESTS_SUPPORTED + row != NULL && row_info != NULL && +#endif + row_info->bit_depth == 16) + { + png_bytep rp = row; + png_uint_32 i; + png_uint_32 istop= row_info->width * row_info->channels; + + for (i = 0; i < istop; i++, rp += 2) + { + png_byte t = *rp; + *rp = *(rp + 1); + *(rp + 1) = t; + } + } +} +#endif + +#if defined(PNG_READ_PACKSWAP_SUPPORTED)||defined(PNG_WRITE_PACKSWAP_SUPPORTED) +static PNG_CONST png_byte onebppswaptable[256] = { + 0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0, + 0x10, 0x90, 0x50, 0xD0, 0x30, 0xB0, 0x70, 0xF0, + 0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8, + 0x18, 0x98, 0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8, + 0x04, 0x84, 0x44, 0xC4, 0x24, 0xA4, 0x64, 0xE4, + 0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x74, 0xF4, + 0x0C, 0x8C, 0x4C, 0xCC, 0x2C, 0xAC, 0x6C, 0xEC, + 0x1C, 0x9C, 0x5C, 0xDC, 0x3C, 0xBC, 0x7C, 0xFC, + 0x02, 0x82, 0x42, 0xC2, 0x22, 0xA2, 0x62, 0xE2, + 0x12, 0x92, 0x52, 0xD2, 0x32, 0xB2, 0x72, 0xF2, + 0x0A, 0x8A, 0x4A, 0xCA, 0x2A, 0xAA, 0x6A, 0xEA, + 0x1A, 0x9A, 0x5A, 0xDA, 0x3A, 0xBA, 0x7A, 0xFA, + 0x06, 0x86, 0x46, 0xC6, 0x26, 0xA6, 0x66, 0xE6, + 0x16, 0x96, 0x56, 0xD6, 0x36, 0xB6, 0x76, 0xF6, + 0x0E, 0x8E, 0x4E, 0xCE, 0x2E, 0xAE, 0x6E, 0xEE, + 0x1E, 0x9E, 0x5E, 0xDE, 0x3E, 0xBE, 0x7E, 0xFE, + 0x01, 0x81, 0x41, 0xC1, 0x21, 0xA1, 0x61, 0xE1, + 0x11, 0x91, 0x51, 0xD1, 0x31, 0xB1, 0x71, 0xF1, + 0x09, 0x89, 0x49, 0xC9, 0x29, 0xA9, 0x69, 0xE9, + 0x19, 0x99, 0x59, 0xD9, 0x39, 0xB9, 0x79, 0xF9, + 0x05, 0x85, 0x45, 0xC5, 0x25, 0xA5, 0x65, 0xE5, + 0x15, 0x95, 0x55, 0xD5, 0x35, 0xB5, 0x75, 0xF5, + 0x0D, 0x8D, 0x4D, 0xCD, 0x2D, 0xAD, 0x6D, 0xED, + 0x1D, 0x9D, 0x5D, 0xDD, 0x3D, 0xBD, 0x7D, 0xFD, + 0x03, 0x83, 0x43, 0xC3, 0x23, 0xA3, 0x63, 0xE3, + 0x13, 0x93, 0x53, 0xD3, 0x33, 0xB3, 0x73, 0xF3, + 0x0B, 0x8B, 0x4B, 0xCB, 0x2B, 0xAB, 0x6B, 0xEB, + 0x1B, 0x9B, 0x5B, 0xDB, 0x3B, 0xBB, 0x7B, 0xFB, + 0x07, 0x87, 0x47, 0xC7, 0x27, 0xA7, 0x67, 0xE7, + 0x17, 0x97, 0x57, 0xD7, 0x37, 0xB7, 0x77, 0xF7, + 0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F, 0xEF, + 0x1F, 0x9F, 0x5F, 0xDF, 0x3F, 0xBF, 0x7F, 0xFF +}; + +static PNG_CONST png_byte twobppswaptable[256] = { + 0x00, 0x40, 0x80, 0xC0, 0x10, 0x50, 0x90, 0xD0, + 0x20, 0x60, 0xA0, 0xE0, 0x30, 0x70, 0xB0, 0xF0, + 0x04, 0x44, 0x84, 0xC4, 0x14, 0x54, 0x94, 0xD4, + 0x24, 0x64, 0xA4, 0xE4, 0x34, 0x74, 0xB4, 0xF4, + 0x08, 0x48, 0x88, 0xC8, 0x18, 0x58, 0x98, 0xD8, + 0x28, 0x68, 0xA8, 0xE8, 0x38, 0x78, 0xB8, 0xF8, + 0x0C, 0x4C, 0x8C, 0xCC, 0x1C, 0x5C, 0x9C, 0xDC, + 0x2C, 0x6C, 0xAC, 0xEC, 0x3C, 0x7C, 0xBC, 0xFC, + 0x01, 0x41, 0x81, 0xC1, 0x11, 0x51, 0x91, 0xD1, + 0x21, 0x61, 0xA1, 0xE1, 0x31, 0x71, 0xB1, 0xF1, + 0x05, 0x45, 0x85, 0xC5, 0x15, 0x55, 0x95, 0xD5, + 0x25, 0x65, 0xA5, 0xE5, 0x35, 0x75, 0xB5, 0xF5, + 0x09, 0x49, 0x89, 0xC9, 0x19, 0x59, 0x99, 0xD9, + 0x29, 0x69, 0xA9, 0xE9, 0x39, 0x79, 0xB9, 0xF9, + 0x0D, 0x4D, 0x8D, 0xCD, 0x1D, 0x5D, 0x9D, 0xDD, + 0x2D, 0x6D, 0xAD, 0xED, 0x3D, 0x7D, 0xBD, 0xFD, + 0x02, 0x42, 0x82, 0xC2, 0x12, 0x52, 0x92, 0xD2, + 0x22, 0x62, 0xA2, 0xE2, 0x32, 0x72, 0xB2, 0xF2, + 0x06, 0x46, 0x86, 0xC6, 0x16, 0x56, 0x96, 0xD6, + 0x26, 0x66, 0xA6, 0xE6, 0x36, 0x76, 0xB6, 0xF6, + 0x0A, 0x4A, 0x8A, 0xCA, 0x1A, 0x5A, 0x9A, 0xDA, + 0x2A, 0x6A, 0xAA, 0xEA, 0x3A, 0x7A, 0xBA, 0xFA, + 0x0E, 0x4E, 0x8E, 0xCE, 0x1E, 0x5E, 0x9E, 0xDE, + 0x2E, 0x6E, 0xAE, 0xEE, 0x3E, 0x7E, 0xBE, 0xFE, + 0x03, 0x43, 0x83, 0xC3, 0x13, 0x53, 0x93, 0xD3, + 0x23, 0x63, 0xA3, 0xE3, 0x33, 0x73, 0xB3, 0xF3, + 0x07, 0x47, 0x87, 0xC7, 0x17, 0x57, 0x97, 0xD7, + 0x27, 0x67, 0xA7, 0xE7, 0x37, 0x77, 0xB7, 0xF7, + 0x0B, 0x4B, 0x8B, 0xCB, 0x1B, 0x5B, 0x9B, 0xDB, + 0x2B, 0x6B, 0xAB, 0xEB, 0x3B, 0x7B, 0xBB, 0xFB, + 0x0F, 0x4F, 0x8F, 0xCF, 0x1F, 0x5F, 0x9F, 0xDF, + 0x2F, 0x6F, 0xAF, 0xEF, 0x3F, 0x7F, 0xBF, 0xFF +}; + +static PNG_CONST png_byte fourbppswaptable[256] = { + 0x00, 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, + 0x80, 0x90, 0xA0, 0xB0, 0xC0, 0xD0, 0xE0, 0xF0, + 0x01, 0x11, 0x21, 0x31, 0x41, 0x51, 0x61, 0x71, + 0x81, 0x91, 0xA1, 0xB1, 0xC1, 0xD1, 0xE1, 0xF1, + 0x02, 0x12, 0x22, 0x32, 0x42, 0x52, 0x62, 0x72, + 0x82, 0x92, 0xA2, 0xB2, 0xC2, 0xD2, 0xE2, 0xF2, + 0x03, 0x13, 0x23, 0x33, 0x43, 0x53, 0x63, 0x73, + 0x83, 0x93, 0xA3, 0xB3, 0xC3, 0xD3, 0xE3, 0xF3, + 0x04, 0x14, 0x24, 0x34, 0x44, 0x54, 0x64, 0x74, + 0x84, 0x94, 0xA4, 0xB4, 0xC4, 0xD4, 0xE4, 0xF4, + 0x05, 0x15, 0x25, 0x35, 0x45, 0x55, 0x65, 0x75, + 0x85, 0x95, 0xA5, 0xB5, 0xC5, 0xD5, 0xE5, 0xF5, + 0x06, 0x16, 0x26, 0x36, 0x46, 0x56, 0x66, 0x76, + 0x86, 0x96, 0xA6, 0xB6, 0xC6, 0xD6, 0xE6, 0xF6, + 0x07, 0x17, 0x27, 0x37, 0x47, 0x57, 0x67, 0x77, + 0x87, 0x97, 0xA7, 0xB7, 0xC7, 0xD7, 0xE7, 0xF7, + 0x08, 0x18, 0x28, 0x38, 0x48, 0x58, 0x68, 0x78, + 0x88, 0x98, 0xA8, 0xB8, 0xC8, 0xD8, 0xE8, 0xF8, + 0x09, 0x19, 0x29, 0x39, 0x49, 0x59, 0x69, 0x79, + 0x89, 0x99, 0xA9, 0xB9, 0xC9, 0xD9, 0xE9, 0xF9, + 0x0A, 0x1A, 0x2A, 0x3A, 0x4A, 0x5A, 0x6A, 0x7A, + 0x8A, 0x9A, 0xAA, 0xBA, 0xCA, 0xDA, 0xEA, 0xFA, + 0x0B, 0x1B, 0x2B, 0x3B, 0x4B, 0x5B, 0x6B, 0x7B, + 0x8B, 0x9B, 0xAB, 0xBB, 0xCB, 0xDB, 0xEB, 0xFB, + 0x0C, 0x1C, 0x2C, 0x3C, 0x4C, 0x5C, 0x6C, 0x7C, + 0x8C, 0x9C, 0xAC, 0xBC, 0xCC, 0xDC, 0xEC, 0xFC, + 0x0D, 0x1D, 0x2D, 0x3D, 0x4D, 0x5D, 0x6D, 0x7D, + 0x8D, 0x9D, 0xAD, 0xBD, 0xCD, 0xDD, 0xED, 0xFD, + 0x0E, 0x1E, 0x2E, 0x3E, 0x4E, 0x5E, 0x6E, 0x7E, + 0x8E, 0x9E, 0xAE, 0xBE, 0xCE, 0xDE, 0xEE, 0xFE, + 0x0F, 0x1F, 0x2F, 0x3F, 0x4F, 0x5F, 0x6F, 0x7F, + 0x8F, 0x9F, 0xAF, 0xBF, 0xCF, 0xDF, 0xEF, 0xFF +}; + +/* Swaps pixel packing order within bytes */ +void /* PRIVATE */ +png_do_packswap(png_row_infop row_info, png_bytep row) +{ + png_debug(1, "in png_do_packswap"); + + if ( +#ifdef PNG_USELESS_TESTS_SUPPORTED + row != NULL && row_info != NULL && +#endif + row_info->bit_depth < 8) + { + png_bytep rp, end, table; + + end = row + row_info->rowbytes; + + if (row_info->bit_depth == 1) + table = (png_bytep)onebppswaptable; + else if (row_info->bit_depth == 2) + table = (png_bytep)twobppswaptable; + else if (row_info->bit_depth == 4) + table = (png_bytep)fourbppswaptable; + else + return; + + for (rp = row; rp < end; rp++) + *rp = table[*rp]; + } +} +#endif /* PNG_READ_PACKSWAP_SUPPORTED or PNG_WRITE_PACKSWAP_SUPPORTED */ + +#if defined(PNG_WRITE_FILLER_SUPPORTED) || \ + defined(PNG_READ_STRIP_ALPHA_SUPPORTED) +/* Remove filler or alpha byte(s) */ +void /* PRIVATE */ +png_do_strip_filler(png_row_infop row_info, png_bytep row, png_uint_32 flags) +{ + png_debug(1, "in png_do_strip_filler"); + +#ifdef PNG_USELESS_TESTS_SUPPORTED + if (row != NULL && row_info != NULL) +#endif + { + png_bytep sp=row; + png_bytep dp=row; + png_uint_32 row_width=row_info->width; + png_uint_32 i; + + if ((row_info->color_type == PNG_COLOR_TYPE_RGB || + (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA && + (flags & PNG_FLAG_STRIP_ALPHA))) && + row_info->channels == 4) + { + if (row_info->bit_depth == 8) + { + /* This converts from RGBX or RGBA to RGB */ + if (flags & PNG_FLAG_FILLER_AFTER) + { + dp+=3; sp+=4; + for (i = 1; i < row_width; i++) + { + *dp++ = *sp++; + *dp++ = *sp++; + *dp++ = *sp++; + sp++; + } + } + /* This converts from XRGB or ARGB to RGB */ + else + { + for (i = 0; i < row_width; i++) + { + sp++; + *dp++ = *sp++; + *dp++ = *sp++; + *dp++ = *sp++; + } + } + row_info->pixel_depth = 24; + row_info->rowbytes = row_width * 3; + } + else /* if (row_info->bit_depth == 16) */ + { + if (flags & PNG_FLAG_FILLER_AFTER) + { + /* This converts from RRGGBBXX or RRGGBBAA to RRGGBB */ + sp += 8; dp += 6; + for (i = 1; i < row_width; i++) + { + /* This could be (although png_memcpy is probably slower): + png_memcpy(dp, sp, 6); + sp += 8; + dp += 6; + */ + + *dp++ = *sp++; + *dp++ = *sp++; + *dp++ = *sp++; + *dp++ = *sp++; + *dp++ = *sp++; + *dp++ = *sp++; + sp += 2; + } + } + else + { + /* This converts from XXRRGGBB or AARRGGBB to RRGGBB */ + for (i = 0; i < row_width; i++) + { + /* This could be (although png_memcpy is probably slower): + png_memcpy(dp, sp, 6); + sp += 8; + dp += 6; + */ + + sp+=2; + *dp++ = *sp++; + *dp++ = *sp++; + *dp++ = *sp++; + *dp++ = *sp++; + *dp++ = *sp++; + *dp++ = *sp++; + } + } + row_info->pixel_depth = 48; + row_info->rowbytes = row_width * 6; + } + row_info->channels = 3; + } + else if ((row_info->color_type == PNG_COLOR_TYPE_GRAY || + (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA && + (flags & PNG_FLAG_STRIP_ALPHA))) && + row_info->channels == 2) + { + if (row_info->bit_depth == 8) + { + /* This converts from GX or GA to G */ + if (flags & PNG_FLAG_FILLER_AFTER) + { + for (i = 0; i < row_width; i++) + { + *dp++ = *sp++; + sp++; + } + } + /* This converts from XG or AG to G */ + else + { + for (i = 0; i < row_width; i++) + { + sp++; + *dp++ = *sp++; + } + } + row_info->pixel_depth = 8; + row_info->rowbytes = row_width; + } + else /* if (row_info->bit_depth == 16) */ + { + if (flags & PNG_FLAG_FILLER_AFTER) + { + /* This converts from GGXX or GGAA to GG */ + sp += 4; dp += 2; + for (i = 1; i < row_width; i++) + { + *dp++ = *sp++; + *dp++ = *sp++; + sp += 2; + } + } + else + { + /* This converts from XXGG or AAGG to GG */ + for (i = 0; i < row_width; i++) + { + sp += 2; + *dp++ = *sp++; + *dp++ = *sp++; + } + } + row_info->pixel_depth = 16; + row_info->rowbytes = row_width * 2; + } + row_info->channels = 1; + } + if (flags & PNG_FLAG_STRIP_ALPHA) + row_info->color_type &= ~PNG_COLOR_MASK_ALPHA; + } +} +#endif + +#if defined(PNG_READ_BGR_SUPPORTED) || defined(PNG_WRITE_BGR_SUPPORTED) +/* Swaps red and blue bytes within a pixel */ +void /* PRIVATE */ +png_do_bgr(png_row_infop row_info, png_bytep row) +{ + png_debug(1, "in png_do_bgr"); + + if ( +#ifdef PNG_USELESS_TESTS_SUPPORTED + row != NULL && row_info != NULL && +#endif + (row_info->color_type & PNG_COLOR_MASK_COLOR)) + { + png_uint_32 row_width = row_info->width; + if (row_info->bit_depth == 8) + { + if (row_info->color_type == PNG_COLOR_TYPE_RGB) + { + png_bytep rp; + png_uint_32 i; + + for (i = 0, rp = row; i < row_width; i++, rp += 3) + { + png_byte save = *rp; + *rp = *(rp + 2); + *(rp + 2) = save; + } + } + else if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) + { + png_bytep rp; + png_uint_32 i; + + for (i = 0, rp = row; i < row_width; i++, rp += 4) + { + png_byte save = *rp; + *rp = *(rp + 2); + *(rp + 2) = save; + } + } + } + else if (row_info->bit_depth == 16) + { + if (row_info->color_type == PNG_COLOR_TYPE_RGB) + { + png_bytep rp; + png_uint_32 i; + + for (i = 0, rp = row; i < row_width; i++, rp += 6) + { + png_byte save = *rp; + *rp = *(rp + 4); + *(rp + 4) = save; + save = *(rp + 1); + *(rp + 1) = *(rp + 5); + *(rp + 5) = save; + } + } + else if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) + { + png_bytep rp; + png_uint_32 i; + + for (i = 0, rp = row; i < row_width; i++, rp += 8) + { + png_byte save = *rp; + *rp = *(rp + 4); + *(rp + 4) = save; + save = *(rp + 1); + *(rp + 1) = *(rp + 5); + *(rp + 5) = save; + } + } + } + } +} +#endif /* PNG_READ_BGR_SUPPORTED or PNG_WRITE_BGR_SUPPORTED */ + +#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) || \ + defined(PNG_LEGACY_SUPPORTED) || \ + defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED) +void PNGAPI +png_set_user_transform_info(png_structp png_ptr, png_voidp + user_transform_ptr, int user_transform_depth, int user_transform_channels) +{ + png_debug(1, "in png_set_user_transform_info"); + + if (png_ptr == NULL) + return; +#ifdef PNG_USER_TRANSFORM_PTR_SUPPORTED + png_ptr->user_transform_ptr = user_transform_ptr; + png_ptr->user_transform_depth = (png_byte)user_transform_depth; + png_ptr->user_transform_channels = (png_byte)user_transform_channels; +#else + if (user_transform_ptr || user_transform_depth || user_transform_channels) + png_warning(png_ptr, + "This version of libpng does not support user transform info"); +#endif +} +#endif + +/* This function returns a pointer to the user_transform_ptr associated with + * the user transform functions. The application should free any memory + * associated with this pointer before png_write_destroy and png_read_destroy + * are called. + */ +png_voidp PNGAPI +png_get_user_transform_ptr(png_structp png_ptr) +{ + if (png_ptr == NULL) + return (NULL); +#ifdef PNG_USER_TRANSFORM_PTR_SUPPORTED + return ((png_voidp)png_ptr->user_transform_ptr); +#else + return (NULL); +#endif +} +#endif /* PNG_READ_SUPPORTED || PNG_WRITE_SUPPORTED */ diff --git a/src/libpng/pngwio.c b/src/libpng/pngwio.c new file mode 100644 index 00000000..44e5ea91 --- /dev/null +++ b/src/libpng/pngwio.c @@ -0,0 +1,260 @@ + +/* pngwio.c - functions for data output + * + * Last changed in libpng 1.2.41 [December 3, 2009] + * Copyright (c) 1998-2009 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + * + * This code is released under the libpng license. + * For conditions of distribution and use, see the disclaimer + * and license in png.h + * + * This file provides a location for all output. Users who need + * special handling are expected to write functions that have the same + * arguments as these and perform similar functions, but that possibly + * use different output methods. Note that you shouldn't change these + * functions, but rather write replacement functions and then change + * them at run time with png_set_write_fn(...). + */ + +#define PNG_INTERNAL +#define PNG_NO_PEDANTIC_WARNINGS +#include "png.h" +#ifdef PNG_WRITE_SUPPORTED + +/* Write the data to whatever output you are using. The default routine + * writes to a file pointer. Note that this routine sometimes gets called + * with very small lengths, so you should implement some kind of simple + * buffering if you are using unbuffered writes. This should never be asked + * to write more than 64K on a 16 bit machine. + */ + +void /* PRIVATE */ +png_write_data(png_structp png_ptr, png_bytep data, png_size_t length) +{ + if (png_ptr->write_data_fn != NULL ) + (*(png_ptr->write_data_fn))(png_ptr, data, length); + else + png_error(png_ptr, "Call to NULL write function"); +} + +#ifdef PNG_STDIO_SUPPORTED +/* This is the function that does the actual writing of data. If you are + * not writing to a standard C stream, you should create a replacement + * write_data function and use it at run time with png_set_write_fn(), rather + * than changing the library. + */ +#ifndef USE_FAR_KEYWORD +void PNGAPI +png_default_write_data(png_structp png_ptr, png_bytep data, png_size_t length) +{ + png_uint_32 check; + + if (png_ptr == NULL) + return; +#ifdef _WIN32_WCE + if ( !WriteFile((HANDLE)(png_ptr->io_ptr), data, length, &check, NULL) ) + check = 0; +#else + check = fwrite(data, 1, length, (png_FILE_p)(png_ptr->io_ptr)); +#endif + if (check != length) + png_error(png_ptr, "Write Error"); +} +#else +/* This is the model-independent version. Since the standard I/O library + * can't handle far buffers in the medium and small models, we have to copy + * the data. + */ + +#define NEAR_BUF_SIZE 1024 +#define MIN(a,b) (a <= b ? a : b) + +void PNGAPI +png_default_write_data(png_structp png_ptr, png_bytep data, png_size_t length) +{ + png_uint_32 check; + png_byte *near_data; /* Needs to be "png_byte *" instead of "png_bytep" */ + png_FILE_p io_ptr; + + if (png_ptr == NULL) + return; + /* Check if data really is near. If so, use usual code. */ + near_data = (png_byte *)CVT_PTR_NOCHECK(data); + io_ptr = (png_FILE_p)CVT_PTR(png_ptr->io_ptr); + if ((png_bytep)near_data == data) + { +#ifdef _WIN32_WCE + if ( !WriteFile(io_ptr, near_data, length, &check, NULL) ) + check = 0; +#else + check = fwrite(near_data, 1, length, io_ptr); +#endif + } + else + { + png_byte buf[NEAR_BUF_SIZE]; + png_size_t written, remaining, err; + check = 0; + remaining = length; + do + { + written = MIN(NEAR_BUF_SIZE, remaining); + png_memcpy(buf, data, written); /* Copy far buffer to near buffer */ +#ifdef _WIN32_WCE + if ( !WriteFile(io_ptr, buf, written, &err, NULL) ) + err = 0; +#else + err = fwrite(buf, 1, written, io_ptr); +#endif + if (err != written) + break; + + else + check += err; + + data += written; + remaining -= written; + } + while (remaining != 0); + } + if (check != length) + png_error(png_ptr, "Write Error"); +} + +#endif +#endif + +/* This function is called to output any data pending writing (normally + * to disk). After png_flush is called, there should be no data pending + * writing in any buffers. + */ +#ifdef PNG_WRITE_FLUSH_SUPPORTED +void /* PRIVATE */ +png_flush(png_structp png_ptr) +{ + if (png_ptr->output_flush_fn != NULL) + (*(png_ptr->output_flush_fn))(png_ptr); +} + +#ifdef PNG_STDIO_SUPPORTED +void PNGAPI +png_default_flush(png_structp png_ptr) +{ +#ifndef _WIN32_WCE + png_FILE_p io_ptr; +#endif + if (png_ptr == NULL) + return; +#ifndef _WIN32_WCE + io_ptr = (png_FILE_p)CVT_PTR((png_ptr->io_ptr)); + fflush(io_ptr); +#endif +} +#endif +#endif + +/* This function allows the application to supply new output functions for + * libpng if standard C streams aren't being used. + * + * This function takes as its arguments: + * png_ptr - pointer to a png output data structure + * io_ptr - pointer to user supplied structure containing info about + * the output functions. May be NULL. + * write_data_fn - pointer to a new output function that takes as its + * arguments a pointer to a png_struct, a pointer to + * data to be written, and a 32-bit unsigned int that is + * the number of bytes to be written. The new write + * function should call png_error(png_ptr, "Error msg") + * to exit and output any fatal error messages. May be + * NULL, in which case libpng's default function will + * be used. + * flush_data_fn - pointer to a new flush function that takes as its + * arguments a pointer to a png_struct. After a call to + * the flush function, there should be no data in any buffers + * or pending transmission. If the output method doesn't do + * any buffering of output, a function prototype must still be + * supplied although it doesn't have to do anything. If + * PNG_WRITE_FLUSH_SUPPORTED is not defined at libpng compile + * time, output_flush_fn will be ignored, although it must be + * supplied for compatibility. May be NULL, in which case + * libpng's default function will be used, if + * PNG_WRITE_FLUSH_SUPPORTED is defined. This is not + * a good idea if io_ptr does not point to a standard + * *FILE structure. + */ +void PNGAPI +png_set_write_fn(png_structp png_ptr, png_voidp io_ptr, + png_rw_ptr write_data_fn, png_flush_ptr output_flush_fn) +{ + if (png_ptr == NULL) + return; + + png_ptr->io_ptr = io_ptr; + +#ifdef PNG_STDIO_SUPPORTED + if (write_data_fn != NULL) + png_ptr->write_data_fn = write_data_fn; + + else + png_ptr->write_data_fn = png_default_write_data; +#else + png_ptr->write_data_fn = write_data_fn; +#endif + +#ifdef PNG_WRITE_FLUSH_SUPPORTED +#ifdef PNG_STDIO_SUPPORTED + if (output_flush_fn != NULL) + png_ptr->output_flush_fn = output_flush_fn; + + else + png_ptr->output_flush_fn = png_default_flush; +#else + png_ptr->output_flush_fn = output_flush_fn; +#endif +#endif /* PNG_WRITE_FLUSH_SUPPORTED */ + + /* It is an error to read while writing a png file */ + if (png_ptr->read_data_fn != NULL) + { + png_ptr->read_data_fn = NULL; + png_warning(png_ptr, + "Attempted to set both read_data_fn and write_data_fn in"); + png_warning(png_ptr, + "the same structure. Resetting read_data_fn to NULL."); + } +} + +#ifdef USE_FAR_KEYWORD +#ifdef _MSC_VER +void *png_far_to_near(png_structp png_ptr, png_voidp ptr, int check) +{ + void *near_ptr; + void FAR *far_ptr; + FP_OFF(near_ptr) = FP_OFF(ptr); + far_ptr = (void FAR *)near_ptr; + + if (check != 0) + if (FP_SEG(ptr) != FP_SEG(far_ptr)) + png_error(png_ptr, "segment lost in conversion"); + + return(near_ptr); +} +# else +void *png_far_to_near(png_structp png_ptr, png_voidp ptr, int check) +{ + void *near_ptr; + void FAR *far_ptr; + near_ptr = (void FAR *)ptr; + far_ptr = (void FAR *)near_ptr; + + if (check != 0) + if (far_ptr != ptr) + png_error(png_ptr, "segment lost in conversion"); + + return(near_ptr); +} +# endif +# endif +#endif /* PNG_WRITE_SUPPORTED */ diff --git a/src/libpng/pngwrite.c b/src/libpng/pngwrite.c new file mode 100644 index 00000000..88a27ccd --- /dev/null +++ b/src/libpng/pngwrite.c @@ -0,0 +1,1596 @@ +#ifdef _WIN32 +#pragma warning(disable:4996) // Disable messages like this one: '_snprintf': This function or variable may be unsafe. Consider using _snprintf_s instead. To disable deprecation, use _CRT_SECURE_NO_WARNINGS. +#endif + +/* pngwrite.c - general routines to write a PNG file + * + * Last changed in libpng 1.2.45 [July 7, 2011] + * Copyright (c) 1998-2011 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + * + * This code is released under the libpng license. + * For conditions of distribution and use, see the disclaimer + * and license in png.h + */ + +/* Get internal access to png.h */ +#define PNG_INTERNAL +#define PNG_NO_PEDANTIC_WARNINGS +#include "png.h" +#ifdef PNG_WRITE_SUPPORTED + +/* Writes all the PNG information. This is the suggested way to use the + * library. If you have a new chunk to add, make a function to write it, + * and put it in the correct location here. If you want the chunk written + * after the image data, put it in png_write_end(). I strongly encourage + * you to supply a PNG_INFO_ flag, and check info_ptr->valid before writing + * the chunk, as that will keep the code from breaking if you want to just + * write a plain PNG file. If you have long comments, I suggest writing + * them in png_write_end(), and compressing them. + */ +void PNGAPI +png_write_info_before_PLTE(png_structp png_ptr, png_infop info_ptr) +{ + png_debug(1, "in png_write_info_before_PLTE"); + + if (png_ptr == NULL || info_ptr == NULL) + return; + if (!(png_ptr->mode & PNG_WROTE_INFO_BEFORE_PLTE)) + { + /* Write PNG signature */ + png_write_sig(png_ptr); +#ifdef PNG_MNG_FEATURES_SUPPORTED + if ((png_ptr->mode&PNG_HAVE_PNG_SIGNATURE) && \ + (png_ptr->mng_features_permitted)) + { + png_warning(png_ptr, "MNG features are not allowed in a PNG datastream"); + png_ptr->mng_features_permitted = 0; + } +#endif + /* Write IHDR information. */ + png_write_IHDR(png_ptr, info_ptr->width, info_ptr->height, + info_ptr->bit_depth, info_ptr->color_type, info_ptr->compression_type, + info_ptr->filter_type, +#ifdef PNG_WRITE_INTERLACING_SUPPORTED + info_ptr->interlace_type); +#else + 0); +#endif + /* The rest of these check to see if the valid field has the appropriate + * flag set, and if it does, writes the chunk. + */ +#ifdef PNG_WRITE_gAMA_SUPPORTED + if (info_ptr->valid & PNG_INFO_gAMA) + { +# ifdef PNG_FLOATING_POINT_SUPPORTED + png_write_gAMA(png_ptr, info_ptr->gamma); +#else +#ifdef PNG_FIXED_POINT_SUPPORTED + png_write_gAMA_fixed(png_ptr, info_ptr->int_gamma); +# endif +#endif + } +#endif +#ifdef PNG_WRITE_sRGB_SUPPORTED + if (info_ptr->valid & PNG_INFO_sRGB) + png_write_sRGB(png_ptr, (int)info_ptr->srgb_intent); +#endif +#ifdef PNG_WRITE_iCCP_SUPPORTED + if (info_ptr->valid & PNG_INFO_iCCP) + png_write_iCCP(png_ptr, info_ptr->iccp_name, PNG_COMPRESSION_TYPE_BASE, + info_ptr->iccp_profile, (int)info_ptr->iccp_proflen); +#endif +#ifdef PNG_WRITE_sBIT_SUPPORTED + if (info_ptr->valid & PNG_INFO_sBIT) + png_write_sBIT(png_ptr, &(info_ptr->sig_bit), info_ptr->color_type); +#endif +#ifdef PNG_WRITE_cHRM_SUPPORTED + if (info_ptr->valid & PNG_INFO_cHRM) + { +#ifdef PNG_FLOATING_POINT_SUPPORTED + png_write_cHRM(png_ptr, + info_ptr->x_white, info_ptr->y_white, + info_ptr->x_red, info_ptr->y_red, + info_ptr->x_green, info_ptr->y_green, + info_ptr->x_blue, info_ptr->y_blue); +#else +# ifdef PNG_FIXED_POINT_SUPPORTED + png_write_cHRM_fixed(png_ptr, + info_ptr->int_x_white, info_ptr->int_y_white, + info_ptr->int_x_red, info_ptr->int_y_red, + info_ptr->int_x_green, info_ptr->int_y_green, + info_ptr->int_x_blue, info_ptr->int_y_blue); +# endif +#endif + } +#endif +#ifdef PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED + if (info_ptr->unknown_chunks_num) + { + png_unknown_chunk *up; + + png_debug(5, "writing extra chunks"); + + for (up = info_ptr->unknown_chunks; + up < info_ptr->unknown_chunks + info_ptr->unknown_chunks_num; + up++) + { + int keep = png_handle_as_unknown(png_ptr, up->name); + if (keep != PNG_HANDLE_CHUNK_NEVER && + up->location && !(up->location & PNG_HAVE_PLTE) && + !(up->location & PNG_HAVE_IDAT) && + ((up->name[3] & 0x20) || keep == PNG_HANDLE_CHUNK_ALWAYS || + (png_ptr->flags & PNG_FLAG_KEEP_UNSAFE_CHUNKS))) + { + if (up->size == 0) + png_warning(png_ptr, "Writing zero-length unknown chunk"); + png_write_chunk(png_ptr, up->name, up->data, up->size); + } + } + } +#endif + png_ptr->mode |= PNG_WROTE_INFO_BEFORE_PLTE; + } +} + +void PNGAPI +png_write_info(png_structp png_ptr, png_infop info_ptr) +{ +#if defined(PNG_WRITE_TEXT_SUPPORTED) || defined(PNG_WRITE_sPLT_SUPPORTED) + int i; +#endif + + png_debug(1, "in png_write_info"); + + if (png_ptr == NULL || info_ptr == NULL) + return; + + png_write_info_before_PLTE(png_ptr, info_ptr); + + if (info_ptr->valid & PNG_INFO_PLTE) + png_write_PLTE(png_ptr, info_ptr->palette, + (png_uint_32)info_ptr->num_palette); + else if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + png_error(png_ptr, "Valid palette required for paletted images"); + +#ifdef PNG_WRITE_tRNS_SUPPORTED + if (info_ptr->valid & PNG_INFO_tRNS) + { +#ifdef PNG_WRITE_INVERT_ALPHA_SUPPORTED + /* Invert the alpha channel (in tRNS) */ + if ((png_ptr->transformations & PNG_INVERT_ALPHA) && + info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + { + int j; + for (j = 0; j<(int)info_ptr->num_trans; j++) + info_ptr->trans[j] = (png_byte)(255 - info_ptr->trans[j]); + } +#endif + png_write_tRNS(png_ptr, info_ptr->trans, &(info_ptr->trans_values), + info_ptr->num_trans, info_ptr->color_type); + } +#endif +#ifdef PNG_WRITE_bKGD_SUPPORTED + if (info_ptr->valid & PNG_INFO_bKGD) + png_write_bKGD(png_ptr, &(info_ptr->background), info_ptr->color_type); +#endif +#ifdef PNG_WRITE_hIST_SUPPORTED + if (info_ptr->valid & PNG_INFO_hIST) + png_write_hIST(png_ptr, info_ptr->hist, info_ptr->num_palette); +#endif +#ifdef PNG_WRITE_oFFs_SUPPORTED + if (info_ptr->valid & PNG_INFO_oFFs) + png_write_oFFs(png_ptr, info_ptr->x_offset, info_ptr->y_offset, + info_ptr->offset_unit_type); +#endif +#ifdef PNG_WRITE_pCAL_SUPPORTED + if (info_ptr->valid & PNG_INFO_pCAL) + png_write_pCAL(png_ptr, info_ptr->pcal_purpose, info_ptr->pcal_X0, + info_ptr->pcal_X1, info_ptr->pcal_type, info_ptr->pcal_nparams, + info_ptr->pcal_units, info_ptr->pcal_params); +#endif + +#ifdef PNG_sCAL_SUPPORTED + if (info_ptr->valid & PNG_INFO_sCAL) +#ifdef PNG_WRITE_sCAL_SUPPORTED +#if defined(PNG_FLOATING_POINT_SUPPORTED) && defined(PNG_STDIO_SUPPORTED) + png_write_sCAL(png_ptr, (int)info_ptr->scal_unit, + info_ptr->scal_pixel_width, info_ptr->scal_pixel_height); +#else /* !FLOATING_POINT */ +#ifdef PNG_FIXED_POINT_SUPPORTED + png_write_sCAL_s(png_ptr, (int)info_ptr->scal_unit, + info_ptr->scal_s_width, info_ptr->scal_s_height); +#endif /* FIXED_POINT */ +#endif /* FLOATING_POINT */ +#else /* !WRITE_sCAL */ + png_warning(png_ptr, + "png_write_sCAL not supported; sCAL chunk not written."); +#endif /* WRITE_sCAL */ +#endif /* sCAL */ + +#ifdef PNG_WRITE_pHYs_SUPPORTED + if (info_ptr->valid & PNG_INFO_pHYs) + png_write_pHYs(png_ptr, info_ptr->x_pixels_per_unit, + info_ptr->y_pixels_per_unit, info_ptr->phys_unit_type); +#endif /* pHYs */ + +#ifdef PNG_WRITE_tIME_SUPPORTED + if (info_ptr->valid & PNG_INFO_tIME) + { + png_write_tIME(png_ptr, &(info_ptr->mod_time)); + png_ptr->mode |= PNG_WROTE_tIME; + } +#endif /* tIME */ + +#ifdef PNG_WRITE_sPLT_SUPPORTED + if (info_ptr->valid & PNG_INFO_sPLT) + for (i = 0; i < (int)info_ptr->splt_palettes_num; i++) + png_write_sPLT(png_ptr, info_ptr->splt_palettes + i); +#endif /* sPLT */ + +#ifdef PNG_WRITE_TEXT_SUPPORTED + /* Check to see if we need to write text chunks */ + for (i = 0; i < info_ptr->num_text; i++) + { + png_debug2(2, "Writing header text chunk %d, type %d", i, + info_ptr->text[i].compression); + /* An internationalized chunk? */ + if (info_ptr->text[i].compression > 0) + { +#ifdef PNG_WRITE_iTXt_SUPPORTED + /* Write international chunk */ + png_write_iTXt(png_ptr, + info_ptr->text[i].compression, + info_ptr->text[i].key, + info_ptr->text[i].lang, + info_ptr->text[i].lang_key, + info_ptr->text[i].text); +#else + png_warning(png_ptr, "Unable to write international text"); +#endif + /* Mark this chunk as written */ + info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_NONE_WR; + } + /* If we want a compressed text chunk */ + else if (info_ptr->text[i].compression == PNG_TEXT_COMPRESSION_zTXt) + { +#ifdef PNG_WRITE_zTXt_SUPPORTED + /* Write compressed chunk */ + png_write_zTXt(png_ptr, info_ptr->text[i].key, + info_ptr->text[i].text, 0, + info_ptr->text[i].compression); +#else + png_warning(png_ptr, "Unable to write compressed text"); +#endif + /* Mark this chunk as written */ + info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_zTXt_WR; + } + else if (info_ptr->text[i].compression == PNG_TEXT_COMPRESSION_NONE) + { +#ifdef PNG_WRITE_tEXt_SUPPORTED + /* Write uncompressed chunk */ + png_write_tEXt(png_ptr, info_ptr->text[i].key, + info_ptr->text[i].text, + 0); + /* Mark this chunk as written */ + info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_NONE_WR; +#else + /* Can't get here */ + png_warning(png_ptr, "Unable to write uncompressed text"); +#endif + } + } +#endif /* tEXt */ + +#ifdef PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED + if (info_ptr->unknown_chunks_num) + { + png_unknown_chunk *up; + + png_debug(5, "writing extra chunks"); + + for (up = info_ptr->unknown_chunks; + up < info_ptr->unknown_chunks + info_ptr->unknown_chunks_num; + up++) + { + int keep = png_handle_as_unknown(png_ptr, up->name); + if (keep != PNG_HANDLE_CHUNK_NEVER && + up->location && (up->location & PNG_HAVE_PLTE) && + !(up->location & PNG_HAVE_IDAT) && + !(up->location & PNG_AFTER_IDAT) && + ((up->name[3] & 0x20) || keep == PNG_HANDLE_CHUNK_ALWAYS || + (png_ptr->flags & PNG_FLAG_KEEP_UNSAFE_CHUNKS))) + { + png_write_chunk(png_ptr, up->name, up->data, up->size); + } + } + } +#endif +} + +/* Writes the end of the PNG file. If you don't want to write comments or + * time information, you can pass NULL for info. If you already wrote these + * in png_write_info(), do not write them again here. If you have long + * comments, I suggest writing them here, and compressing them. + */ +void PNGAPI +png_write_end(png_structp png_ptr, png_infop info_ptr) +{ + png_debug(1, "in png_write_end"); + + if (png_ptr == NULL) + return; + if (!(png_ptr->mode & PNG_HAVE_IDAT)) + png_error(png_ptr, "No IDATs written into file"); + + /* See if user wants us to write information chunks */ + if (info_ptr != NULL) + { +#ifdef PNG_WRITE_TEXT_SUPPORTED + int i; /* local index variable */ +#endif +#ifdef PNG_WRITE_tIME_SUPPORTED + /* Check to see if user has supplied a time chunk */ + if ((info_ptr->valid & PNG_INFO_tIME) && + !(png_ptr->mode & PNG_WROTE_tIME)) + png_write_tIME(png_ptr, &(info_ptr->mod_time)); +#endif +#ifdef PNG_WRITE_TEXT_SUPPORTED + /* Loop through comment chunks */ + for (i = 0; i < info_ptr->num_text; i++) + { + png_debug2(2, "Writing trailer text chunk %d, type %d", i, + info_ptr->text[i].compression); + /* An internationalized chunk? */ + if (info_ptr->text[i].compression > 0) + { +#ifdef PNG_WRITE_iTXt_SUPPORTED + /* Write international chunk */ + png_write_iTXt(png_ptr, + info_ptr->text[i].compression, + info_ptr->text[i].key, + info_ptr->text[i].lang, + info_ptr->text[i].lang_key, + info_ptr->text[i].text); +#else + png_warning(png_ptr, "Unable to write international text"); +#endif + /* Mark this chunk as written */ + info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_NONE_WR; + } + else if (info_ptr->text[i].compression >= PNG_TEXT_COMPRESSION_zTXt) + { +#ifdef PNG_WRITE_zTXt_SUPPORTED + /* Write compressed chunk */ + png_write_zTXt(png_ptr, info_ptr->text[i].key, + info_ptr->text[i].text, 0, + info_ptr->text[i].compression); +#else + png_warning(png_ptr, "Unable to write compressed text"); +#endif + /* Mark this chunk as written */ + info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_zTXt_WR; + } + else if (info_ptr->text[i].compression == PNG_TEXT_COMPRESSION_NONE) + { +#ifdef PNG_WRITE_tEXt_SUPPORTED + /* Write uncompressed chunk */ + png_write_tEXt(png_ptr, info_ptr->text[i].key, + info_ptr->text[i].text, 0); +#else + png_warning(png_ptr, "Unable to write uncompressed text"); +#endif + + /* Mark this chunk as written */ + info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_NONE_WR; + } + } +#endif +#ifdef PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED + if (info_ptr->unknown_chunks_num) + { + png_unknown_chunk *up; + + png_debug(5, "writing extra chunks"); + + for (up = info_ptr->unknown_chunks; + up < info_ptr->unknown_chunks + info_ptr->unknown_chunks_num; + up++) + { + int keep = png_handle_as_unknown(png_ptr, up->name); + if (keep != PNG_HANDLE_CHUNK_NEVER && + up->location && (up->location & PNG_AFTER_IDAT) && + ((up->name[3] & 0x20) || keep == PNG_HANDLE_CHUNK_ALWAYS || + (png_ptr->flags & PNG_FLAG_KEEP_UNSAFE_CHUNKS))) + { + png_write_chunk(png_ptr, up->name, up->data, up->size); + } + } + } +#endif + } + + png_ptr->mode |= PNG_AFTER_IDAT; + + /* Write end of PNG file */ + png_write_IEND(png_ptr); + /* This flush, added in libpng-1.0.8, removed from libpng-1.0.9beta03, + * and restored again in libpng-1.2.30, may cause some applications that + * do not set png_ptr->output_flush_fn to crash. If your application + * experiences a problem, please try building libpng with + * PNG_WRITE_FLUSH_AFTER_IEND_SUPPORTED defined, and report the event to + * png-mng-implement at lists.sf.net . + */ +#ifdef PNG_WRITE_FLUSH_SUPPORTED +# ifdef PNG_WRITE_FLUSH_AFTER_IEND_SUPPORTED + png_flush(png_ptr); +# endif +#endif +} + +#ifdef PNG_CONVERT_tIME_SUPPORTED +/* "tm" structure is not supported on WindowsCE */ +void PNGAPI +png_convert_from_struct_tm(png_timep ptime, struct tm FAR * ttime) +{ + png_debug(1, "in png_convert_from_struct_tm"); + + ptime->year = (png_uint_16)(1900 + ttime->tm_year); + ptime->month = (png_byte)(ttime->tm_mon + 1); + ptime->day = (png_byte)ttime->tm_mday; + ptime->hour = (png_byte)ttime->tm_hour; + ptime->minute = (png_byte)ttime->tm_min; + ptime->second = (png_byte)ttime->tm_sec; +} + +void PNGAPI +png_convert_from_time_t(png_timep ptime, time_t ttime) +{ + struct tm *tbuf; + + png_debug(1, "in png_convert_from_time_t"); + + tbuf = gmtime(&ttime); + png_convert_from_struct_tm(ptime, tbuf); +} +#endif + +/* Initialize png_ptr structure, and allocate any memory needed */ +png_structp PNGAPI +png_create_write_struct(png_const_charp user_png_ver, png_voidp error_ptr, + png_error_ptr error_fn, png_error_ptr warn_fn) +{ +#ifdef PNG_USER_MEM_SUPPORTED + return (png_create_write_struct_2(user_png_ver, error_ptr, error_fn, + warn_fn, png_voidp_NULL, png_malloc_ptr_NULL, png_free_ptr_NULL)); +} + +/* Alternate initialize png_ptr structure, and allocate any memory needed */ +png_structp PNGAPI +png_create_write_struct_2(png_const_charp user_png_ver, png_voidp error_ptr, + png_error_ptr error_fn, png_error_ptr warn_fn, png_voidp mem_ptr, + png_malloc_ptr malloc_fn, png_free_ptr free_fn) +{ +#endif /* PNG_USER_MEM_SUPPORTED */ +#ifdef PNG_SETJMP_SUPPORTED + volatile +#endif + png_structp png_ptr; +#ifdef PNG_SETJMP_SUPPORTED +#ifdef USE_FAR_KEYWORD + jmp_buf jmpbuf; +#endif +#endif + int i; + + png_debug(1, "in png_create_write_struct"); + +#ifdef PNG_USER_MEM_SUPPORTED + png_ptr = (png_structp)png_create_struct_2(PNG_STRUCT_PNG, + (png_malloc_ptr)malloc_fn, (png_voidp)mem_ptr); +#else + png_ptr = (png_structp)png_create_struct(PNG_STRUCT_PNG); +#endif /* PNG_USER_MEM_SUPPORTED */ + if (png_ptr == NULL) + return (NULL); + + /* Added at libpng-1.2.6 */ +#ifdef PNG_SET_USER_LIMITS_SUPPORTED + png_ptr->user_width_max = PNG_USER_WIDTH_MAX; + png_ptr->user_height_max = PNG_USER_HEIGHT_MAX; +#endif + +#ifdef PNG_SETJMP_SUPPORTED +#ifdef USE_FAR_KEYWORD + if (setjmp(jmpbuf)) +#else + if (setjmp(png_ptr->jmpbuf)) +#endif + { + png_free(png_ptr, png_ptr->zbuf); + png_ptr->zbuf = NULL; +#ifdef PNG_USER_MEM_SUPPORTED + png_destroy_struct_2((png_voidp)png_ptr, + (png_free_ptr)free_fn, (png_voidp)mem_ptr); +#else + png_destroy_struct((png_voidp)png_ptr); +#endif + return (NULL); + } +#ifdef USE_FAR_KEYWORD + png_memcpy(png_ptr->jmpbuf, jmpbuf, png_sizeof(jmp_buf)); +#endif +#endif + +#ifdef PNG_USER_MEM_SUPPORTED + png_set_mem_fn(png_ptr, mem_ptr, malloc_fn, free_fn); +#endif /* PNG_USER_MEM_SUPPORTED */ + png_set_error_fn(png_ptr, error_ptr, error_fn, warn_fn); + + if (user_png_ver) + { + i = 0; + do + { + if (user_png_ver[i] != png_libpng_ver[i]) + png_ptr->flags |= PNG_FLAG_LIBRARY_MISMATCH; + } while (png_libpng_ver[i++]); + } + + if (png_ptr->flags & PNG_FLAG_LIBRARY_MISMATCH) + { + /* Libpng 0.90 and later are binary incompatible with libpng 0.89, so + * we must recompile any applications that use any older library version. + * For versions after libpng 1.0, we will be compatible, so we need + * only check the first digit. + */ + if (user_png_ver == NULL || user_png_ver[0] != png_libpng_ver[0] || + (user_png_ver[0] == '1' && user_png_ver[2] != png_libpng_ver[2]) || + (user_png_ver[0] == '0' && user_png_ver[2] < '9')) + { +#if defined(PNG_STDIO_SUPPORTED) && !defined(_WIN32_WCE) + char msg[80]; + if (user_png_ver) + { + png_snprintf(msg, 80, + "Application was compiled with png.h from libpng-%.20s", + user_png_ver); + png_warning(png_ptr, msg); + } + png_snprintf(msg, 80, + "Application is running with png.c from libpng-%.20s", + png_libpng_ver); + png_warning(png_ptr, msg); +#endif +#ifdef PNG_ERROR_NUMBERS_SUPPORTED + png_ptr->flags = 0; +#endif + png_error(png_ptr, + "Incompatible libpng version in application and library"); + } + } + + /* Initialize zbuf - compression buffer */ + png_ptr->zbuf_size = PNG_ZBUF_SIZE; + png_ptr->zbuf = (png_bytep)png_malloc(png_ptr, + (png_uint_32)png_ptr->zbuf_size); + + png_set_write_fn(png_ptr, png_voidp_NULL, png_rw_ptr_NULL, + png_flush_ptr_NULL); + +#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED + png_set_filter_heuristics(png_ptr, PNG_FILTER_HEURISTIC_DEFAULT, + 1, png_doublep_NULL, png_doublep_NULL); +#endif + +#ifdef PNG_SETJMP_SUPPORTED + /* Applications that neglect to set up their own setjmp() and then + * encounter a png_error() will longjmp here. Since the jmpbuf is + * then meaningless we abort instead of returning. + */ +#ifdef USE_FAR_KEYWORD + if (setjmp(jmpbuf)) + PNG_ABORT(); + png_memcpy(png_ptr->jmpbuf, jmpbuf, png_sizeof(jmp_buf)); +#else + if (setjmp(png_ptr->jmpbuf)) + PNG_ABORT(); +#endif +#endif + return (png_ptr); +} + +/* Initialize png_ptr structure, and allocate any memory needed */ +#if defined(PNG_1_0_X) || defined(PNG_1_2_X) +/* Deprecated. */ +#undef png_write_init +void PNGAPI +png_write_init(png_structp png_ptr) +{ + /* We only come here via pre-1.0.7-compiled applications */ + png_write_init_2(png_ptr, "1.0.6 or earlier", 0, 0); +} + +void PNGAPI +png_write_init_2(png_structp png_ptr, png_const_charp user_png_ver, + png_size_t png_struct_size, png_size_t png_info_size) +{ + /* We only come here via pre-1.0.12-compiled applications */ + if (png_ptr == NULL) return; +#if defined(PNG_STDIO_SUPPORTED) && !defined(_WIN32_WCE) + if (png_sizeof(png_struct) > png_struct_size || + png_sizeof(png_info) > png_info_size) + { + char msg[80]; + png_ptr->warning_fn = NULL; + if (user_png_ver) + { + png_snprintf(msg, 80, + "Application was compiled with png.h from libpng-%.20s", + user_png_ver); + png_warning(png_ptr, msg); + } + png_snprintf(msg, 80, + "Application is running with png.c from libpng-%.20s", + png_libpng_ver); + png_warning(png_ptr, msg); + } +#endif + if (png_sizeof(png_struct) > png_struct_size) + { + png_ptr->error_fn = NULL; +#ifdef PNG_ERROR_NUMBERS_SUPPORTED + png_ptr->flags = 0; +#endif + png_error(png_ptr, + "The png struct allocated by the application for writing is" + " too small."); + } + if (png_sizeof(png_info) > png_info_size) + { + png_ptr->error_fn = NULL; +#ifdef PNG_ERROR_NUMBERS_SUPPORTED + png_ptr->flags = 0; +#endif + png_error(png_ptr, + "The info struct allocated by the application for writing is" + " too small."); + } + png_write_init_3(&png_ptr, user_png_ver, png_struct_size); +} +#endif /* PNG_1_0_X || PNG_1_2_X */ + + +void PNGAPI +png_write_init_3(png_structpp ptr_ptr, png_const_charp user_png_ver, + png_size_t png_struct_size) +{ + png_structp png_ptr = *ptr_ptr; +#ifdef PNG_SETJMP_SUPPORTED + jmp_buf tmp_jmp; /* to save current jump buffer */ +#endif + + int i = 0; + + if (png_ptr == NULL) + return; + + do + { + if (user_png_ver[i] != png_libpng_ver[i]) + { +#ifdef PNG_LEGACY_SUPPORTED + png_ptr->flags |= PNG_FLAG_LIBRARY_MISMATCH; +#else + png_ptr->warning_fn = NULL; + png_warning(png_ptr, + "Application uses deprecated png_write_init() and should be recompiled."); +#endif + } + } while (png_libpng_ver[i++]); + + png_debug(1, "in png_write_init_3"); + +#ifdef PNG_SETJMP_SUPPORTED + /* Save jump buffer and error functions */ + png_memcpy(tmp_jmp, png_ptr->jmpbuf, png_sizeof(jmp_buf)); +#endif + + if (png_sizeof(png_struct) > png_struct_size) + { + png_destroy_struct(png_ptr); + png_ptr = (png_structp)png_create_struct(PNG_STRUCT_PNG); + *ptr_ptr = png_ptr; + } + + /* Reset all variables to 0 */ + png_memset(png_ptr, 0, png_sizeof(png_struct)); + + /* Added at libpng-1.2.6 */ +#ifdef PNG_SET_USER_LIMITS_SUPPORTED + png_ptr->user_width_max = PNG_USER_WIDTH_MAX; + png_ptr->user_height_max = PNG_USER_HEIGHT_MAX; +#endif + +#ifdef PNG_SETJMP_SUPPORTED + /* Restore jump buffer */ + png_memcpy(png_ptr->jmpbuf, tmp_jmp, png_sizeof(jmp_buf)); +#endif + + png_set_write_fn(png_ptr, png_voidp_NULL, png_rw_ptr_NULL, + png_flush_ptr_NULL); + + /* Initialize zbuf - compression buffer */ + png_ptr->zbuf_size = PNG_ZBUF_SIZE; + png_ptr->zbuf = (png_bytep)png_malloc(png_ptr, + (png_uint_32)png_ptr->zbuf_size); +#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED + png_set_filter_heuristics(png_ptr, PNG_FILTER_HEURISTIC_DEFAULT, + 1, png_doublep_NULL, png_doublep_NULL); +#endif +} + +/* Write a few rows of image data. If the image is interlaced, + * either you will have to write the 7 sub images, or, if you + * have called png_set_interlace_handling(), you will have to + * "write" the image seven times. + */ +void PNGAPI +png_write_rows(png_structp png_ptr, png_bytepp row, + png_uint_32 num_rows) +{ + png_uint_32 i; /* row counter */ + png_bytepp rp; /* row pointer */ + + png_debug(1, "in png_write_rows"); + + if (png_ptr == NULL) + return; + + /* Loop through the rows */ + for (i = 0, rp = row; i < num_rows; i++, rp++) + { + png_write_row(png_ptr, *rp); + } +} + +/* Write the image. You only need to call this function once, even + * if you are writing an interlaced image. + */ +void PNGAPI +png_write_image(png_structp png_ptr, png_bytepp image) +{ + png_uint_32 i; /* row index */ + int pass, num_pass; /* pass variables */ + png_bytepp rp; /* points to current row */ + + if (png_ptr == NULL) + return; + + png_debug(1, "in png_write_image"); + +#ifdef PNG_WRITE_INTERLACING_SUPPORTED + /* Initialize interlace handling. If image is not interlaced, + * this will set pass to 1 + */ + num_pass = png_set_interlace_handling(png_ptr); +#else + num_pass = 1; +#endif + /* Loop through passes */ + for (pass = 0; pass < num_pass; pass++) + { + /* Loop through image */ + for (i = 0, rp = image; i < png_ptr->height; i++, rp++) + { + png_write_row(png_ptr, *rp); + } + } +} + +/* Called by user to write a row of image data */ +void PNGAPI +png_write_row(png_structp png_ptr, png_bytep row) +{ + if (png_ptr == NULL) + return; + + png_debug2(1, "in png_write_row (row %ld, pass %d)", + png_ptr->row_number, png_ptr->pass); + + /* Initialize transformations and other stuff if first time */ + if (png_ptr->row_number == 0 && png_ptr->pass == 0) + { + /* Make sure we wrote the header info */ + if (!(png_ptr->mode & PNG_WROTE_INFO_BEFORE_PLTE)) + png_error(png_ptr, + "png_write_info was never called before png_write_row."); + + /* Check for transforms that have been set but were defined out */ +#if !defined(PNG_WRITE_INVERT_SUPPORTED) && defined(PNG_READ_INVERT_SUPPORTED) + if (png_ptr->transformations & PNG_INVERT_MONO) + png_warning(png_ptr, + "PNG_WRITE_INVERT_SUPPORTED is not defined."); +#endif +#if !defined(PNG_WRITE_FILLER_SUPPORTED) && defined(PNG_READ_FILLER_SUPPORTED) + if (png_ptr->transformations & PNG_FILLER) + png_warning(png_ptr, + "PNG_WRITE_FILLER_SUPPORTED is not defined."); +#endif +#if !defined(PNG_WRITE_PACKSWAP_SUPPORTED) && \ + defined(PNG_READ_PACKSWAP_SUPPORTED) + if (png_ptr->transformations & PNG_PACKSWAP) + png_warning(png_ptr, + "PNG_WRITE_PACKSWAP_SUPPORTED is not defined."); +#endif +#if !defined(PNG_WRITE_PACK_SUPPORTED) && defined(PNG_READ_PACK_SUPPORTED) + if (png_ptr->transformations & PNG_PACK) + png_warning(png_ptr, "PNG_WRITE_PACK_SUPPORTED is not defined."); +#endif +#if !defined(PNG_WRITE_SHIFT_SUPPORTED) && defined(PNG_READ_SHIFT_SUPPORTED) + if (png_ptr->transformations & PNG_SHIFT) + png_warning(png_ptr, "PNG_WRITE_SHIFT_SUPPORTED is not defined."); +#endif +#if !defined(PNG_WRITE_BGR_SUPPORTED) && defined(PNG_READ_BGR_SUPPORTED) + if (png_ptr->transformations & PNG_BGR) + png_warning(png_ptr, "PNG_WRITE_BGR_SUPPORTED is not defined."); +#endif +#if !defined(PNG_WRITE_SWAP_SUPPORTED) && defined(PNG_READ_SWAP_SUPPORTED) + if (png_ptr->transformations & PNG_SWAP_BYTES) + png_warning(png_ptr, "PNG_WRITE_SWAP_SUPPORTED is not defined."); +#endif + + png_write_start_row(png_ptr); + } + +#ifdef PNG_WRITE_INTERLACING_SUPPORTED + /* If interlaced and not interested in row, return */ + if (png_ptr->interlaced && (png_ptr->transformations & PNG_INTERLACE)) + { + switch (png_ptr->pass) + { + case 0: + if (png_ptr->row_number & 0x07) + { + png_write_finish_row(png_ptr); + return; + } + break; + case 1: + if ((png_ptr->row_number & 0x07) || png_ptr->width < 5) + { + png_write_finish_row(png_ptr); + return; + } + break; + case 2: + if ((png_ptr->row_number & 0x07) != 4) + { + png_write_finish_row(png_ptr); + return; + } + break; + case 3: + if ((png_ptr->row_number & 0x03) || png_ptr->width < 3) + { + png_write_finish_row(png_ptr); + return; + } + break; + case 4: + if ((png_ptr->row_number & 0x03) != 2) + { + png_write_finish_row(png_ptr); + return; + } + break; + case 5: + if ((png_ptr->row_number & 0x01) || png_ptr->width < 2) + { + png_write_finish_row(png_ptr); + return; + } + break; + case 6: + if (!(png_ptr->row_number & 0x01)) + { + png_write_finish_row(png_ptr); + return; + } + break; + } + } +#endif + + /* Set up row info for transformations */ + png_ptr->row_info.color_type = png_ptr->color_type; + png_ptr->row_info.width = png_ptr->usr_width; + png_ptr->row_info.channels = png_ptr->usr_channels; + png_ptr->row_info.bit_depth = png_ptr->usr_bit_depth; + png_ptr->row_info.pixel_depth = (png_byte)(png_ptr->row_info.bit_depth * + png_ptr->row_info.channels); + + png_ptr->row_info.rowbytes = PNG_ROWBYTES(png_ptr->row_info.pixel_depth, + png_ptr->row_info.width); + + png_debug1(3, "row_info->color_type = %d", png_ptr->row_info.color_type); + png_debug1(3, "row_info->width = %lu", png_ptr->row_info.width); + png_debug1(3, "row_info->channels = %d", png_ptr->row_info.channels); + png_debug1(3, "row_info->bit_depth = %d", png_ptr->row_info.bit_depth); + png_debug1(3, "row_info->pixel_depth = %d", png_ptr->row_info.pixel_depth); + png_debug1(3, "row_info->rowbytes = %lu", png_ptr->row_info.rowbytes); + + /* Copy user's row into buffer, leaving room for filter byte. */ + png_memcpy_check(png_ptr, png_ptr->row_buf + 1, row, + png_ptr->row_info.rowbytes); + +#ifdef PNG_WRITE_INTERLACING_SUPPORTED + /* Handle interlacing */ + if (png_ptr->interlaced && png_ptr->pass < 6 && + (png_ptr->transformations & PNG_INTERLACE)) + { + png_do_write_interlace(&(png_ptr->row_info), + png_ptr->row_buf + 1, png_ptr->pass); + /* This should always get caught above, but still ... */ + if (!(png_ptr->row_info.width)) + { + png_write_finish_row(png_ptr); + return; + } + } +#endif + + /* Handle other transformations */ + if (png_ptr->transformations) + png_do_write_transformations(png_ptr); + +#ifdef PNG_MNG_FEATURES_SUPPORTED + /* Write filter_method 64 (intrapixel differencing) only if + * 1. Libpng was compiled with PNG_MNG_FEATURES_SUPPORTED and + * 2. Libpng did not write a PNG signature (this filter_method is only + * used in PNG datastreams that are embedded in MNG datastreams) and + * 3. The application called png_permit_mng_features with a mask that + * included PNG_FLAG_MNG_FILTER_64 and + * 4. The filter_method is 64 and + * 5. The color_type is RGB or RGBA + */ + if ((png_ptr->mng_features_permitted & PNG_FLAG_MNG_FILTER_64) && + (png_ptr->filter_type == PNG_INTRAPIXEL_DIFFERENCING)) + { + /* Intrapixel differencing */ + png_do_write_intrapixel(&(png_ptr->row_info), png_ptr->row_buf + 1); + } +#endif + + /* Find a filter if necessary, filter the row and write it out. */ + png_write_find_filter(png_ptr, &(png_ptr->row_info)); + + if (png_ptr->write_row_fn != NULL) + (*(png_ptr->write_row_fn))(png_ptr, png_ptr->row_number, png_ptr->pass); +} + +#ifdef PNG_WRITE_FLUSH_SUPPORTED +/* Set the automatic flush interval or 0 to turn flushing off */ +void PNGAPI +png_set_flush(png_structp png_ptr, int nrows) +{ + png_debug(1, "in png_set_flush"); + + if (png_ptr == NULL) + return; + png_ptr->flush_dist = (nrows < 0 ? 0 : nrows); +} + +/* Flush the current output buffers now */ +void PNGAPI +png_write_flush(png_structp png_ptr) +{ + int wrote_IDAT; + + png_debug(1, "in png_write_flush"); + + if (png_ptr == NULL) + return; + /* We have already written out all of the data */ + if (png_ptr->row_number >= png_ptr->num_rows) + return; + + do + { + int ret; + + /* Compress the data */ + ret = deflate(&png_ptr->zstream, Z_SYNC_FLUSH); + wrote_IDAT = 0; + + /* Check for compression errors */ + if (ret != Z_OK) + { + if (png_ptr->zstream.msg != NULL) + png_error(png_ptr, png_ptr->zstream.msg); + else + png_error(png_ptr, "zlib error"); + } + + if (!(png_ptr->zstream.avail_out)) + { + /* Write the IDAT and reset the zlib output buffer */ + png_write_IDAT(png_ptr, png_ptr->zbuf, + png_ptr->zbuf_size); + png_ptr->zstream.next_out = png_ptr->zbuf; + png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size; + wrote_IDAT = 1; + } + } while(wrote_IDAT == 1); + + /* If there is any data left to be output, write it into a new IDAT */ + if (png_ptr->zbuf_size != png_ptr->zstream.avail_out) + { + /* Write the IDAT and reset the zlib output buffer */ + png_write_IDAT(png_ptr, png_ptr->zbuf, + png_ptr->zbuf_size - png_ptr->zstream.avail_out); + png_ptr->zstream.next_out = png_ptr->zbuf; + png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size; + } + png_ptr->flush_rows = 0; + png_flush(png_ptr); +} +#endif /* PNG_WRITE_FLUSH_SUPPORTED */ + +/* Free all memory used by the write */ +void PNGAPI +png_destroy_write_struct(png_structpp png_ptr_ptr, png_infopp info_ptr_ptr) +{ + png_structp png_ptr = NULL; + png_infop info_ptr = NULL; +#ifdef PNG_USER_MEM_SUPPORTED + png_free_ptr free_fn = NULL; + png_voidp mem_ptr = NULL; +#endif + + png_debug(1, "in png_destroy_write_struct"); + + if (png_ptr_ptr != NULL) + { + png_ptr = *png_ptr_ptr; +#ifdef PNG_USER_MEM_SUPPORTED + free_fn = png_ptr->free_fn; + mem_ptr = png_ptr->mem_ptr; +#endif + } + +#ifdef PNG_USER_MEM_SUPPORTED + if (png_ptr != NULL) + { + free_fn = png_ptr->free_fn; + mem_ptr = png_ptr->mem_ptr; + } +#endif + + if (info_ptr_ptr != NULL) + info_ptr = *info_ptr_ptr; + + if (info_ptr != NULL) + { + if (png_ptr != NULL) + { + png_free_data(png_ptr, info_ptr, PNG_FREE_ALL, -1); + +#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED + if (png_ptr->num_chunk_list) + { + png_free(png_ptr, png_ptr->chunk_list); + png_ptr->chunk_list = NULL; + png_ptr->num_chunk_list = 0; + } +#endif + } + +#ifdef PNG_USER_MEM_SUPPORTED + png_destroy_struct_2((png_voidp)info_ptr, (png_free_ptr)free_fn, + (png_voidp)mem_ptr); +#else + png_destroy_struct((png_voidp)info_ptr); +#endif + *info_ptr_ptr = NULL; + } + + if (png_ptr != NULL) + { + png_write_destroy(png_ptr); +#ifdef PNG_USER_MEM_SUPPORTED + png_destroy_struct_2((png_voidp)png_ptr, (png_free_ptr)free_fn, + (png_voidp)mem_ptr); +#else + png_destroy_struct((png_voidp)png_ptr); +#endif + *png_ptr_ptr = NULL; + } +} + + +/* Free any memory used in png_ptr struct (old method) */ +void /* PRIVATE */ +png_write_destroy(png_structp png_ptr) +{ +#ifdef PNG_SETJMP_SUPPORTED + jmp_buf tmp_jmp; /* Save jump buffer */ +#endif + png_error_ptr error_fn; + png_error_ptr warning_fn; + png_voidp error_ptr; +#ifdef PNG_USER_MEM_SUPPORTED + png_free_ptr free_fn; +#endif + + png_debug(1, "in png_write_destroy"); + + /* Free any memory zlib uses */ + deflateEnd(&png_ptr->zstream); + + /* Free our memory. png_free checks NULL for us. */ + png_free(png_ptr, png_ptr->zbuf); + png_free(png_ptr, png_ptr->row_buf); +#ifdef PNG_WRITE_FILTER_SUPPORTED + png_free(png_ptr, png_ptr->prev_row); + png_free(png_ptr, png_ptr->sub_row); + png_free(png_ptr, png_ptr->up_row); + png_free(png_ptr, png_ptr->avg_row); + png_free(png_ptr, png_ptr->paeth_row); +#endif + +#ifdef PNG_TIME_RFC1123_SUPPORTED + png_free(png_ptr, png_ptr->time_buffer); +#endif + +#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED + png_free(png_ptr, png_ptr->prev_filters); + png_free(png_ptr, png_ptr->filter_weights); + png_free(png_ptr, png_ptr->inv_filter_weights); + png_free(png_ptr, png_ptr->filter_costs); + png_free(png_ptr, png_ptr->inv_filter_costs); +#endif + +#ifdef PNG_SETJMP_SUPPORTED + /* Reset structure */ + png_memcpy(tmp_jmp, png_ptr->jmpbuf, png_sizeof(jmp_buf)); +#endif + + error_fn = png_ptr->error_fn; + warning_fn = png_ptr->warning_fn; + error_ptr = png_ptr->error_ptr; +#ifdef PNG_USER_MEM_SUPPORTED + free_fn = png_ptr->free_fn; +#endif + + png_memset(png_ptr, 0, png_sizeof(png_struct)); + + png_ptr->error_fn = error_fn; + png_ptr->warning_fn = warning_fn; + png_ptr->error_ptr = error_ptr; +#ifdef PNG_USER_MEM_SUPPORTED + png_ptr->free_fn = free_fn; +#endif + +#ifdef PNG_SETJMP_SUPPORTED + png_memcpy(png_ptr->jmpbuf, tmp_jmp, png_sizeof(jmp_buf)); +#endif +} + +/* Allow the application to select one or more row filters to use. */ +void PNGAPI +png_set_filter(png_structp png_ptr, int method, int filters) +{ + png_debug(1, "in png_set_filter"); + + if (png_ptr == NULL) + return; +#ifdef PNG_MNG_FEATURES_SUPPORTED + if ((png_ptr->mng_features_permitted & PNG_FLAG_MNG_FILTER_64) && + (method == PNG_INTRAPIXEL_DIFFERENCING)) + method = PNG_FILTER_TYPE_BASE; +#endif + if (method == PNG_FILTER_TYPE_BASE) + { + switch (filters & (PNG_ALL_FILTERS | 0x07)) + { +#ifdef PNG_WRITE_FILTER_SUPPORTED + case 5: + case 6: + case 7: png_warning(png_ptr, "Unknown row filter for method 0"); +#endif /* PNG_WRITE_FILTER_SUPPORTED */ + case PNG_FILTER_VALUE_NONE: + png_ptr->do_filter = PNG_FILTER_NONE; break; +#ifdef PNG_WRITE_FILTER_SUPPORTED + case PNG_FILTER_VALUE_SUB: + png_ptr->do_filter = PNG_FILTER_SUB; break; + case PNG_FILTER_VALUE_UP: + png_ptr->do_filter = PNG_FILTER_UP; break; + case PNG_FILTER_VALUE_AVG: + png_ptr->do_filter = PNG_FILTER_AVG; break; + case PNG_FILTER_VALUE_PAETH: + png_ptr->do_filter = PNG_FILTER_PAETH; break; + default: png_ptr->do_filter = (png_byte)filters; break; +#else + default: png_warning(png_ptr, "Unknown row filter for method 0"); +#endif /* PNG_WRITE_FILTER_SUPPORTED */ + } + + /* If we have allocated the row_buf, this means we have already started + * with the image and we should have allocated all of the filter buffers + * that have been selected. If prev_row isn't already allocated, then + * it is too late to start using the filters that need it, since we + * will be missing the data in the previous row. If an application + * wants to start and stop using particular filters during compression, + * it should start out with all of the filters, and then add and + * remove them after the start of compression. + */ + if (png_ptr->row_buf != NULL) + { +#ifdef PNG_WRITE_FILTER_SUPPORTED + if ((png_ptr->do_filter & PNG_FILTER_SUB) && png_ptr->sub_row == NULL) + { + png_ptr->sub_row = (png_bytep)png_malloc(png_ptr, + (png_ptr->rowbytes + 1)); + png_ptr->sub_row[0] = PNG_FILTER_VALUE_SUB; + } + + if ((png_ptr->do_filter & PNG_FILTER_UP) && png_ptr->up_row == NULL) + { + if (png_ptr->prev_row == NULL) + { + png_warning(png_ptr, "Can't add Up filter after starting"); + png_ptr->do_filter &= ~PNG_FILTER_UP; + } + else + { + png_ptr->up_row = (png_bytep)png_malloc(png_ptr, + (png_ptr->rowbytes + 1)); + png_ptr->up_row[0] = PNG_FILTER_VALUE_UP; + } + } + + if ((png_ptr->do_filter & PNG_FILTER_AVG) && png_ptr->avg_row == NULL) + { + if (png_ptr->prev_row == NULL) + { + png_warning(png_ptr, "Can't add Average filter after starting"); + png_ptr->do_filter &= ~PNG_FILTER_AVG; + } + else + { + png_ptr->avg_row = (png_bytep)png_malloc(png_ptr, + (png_ptr->rowbytes + 1)); + png_ptr->avg_row[0] = PNG_FILTER_VALUE_AVG; + } + } + + if ((png_ptr->do_filter & PNG_FILTER_PAETH) && + png_ptr->paeth_row == NULL) + { + if (png_ptr->prev_row == NULL) + { + png_warning(png_ptr, "Can't add Paeth filter after starting"); + png_ptr->do_filter &= (png_byte)(~PNG_FILTER_PAETH); + } + else + { + png_ptr->paeth_row = (png_bytep)png_malloc(png_ptr, + (png_ptr->rowbytes + 1)); + png_ptr->paeth_row[0] = PNG_FILTER_VALUE_PAETH; + } + } + + if (png_ptr->do_filter == PNG_NO_FILTERS) +#endif /* PNG_WRITE_FILTER_SUPPORTED */ + png_ptr->do_filter = PNG_FILTER_NONE; + } + } + else + png_error(png_ptr, "Unknown custom filter method"); +} + +/* This allows us to influence the way in which libpng chooses the "best" + * filter for the current scanline. While the "minimum-sum-of-absolute- + * differences metric is relatively fast and effective, there is some + * question as to whether it can be improved upon by trying to keep the + * filtered data going to zlib more consistent, hopefully resulting in + * better compression. + */ +#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED /* GRR 970116 */ +void PNGAPI +png_set_filter_heuristics(png_structp png_ptr, int heuristic_method, + int num_weights, png_doublep filter_weights, + png_doublep filter_costs) +{ + int i; + + png_debug(1, "in png_set_filter_heuristics"); + + if (png_ptr == NULL) + return; + if (heuristic_method >= PNG_FILTER_HEURISTIC_LAST) + { + png_warning(png_ptr, "Unknown filter heuristic method"); + return; + } + + if (heuristic_method == PNG_FILTER_HEURISTIC_DEFAULT) + { + heuristic_method = PNG_FILTER_HEURISTIC_UNWEIGHTED; + } + + if (num_weights < 0 || filter_weights == NULL || + heuristic_method == PNG_FILTER_HEURISTIC_UNWEIGHTED) + { + num_weights = 0; + } + + png_ptr->num_prev_filters = (png_byte)num_weights; + png_ptr->heuristic_method = (png_byte)heuristic_method; + + if (num_weights > 0) + { + if (png_ptr->prev_filters == NULL) + { + png_ptr->prev_filters = (png_bytep)png_malloc(png_ptr, + (png_uint_32)(png_sizeof(png_byte) * num_weights)); + + /* To make sure that the weighting starts out fairly */ + for (i = 0; i < num_weights; i++) + { + png_ptr->prev_filters[i] = 255; + } + } + + if (png_ptr->filter_weights == NULL) + { + png_ptr->filter_weights = (png_uint_16p)png_malloc(png_ptr, + (png_uint_32)(png_sizeof(png_uint_16) * num_weights)); + + png_ptr->inv_filter_weights = (png_uint_16p)png_malloc(png_ptr, + (png_uint_32)(png_sizeof(png_uint_16) * num_weights)); + for (i = 0; i < num_weights; i++) + { + png_ptr->inv_filter_weights[i] = + png_ptr->filter_weights[i] = PNG_WEIGHT_FACTOR; + } + } + + for (i = 0; i < num_weights; i++) + { + if (filter_weights[i] < 0.0) + { + png_ptr->inv_filter_weights[i] = + png_ptr->filter_weights[i] = PNG_WEIGHT_FACTOR; + } + else + { + png_ptr->inv_filter_weights[i] = + (png_uint_16)((double)PNG_WEIGHT_FACTOR*filter_weights[i]+0.5); + png_ptr->filter_weights[i] = + (png_uint_16)((double)PNG_WEIGHT_FACTOR/filter_weights[i]+0.5); + } + } + } + + /* If, in the future, there are other filter methods, this would + * need to be based on png_ptr->filter. + */ + if (png_ptr->filter_costs == NULL) + { + png_ptr->filter_costs = (png_uint_16p)png_malloc(png_ptr, + (png_uint_32)(png_sizeof(png_uint_16) * PNG_FILTER_VALUE_LAST)); + + png_ptr->inv_filter_costs = (png_uint_16p)png_malloc(png_ptr, + (png_uint_32)(png_sizeof(png_uint_16) * PNG_FILTER_VALUE_LAST)); + + for (i = 0; i < PNG_FILTER_VALUE_LAST; i++) + { + png_ptr->inv_filter_costs[i] = + png_ptr->filter_costs[i] = PNG_COST_FACTOR; + } + } + + /* Here is where we set the relative costs of the different filters. We + * should take the desired compression level into account when setting + * the costs, so that Paeth, for instance, has a high relative cost at low + * compression levels, while it has a lower relative cost at higher + * compression settings. The filter types are in order of increasing + * relative cost, so it would be possible to do this with an algorithm. + */ + for (i = 0; i < PNG_FILTER_VALUE_LAST; i++) + { + if (filter_costs == NULL || filter_costs[i] < 0.0) + { + png_ptr->inv_filter_costs[i] = + png_ptr->filter_costs[i] = PNG_COST_FACTOR; + } + else if (filter_costs[i] >= 1.0) + { + png_ptr->inv_filter_costs[i] = + (png_uint_16)((double)PNG_COST_FACTOR / filter_costs[i] + 0.5); + png_ptr->filter_costs[i] = + (png_uint_16)((double)PNG_COST_FACTOR * filter_costs[i] + 0.5); + } + } +} +#endif /* PNG_WRITE_WEIGHTED_FILTER_SUPPORTED */ + +void PNGAPI +png_set_compression_level(png_structp png_ptr, int level) +{ + png_debug(1, "in png_set_compression_level"); + + if (png_ptr == NULL) + return; + png_ptr->flags |= PNG_FLAG_ZLIB_CUSTOM_LEVEL; + png_ptr->zlib_level = level; +} + +void PNGAPI +png_set_compression_mem_level(png_structp png_ptr, int mem_level) +{ + png_debug(1, "in png_set_compression_mem_level"); + + if (png_ptr == NULL) + return; + png_ptr->flags |= PNG_FLAG_ZLIB_CUSTOM_MEM_LEVEL; + png_ptr->zlib_mem_level = mem_level; +} + +void PNGAPI +png_set_compression_strategy(png_structp png_ptr, int strategy) +{ + png_debug(1, "in png_set_compression_strategy"); + + if (png_ptr == NULL) + return; + png_ptr->flags |= PNG_FLAG_ZLIB_CUSTOM_STRATEGY; + png_ptr->zlib_strategy = strategy; +} + +void PNGAPI +png_set_compression_window_bits(png_structp png_ptr, int window_bits) +{ + if (png_ptr == NULL) + return; + if (window_bits > 15) + png_warning(png_ptr, "Only compression windows <= 32k supported by PNG"); + else if (window_bits < 8) + png_warning(png_ptr, "Only compression windows >= 256 supported by PNG"); +#ifndef WBITS_8_OK + /* Avoid libpng bug with 256-byte windows */ + if (window_bits == 8) + { + png_warning(png_ptr, "Compression window is being reset to 512"); + window_bits = 9; + } +#endif + png_ptr->flags |= PNG_FLAG_ZLIB_CUSTOM_WINDOW_BITS; + png_ptr->zlib_window_bits = window_bits; +} + +void PNGAPI +png_set_compression_method(png_structp png_ptr, int method) +{ + png_debug(1, "in png_set_compression_method"); + + if (png_ptr == NULL) + return; + if (method != 8) + png_warning(png_ptr, "Only compression method 8 is supported by PNG"); + png_ptr->flags |= PNG_FLAG_ZLIB_CUSTOM_METHOD; + png_ptr->zlib_method = method; +} + +void PNGAPI +png_set_write_status_fn(png_structp png_ptr, png_write_status_ptr write_row_fn) +{ + if (png_ptr == NULL) + return; + png_ptr->write_row_fn = write_row_fn; +} + +#ifdef PNG_WRITE_USER_TRANSFORM_SUPPORTED +void PNGAPI +png_set_write_user_transform_fn(png_structp png_ptr, png_user_transform_ptr + write_user_transform_fn) +{ + png_debug(1, "in png_set_write_user_transform_fn"); + + if (png_ptr == NULL) + return; + png_ptr->transformations |= PNG_USER_TRANSFORM; + png_ptr->write_user_transform_fn = write_user_transform_fn; +} +#endif + + +#ifdef PNG_INFO_IMAGE_SUPPORTED +void PNGAPI +png_write_png(png_structp png_ptr, png_infop info_ptr, + int transforms, voidp params) +{ + if (png_ptr == NULL || info_ptr == NULL) + return; + + /* Write the file header information. */ + png_write_info(png_ptr, info_ptr); + + /* ------ these transformations don't touch the info structure ------- */ + +#ifdef PNG_WRITE_INVERT_SUPPORTED + /* Invert monochrome pixels */ + if (transforms & PNG_TRANSFORM_INVERT_MONO) + png_set_invert_mono(png_ptr); +#endif + +#ifdef PNG_WRITE_SHIFT_SUPPORTED + /* Shift the pixels up to a legal bit depth and fill in + * as appropriate to correctly scale the image. + */ + if ((transforms & PNG_TRANSFORM_SHIFT) + && (info_ptr->valid & PNG_INFO_sBIT)) + png_set_shift(png_ptr, &info_ptr->sig_bit); +#endif + +#ifdef PNG_WRITE_PACK_SUPPORTED + /* Pack pixels into bytes */ + if (transforms & PNG_TRANSFORM_PACKING) + png_set_packing(png_ptr); +#endif + +#ifdef PNG_WRITE_SWAP_ALPHA_SUPPORTED + /* Swap location of alpha bytes from ARGB to RGBA */ + if (transforms & PNG_TRANSFORM_SWAP_ALPHA) + png_set_swap_alpha(png_ptr); +#endif + +#ifdef PNG_WRITE_FILLER_SUPPORTED + /* Pack XRGB/RGBX/ARGB/RGBA into * RGB (4 channels -> 3 channels) */ + if (transforms & PNG_TRANSFORM_STRIP_FILLER_AFTER) + png_set_filler(png_ptr, 0, PNG_FILLER_AFTER); + else if (transforms & PNG_TRANSFORM_STRIP_FILLER_BEFORE) + png_set_filler(png_ptr, 0, PNG_FILLER_BEFORE); +#endif + +#ifdef PNG_WRITE_BGR_SUPPORTED + /* Flip BGR pixels to RGB */ + if (transforms & PNG_TRANSFORM_BGR) + png_set_bgr(png_ptr); +#endif + +#ifdef PNG_WRITE_SWAP_SUPPORTED + /* Swap bytes of 16-bit files to most significant byte first */ + if (transforms & PNG_TRANSFORM_SWAP_ENDIAN) + png_set_swap(png_ptr); +#endif + +#ifdef PNG_WRITE_PACKSWAP_SUPPORTED + /* Swap bits of 1, 2, 4 bit packed pixel formats */ + if (transforms & PNG_TRANSFORM_PACKSWAP) + png_set_packswap(png_ptr); +#endif + +#ifdef PNG_WRITE_INVERT_ALPHA_SUPPORTED + /* Invert the alpha channel from opacity to transparency */ + if (transforms & PNG_TRANSFORM_INVERT_ALPHA) + png_set_invert_alpha(png_ptr); +#endif + + /* ----------------------- end of transformations ------------------- */ + + /* Write the bits */ + if (info_ptr->valid & PNG_INFO_IDAT) + png_write_image(png_ptr, info_ptr->row_pointers); + + /* It is REQUIRED to call this to finish writing the rest of the file */ + png_write_end(png_ptr, info_ptr); + + transforms = transforms; /* Quiet compiler warnings */ + params = params; +} +#endif +#endif /* PNG_WRITE_SUPPORTED */ diff --git a/src/libpng/pngwtran.c b/src/libpng/pngwtran.c new file mode 100644 index 00000000..0ce9b9b5 --- /dev/null +++ b/src/libpng/pngwtran.c @@ -0,0 +1,582 @@ + +/* pngwtran.c - transforms the data in a row for PNG writers + * + * Last changed in libpng 1.2.43 [February 25, 2010] + * Copyright (c) 1998-2010 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + * + * This code is released under the libpng license. + * For conditions of distribution and use, see the disclaimer + * and license in png.h + */ + +#define PNG_INTERNAL +#define PNG_NO_PEDANTIC_WARNINGS +#include "png.h" +#ifdef PNG_WRITE_SUPPORTED + +/* Transform the data according to the user's wishes. The order of + * transformations is significant. + */ +void /* PRIVATE */ +png_do_write_transformations(png_structp png_ptr) +{ + png_debug(1, "in png_do_write_transformations"); + + if (png_ptr == NULL) + return; + +#ifdef PNG_WRITE_USER_TRANSFORM_SUPPORTED + if (png_ptr->transformations & PNG_USER_TRANSFORM) + if (png_ptr->write_user_transform_fn != NULL) + (*(png_ptr->write_user_transform_fn)) /* User write transform + function */ + (png_ptr, /* png_ptr */ + &(png_ptr->row_info), /* row_info: */ + /* png_uint_32 width; width of row */ + /* png_uint_32 rowbytes; number of bytes in row */ + /* png_byte color_type; color type of pixels */ + /* png_byte bit_depth; bit depth of samples */ + /* png_byte channels; number of channels (1-4) */ + /* png_byte pixel_depth; bits per pixel (depth*channels) */ + png_ptr->row_buf + 1); /* start of pixel data for row */ +#endif +#ifdef PNG_WRITE_FILLER_SUPPORTED + if (png_ptr->transformations & PNG_FILLER) + png_do_strip_filler(&(png_ptr->row_info), png_ptr->row_buf + 1, + png_ptr->flags); +#endif +#ifdef PNG_WRITE_PACKSWAP_SUPPORTED + if (png_ptr->transformations & PNG_PACKSWAP) + png_do_packswap(&(png_ptr->row_info), png_ptr->row_buf + 1); +#endif +#ifdef PNG_WRITE_PACK_SUPPORTED + if (png_ptr->transformations & PNG_PACK) + png_do_pack(&(png_ptr->row_info), png_ptr->row_buf + 1, + (png_uint_32)png_ptr->bit_depth); +#endif +#ifdef PNG_WRITE_SWAP_SUPPORTED + if (png_ptr->transformations & PNG_SWAP_BYTES) + png_do_swap(&(png_ptr->row_info), png_ptr->row_buf + 1); +#endif +#ifdef PNG_WRITE_SHIFT_SUPPORTED + if (png_ptr->transformations & PNG_SHIFT) + png_do_shift(&(png_ptr->row_info), png_ptr->row_buf + 1, + &(png_ptr->shift)); +#endif +#ifdef PNG_WRITE_SWAP_ALPHA_SUPPORTED + if (png_ptr->transformations & PNG_SWAP_ALPHA) + png_do_write_swap_alpha(&(png_ptr->row_info), png_ptr->row_buf + 1); +#endif +#ifdef PNG_WRITE_INVERT_ALPHA_SUPPORTED + if (png_ptr->transformations & PNG_INVERT_ALPHA) + png_do_write_invert_alpha(&(png_ptr->row_info), png_ptr->row_buf + 1); +#endif +#ifdef PNG_WRITE_BGR_SUPPORTED + if (png_ptr->transformations & PNG_BGR) + png_do_bgr(&(png_ptr->row_info), png_ptr->row_buf + 1); +#endif +#ifdef PNG_WRITE_INVERT_SUPPORTED + if (png_ptr->transformations & PNG_INVERT_MONO) + png_do_invert(&(png_ptr->row_info), png_ptr->row_buf + 1); +#endif +} + +#ifdef PNG_WRITE_PACK_SUPPORTED +/* Pack pixels into bytes. Pass the true bit depth in bit_depth. The + * row_info bit depth should be 8 (one pixel per byte). The channels + * should be 1 (this only happens on grayscale and paletted images). + */ +void /* PRIVATE */ +png_do_pack(png_row_infop row_info, png_bytep row, png_uint_32 bit_depth) +{ + png_debug(1, "in png_do_pack"); + + if (row_info->bit_depth == 8 && +#ifdef PNG_USELESS_TESTS_SUPPORTED + row != NULL && row_info != NULL && +#endif + row_info->channels == 1) + { + switch ((int)bit_depth) + { + case 1: + { + png_bytep sp, dp; + int mask, v; + png_uint_32 i; + png_uint_32 row_width = row_info->width; + + sp = row; + dp = row; + mask = 0x80; + v = 0; + + for (i = 0; i < row_width; i++) + { + if (*sp != 0) + v |= mask; + sp++; + if (mask > 1) + mask >>= 1; + else + { + mask = 0x80; + *dp = (png_byte)v; + dp++; + v = 0; + } + } + if (mask != 0x80) + *dp = (png_byte)v; + break; + } + case 2: + { + png_bytep sp, dp; + int shift, v; + png_uint_32 i; + png_uint_32 row_width = row_info->width; + + sp = row; + dp = row; + shift = 6; + v = 0; + for (i = 0; i < row_width; i++) + { + png_byte value; + + value = (png_byte)(*sp & 0x03); + v |= (value << shift); + if (shift == 0) + { + shift = 6; + *dp = (png_byte)v; + dp++; + v = 0; + } + else + shift -= 2; + sp++; + } + if (shift != 6) + *dp = (png_byte)v; + break; + } + case 4: + { + png_bytep sp, dp; + int shift, v; + png_uint_32 i; + png_uint_32 row_width = row_info->width; + + sp = row; + dp = row; + shift = 4; + v = 0; + for (i = 0; i < row_width; i++) + { + png_byte value; + + value = (png_byte)(*sp & 0x0f); + v |= (value << shift); + + if (shift == 0) + { + shift = 4; + *dp = (png_byte)v; + dp++; + v = 0; + } + else + shift -= 4; + + sp++; + } + if (shift != 4) + *dp = (png_byte)v; + break; + } + } + row_info->bit_depth = (png_byte)bit_depth; + row_info->pixel_depth = (png_byte)(bit_depth * row_info->channels); + row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth, + row_info->width); + } +} +#endif + +#ifdef PNG_WRITE_SHIFT_SUPPORTED +/* Shift pixel values to take advantage of whole range. Pass the + * true number of bits in bit_depth. The row should be packed + * according to row_info->bit_depth. Thus, if you had a row of + * bit depth 4, but the pixels only had values from 0 to 7, you + * would pass 3 as bit_depth, and this routine would translate the + * data to 0 to 15. + */ +void /* PRIVATE */ +png_do_shift(png_row_infop row_info, png_bytep row, png_color_8p bit_depth) +{ + png_debug(1, "in png_do_shift"); + +#ifdef PNG_USELESS_TESTS_SUPPORTED + if (row != NULL && row_info != NULL && +#else + if ( +#endif + row_info->color_type != PNG_COLOR_TYPE_PALETTE) + { + int shift_start[4], shift_dec[4]; + int channels = 0; + + if (row_info->color_type & PNG_COLOR_MASK_COLOR) + { + shift_start[channels] = row_info->bit_depth - bit_depth->red; + shift_dec[channels] = bit_depth->red; + channels++; + shift_start[channels] = row_info->bit_depth - bit_depth->green; + shift_dec[channels] = bit_depth->green; + channels++; + shift_start[channels] = row_info->bit_depth - bit_depth->blue; + shift_dec[channels] = bit_depth->blue; + channels++; + } + else + { + shift_start[channels] = row_info->bit_depth - bit_depth->gray; + shift_dec[channels] = bit_depth->gray; + channels++; + } + if (row_info->color_type & PNG_COLOR_MASK_ALPHA) + { + shift_start[channels] = row_info->bit_depth - bit_depth->alpha; + shift_dec[channels] = bit_depth->alpha; + channels++; + } + + /* With low row depths, could only be grayscale, so one channel */ + if (row_info->bit_depth < 8) + { + png_bytep bp = row; + png_uint_32 i; + png_byte mask; + png_uint_32 row_bytes = row_info->rowbytes; + + if (bit_depth->gray == 1 && row_info->bit_depth == 2) + mask = 0x55; + else if (row_info->bit_depth == 4 && bit_depth->gray == 3) + mask = 0x11; + else + mask = 0xff; + + for (i = 0; i < row_bytes; i++, bp++) + { + png_uint_16 v; + int j; + + v = *bp; + *bp = 0; + for (j = shift_start[0]; j > -shift_dec[0]; j -= shift_dec[0]) + { + if (j > 0) + *bp |= (png_byte)((v << j) & 0xff); + else + *bp |= (png_byte)((v >> (-j)) & mask); + } + } + } + else if (row_info->bit_depth == 8) + { + png_bytep bp = row; + png_uint_32 i; + png_uint_32 istop = channels * row_info->width; + + for (i = 0; i < istop; i++, bp++) + { + + png_uint_16 v; + int j; + int c = (int)(i%channels); + + v = *bp; + *bp = 0; + for (j = shift_start[c]; j > -shift_dec[c]; j -= shift_dec[c]) + { + if (j > 0) + *bp |= (png_byte)((v << j) & 0xff); + else + *bp |= (png_byte)((v >> (-j)) & 0xff); + } + } + } + else + { + png_bytep bp; + png_uint_32 i; + png_uint_32 istop = channels * row_info->width; + + for (bp = row, i = 0; i < istop; i++) + { + int c = (int)(i%channels); + png_uint_16 value, v; + int j; + + v = (png_uint_16)(((png_uint_16)(*bp) << 8) + *(bp + 1)); + value = 0; + for (j = shift_start[c]; j > -shift_dec[c]; j -= shift_dec[c]) + { + if (j > 0) + value |= (png_uint_16)((v << j) & (png_uint_16)0xffff); + else + value |= (png_uint_16)((v >> (-j)) & (png_uint_16)0xffff); + } + *bp++ = (png_byte)(value >> 8); + *bp++ = (png_byte)(value & 0xff); + } + } + } +} +#endif + +#ifdef PNG_WRITE_SWAP_ALPHA_SUPPORTED +void /* PRIVATE */ +png_do_write_swap_alpha(png_row_infop row_info, png_bytep row) +{ + png_debug(1, "in png_do_write_swap_alpha"); + +#ifdef PNG_USELESS_TESTS_SUPPORTED + if (row != NULL && row_info != NULL) +#endif + { + if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) + { + /* This converts from ARGB to RGBA */ + if (row_info->bit_depth == 8) + { + png_bytep sp, dp; + png_uint_32 i; + png_uint_32 row_width = row_info->width; + for (i = 0, sp = dp = row; i < row_width; i++) + { + png_byte save = *(sp++); + *(dp++) = *(sp++); + *(dp++) = *(sp++); + *(dp++) = *(sp++); + *(dp++) = save; + } + } + /* This converts from AARRGGBB to RRGGBBAA */ + else + { + png_bytep sp, dp; + png_uint_32 i; + png_uint_32 row_width = row_info->width; + + for (i = 0, sp = dp = row; i < row_width; i++) + { + png_byte save[2]; + save[0] = *(sp++); + save[1] = *(sp++); + *(dp++) = *(sp++); + *(dp++) = *(sp++); + *(dp++) = *(sp++); + *(dp++) = *(sp++); + *(dp++) = *(sp++); + *(dp++) = *(sp++); + *(dp++) = save[0]; + *(dp++) = save[1]; + } + } + } + else if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA) + { + /* This converts from AG to GA */ + if (row_info->bit_depth == 8) + { + png_bytep sp, dp; + png_uint_32 i; + png_uint_32 row_width = row_info->width; + + for (i = 0, sp = dp = row; i < row_width; i++) + { + png_byte save = *(sp++); + *(dp++) = *(sp++); + *(dp++) = save; + } + } + /* This converts from AAGG to GGAA */ + else + { + png_bytep sp, dp; + png_uint_32 i; + png_uint_32 row_width = row_info->width; + + for (i = 0, sp = dp = row; i < row_width; i++) + { + png_byte save[2]; + save[0] = *(sp++); + save[1] = *(sp++); + *(dp++) = *(sp++); + *(dp++) = *(sp++); + *(dp++) = save[0]; + *(dp++) = save[1]; + } + } + } + } +} +#endif + +#ifdef PNG_WRITE_INVERT_ALPHA_SUPPORTED +void /* PRIVATE */ +png_do_write_invert_alpha(png_row_infop row_info, png_bytep row) +{ + png_debug(1, "in png_do_write_invert_alpha"); + +#ifdef PNG_USELESS_TESTS_SUPPORTED + if (row != NULL && row_info != NULL) +#endif + { + if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) + { + /* This inverts the alpha channel in RGBA */ + if (row_info->bit_depth == 8) + { + png_bytep sp, dp; + png_uint_32 i; + png_uint_32 row_width = row_info->width; + for (i = 0, sp = dp = row; i < row_width; i++) + { + /* Does nothing + *(dp++) = *(sp++); + *(dp++) = *(sp++); + *(dp++) = *(sp++); + */ + sp+=3; dp = sp; + *(dp++) = (png_byte)(255 - *(sp++)); + } + } + /* This inverts the alpha channel in RRGGBBAA */ + else + { + png_bytep sp, dp; + png_uint_32 i; + png_uint_32 row_width = row_info->width; + + for (i = 0, sp = dp = row; i < row_width; i++) + { + /* Does nothing + *(dp++) = *(sp++); + *(dp++) = *(sp++); + *(dp++) = *(sp++); + *(dp++) = *(sp++); + *(dp++) = *(sp++); + *(dp++) = *(sp++); + */ + sp+=6; dp = sp; + *(dp++) = (png_byte)(255 - *(sp++)); + *(dp++) = (png_byte)(255 - *(sp++)); + } + } + } + else if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA) + { + /* This inverts the alpha channel in GA */ + if (row_info->bit_depth == 8) + { + png_bytep sp, dp; + png_uint_32 i; + png_uint_32 row_width = row_info->width; + + for (i = 0, sp = dp = row; i < row_width; i++) + { + *(dp++) = *(sp++); + *(dp++) = (png_byte)(255 - *(sp++)); + } + } + /* This inverts the alpha channel in GGAA */ + else + { + png_bytep sp, dp; + png_uint_32 i; + png_uint_32 row_width = row_info->width; + + for (i = 0, sp = dp = row; i < row_width; i++) + { + /* Does nothing + *(dp++) = *(sp++); + *(dp++) = *(sp++); + */ + sp+=2; dp = sp; + *(dp++) = (png_byte)(255 - *(sp++)); + *(dp++) = (png_byte)(255 - *(sp++)); + } + } + } + } +} +#endif + +#ifdef PNG_MNG_FEATURES_SUPPORTED +/* Undoes intrapixel differencing */ +void /* PRIVATE */ +png_do_write_intrapixel(png_row_infop row_info, png_bytep row) +{ + png_debug(1, "in png_do_write_intrapixel"); + + if ( +#ifdef PNG_USELESS_TESTS_SUPPORTED + row != NULL && row_info != NULL && +#endif + (row_info->color_type & PNG_COLOR_MASK_COLOR)) + { + int bytes_per_pixel; + png_uint_32 row_width = row_info->width; + if (row_info->bit_depth == 8) + { + png_bytep rp; + png_uint_32 i; + + if (row_info->color_type == PNG_COLOR_TYPE_RGB) + bytes_per_pixel = 3; + else if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) + bytes_per_pixel = 4; + else + return; + + for (i = 0, rp = row; i < row_width; i++, rp += bytes_per_pixel) + { + *(rp) = (png_byte)((*rp - *(rp+1))&0xff); + *(rp+2) = (png_byte)((*(rp+2) - *(rp+1))&0xff); + } + } + else if (row_info->bit_depth == 16) + { + png_bytep rp; + png_uint_32 i; + + if (row_info->color_type == PNG_COLOR_TYPE_RGB) + bytes_per_pixel = 6; + else if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) + bytes_per_pixel = 8; + else + return; + + for (i = 0, rp = row; i < row_width; i++, rp += bytes_per_pixel) + { + png_uint_32 s0 = (*(rp ) << 8) | *(rp+1); + png_uint_32 s1 = (*(rp+2) << 8) | *(rp+3); + png_uint_32 s2 = (*(rp+4) << 8) | *(rp+5); + png_uint_32 red = (png_uint_32)((s0 - s1) & 0xffffL); + png_uint_32 blue = (png_uint_32)((s2 - s1) & 0xffffL); + *(rp ) = (png_byte)((red >> 8) & 0xff); + *(rp+1) = (png_byte)(red & 0xff); + *(rp+4) = (png_byte)((blue >> 8) & 0xff); + *(rp+5) = (png_byte)(blue & 0xff); + } + } + } +} +#endif /* PNG_MNG_FEATURES_SUPPORTED */ +#endif /* PNG_WRITE_SUPPORTED */ diff --git a/src/libpng/pngwutil.c b/src/libpng/pngwutil.c new file mode 100644 index 00000000..87af1b6d --- /dev/null +++ b/src/libpng/pngwutil.c @@ -0,0 +1,2835 @@ +#ifdef _WIN32 +#pragma warning(disable:4996) // Disable messages like this one: '_snprintf': This function or variable may be unsafe. Consider using _snprintf_s instead. To disable deprecation, use _CRT_SECURE_NO_WARNINGS. +#endif + +/* pngwutil.c - utilities to write a PNG file + * + * Last changed in libpng 1.2.43 [February 25, 2010] + * Copyright (c) 1998-2010 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + * + * This code is released under the libpng license. + * For conditions of distribution and use, see the disclaimer + * and license in png.h + */ + +#define PNG_INTERNAL +#define PNG_NO_PEDANTIC_WARNINGS +#include "png.h" +#ifdef PNG_WRITE_SUPPORTED + +/* Place a 32-bit number into a buffer in PNG byte order. We work + * with unsigned numbers for convenience, although one supported + * ancillary chunk uses signed (two's complement) numbers. + */ +void PNGAPI +png_save_uint_32(png_bytep buf, png_uint_32 i) +{ + buf[0] = (png_byte)((i >> 24) & 0xff); + buf[1] = (png_byte)((i >> 16) & 0xff); + buf[2] = (png_byte)((i >> 8) & 0xff); + buf[3] = (png_byte)(i & 0xff); +} + +/* The png_save_int_32 function assumes integers are stored in two's + * complement format. If this isn't the case, then this routine needs to + * be modified to write data in two's complement format. + */ +void PNGAPI +png_save_int_32(png_bytep buf, png_int_32 i) +{ + buf[0] = (png_byte)((i >> 24) & 0xff); + buf[1] = (png_byte)((i >> 16) & 0xff); + buf[2] = (png_byte)((i >> 8) & 0xff); + buf[3] = (png_byte)(i & 0xff); +} + +/* Place a 16-bit number into a buffer in PNG byte order. + * The parameter is declared unsigned int, not png_uint_16, + * just to avoid potential problems on pre-ANSI C compilers. + */ +void PNGAPI +png_save_uint_16(png_bytep buf, unsigned int i) +{ + buf[0] = (png_byte)((i >> 8) & 0xff); + buf[1] = (png_byte)(i & 0xff); +} + +/* Simple function to write the signature. If we have already written + * the magic bytes of the signature, or more likely, the PNG stream is + * being embedded into another stream and doesn't need its own signature, + * we should call png_set_sig_bytes() to tell libpng how many of the + * bytes have already been written. + */ +void /* PRIVATE */ +png_write_sig(png_structp png_ptr) +{ + png_byte png_signature[8] = {137, 80, 78, 71, 13, 10, 26, 10}; + + /* Write the rest of the 8 byte signature */ + png_write_data(png_ptr, &png_signature[png_ptr->sig_bytes], + (png_size_t)(8 - png_ptr->sig_bytes)); + if (png_ptr->sig_bytes < 3) + png_ptr->mode |= PNG_HAVE_PNG_SIGNATURE; +} + +/* Write a PNG chunk all at once. The type is an array of ASCII characters + * representing the chunk name. The array must be at least 4 bytes in + * length, and does not need to be null terminated. To be safe, pass the + * pre-defined chunk names here, and if you need a new one, define it + * where the others are defined. The length is the length of the data. + * All the data must be present. If that is not possible, use the + * png_write_chunk_start(), png_write_chunk_data(), and png_write_chunk_end() + * functions instead. + */ +void PNGAPI +png_write_chunk(png_structp png_ptr, png_bytep chunk_name, + png_bytep data, png_size_t length) +{ + if (png_ptr == NULL) + return; + png_write_chunk_start(png_ptr, chunk_name, (png_uint_32)length); + png_write_chunk_data(png_ptr, data, (png_size_t)length); + png_write_chunk_end(png_ptr); +} + +/* Write the start of a PNG chunk. The type is the chunk type. + * The total_length is the sum of the lengths of all the data you will be + * passing in png_write_chunk_data(). + */ +void PNGAPI +png_write_chunk_start(png_structp png_ptr, png_bytep chunk_name, + png_uint_32 length) +{ + png_byte buf[8]; + + png_debug2(0, "Writing %s chunk, length = %lu", chunk_name, + (unsigned long)length); + + if (png_ptr == NULL) + return; + + + /* Write the length and the chunk name */ + png_save_uint_32(buf, length); + png_memcpy(buf + 4, chunk_name, 4); + png_write_data(png_ptr, buf, (png_size_t)8); + /* Put the chunk name into png_ptr->chunk_name */ + png_memcpy(png_ptr->chunk_name, chunk_name, 4); + /* Reset the crc and run it over the chunk name */ + png_reset_crc(png_ptr); + png_calculate_crc(png_ptr, chunk_name, (png_size_t)4); +} + +/* Write the data of a PNG chunk started with png_write_chunk_start(). + * Note that multiple calls to this function are allowed, and that the + * sum of the lengths from these calls *must* add up to the total_length + * given to png_write_chunk_start(). + */ +void PNGAPI +png_write_chunk_data(png_structp png_ptr, png_bytep data, png_size_t length) +{ + /* Write the data, and run the CRC over it */ + if (png_ptr == NULL) + return; + if (data != NULL && length > 0) + { + png_write_data(png_ptr, data, length); + /* Update the CRC after writing the data, + * in case that the user I/O routine alters it. + */ + png_calculate_crc(png_ptr, data, length); + } +} + +/* Finish a chunk started with png_write_chunk_start(). */ +void PNGAPI +png_write_chunk_end(png_structp png_ptr) +{ + png_byte buf[4]; + + if (png_ptr == NULL) return; + + /* Write the crc in a single operation */ + png_save_uint_32(buf, png_ptr->crc); + + png_write_data(png_ptr, buf, (png_size_t)4); +} + +#if defined(PNG_WRITE_TEXT_SUPPORTED) || defined(PNG_WRITE_iCCP_SUPPORTED) +/* This pair of functions encapsulates the operation of (a) compressing a + * text string, and (b) issuing it later as a series of chunk data writes. + * The compression_state structure is shared context for these functions + * set up by the caller in order to make the whole mess thread-safe. + */ + +typedef struct +{ + char *input; /* The uncompressed input data */ + int input_len; /* Its length */ + int num_output_ptr; /* Number of output pointers used */ + int max_output_ptr; /* Size of output_ptr */ + png_charpp output_ptr; /* Array of pointers to output */ +} compression_state; + +/* Compress given text into storage in the png_ptr structure */ +static int /* PRIVATE */ +png_text_compress(png_structp png_ptr, + png_charp text, png_size_t text_len, int compression, + compression_state *comp) +{ + int ret; + + comp->num_output_ptr = 0; + comp->max_output_ptr = 0; + comp->output_ptr = NULL; + comp->input = NULL; + comp->input_len = 0; + + /* We may just want to pass the text right through */ + if (compression == PNG_TEXT_COMPRESSION_NONE) + { + comp->input = text; + comp->input_len = text_len; + return((int)text_len); + } + + if (compression >= PNG_TEXT_COMPRESSION_LAST) + { +#if defined(PNG_STDIO_SUPPORTED) && !defined(_WIN32_WCE) + char msg[50]; + png_snprintf(msg, 50, "Unknown compression type %d", compression); + png_warning(png_ptr, msg); +#else + png_warning(png_ptr, "Unknown compression type"); +#endif + } + + /* We can't write the chunk until we find out how much data we have, + * which means we need to run the compressor first and save the + * output. This shouldn't be a problem, as the vast majority of + * comments should be reasonable, but we will set up an array of + * malloc'd pointers to be sure. + * + * If we knew the application was well behaved, we could simplify this + * greatly by assuming we can always malloc an output buffer large + * enough to hold the compressed text ((1001 * text_len / 1000) + 12) + * and malloc this directly. The only time this would be a bad idea is + * if we can't malloc more than 64K and we have 64K of random input + * data, or if the input string is incredibly large (although this + * wouldn't cause a failure, just a slowdown due to swapping). + */ + + /* Set up the compression buffers */ + png_ptr->zstream.avail_in = (uInt)text_len; + png_ptr->zstream.next_in = (Bytef *)text; + png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size; + png_ptr->zstream.next_out = (Bytef *)png_ptr->zbuf; + + /* This is the same compression loop as in png_write_row() */ + do + { + /* Compress the data */ + ret = deflate(&png_ptr->zstream, Z_NO_FLUSH); + if (ret != Z_OK) + { + /* Error */ + if (png_ptr->zstream.msg != NULL) + png_error(png_ptr, png_ptr->zstream.msg); + else + png_error(png_ptr, "zlib error"); + } + /* Check to see if we need more room */ + if (!(png_ptr->zstream.avail_out)) + { + /* Make sure the output array has room */ + if (comp->num_output_ptr >= comp->max_output_ptr) + { + int old_max; + + old_max = comp->max_output_ptr; + comp->max_output_ptr = comp->num_output_ptr + 4; + if (comp->output_ptr != NULL) + { + png_charpp old_ptr; + + old_ptr = comp->output_ptr; + comp->output_ptr = (png_charpp)png_malloc(png_ptr, + (png_uint_32) + (comp->max_output_ptr * png_sizeof(png_charpp))); + png_memcpy(comp->output_ptr, old_ptr, old_max + * png_sizeof(png_charp)); + png_free(png_ptr, old_ptr); + } + else + comp->output_ptr = (png_charpp)png_malloc(png_ptr, + (png_uint_32) + (comp->max_output_ptr * png_sizeof(png_charp))); + } + + /* Save the data */ + comp->output_ptr[comp->num_output_ptr] = + (png_charp)png_malloc(png_ptr, + (png_uint_32)png_ptr->zbuf_size); + png_memcpy(comp->output_ptr[comp->num_output_ptr], png_ptr->zbuf, + png_ptr->zbuf_size); + comp->num_output_ptr++; + + /* and reset the buffer */ + png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size; + png_ptr->zstream.next_out = png_ptr->zbuf; + } + /* Continue until we don't have any more to compress */ + } while (png_ptr->zstream.avail_in); + + /* Finish the compression */ + do + { + /* Tell zlib we are finished */ + ret = deflate(&png_ptr->zstream, Z_FINISH); + + if (ret == Z_OK) + { + /* Check to see if we need more room */ + if (!(png_ptr->zstream.avail_out)) + { + /* Check to make sure our output array has room */ + if (comp->num_output_ptr >= comp->max_output_ptr) + { + int old_max; + + old_max = comp->max_output_ptr; + comp->max_output_ptr = comp->num_output_ptr + 4; + if (comp->output_ptr != NULL) + { + png_charpp old_ptr; + + old_ptr = comp->output_ptr; + /* This could be optimized to realloc() */ + comp->output_ptr = (png_charpp)png_malloc(png_ptr, + (png_uint_32)(comp->max_output_ptr * + png_sizeof(png_charp))); + png_memcpy(comp->output_ptr, old_ptr, + old_max * png_sizeof(png_charp)); + png_free(png_ptr, old_ptr); + } + else + comp->output_ptr = (png_charpp)png_malloc(png_ptr, + (png_uint_32)(comp->max_output_ptr * + png_sizeof(png_charp))); + } + + /* Save the data */ + comp->output_ptr[comp->num_output_ptr] = + (png_charp)png_malloc(png_ptr, + (png_uint_32)png_ptr->zbuf_size); + png_memcpy(comp->output_ptr[comp->num_output_ptr], png_ptr->zbuf, + png_ptr->zbuf_size); + comp->num_output_ptr++; + + /* and reset the buffer pointers */ + png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size; + png_ptr->zstream.next_out = png_ptr->zbuf; + } + } + else if (ret != Z_STREAM_END) + { + /* We got an error */ + if (png_ptr->zstream.msg != NULL) + png_error(png_ptr, png_ptr->zstream.msg); + else + png_error(png_ptr, "zlib error"); + } + } while (ret != Z_STREAM_END); + + /* Text length is number of buffers plus last buffer */ + text_len = png_ptr->zbuf_size * comp->num_output_ptr; + if (png_ptr->zstream.avail_out < png_ptr->zbuf_size) + text_len += png_ptr->zbuf_size - (png_size_t)png_ptr->zstream.avail_out; + + return((int)text_len); +} + +/* Ship the compressed text out via chunk writes */ +static void /* PRIVATE */ +png_write_compressed_data_out(png_structp png_ptr, compression_state *comp) +{ + int i; + + /* Handle the no-compression case */ + if (comp->input) + { + png_write_chunk_data(png_ptr, (png_bytep)comp->input, + (png_size_t)comp->input_len); + return; + } + + /* Write saved output buffers, if any */ + for (i = 0; i < comp->num_output_ptr; i++) + { + png_write_chunk_data(png_ptr, (png_bytep)comp->output_ptr[i], + (png_size_t)png_ptr->zbuf_size); + png_free(png_ptr, comp->output_ptr[i]); + comp->output_ptr[i]=NULL; + } + if (comp->max_output_ptr != 0) + png_free(png_ptr, comp->output_ptr); + comp->output_ptr=NULL; + /* Write anything left in zbuf */ + if (png_ptr->zstream.avail_out < (png_uint_32)png_ptr->zbuf_size) + png_write_chunk_data(png_ptr, png_ptr->zbuf, + (png_size_t)(png_ptr->zbuf_size - png_ptr->zstream.avail_out)); + + /* Reset zlib for another zTXt/iTXt or image data */ + deflateReset(&png_ptr->zstream); + png_ptr->zstream.data_type = Z_BINARY; +} +#endif + +/* Write the IHDR chunk, and update the png_struct with the necessary + * information. Note that the rest of this code depends upon this + * information being correct. + */ +void /* PRIVATE */ +png_write_IHDR(png_structp png_ptr, png_uint_32 width, png_uint_32 height, + int bit_depth, int color_type, int compression_type, int filter_type, + int interlace_type) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_IHDR; +#endif + int ret; + + png_byte buf[13]; /* Buffer to store the IHDR info */ + + png_debug(1, "in png_write_IHDR"); + + /* Check that we have valid input data from the application info */ + switch (color_type) + { + case PNG_COLOR_TYPE_GRAY: + switch (bit_depth) + { + case 1: + case 2: + case 4: + case 8: + case 16: png_ptr->channels = 1; break; + default: png_error(png_ptr, + "Invalid bit depth for grayscale image"); + } + break; + case PNG_COLOR_TYPE_RGB: + if (bit_depth != 8 && bit_depth != 16) + png_error(png_ptr, "Invalid bit depth for RGB image"); + png_ptr->channels = 3; + break; + case PNG_COLOR_TYPE_PALETTE: + switch (bit_depth) + { + case 1: + case 2: + case 4: + case 8: png_ptr->channels = 1; break; + default: png_error(png_ptr, "Invalid bit depth for paletted image"); + } + break; + case PNG_COLOR_TYPE_GRAY_ALPHA: + if (bit_depth != 8 && bit_depth != 16) + png_error(png_ptr, "Invalid bit depth for grayscale+alpha image"); + png_ptr->channels = 2; + break; + case PNG_COLOR_TYPE_RGB_ALPHA: + if (bit_depth != 8 && bit_depth != 16) + png_error(png_ptr, "Invalid bit depth for RGBA image"); + png_ptr->channels = 4; + break; + default: + png_error(png_ptr, "Invalid image color type specified"); + } + + if (compression_type != PNG_COMPRESSION_TYPE_BASE) + { + png_warning(png_ptr, "Invalid compression type specified"); + compression_type = PNG_COMPRESSION_TYPE_BASE; + } + + /* Write filter_method 64 (intrapixel differencing) only if + * 1. Libpng was compiled with PNG_MNG_FEATURES_SUPPORTED and + * 2. Libpng did not write a PNG signature (this filter_method is only + * used in PNG datastreams that are embedded in MNG datastreams) and + * 3. The application called png_permit_mng_features with a mask that + * included PNG_FLAG_MNG_FILTER_64 and + * 4. The filter_method is 64 and + * 5. The color_type is RGB or RGBA + */ + if ( +#ifdef PNG_MNG_FEATURES_SUPPORTED + !((png_ptr->mng_features_permitted & PNG_FLAG_MNG_FILTER_64) && + ((png_ptr->mode&PNG_HAVE_PNG_SIGNATURE) == 0) && + (color_type == PNG_COLOR_TYPE_RGB || + color_type == PNG_COLOR_TYPE_RGB_ALPHA) && + (filter_type == PNG_INTRAPIXEL_DIFFERENCING)) && +#endif + filter_type != PNG_FILTER_TYPE_BASE) + { + png_warning(png_ptr, "Invalid filter type specified"); + filter_type = PNG_FILTER_TYPE_BASE; + } + +#ifdef PNG_WRITE_INTERLACING_SUPPORTED + if (interlace_type != PNG_INTERLACE_NONE && + interlace_type != PNG_INTERLACE_ADAM7) + { + png_warning(png_ptr, "Invalid interlace type specified"); + interlace_type = PNG_INTERLACE_ADAM7; + } +#else + interlace_type=PNG_INTERLACE_NONE; +#endif + + /* Save the relevent information */ + png_ptr->bit_depth = (png_byte)bit_depth; + png_ptr->color_type = (png_byte)color_type; + png_ptr->interlaced = (png_byte)interlace_type; +#ifdef PNG_MNG_FEATURES_SUPPORTED + png_ptr->filter_type = (png_byte)filter_type; +#endif + png_ptr->compression_type = (png_byte)compression_type; + png_ptr->width = width; + png_ptr->height = height; + + png_ptr->pixel_depth = (png_byte)(bit_depth * png_ptr->channels); + png_ptr->rowbytes = PNG_ROWBYTES(png_ptr->pixel_depth, width); + /* Set the usr info, so any transformations can modify it */ + png_ptr->usr_width = png_ptr->width; + png_ptr->usr_bit_depth = png_ptr->bit_depth; + png_ptr->usr_channels = png_ptr->channels; + + /* Pack the header information into the buffer */ + png_save_uint_32(buf, width); + png_save_uint_32(buf + 4, height); + buf[8] = (png_byte)bit_depth; + buf[9] = (png_byte)color_type; + buf[10] = (png_byte)compression_type; + buf[11] = (png_byte)filter_type; + buf[12] = (png_byte)interlace_type; + + /* Write the chunk */ + png_write_chunk(png_ptr, (png_bytep)png_IHDR, buf, (png_size_t)13); + + /* Initialize zlib with PNG info */ + png_ptr->zstream.zalloc = png_zalloc; + png_ptr->zstream.zfree = png_zfree; + png_ptr->zstream.opaque = (voidpf)png_ptr; + if (!(png_ptr->do_filter)) + { + if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE || + png_ptr->bit_depth < 8) + png_ptr->do_filter = PNG_FILTER_NONE; + else + png_ptr->do_filter = PNG_ALL_FILTERS; + } + if (!(png_ptr->flags & PNG_FLAG_ZLIB_CUSTOM_STRATEGY)) + { + if (png_ptr->do_filter != PNG_FILTER_NONE) + png_ptr->zlib_strategy = Z_FILTERED; + else + png_ptr->zlib_strategy = Z_DEFAULT_STRATEGY; + } + if (!(png_ptr->flags & PNG_FLAG_ZLIB_CUSTOM_LEVEL)) + png_ptr->zlib_level = Z_DEFAULT_COMPRESSION; + if (!(png_ptr->flags & PNG_FLAG_ZLIB_CUSTOM_MEM_LEVEL)) + png_ptr->zlib_mem_level = 8; + if (!(png_ptr->flags & PNG_FLAG_ZLIB_CUSTOM_WINDOW_BITS)) + png_ptr->zlib_window_bits = 15; + if (!(png_ptr->flags & PNG_FLAG_ZLIB_CUSTOM_METHOD)) + png_ptr->zlib_method = 8; + ret = deflateInit2(&png_ptr->zstream, png_ptr->zlib_level, + png_ptr->zlib_method, png_ptr->zlib_window_bits, + png_ptr->zlib_mem_level, png_ptr->zlib_strategy); + if (ret != Z_OK) + { + if (ret == Z_VERSION_ERROR) png_error(png_ptr, + "zlib failed to initialize compressor -- version error"); + if (ret == Z_STREAM_ERROR) png_error(png_ptr, + "zlib failed to initialize compressor -- stream error"); + if (ret == Z_MEM_ERROR) png_error(png_ptr, + "zlib failed to initialize compressor -- mem error"); + png_error(png_ptr, "zlib failed to initialize compressor"); + } + png_ptr->zstream.next_out = png_ptr->zbuf; + png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size; + /* libpng is not interested in zstream.data_type */ + /* Set it to a predefined value, to avoid its evaluation inside zlib */ + png_ptr->zstream.data_type = Z_BINARY; + + png_ptr->mode = PNG_HAVE_IHDR; +} + +/* Write the palette. We are careful not to trust png_color to be in the + * correct order for PNG, so people can redefine it to any convenient + * structure. + */ +void /* PRIVATE */ +png_write_PLTE(png_structp png_ptr, png_colorp palette, png_uint_32 num_pal) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_PLTE; +#endif + png_uint_32 i; + png_colorp pal_ptr; + png_byte buf[3]; + + png_debug(1, "in png_write_PLTE"); + + if (( +#ifdef PNG_MNG_FEATURES_SUPPORTED + !(png_ptr->mng_features_permitted & PNG_FLAG_MNG_EMPTY_PLTE) && +#endif + num_pal == 0) || num_pal > 256) + { + if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + { + png_error(png_ptr, "Invalid number of colors in palette"); + } + else + { + png_warning(png_ptr, "Invalid number of colors in palette"); + return; + } + } + + if (!(png_ptr->color_type&PNG_COLOR_MASK_COLOR)) + { + png_warning(png_ptr, + "Ignoring request to write a PLTE chunk in grayscale PNG"); + return; + } + + png_ptr->num_palette = (png_uint_16)num_pal; + png_debug1(3, "num_palette = %d", png_ptr->num_palette); + + png_write_chunk_start(png_ptr, (png_bytep)png_PLTE, + (png_uint_32)(num_pal * 3)); +#ifdef PNG_POINTER_INDEXING_SUPPORTED + for (i = 0, pal_ptr = palette; i < num_pal; i++, pal_ptr++) + { + buf[0] = pal_ptr->red; + buf[1] = pal_ptr->green; + buf[2] = pal_ptr->blue; + png_write_chunk_data(png_ptr, buf, (png_size_t)3); + } +#else + /* This is a little slower but some buggy compilers need to do this + * instead + */ + pal_ptr=palette; + for (i = 0; i < num_pal; i++) + { + buf[0] = pal_ptr[i].red; + buf[1] = pal_ptr[i].green; + buf[2] = pal_ptr[i].blue; + png_write_chunk_data(png_ptr, buf, (png_size_t)3); + } +#endif + png_write_chunk_end(png_ptr); + png_ptr->mode |= PNG_HAVE_PLTE; +} + +/* Write an IDAT chunk */ +void /* PRIVATE */ +png_write_IDAT(png_structp png_ptr, png_bytep data, png_size_t length) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_IDAT; +#endif + + png_debug(1, "in png_write_IDAT"); + + /* Optimize the CMF field in the zlib stream. */ + /* This hack of the zlib stream is compliant to the stream specification. */ + if (!(png_ptr->mode & PNG_HAVE_IDAT) && + png_ptr->compression_type == PNG_COMPRESSION_TYPE_BASE) + { + unsigned int z_cmf = data[0]; /* zlib compression method and flags */ + if ((z_cmf & 0x0f) == 8 && (z_cmf & 0xf0) <= 0x70) + { + /* Avoid memory underflows and multiplication overflows. + * + * The conditions below are practically always satisfied; + * however, they still must be checked. + */ + if (length >= 2 && + png_ptr->height < 16384 && png_ptr->width < 16384) + { + png_uint_32 uncompressed_idat_size = png_ptr->height * + ((png_ptr->width * + png_ptr->channels * png_ptr->bit_depth + 15) >> 3); + unsigned int z_cinfo = z_cmf >> 4; + unsigned int half_z_window_size = 1 << (z_cinfo + 7); + while (uncompressed_idat_size <= half_z_window_size && + half_z_window_size >= 256) + { + z_cinfo--; + half_z_window_size >>= 1; + } + z_cmf = (z_cmf & 0x0f) | (z_cinfo << 4); + if (data[0] != (png_byte)z_cmf) + { + data[0] = (png_byte)z_cmf; + data[1] &= 0xe0; + data[1] += (png_byte)(0x1f - ((z_cmf << 8) + data[1]) % 0x1f); + } + } + } + else + png_error(png_ptr, + "Invalid zlib compression method or flags in IDAT"); + } + + png_write_chunk(png_ptr, (png_bytep)png_IDAT, data, length); + png_ptr->mode |= PNG_HAVE_IDAT; +} + +/* Write an IEND chunk */ +void /* PRIVATE */ +png_write_IEND(png_structp png_ptr) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_IEND; +#endif + + png_debug(1, "in png_write_IEND"); + + png_write_chunk(png_ptr, (png_bytep)png_IEND, png_bytep_NULL, + (png_size_t)0); + png_ptr->mode |= PNG_HAVE_IEND; +} + +#ifdef PNG_WRITE_gAMA_SUPPORTED +/* Write a gAMA chunk */ +#ifdef PNG_FLOATING_POINT_SUPPORTED +void /* PRIVATE */ +png_write_gAMA(png_structp png_ptr, double file_gamma) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_gAMA; +#endif + png_uint_32 igamma; + png_byte buf[4]; + + png_debug(1, "in png_write_gAMA"); + + /* file_gamma is saved in 1/100,000ths */ + igamma = (png_uint_32)(file_gamma * 100000.0 + 0.5); + png_save_uint_32(buf, igamma); + png_write_chunk(png_ptr, (png_bytep)png_gAMA, buf, (png_size_t)4); +} +#endif +#ifdef PNG_FIXED_POINT_SUPPORTED +void /* PRIVATE */ +png_write_gAMA_fixed(png_structp png_ptr, png_fixed_point file_gamma) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_gAMA; +#endif + png_byte buf[4]; + + png_debug(1, "in png_write_gAMA"); + + /* file_gamma is saved in 1/100,000ths */ + png_save_uint_32(buf, (png_uint_32)file_gamma); + png_write_chunk(png_ptr, (png_bytep)png_gAMA, buf, (png_size_t)4); +} +#endif +#endif + +#ifdef PNG_WRITE_sRGB_SUPPORTED +/* Write a sRGB chunk */ +void /* PRIVATE */ +png_write_sRGB(png_structp png_ptr, int srgb_intent) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_sRGB; +#endif + png_byte buf[1]; + + png_debug(1, "in png_write_sRGB"); + + if (srgb_intent >= PNG_sRGB_INTENT_LAST) + png_warning(png_ptr, + "Invalid sRGB rendering intent specified"); + buf[0]=(png_byte)srgb_intent; + png_write_chunk(png_ptr, (png_bytep)png_sRGB, buf, (png_size_t)1); +} +#endif + +#ifdef PNG_WRITE_iCCP_SUPPORTED +/* Write an iCCP chunk */ +void /* PRIVATE */ +png_write_iCCP(png_structp png_ptr, png_charp name, int compression_type, + png_charp profile, int profile_len) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_iCCP; +#endif + png_size_t name_len; + png_charp new_name; + compression_state comp; + int embedded_profile_len = 0; + + png_debug(1, "in png_write_iCCP"); + + comp.num_output_ptr = 0; + comp.max_output_ptr = 0; + comp.output_ptr = NULL; + comp.input = NULL; + comp.input_len = 0; + + if ((name_len = png_check_keyword(png_ptr, name, + &new_name)) == 0) + return; + + if (compression_type != PNG_COMPRESSION_TYPE_BASE) + png_warning(png_ptr, "Unknown compression type in iCCP chunk"); + + if (profile == NULL) + profile_len = 0; + + if (profile_len > 3) + embedded_profile_len = + ((*( (png_bytep)profile ))<<24) | + ((*( (png_bytep)profile + 1))<<16) | + ((*( (png_bytep)profile + 2))<< 8) | + ((*( (png_bytep)profile + 3)) ); + + if (embedded_profile_len < 0) + { + png_warning(png_ptr, + "Embedded profile length in iCCP chunk is negative"); + png_free(png_ptr, new_name); + return; + } + + if (profile_len < embedded_profile_len) + { + png_warning(png_ptr, + "Embedded profile length too large in iCCP chunk"); + png_free(png_ptr, new_name); + return; + } + + if (profile_len > embedded_profile_len) + { + png_warning(png_ptr, + "Truncating profile to actual length in iCCP chunk"); + profile_len = embedded_profile_len; + } + + if (profile_len) + profile_len = png_text_compress(png_ptr, profile, + (png_size_t)profile_len, PNG_COMPRESSION_TYPE_BASE, &comp); + + /* Make sure we include the NULL after the name and the compression type */ + png_write_chunk_start(png_ptr, (png_bytep)png_iCCP, + (png_uint_32)(name_len + profile_len + 2)); + new_name[name_len + 1] = 0x00; + png_write_chunk_data(png_ptr, (png_bytep)new_name, + (png_size_t)(name_len + 2)); + + if (profile_len) + png_write_compressed_data_out(png_ptr, &comp); + + png_write_chunk_end(png_ptr); + png_free(png_ptr, new_name); +} +#endif + +#ifdef PNG_WRITE_sPLT_SUPPORTED +/* Write a sPLT chunk */ +void /* PRIVATE */ +png_write_sPLT(png_structp png_ptr, png_sPLT_tp spalette) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_sPLT; +#endif + png_size_t name_len; + png_charp new_name; + png_byte entrybuf[10]; + int entry_size = (spalette->depth == 8 ? 6 : 10); + int palette_size = entry_size * spalette->nentries; + png_sPLT_entryp ep; +#ifndef PNG_POINTER_INDEXING_SUPPORTED + int i; +#endif + + png_debug(1, "in png_write_sPLT"); + + if ((name_len = png_check_keyword(png_ptr,spalette->name, &new_name))==0) + return; + + /* Make sure we include the NULL after the name */ + png_write_chunk_start(png_ptr, (png_bytep)png_sPLT, + (png_uint_32)(name_len + 2 + palette_size)); + png_write_chunk_data(png_ptr, (png_bytep)new_name, + (png_size_t)(name_len + 1)); + png_write_chunk_data(png_ptr, (png_bytep)&spalette->depth, (png_size_t)1); + + /* Loop through each palette entry, writing appropriately */ +#ifdef PNG_POINTER_INDEXING_SUPPORTED + for (ep = spalette->entries; epentries + spalette->nentries; ep++) + { + if (spalette->depth == 8) + { + entrybuf[0] = (png_byte)ep->red; + entrybuf[1] = (png_byte)ep->green; + entrybuf[2] = (png_byte)ep->blue; + entrybuf[3] = (png_byte)ep->alpha; + png_save_uint_16(entrybuf + 4, ep->frequency); + } + else + { + png_save_uint_16(entrybuf + 0, ep->red); + png_save_uint_16(entrybuf + 2, ep->green); + png_save_uint_16(entrybuf + 4, ep->blue); + png_save_uint_16(entrybuf + 6, ep->alpha); + png_save_uint_16(entrybuf + 8, ep->frequency); + } + png_write_chunk_data(png_ptr, entrybuf, (png_size_t)entry_size); + } +#else + ep=spalette->entries; + for (i=0; i>spalette->nentries; i++) + { + if (spalette->depth == 8) + { + entrybuf[0] = (png_byte)ep[i].red; + entrybuf[1] = (png_byte)ep[i].green; + entrybuf[2] = (png_byte)ep[i].blue; + entrybuf[3] = (png_byte)ep[i].alpha; + png_save_uint_16(entrybuf + 4, ep[i].frequency); + } + else + { + png_save_uint_16(entrybuf + 0, ep[i].red); + png_save_uint_16(entrybuf + 2, ep[i].green); + png_save_uint_16(entrybuf + 4, ep[i].blue); + png_save_uint_16(entrybuf + 6, ep[i].alpha); + png_save_uint_16(entrybuf + 8, ep[i].frequency); + } + png_write_chunk_data(png_ptr, entrybuf, (png_size_t)entry_size); + } +#endif + + png_write_chunk_end(png_ptr); + png_free(png_ptr, new_name); +} +#endif + +#ifdef PNG_WRITE_sBIT_SUPPORTED +/* Write the sBIT chunk */ +void /* PRIVATE */ +png_write_sBIT(png_structp png_ptr, png_color_8p sbit, int color_type) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_sBIT; +#endif + png_byte buf[4]; + png_size_t size; + + png_debug(1, "in png_write_sBIT"); + + /* Make sure we don't depend upon the order of PNG_COLOR_8 */ + if (color_type & PNG_COLOR_MASK_COLOR) + { + png_byte maxbits; + + maxbits = (png_byte)(color_type==PNG_COLOR_TYPE_PALETTE ? 8 : + png_ptr->usr_bit_depth); + if (sbit->red == 0 || sbit->red > maxbits || + sbit->green == 0 || sbit->green > maxbits || + sbit->blue == 0 || sbit->blue > maxbits) + { + png_warning(png_ptr, "Invalid sBIT depth specified"); + return; + } + buf[0] = sbit->red; + buf[1] = sbit->green; + buf[2] = sbit->blue; + size = 3; + } + else + { + if (sbit->gray == 0 || sbit->gray > png_ptr->usr_bit_depth) + { + png_warning(png_ptr, "Invalid sBIT depth specified"); + return; + } + buf[0] = sbit->gray; + size = 1; + } + + if (color_type & PNG_COLOR_MASK_ALPHA) + { + if (sbit->alpha == 0 || sbit->alpha > png_ptr->usr_bit_depth) + { + png_warning(png_ptr, "Invalid sBIT depth specified"); + return; + } + buf[size++] = sbit->alpha; + } + + png_write_chunk(png_ptr, (png_bytep)png_sBIT, buf, size); +} +#endif + +#ifdef PNG_WRITE_cHRM_SUPPORTED +/* Write the cHRM chunk */ +#ifdef PNG_FLOATING_POINT_SUPPORTED +void /* PRIVATE */ +png_write_cHRM(png_structp png_ptr, double white_x, double white_y, + double red_x, double red_y, double green_x, double green_y, + double blue_x, double blue_y) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_cHRM; +#endif + png_byte buf[32]; + + png_fixed_point int_white_x, int_white_y, int_red_x, int_red_y, + int_green_x, int_green_y, int_blue_x, int_blue_y; + + png_debug(1, "in png_write_cHRM"); + + int_white_x = (png_uint_32)(white_x * 100000.0 + 0.5); + int_white_y = (png_uint_32)(white_y * 100000.0 + 0.5); + int_red_x = (png_uint_32)(red_x * 100000.0 + 0.5); + int_red_y = (png_uint_32)(red_y * 100000.0 + 0.5); + int_green_x = (png_uint_32)(green_x * 100000.0 + 0.5); + int_green_y = (png_uint_32)(green_y * 100000.0 + 0.5); + int_blue_x = (png_uint_32)(blue_x * 100000.0 + 0.5); + int_blue_y = (png_uint_32)(blue_y * 100000.0 + 0.5); + +#ifdef PNG_CHECK_cHRM_SUPPORTED + if (png_check_cHRM_fixed(png_ptr, int_white_x, int_white_y, + int_red_x, int_red_y, int_green_x, int_green_y, int_blue_x, int_blue_y)) +#endif + { + /* Each value is saved in 1/100,000ths */ + + png_save_uint_32(buf, int_white_x); + png_save_uint_32(buf + 4, int_white_y); + + png_save_uint_32(buf + 8, int_red_x); + png_save_uint_32(buf + 12, int_red_y); + + png_save_uint_32(buf + 16, int_green_x); + png_save_uint_32(buf + 20, int_green_y); + + png_save_uint_32(buf + 24, int_blue_x); + png_save_uint_32(buf + 28, int_blue_y); + + png_write_chunk(png_ptr, (png_bytep)png_cHRM, buf, (png_size_t)32); + } +} +#endif +#ifdef PNG_FIXED_POINT_SUPPORTED +void /* PRIVATE */ +png_write_cHRM_fixed(png_structp png_ptr, png_fixed_point white_x, + png_fixed_point white_y, png_fixed_point red_x, png_fixed_point red_y, + png_fixed_point green_x, png_fixed_point green_y, png_fixed_point blue_x, + png_fixed_point blue_y) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_cHRM; +#endif + png_byte buf[32]; + + png_debug(1, "in png_write_cHRM"); + + /* Each value is saved in 1/100,000ths */ +#ifdef PNG_CHECK_cHRM_SUPPORTED + if (png_check_cHRM_fixed(png_ptr, white_x, white_y, red_x, red_y, + green_x, green_y, blue_x, blue_y)) +#endif + { + png_save_uint_32(buf, (png_uint_32)white_x); + png_save_uint_32(buf + 4, (png_uint_32)white_y); + + png_save_uint_32(buf + 8, (png_uint_32)red_x); + png_save_uint_32(buf + 12, (png_uint_32)red_y); + + png_save_uint_32(buf + 16, (png_uint_32)green_x); + png_save_uint_32(buf + 20, (png_uint_32)green_y); + + png_save_uint_32(buf + 24, (png_uint_32)blue_x); + png_save_uint_32(buf + 28, (png_uint_32)blue_y); + + png_write_chunk(png_ptr, (png_bytep)png_cHRM, buf, (png_size_t)32); + } +} +#endif +#endif + +#ifdef PNG_WRITE_tRNS_SUPPORTED +/* Write the tRNS chunk */ +void /* PRIVATE */ +png_write_tRNS(png_structp png_ptr, png_bytep trans, png_color_16p tran, + int num_trans, int color_type) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_tRNS; +#endif + png_byte buf[6]; + + png_debug(1, "in png_write_tRNS"); + + if (color_type == PNG_COLOR_TYPE_PALETTE) + { + if (num_trans <= 0 || num_trans > (int)png_ptr->num_palette) + { + png_warning(png_ptr, "Invalid number of transparent colors specified"); + return; + } + /* Write the chunk out as it is */ + png_write_chunk(png_ptr, (png_bytep)png_tRNS, trans, + (png_size_t)num_trans); + } + else if (color_type == PNG_COLOR_TYPE_GRAY) + { + /* One 16 bit value */ + if (tran->gray >= (1 << png_ptr->bit_depth)) + { + png_warning(png_ptr, + "Ignoring attempt to write tRNS chunk out-of-range for bit_depth"); + return; + } + png_save_uint_16(buf, tran->gray); + png_write_chunk(png_ptr, (png_bytep)png_tRNS, buf, (png_size_t)2); + } + else if (color_type == PNG_COLOR_TYPE_RGB) + { + /* Three 16 bit values */ + png_save_uint_16(buf, tran->red); + png_save_uint_16(buf + 2, tran->green); + png_save_uint_16(buf + 4, tran->blue); + if (png_ptr->bit_depth == 8 && (buf[0] | buf[2] | buf[4])) + { + png_warning(png_ptr, + "Ignoring attempt to write 16-bit tRNS chunk when bit_depth is 8"); + return; + } + png_write_chunk(png_ptr, (png_bytep)png_tRNS, buf, (png_size_t)6); + } + else + { + png_warning(png_ptr, "Can't write tRNS with an alpha channel"); + } +} +#endif + +#ifdef PNG_WRITE_bKGD_SUPPORTED +/* Write the background chunk */ +void /* PRIVATE */ +png_write_bKGD(png_structp png_ptr, png_color_16p back, int color_type) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_bKGD; +#endif + png_byte buf[6]; + + png_debug(1, "in png_write_bKGD"); + + if (color_type == PNG_COLOR_TYPE_PALETTE) + { + if ( +#ifdef PNG_MNG_FEATURES_SUPPORTED + (png_ptr->num_palette || + (!(png_ptr->mng_features_permitted & PNG_FLAG_MNG_EMPTY_PLTE))) && +#endif + back->index >= png_ptr->num_palette) + { + png_warning(png_ptr, "Invalid background palette index"); + return; + } + buf[0] = back->index; + png_write_chunk(png_ptr, (png_bytep)png_bKGD, buf, (png_size_t)1); + } + else if (color_type & PNG_COLOR_MASK_COLOR) + { + png_save_uint_16(buf, back->red); + png_save_uint_16(buf + 2, back->green); + png_save_uint_16(buf + 4, back->blue); + if (png_ptr->bit_depth == 8 && (buf[0] | buf[2] | buf[4])) + { + png_warning(png_ptr, + "Ignoring attempt to write 16-bit bKGD chunk when bit_depth is 8"); + return; + } + png_write_chunk(png_ptr, (png_bytep)png_bKGD, buf, (png_size_t)6); + } + else + { + if (back->gray >= (1 << png_ptr->bit_depth)) + { + png_warning(png_ptr, + "Ignoring attempt to write bKGD chunk out-of-range for bit_depth"); + return; + } + png_save_uint_16(buf, back->gray); + png_write_chunk(png_ptr, (png_bytep)png_bKGD, buf, (png_size_t)2); + } +} +#endif + +#ifdef PNG_WRITE_hIST_SUPPORTED +/* Write the histogram */ +void /* PRIVATE */ +png_write_hIST(png_structp png_ptr, png_uint_16p hist, int num_hist) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_hIST; +#endif + int i; + png_byte buf[3]; + + png_debug(1, "in png_write_hIST"); + + if (num_hist > (int)png_ptr->num_palette) + { + png_debug2(3, "num_hist = %d, num_palette = %d", num_hist, + png_ptr->num_palette); + png_warning(png_ptr, "Invalid number of histogram entries specified"); + return; + } + + png_write_chunk_start(png_ptr, (png_bytep)png_hIST, + (png_uint_32)(num_hist * 2)); + for (i = 0; i < num_hist; i++) + { + png_save_uint_16(buf, hist[i]); + png_write_chunk_data(png_ptr, buf, (png_size_t)2); + } + png_write_chunk_end(png_ptr); +} +#endif + +#if defined(PNG_WRITE_TEXT_SUPPORTED) || defined(PNG_WRITE_pCAL_SUPPORTED) || \ + defined(PNG_WRITE_iCCP_SUPPORTED) || defined(PNG_WRITE_sPLT_SUPPORTED) +/* Check that the tEXt or zTXt keyword is valid per PNG 1.0 specification, + * and if invalid, correct the keyword rather than discarding the entire + * chunk. The PNG 1.0 specification requires keywords 1-79 characters in + * length, forbids leading or trailing whitespace, multiple internal spaces, + * and the non-break space (0x80) from ISO 8859-1. Returns keyword length. + * + * The new_key is allocated to hold the corrected keyword and must be freed + * by the calling routine. This avoids problems with trying to write to + * static keywords without having to have duplicate copies of the strings. + */ +png_size_t /* PRIVATE */ +png_check_keyword(png_structp png_ptr, png_charp key, png_charpp new_key) +{ + png_size_t key_len; + png_charp kp, dp; + int kflag; + int kwarn=0; + + png_debug(1, "in png_check_keyword"); + + *new_key = NULL; + + if (key == NULL || (key_len = png_strlen(key)) == 0) + { + png_warning(png_ptr, "zero length keyword"); + return ((png_size_t)0); + } + + png_debug1(2, "Keyword to be checked is '%s'", key); + + *new_key = (png_charp)png_malloc_warn(png_ptr, (png_uint_32)(key_len + 2)); + if (*new_key == NULL) + { + png_warning(png_ptr, "Out of memory while procesing keyword"); + return ((png_size_t)0); + } + + /* Replace non-printing characters with a blank and print a warning */ + for (kp = key, dp = *new_key; *kp != '\0'; kp++, dp++) + { + if ((png_byte)*kp < 0x20 || + ((png_byte)*kp > 0x7E && (png_byte)*kp < 0xA1)) + { +#if defined(PNG_STDIO_SUPPORTED) && !defined(_WIN32_WCE) + char msg[40]; + + png_snprintf(msg, 40, + "invalid keyword character 0x%02X", (png_byte)*kp); + png_warning(png_ptr, msg); +#else + png_warning(png_ptr, "invalid character in keyword"); +#endif + *dp = ' '; + } + else + { + *dp = *kp; + } + } + *dp = '\0'; + + /* Remove any trailing white space. */ + kp = *new_key + key_len - 1; + if (*kp == ' ') + { + png_warning(png_ptr, "trailing spaces removed from keyword"); + + while (*kp == ' ') + { + *(kp--) = '\0'; + key_len--; + } + } + + /* Remove any leading white space. */ + kp = *new_key; + if (*kp == ' ') + { + png_warning(png_ptr, "leading spaces removed from keyword"); + + while (*kp == ' ') + { + kp++; + key_len--; + } + } + + png_debug1(2, "Checking for multiple internal spaces in '%s'", kp); + + /* Remove multiple internal spaces. */ + for (kflag = 0, dp = *new_key; *kp != '\0'; kp++) + { + if (*kp == ' ' && kflag == 0) + { + *(dp++) = *kp; + kflag = 1; + } + else if (*kp == ' ') + { + key_len--; + kwarn=1; + } + else + { + *(dp++) = *kp; + kflag = 0; + } + } + *dp = '\0'; + if (kwarn) + png_warning(png_ptr, "extra interior spaces removed from keyword"); + + if (key_len == 0) + { + png_free(png_ptr, *new_key); + *new_key=NULL; + png_warning(png_ptr, "Zero length keyword"); + } + + if (key_len > 79) + { + png_warning(png_ptr, "keyword length must be 1 - 79 characters"); + (*new_key)[79] = '\0'; + key_len = 79; + } + + return (key_len); +} +#endif + +#ifdef PNG_WRITE_tEXt_SUPPORTED +/* Write a tEXt chunk */ +void /* PRIVATE */ +png_write_tEXt(png_structp png_ptr, png_charp key, png_charp text, + png_size_t text_len) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_tEXt; +#endif + png_size_t key_len; + png_charp new_key; + + png_debug(1, "in png_write_tEXt"); + + if ((key_len = png_check_keyword(png_ptr, key, &new_key))==0) + return; + + if (text == NULL || *text == '\0') + text_len = 0; + else + text_len = png_strlen(text); + + /* Make sure we include the 0 after the key */ + png_write_chunk_start(png_ptr, (png_bytep)png_tEXt, + (png_uint_32)(key_len + text_len + 1)); + /* + * We leave it to the application to meet PNG-1.0 requirements on the + * contents of the text. PNG-1.0 through PNG-1.2 discourage the use of + * any non-Latin-1 characters except for NEWLINE. ISO PNG will forbid them. + * The NUL character is forbidden by PNG-1.0 through PNG-1.2 and ISO PNG. + */ + png_write_chunk_data(png_ptr, (png_bytep)new_key, + (png_size_t)(key_len + 1)); + if (text_len) + png_write_chunk_data(png_ptr, (png_bytep)text, (png_size_t)text_len); + + png_write_chunk_end(png_ptr); + png_free(png_ptr, new_key); +} +#endif + +#ifdef PNG_WRITE_zTXt_SUPPORTED +/* Write a compressed text chunk */ +void /* PRIVATE */ +png_write_zTXt(png_structp png_ptr, png_charp key, png_charp text, + png_size_t text_len, int compression) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_zTXt; +#endif + png_size_t key_len; + char buf[1]; + png_charp new_key; + compression_state comp; + + png_debug(1, "in png_write_zTXt"); + + comp.num_output_ptr = 0; + comp.max_output_ptr = 0; + comp.output_ptr = NULL; + comp.input = NULL; + comp.input_len = 0; + + if ((key_len = png_check_keyword(png_ptr, key, &new_key))==0) + { + png_free(png_ptr, new_key); + return; + } + + if (text == NULL || *text == '\0' || compression==PNG_TEXT_COMPRESSION_NONE) + { + png_write_tEXt(png_ptr, new_key, text, (png_size_t)0); + png_free(png_ptr, new_key); + return; + } + + text_len = png_strlen(text); + + /* Compute the compressed data; do it now for the length */ + text_len = png_text_compress(png_ptr, text, text_len, compression, + &comp); + + /* Write start of chunk */ + png_write_chunk_start(png_ptr, (png_bytep)png_zTXt, + (png_uint_32)(key_len+text_len + 2)); + /* Write key */ + png_write_chunk_data(png_ptr, (png_bytep)new_key, + (png_size_t)(key_len + 1)); + png_free(png_ptr, new_key); + + buf[0] = (png_byte)compression; + /* Write compression */ + png_write_chunk_data(png_ptr, (png_bytep)buf, (png_size_t)1); + /* Write the compressed data */ + png_write_compressed_data_out(png_ptr, &comp); + + /* Close the chunk */ + png_write_chunk_end(png_ptr); +} +#endif + +#ifdef PNG_WRITE_iTXt_SUPPORTED +/* Write an iTXt chunk */ +void /* PRIVATE */ +png_write_iTXt(png_structp png_ptr, int compression, png_charp key, + png_charp lang, png_charp lang_key, png_charp text) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_iTXt; +#endif + png_size_t lang_len, key_len, lang_key_len, text_len; + png_charp new_lang; + png_charp new_key = NULL; + png_byte cbuf[2]; + compression_state comp; + + png_debug(1, "in png_write_iTXt"); + + comp.num_output_ptr = 0; + comp.max_output_ptr = 0; + comp.output_ptr = NULL; + comp.input = NULL; + + if ((key_len = png_check_keyword(png_ptr, key, &new_key))==0) + return; + + if ((lang_len = png_check_keyword(png_ptr, lang, &new_lang))==0) + { + png_warning(png_ptr, "Empty language field in iTXt chunk"); + new_lang = NULL; + lang_len = 0; + } + + if (lang_key == NULL) + lang_key_len = 0; + else + lang_key_len = png_strlen(lang_key); + + if (text == NULL) + text_len = 0; + else + text_len = png_strlen(text); + + /* Compute the compressed data; do it now for the length */ + text_len = png_text_compress(png_ptr, text, text_len, compression-2, + &comp); + + + /* Make sure we include the compression flag, the compression byte, + * and the NULs after the key, lang, and lang_key parts */ + + png_write_chunk_start(png_ptr, (png_bytep)png_iTXt, + (png_uint_32)( + 5 /* comp byte, comp flag, terminators for key, lang and lang_key */ + + key_len + + lang_len + + lang_key_len + + text_len)); + + /* We leave it to the application to meet PNG-1.0 requirements on the + * contents of the text. PNG-1.0 through PNG-1.2 discourage the use of + * any non-Latin-1 characters except for NEWLINE. ISO PNG will forbid them. + * The NUL character is forbidden by PNG-1.0 through PNG-1.2 and ISO PNG. + */ + png_write_chunk_data(png_ptr, (png_bytep)new_key, + (png_size_t)(key_len + 1)); + + /* Set the compression flag */ + if (compression == PNG_ITXT_COMPRESSION_NONE || \ + compression == PNG_TEXT_COMPRESSION_NONE) + cbuf[0] = 0; + else /* compression == PNG_ITXT_COMPRESSION_zTXt */ + cbuf[0] = 1; + /* Set the compression method */ + cbuf[1] = 0; + png_write_chunk_data(png_ptr, cbuf, (png_size_t)2); + + cbuf[0] = 0; + png_write_chunk_data(png_ptr, (new_lang ? (png_bytep)new_lang : cbuf), + (png_size_t)(lang_len + 1)); + png_write_chunk_data(png_ptr, (lang_key ? (png_bytep)lang_key : cbuf), + (png_size_t)(lang_key_len + 1)); + png_write_compressed_data_out(png_ptr, &comp); + + png_write_chunk_end(png_ptr); + png_free(png_ptr, new_key); + png_free(png_ptr, new_lang); +} +#endif + +#ifdef PNG_WRITE_oFFs_SUPPORTED +/* Write the oFFs chunk */ +void /* PRIVATE */ +png_write_oFFs(png_structp png_ptr, png_int_32 x_offset, png_int_32 y_offset, + int unit_type) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_oFFs; +#endif + png_byte buf[9]; + + png_debug(1, "in png_write_oFFs"); + + if (unit_type >= PNG_OFFSET_LAST) + png_warning(png_ptr, "Unrecognized unit type for oFFs chunk"); + + png_save_int_32(buf, x_offset); + png_save_int_32(buf + 4, y_offset); + buf[8] = (png_byte)unit_type; + + png_write_chunk(png_ptr, (png_bytep)png_oFFs, buf, (png_size_t)9); +} +#endif +#ifdef PNG_WRITE_pCAL_SUPPORTED +/* Write the pCAL chunk (described in the PNG extensions document) */ +void /* PRIVATE */ +png_write_pCAL(png_structp png_ptr, png_charp purpose, png_int_32 X0, + png_int_32 X1, int type, int nparams, png_charp units, png_charpp params) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_pCAL; +#endif + png_size_t purpose_len, units_len, total_len; + png_uint_32p params_len; + png_byte buf[10]; + png_charp new_purpose; + int i; + + png_debug1(1, "in png_write_pCAL (%d parameters)", nparams); + + if (type >= PNG_EQUATION_LAST) + png_warning(png_ptr, "Unrecognized equation type for pCAL chunk"); + + purpose_len = png_check_keyword(png_ptr, purpose, &new_purpose) + 1; + png_debug1(3, "pCAL purpose length = %d", (int)purpose_len); + units_len = png_strlen(units) + (nparams == 0 ? 0 : 1); + png_debug1(3, "pCAL units length = %d", (int)units_len); + total_len = purpose_len + units_len + 10; + + params_len = (png_uint_32p)png_malloc(png_ptr, + (png_uint_32)(nparams * png_sizeof(png_uint_32))); + + /* Find the length of each parameter, making sure we don't count the + null terminator for the last parameter. */ + for (i = 0; i < nparams; i++) + { + params_len[i] = png_strlen(params[i]) + (i == nparams - 1 ? 0 : 1); + png_debug2(3, "pCAL parameter %d length = %lu", i, + (unsigned long) params_len[i]); + total_len += (png_size_t)params_len[i]; + } + + png_debug1(3, "pCAL total length = %d", (int)total_len); + png_write_chunk_start(png_ptr, (png_bytep)png_pCAL, (png_uint_32)total_len); + png_write_chunk_data(png_ptr, (png_bytep)new_purpose, + (png_size_t)purpose_len); + png_save_int_32(buf, X0); + png_save_int_32(buf + 4, X1); + buf[8] = (png_byte)type; + buf[9] = (png_byte)nparams; + png_write_chunk_data(png_ptr, buf, (png_size_t)10); + png_write_chunk_data(png_ptr, (png_bytep)units, (png_size_t)units_len); + + png_free(png_ptr, new_purpose); + + for (i = 0; i < nparams; i++) + { + png_write_chunk_data(png_ptr, (png_bytep)params[i], + (png_size_t)params_len[i]); + } + + png_free(png_ptr, params_len); + png_write_chunk_end(png_ptr); +} +#endif + +#ifdef PNG_WRITE_sCAL_SUPPORTED +/* Write the sCAL chunk */ +#if defined(PNG_FLOATING_POINT_SUPPORTED) && defined(PNG_STDIO_SUPPORTED) +void /* PRIVATE */ +png_write_sCAL(png_structp png_ptr, int unit, double width, double height) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_sCAL; +#endif + char buf[64]; + png_size_t total_len; + + png_debug(1, "in png_write_sCAL"); + + buf[0] = (char)unit; +#ifdef _WIN32_WCE +/* sprintf() function is not supported on WindowsCE */ + { + wchar_t wc_buf[32]; + size_t wc_len; + swprintf(wc_buf, TEXT("%12.12e"), width); + wc_len = wcslen(wc_buf); + WideCharToMultiByte(CP_ACP, 0, wc_buf, -1, buf + 1, wc_len, NULL, + NULL); + total_len = wc_len + 2; + swprintf(wc_buf, TEXT("%12.12e"), height); + wc_len = wcslen(wc_buf); + WideCharToMultiByte(CP_ACP, 0, wc_buf, -1, buf + total_len, wc_len, + NULL, NULL); + total_len += wc_len; + } +#else + png_snprintf(buf + 1, 63, "%12.12e", width); + total_len = 1 + png_strlen(buf + 1) + 1; + png_snprintf(buf + total_len, 64-total_len, "%12.12e", height); + total_len += png_strlen(buf + total_len); +#endif + + png_debug1(3, "sCAL total length = %u", (unsigned int)total_len); + png_write_chunk(png_ptr, (png_bytep)png_sCAL, (png_bytep)buf, total_len); +} +#else +#ifdef PNG_FIXED_POINT_SUPPORTED +void /* PRIVATE */ +png_write_sCAL_s(png_structp png_ptr, int unit, png_charp width, + png_charp height) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_sCAL; +#endif + png_byte buf[64]; + png_size_t wlen, hlen, total_len; + + png_debug(1, "in png_write_sCAL_s"); + + wlen = png_strlen(width); + hlen = png_strlen(height); + total_len = wlen + hlen + 2; + if (total_len > 64) + { + png_warning(png_ptr, "Can't write sCAL (buffer too small)"); + return; + } + + buf[0] = (png_byte)unit; + png_memcpy(buf + 1, width, wlen + 1); /* Append the '\0' here */ + png_memcpy(buf + wlen + 2, height, hlen); /* Do NOT append the '\0' here */ + + png_debug1(3, "sCAL total length = %u", (unsigned int)total_len); + png_write_chunk(png_ptr, (png_bytep)png_sCAL, buf, total_len); +} +#endif +#endif +#endif + +#ifdef PNG_WRITE_pHYs_SUPPORTED +/* Write the pHYs chunk */ +void /* PRIVATE */ +png_write_pHYs(png_structp png_ptr, png_uint_32 x_pixels_per_unit, + png_uint_32 y_pixels_per_unit, + int unit_type) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_pHYs; +#endif + png_byte buf[9]; + + png_debug(1, "in png_write_pHYs"); + + if (unit_type >= PNG_RESOLUTION_LAST) + png_warning(png_ptr, "Unrecognized unit type for pHYs chunk"); + + png_save_uint_32(buf, x_pixels_per_unit); + png_save_uint_32(buf + 4, y_pixels_per_unit); + buf[8] = (png_byte)unit_type; + + png_write_chunk(png_ptr, (png_bytep)png_pHYs, buf, (png_size_t)9); +} +#endif + +#ifdef PNG_WRITE_tIME_SUPPORTED +/* Write the tIME chunk. Use either png_convert_from_struct_tm() + * or png_convert_from_time_t(), or fill in the structure yourself. + */ +void /* PRIVATE */ +png_write_tIME(png_structp png_ptr, png_timep mod_time) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_tIME; +#endif + png_byte buf[7]; + + png_debug(1, "in png_write_tIME"); + + if (mod_time->month > 12 || mod_time->month < 1 || + mod_time->day > 31 || mod_time->day < 1 || + mod_time->hour > 23 || mod_time->second > 60) + { + png_warning(png_ptr, "Invalid time specified for tIME chunk"); + return; + } + + png_save_uint_16(buf, mod_time->year); + buf[2] = mod_time->month; + buf[3] = mod_time->day; + buf[4] = mod_time->hour; + buf[5] = mod_time->minute; + buf[6] = mod_time->second; + + png_write_chunk(png_ptr, (png_bytep)png_tIME, buf, (png_size_t)7); +} +#endif + +/* Initializes the row writing capability of libpng */ +void /* PRIVATE */ +png_write_start_row(png_structp png_ptr) +{ +#ifdef PNG_WRITE_INTERLACING_SUPPORTED + /* Arrays to facilitate easy interlacing - use pass (0 - 6) as index */ + + /* Start of interlace block */ + int png_pass_start[7] = {0, 4, 0, 2, 0, 1, 0}; + + /* Offset to next interlace block */ + int png_pass_inc[7] = {8, 8, 4, 4, 2, 2, 1}; + + /* Start of interlace block in the y direction */ + int png_pass_ystart[7] = {0, 0, 4, 0, 2, 0, 1}; + + /* Offset to next interlace block in the y direction */ + int png_pass_yinc[7] = {8, 8, 8, 4, 4, 2, 2}; +#endif + + png_size_t buf_size; + + png_debug(1, "in png_write_start_row"); + + buf_size = (png_size_t)(PNG_ROWBYTES( + png_ptr->usr_channels*png_ptr->usr_bit_depth, png_ptr->width) + 1); + + /* Set up row buffer */ + png_ptr->row_buf = (png_bytep)png_malloc(png_ptr, + (png_uint_32)buf_size); + png_ptr->row_buf[0] = PNG_FILTER_VALUE_NONE; + +#ifdef PNG_WRITE_FILTER_SUPPORTED + /* Set up filtering buffer, if using this filter */ + if (png_ptr->do_filter & PNG_FILTER_SUB) + { + png_ptr->sub_row = (png_bytep)png_malloc(png_ptr, + (png_uint_32)(png_ptr->rowbytes + 1)); + png_ptr->sub_row[0] = PNG_FILTER_VALUE_SUB; + } + + /* We only need to keep the previous row if we are using one of these. */ + if (png_ptr->do_filter & (PNG_FILTER_AVG | PNG_FILTER_UP | PNG_FILTER_PAETH)) + { + /* Set up previous row buffer */ + png_ptr->prev_row = (png_bytep)png_calloc(png_ptr, + (png_uint_32)buf_size); + + if (png_ptr->do_filter & PNG_FILTER_UP) + { + png_ptr->up_row = (png_bytep)png_malloc(png_ptr, + (png_uint_32)(png_ptr->rowbytes + 1)); + png_ptr->up_row[0] = PNG_FILTER_VALUE_UP; + } + + if (png_ptr->do_filter & PNG_FILTER_AVG) + { + png_ptr->avg_row = (png_bytep)png_malloc(png_ptr, + (png_uint_32)(png_ptr->rowbytes + 1)); + png_ptr->avg_row[0] = PNG_FILTER_VALUE_AVG; + } + + if (png_ptr->do_filter & PNG_FILTER_PAETH) + { + png_ptr->paeth_row = (png_bytep)png_malloc(png_ptr, + (png_uint_32)(png_ptr->rowbytes + 1)); + png_ptr->paeth_row[0] = PNG_FILTER_VALUE_PAETH; + } + } +#endif /* PNG_WRITE_FILTER_SUPPORTED */ + +#ifdef PNG_WRITE_INTERLACING_SUPPORTED + /* If interlaced, we need to set up width and height of pass */ + if (png_ptr->interlaced) + { + if (!(png_ptr->transformations & PNG_INTERLACE)) + { + png_ptr->num_rows = (png_ptr->height + png_pass_yinc[0] - 1 - + png_pass_ystart[0]) / png_pass_yinc[0]; + png_ptr->usr_width = (png_ptr->width + png_pass_inc[0] - 1 - + png_pass_start[0]) / png_pass_inc[0]; + } + else + { + png_ptr->num_rows = png_ptr->height; + png_ptr->usr_width = png_ptr->width; + } + } + else +#endif + { + png_ptr->num_rows = png_ptr->height; + png_ptr->usr_width = png_ptr->width; + } + png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size; + png_ptr->zstream.next_out = png_ptr->zbuf; +} + +/* Internal use only. Called when finished processing a row of data. */ +void /* PRIVATE */ +png_write_finish_row(png_structp png_ptr) +{ +#ifdef PNG_WRITE_INTERLACING_SUPPORTED + /* Arrays to facilitate easy interlacing - use pass (0 - 6) as index */ + + /* Start of interlace block */ + int png_pass_start[7] = {0, 4, 0, 2, 0, 1, 0}; + + /* Offset to next interlace block */ + int png_pass_inc[7] = {8, 8, 4, 4, 2, 2, 1}; + + /* Start of interlace block in the y direction */ + int png_pass_ystart[7] = {0, 0, 4, 0, 2, 0, 1}; + + /* Offset to next interlace block in the y direction */ + int png_pass_yinc[7] = {8, 8, 8, 4, 4, 2, 2}; +#endif + + int ret; + + png_debug(1, "in png_write_finish_row"); + + /* Next row */ + png_ptr->row_number++; + + /* See if we are done */ + if (png_ptr->row_number < png_ptr->num_rows) + return; + +#ifdef PNG_WRITE_INTERLACING_SUPPORTED + /* If interlaced, go to next pass */ + if (png_ptr->interlaced) + { + png_ptr->row_number = 0; + if (png_ptr->transformations & PNG_INTERLACE) + { + png_ptr->pass++; + } + else + { + /* Loop until we find a non-zero width or height pass */ + do + { + png_ptr->pass++; + if (png_ptr->pass >= 7) + break; + png_ptr->usr_width = (png_ptr->width + + png_pass_inc[png_ptr->pass] - 1 - + png_pass_start[png_ptr->pass]) / + png_pass_inc[png_ptr->pass]; + png_ptr->num_rows = (png_ptr->height + + png_pass_yinc[png_ptr->pass] - 1 - + png_pass_ystart[png_ptr->pass]) / + png_pass_yinc[png_ptr->pass]; + if (png_ptr->transformations & PNG_INTERLACE) + break; + } while (png_ptr->usr_width == 0 || png_ptr->num_rows == 0); + + } + + /* Reset the row above the image for the next pass */ + if (png_ptr->pass < 7) + { + if (png_ptr->prev_row != NULL) + png_memset(png_ptr->prev_row, 0, + (png_size_t)(PNG_ROWBYTES(png_ptr->usr_channels* + png_ptr->usr_bit_depth, png_ptr->width)) + 1); + return; + } + } +#endif + + /* If we get here, we've just written the last row, so we need + to flush the compressor */ + do + { + /* Tell the compressor we are done */ + ret = deflate(&png_ptr->zstream, Z_FINISH); + /* Check for an error */ + if (ret == Z_OK) + { + /* Check to see if we need more room */ + if (!(png_ptr->zstream.avail_out)) + { + png_write_IDAT(png_ptr, png_ptr->zbuf, png_ptr->zbuf_size); + png_ptr->zstream.next_out = png_ptr->zbuf; + png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size; + } + } + else if (ret != Z_STREAM_END) + { + if (png_ptr->zstream.msg != NULL) + png_error(png_ptr, png_ptr->zstream.msg); + else + png_error(png_ptr, "zlib error"); + } + } while (ret != Z_STREAM_END); + + /* Write any extra space */ + if (png_ptr->zstream.avail_out < png_ptr->zbuf_size) + { + png_write_IDAT(png_ptr, png_ptr->zbuf, png_ptr->zbuf_size - + png_ptr->zstream.avail_out); + } + + deflateReset(&png_ptr->zstream); + png_ptr->zstream.data_type = Z_BINARY; +} + +#ifdef PNG_WRITE_INTERLACING_SUPPORTED +/* Pick out the correct pixels for the interlace pass. + * The basic idea here is to go through the row with a source + * pointer and a destination pointer (sp and dp), and copy the + * correct pixels for the pass. As the row gets compacted, + * sp will always be >= dp, so we should never overwrite anything. + * See the default: case for the easiest code to understand. + */ +void /* PRIVATE */ +png_do_write_interlace(png_row_infop row_info, png_bytep row, int pass) +{ + /* Arrays to facilitate easy interlacing - use pass (0 - 6) as index */ + + /* Start of interlace block */ + int png_pass_start[7] = {0, 4, 0, 2, 0, 1, 0}; + + /* Offset to next interlace block */ + int png_pass_inc[7] = {8, 8, 4, 4, 2, 2, 1}; + + png_debug(1, "in png_do_write_interlace"); + + /* We don't have to do anything on the last pass (6) */ +#ifdef PNG_USELESS_TESTS_SUPPORTED + if (row != NULL && row_info != NULL && pass < 6) +#else + if (pass < 6) +#endif + { + /* Each pixel depth is handled separately */ + switch (row_info->pixel_depth) + { + case 1: + { + png_bytep sp; + png_bytep dp; + int shift; + int d; + int value; + png_uint_32 i; + png_uint_32 row_width = row_info->width; + + dp = row; + d = 0; + shift = 7; + for (i = png_pass_start[pass]; i < row_width; + i += png_pass_inc[pass]) + { + sp = row + (png_size_t)(i >> 3); + value = (int)(*sp >> (7 - (int)(i & 0x07))) & 0x01; + d |= (value << shift); + + if (shift == 0) + { + shift = 7; + *dp++ = (png_byte)d; + d = 0; + } + else + shift--; + + } + if (shift != 7) + *dp = (png_byte)d; + break; + } + case 2: + { + png_bytep sp; + png_bytep dp; + int shift; + int d; + int value; + png_uint_32 i; + png_uint_32 row_width = row_info->width; + + dp = row; + shift = 6; + d = 0; + for (i = png_pass_start[pass]; i < row_width; + i += png_pass_inc[pass]) + { + sp = row + (png_size_t)(i >> 2); + value = (*sp >> ((3 - (int)(i & 0x03)) << 1)) & 0x03; + d |= (value << shift); + + if (shift == 0) + { + shift = 6; + *dp++ = (png_byte)d; + d = 0; + } + else + shift -= 2; + } + if (shift != 6) + *dp = (png_byte)d; + break; + } + case 4: + { + png_bytep sp; + png_bytep dp; + int shift; + int d; + int value; + png_uint_32 i; + png_uint_32 row_width = row_info->width; + + dp = row; + shift = 4; + d = 0; + for (i = png_pass_start[pass]; i < row_width; + i += png_pass_inc[pass]) + { + sp = row + (png_size_t)(i >> 1); + value = (*sp >> ((1 - (int)(i & 0x01)) << 2)) & 0x0f; + d |= (value << shift); + + if (shift == 0) + { + shift = 4; + *dp++ = (png_byte)d; + d = 0; + } + else + shift -= 4; + } + if (shift != 4) + *dp = (png_byte)d; + break; + } + default: + { + png_bytep sp; + png_bytep dp; + png_uint_32 i; + png_uint_32 row_width = row_info->width; + png_size_t pixel_bytes; + + /* Start at the beginning */ + dp = row; + /* Find out how many bytes each pixel takes up */ + pixel_bytes = (row_info->pixel_depth >> 3); + /* Loop through the row, only looking at the pixels that + matter */ + for (i = png_pass_start[pass]; i < row_width; + i += png_pass_inc[pass]) + { + /* Find out where the original pixel is */ + sp = row + (png_size_t)i * pixel_bytes; + /* Move the pixel */ + if (dp != sp) + png_memcpy(dp, sp, pixel_bytes); + /* Next pixel */ + dp += pixel_bytes; + } + break; + } + } + /* Set new row width */ + row_info->width = (row_info->width + + png_pass_inc[pass] - 1 - + png_pass_start[pass]) / + png_pass_inc[pass]; + row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth, + row_info->width); + } +} +#endif + +/* This filters the row, chooses which filter to use, if it has not already + * been specified by the application, and then writes the row out with the + * chosen filter. + */ +#define PNG_MAXSUM (((png_uint_32)(-1)) >> 1) +#define PNG_HISHIFT 10 +#define PNG_LOMASK ((png_uint_32)0xffffL) +#define PNG_HIMASK ((png_uint_32)(~PNG_LOMASK >> PNG_HISHIFT)) +void /* PRIVATE */ +png_write_find_filter(png_structp png_ptr, png_row_infop row_info) +{ + png_bytep best_row; +#ifdef PNG_WRITE_FILTER_SUPPORTED + png_bytep prev_row, row_buf; + png_uint_32 mins, bpp; + png_byte filter_to_do = png_ptr->do_filter; + png_uint_32 row_bytes = row_info->rowbytes; +#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED + int num_p_filters = (int)png_ptr->num_prev_filters; +#endif + + png_debug(1, "in png_write_find_filter"); + +#ifndef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED + if (png_ptr->row_number == 0 && filter_to_do == PNG_ALL_FILTERS) + { + /* These will never be selected so we need not test them. */ + filter_to_do &= ~(PNG_FILTER_UP | PNG_FILTER_PAETH); + } +#endif + + /* Find out how many bytes offset each pixel is */ + bpp = (row_info->pixel_depth + 7) >> 3; + + prev_row = png_ptr->prev_row; +#endif + best_row = png_ptr->row_buf; +#ifdef PNG_WRITE_FILTER_SUPPORTED + row_buf = best_row; + mins = PNG_MAXSUM; + + /* The prediction method we use is to find which method provides the + * smallest value when summing the absolute values of the distances + * from zero, using anything >= 128 as negative numbers. This is known + * as the "minimum sum of absolute differences" heuristic. Other + * heuristics are the "weighted minimum sum of absolute differences" + * (experimental and can in theory improve compression), and the "zlib + * predictive" method (not implemented yet), which does test compressions + * of lines using different filter methods, and then chooses the + * (series of) filter(s) that give minimum compressed data size (VERY + * computationally expensive). + * + * GRR 980525: consider also + * (1) minimum sum of absolute differences from running average (i.e., + * keep running sum of non-absolute differences & count of bytes) + * [track dispersion, too? restart average if dispersion too large?] + * (1b) minimum sum of absolute differences from sliding average, probably + * with window size <= deflate window (usually 32K) + * (2) minimum sum of squared differences from zero or running average + * (i.e., ~ root-mean-square approach) + */ + + + /* We don't need to test the 'no filter' case if this is the only filter + * that has been chosen, as it doesn't actually do anything to the data. + */ + if ((filter_to_do & PNG_FILTER_NONE) && + filter_to_do != PNG_FILTER_NONE) + { + png_bytep rp; + png_uint_32 sum = 0; + png_uint_32 i; + int v; + + for (i = 0, rp = row_buf + 1; i < row_bytes; i++, rp++) + { + v = *rp; + sum += (v < 128) ? v : 256 - v; + } + +#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED + if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED) + { + png_uint_32 sumhi, sumlo; + int j; + sumlo = sum & PNG_LOMASK; + sumhi = (sum >> PNG_HISHIFT) & PNG_HIMASK; /* Gives us some footroom */ + + /* Reduce the sum if we match any of the previous rows */ + for (j = 0; j < num_p_filters; j++) + { + if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_NONE) + { + sumlo = (sumlo * png_ptr->filter_weights[j]) >> + PNG_WEIGHT_SHIFT; + sumhi = (sumhi * png_ptr->filter_weights[j]) >> + PNG_WEIGHT_SHIFT; + } + } + + /* Factor in the cost of this filter (this is here for completeness, + * but it makes no sense to have a "cost" for the NONE filter, as + * it has the minimum possible computational cost - none). + */ + sumlo = (sumlo * png_ptr->filter_costs[PNG_FILTER_VALUE_NONE]) >> + PNG_COST_SHIFT; + sumhi = (sumhi * png_ptr->filter_costs[PNG_FILTER_VALUE_NONE]) >> + PNG_COST_SHIFT; + + if (sumhi > PNG_HIMASK) + sum = PNG_MAXSUM; + else + sum = (sumhi << PNG_HISHIFT) + sumlo; + } +#endif + mins = sum; + } + + /* Sub filter */ + if (filter_to_do == PNG_FILTER_SUB) + /* It's the only filter so no testing is needed */ + { + png_bytep rp, lp, dp; + png_uint_32 i; + for (i = 0, rp = row_buf + 1, dp = png_ptr->sub_row + 1; i < bpp; + i++, rp++, dp++) + { + *dp = *rp; + } + for (lp = row_buf + 1; i < row_bytes; + i++, rp++, lp++, dp++) + { + *dp = (png_byte)(((int)*rp - (int)*lp) & 0xff); + } + best_row = png_ptr->sub_row; + } + + else if (filter_to_do & PNG_FILTER_SUB) + { + png_bytep rp, dp, lp; + png_uint_32 sum = 0, lmins = mins; + png_uint_32 i; + int v; + +#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED + /* We temporarily increase the "minimum sum" by the factor we + * would reduce the sum of this filter, so that we can do the + * early exit comparison without scaling the sum each time. + */ + if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED) + { + int j; + png_uint_32 lmhi, lmlo; + lmlo = lmins & PNG_LOMASK; + lmhi = (lmins >> PNG_HISHIFT) & PNG_HIMASK; + + for (j = 0; j < num_p_filters; j++) + { + if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_SUB) + { + lmlo = (lmlo * png_ptr->inv_filter_weights[j]) >> + PNG_WEIGHT_SHIFT; + lmhi = (lmhi * png_ptr->inv_filter_weights[j]) >> + PNG_WEIGHT_SHIFT; + } + } + + lmlo = (lmlo * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_SUB]) >> + PNG_COST_SHIFT; + lmhi = (lmhi * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_SUB]) >> + PNG_COST_SHIFT; + + if (lmhi > PNG_HIMASK) + lmins = PNG_MAXSUM; + else + lmins = (lmhi << PNG_HISHIFT) + lmlo; + } +#endif + + for (i = 0, rp = row_buf + 1, dp = png_ptr->sub_row + 1; i < bpp; + i++, rp++, dp++) + { + v = *dp = *rp; + + sum += (v < 128) ? v : 256 - v; + } + for (lp = row_buf + 1; i < row_bytes; + i++, rp++, lp++, dp++) + { + v = *dp = (png_byte)(((int)*rp - (int)*lp) & 0xff); + + sum += (v < 128) ? v : 256 - v; + + if (sum > lmins) /* We are already worse, don't continue. */ + break; + } + +#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED + if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED) + { + int j; + png_uint_32 sumhi, sumlo; + sumlo = sum & PNG_LOMASK; + sumhi = (sum >> PNG_HISHIFT) & PNG_HIMASK; + + for (j = 0; j < num_p_filters; j++) + { + if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_SUB) + { + sumlo = (sumlo * png_ptr->inv_filter_weights[j]) >> + PNG_WEIGHT_SHIFT; + sumhi = (sumhi * png_ptr->inv_filter_weights[j]) >> + PNG_WEIGHT_SHIFT; + } + } + + sumlo = (sumlo * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_SUB]) >> + PNG_COST_SHIFT; + sumhi = (sumhi * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_SUB]) >> + PNG_COST_SHIFT; + + if (sumhi > PNG_HIMASK) + sum = PNG_MAXSUM; + else + sum = (sumhi << PNG_HISHIFT) + sumlo; + } +#endif + + if (sum < mins) + { + mins = sum; + best_row = png_ptr->sub_row; + } + } + + /* Up filter */ + if (filter_to_do == PNG_FILTER_UP) + { + png_bytep rp, dp, pp; + png_uint_32 i; + + for (i = 0, rp = row_buf + 1, dp = png_ptr->up_row + 1, + pp = prev_row + 1; i < row_bytes; + i++, rp++, pp++, dp++) + { + *dp = (png_byte)(((int)*rp - (int)*pp) & 0xff); + } + best_row = png_ptr->up_row; + } + + else if (filter_to_do & PNG_FILTER_UP) + { + png_bytep rp, dp, pp; + png_uint_32 sum = 0, lmins = mins; + png_uint_32 i; + int v; + + +#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED + if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED) + { + int j; + png_uint_32 lmhi, lmlo; + lmlo = lmins & PNG_LOMASK; + lmhi = (lmins >> PNG_HISHIFT) & PNG_HIMASK; + + for (j = 0; j < num_p_filters; j++) + { + if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_UP) + { + lmlo = (lmlo * png_ptr->inv_filter_weights[j]) >> + PNG_WEIGHT_SHIFT; + lmhi = (lmhi * png_ptr->inv_filter_weights[j]) >> + PNG_WEIGHT_SHIFT; + } + } + + lmlo = (lmlo * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_UP]) >> + PNG_COST_SHIFT; + lmhi = (lmhi * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_UP]) >> + PNG_COST_SHIFT; + + if (lmhi > PNG_HIMASK) + lmins = PNG_MAXSUM; + else + lmins = (lmhi << PNG_HISHIFT) + lmlo; + } +#endif + + for (i = 0, rp = row_buf + 1, dp = png_ptr->up_row + 1, + pp = prev_row + 1; i < row_bytes; i++) + { + v = *dp++ = (png_byte)(((int)*rp++ - (int)*pp++) & 0xff); + + sum += (v < 128) ? v : 256 - v; + + if (sum > lmins) /* We are already worse, don't continue. */ + break; + } + +#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED + if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED) + { + int j; + png_uint_32 sumhi, sumlo; + sumlo = sum & PNG_LOMASK; + sumhi = (sum >> PNG_HISHIFT) & PNG_HIMASK; + + for (j = 0; j < num_p_filters; j++) + { + if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_UP) + { + sumlo = (sumlo * png_ptr->filter_weights[j]) >> + PNG_WEIGHT_SHIFT; + sumhi = (sumhi * png_ptr->filter_weights[j]) >> + PNG_WEIGHT_SHIFT; + } + } + + sumlo = (sumlo * png_ptr->filter_costs[PNG_FILTER_VALUE_UP]) >> + PNG_COST_SHIFT; + sumhi = (sumhi * png_ptr->filter_costs[PNG_FILTER_VALUE_UP]) >> + PNG_COST_SHIFT; + + if (sumhi > PNG_HIMASK) + sum = PNG_MAXSUM; + else + sum = (sumhi << PNG_HISHIFT) + sumlo; + } +#endif + + if (sum < mins) + { + mins = sum; + best_row = png_ptr->up_row; + } + } + + /* Avg filter */ + if (filter_to_do == PNG_FILTER_AVG) + { + png_bytep rp, dp, pp, lp; + png_uint_32 i; + for (i = 0, rp = row_buf + 1, dp = png_ptr->avg_row + 1, + pp = prev_row + 1; i < bpp; i++) + { + *dp++ = (png_byte)(((int)*rp++ - ((int)*pp++ / 2)) & 0xff); + } + for (lp = row_buf + 1; i < row_bytes; i++) + { + *dp++ = (png_byte)(((int)*rp++ - (((int)*pp++ + (int)*lp++) / 2)) + & 0xff); + } + best_row = png_ptr->avg_row; + } + + else if (filter_to_do & PNG_FILTER_AVG) + { + png_bytep rp, dp, pp, lp; + png_uint_32 sum = 0, lmins = mins; + png_uint_32 i; + int v; + +#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED + if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED) + { + int j; + png_uint_32 lmhi, lmlo; + lmlo = lmins & PNG_LOMASK; + lmhi = (lmins >> PNG_HISHIFT) & PNG_HIMASK; + + for (j = 0; j < num_p_filters; j++) + { + if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_AVG) + { + lmlo = (lmlo * png_ptr->inv_filter_weights[j]) >> + PNG_WEIGHT_SHIFT; + lmhi = (lmhi * png_ptr->inv_filter_weights[j]) >> + PNG_WEIGHT_SHIFT; + } + } + + lmlo = (lmlo * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_AVG]) >> + PNG_COST_SHIFT; + lmhi = (lmhi * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_AVG]) >> + PNG_COST_SHIFT; + + if (lmhi > PNG_HIMASK) + lmins = PNG_MAXSUM; + else + lmins = (lmhi << PNG_HISHIFT) + lmlo; + } +#endif + + for (i = 0, rp = row_buf + 1, dp = png_ptr->avg_row + 1, + pp = prev_row + 1; i < bpp; i++) + { + v = *dp++ = (png_byte)(((int)*rp++ - ((int)*pp++ / 2)) & 0xff); + + sum += (v < 128) ? v : 256 - v; + } + for (lp = row_buf + 1; i < row_bytes; i++) + { + v = *dp++ = + (png_byte)(((int)*rp++ - (((int)*pp++ + (int)*lp++) / 2)) & 0xff); + + sum += (v < 128) ? v : 256 - v; + + if (sum > lmins) /* We are already worse, don't continue. */ + break; + } + +#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED + if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED) + { + int j; + png_uint_32 sumhi, sumlo; + sumlo = sum & PNG_LOMASK; + sumhi = (sum >> PNG_HISHIFT) & PNG_HIMASK; + + for (j = 0; j < num_p_filters; j++) + { + if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_NONE) + { + sumlo = (sumlo * png_ptr->filter_weights[j]) >> + PNG_WEIGHT_SHIFT; + sumhi = (sumhi * png_ptr->filter_weights[j]) >> + PNG_WEIGHT_SHIFT; + } + } + + sumlo = (sumlo * png_ptr->filter_costs[PNG_FILTER_VALUE_AVG]) >> + PNG_COST_SHIFT; + sumhi = (sumhi * png_ptr->filter_costs[PNG_FILTER_VALUE_AVG]) >> + PNG_COST_SHIFT; + + if (sumhi > PNG_HIMASK) + sum = PNG_MAXSUM; + else + sum = (sumhi << PNG_HISHIFT) + sumlo; + } +#endif + + if (sum < mins) + { + mins = sum; + best_row = png_ptr->avg_row; + } + } + + /* Paeth filter */ + if (filter_to_do == PNG_FILTER_PAETH) + { + png_bytep rp, dp, pp, cp, lp; + png_uint_32 i; + for (i = 0, rp = row_buf + 1, dp = png_ptr->paeth_row + 1, + pp = prev_row + 1; i < bpp; i++) + { + *dp++ = (png_byte)(((int)*rp++ - (int)*pp++) & 0xff); + } + + for (lp = row_buf + 1, cp = prev_row + 1; i < row_bytes; i++) + { + int a, b, c, pa, pb, pc, p; + + b = *pp++; + c = *cp++; + a = *lp++; + + p = b - c; + pc = a - c; + +#ifdef PNG_USE_ABS + pa = abs(p); + pb = abs(pc); + pc = abs(p + pc); +#else + pa = p < 0 ? -p : p; + pb = pc < 0 ? -pc : pc; + pc = (p + pc) < 0 ? -(p + pc) : p + pc; +#endif + + p = (pa <= pb && pa <=pc) ? a : (pb <= pc) ? b : c; + + *dp++ = (png_byte)(((int)*rp++ - p) & 0xff); + } + best_row = png_ptr->paeth_row; + } + + else if (filter_to_do & PNG_FILTER_PAETH) + { + png_bytep rp, dp, pp, cp, lp; + png_uint_32 sum = 0, lmins = mins; + png_uint_32 i; + int v; + +#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED + if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED) + { + int j; + png_uint_32 lmhi, lmlo; + lmlo = lmins & PNG_LOMASK; + lmhi = (lmins >> PNG_HISHIFT) & PNG_HIMASK; + + for (j = 0; j < num_p_filters; j++) + { + if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_PAETH) + { + lmlo = (lmlo * png_ptr->inv_filter_weights[j]) >> + PNG_WEIGHT_SHIFT; + lmhi = (lmhi * png_ptr->inv_filter_weights[j]) >> + PNG_WEIGHT_SHIFT; + } + } + + lmlo = (lmlo * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_PAETH]) >> + PNG_COST_SHIFT; + lmhi = (lmhi * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_PAETH]) >> + PNG_COST_SHIFT; + + if (lmhi > PNG_HIMASK) + lmins = PNG_MAXSUM; + else + lmins = (lmhi << PNG_HISHIFT) + lmlo; + } +#endif + + for (i = 0, rp = row_buf + 1, dp = png_ptr->paeth_row + 1, + pp = prev_row + 1; i < bpp; i++) + { + v = *dp++ = (png_byte)(((int)*rp++ - (int)*pp++) & 0xff); + + sum += (v < 128) ? v : 256 - v; + } + + for (lp = row_buf + 1, cp = prev_row + 1; i < row_bytes; i++) + { + int a, b, c, pa, pb, pc, p; + + b = *pp++; + c = *cp++; + a = *lp++; + +#ifndef PNG_SLOW_PAETH + p = b - c; + pc = a - c; +#ifdef PNG_USE_ABS + pa = abs(p); + pb = abs(pc); + pc = abs(p + pc); +#else + pa = p < 0 ? -p : p; + pb = pc < 0 ? -pc : pc; + pc = (p + pc) < 0 ? -(p + pc) : p + pc; +#endif + p = (pa <= pb && pa <=pc) ? a : (pb <= pc) ? b : c; +#else /* PNG_SLOW_PAETH */ + p = a + b - c; + pa = abs(p - a); + pb = abs(p - b); + pc = abs(p - c); + if (pa <= pb && pa <= pc) + p = a; + else if (pb <= pc) + p = b; + else + p = c; +#endif /* PNG_SLOW_PAETH */ + + v = *dp++ = (png_byte)(((int)*rp++ - p) & 0xff); + + sum += (v < 128) ? v : 256 - v; + + if (sum > lmins) /* We are already worse, don't continue. */ + break; + } + +#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED + if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED) + { + int j; + png_uint_32 sumhi, sumlo; + sumlo = sum & PNG_LOMASK; + sumhi = (sum >> PNG_HISHIFT) & PNG_HIMASK; + + for (j = 0; j < num_p_filters; j++) + { + if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_PAETH) + { + sumlo = (sumlo * png_ptr->filter_weights[j]) >> + PNG_WEIGHT_SHIFT; + sumhi = (sumhi * png_ptr->filter_weights[j]) >> + PNG_WEIGHT_SHIFT; + } + } + + sumlo = (sumlo * png_ptr->filter_costs[PNG_FILTER_VALUE_PAETH]) >> + PNG_COST_SHIFT; + sumhi = (sumhi * png_ptr->filter_costs[PNG_FILTER_VALUE_PAETH]) >> + PNG_COST_SHIFT; + + if (sumhi > PNG_HIMASK) + sum = PNG_MAXSUM; + else + sum = (sumhi << PNG_HISHIFT) + sumlo; + } +#endif + + if (sum < mins) + { + best_row = png_ptr->paeth_row; + } + } +#endif /* PNG_WRITE_FILTER_SUPPORTED */ + /* Do the actual writing of the filtered row data from the chosen filter. */ + + png_write_filtered_row(png_ptr, best_row); + +#ifdef PNG_WRITE_FILTER_SUPPORTED +#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED + /* Save the type of filter we picked this time for future calculations */ + if (png_ptr->num_prev_filters > 0) + { + int j; + for (j = 1; j < num_p_filters; j++) + { + png_ptr->prev_filters[j] = png_ptr->prev_filters[j - 1]; + } + png_ptr->prev_filters[j] = best_row[0]; + } +#endif +#endif /* PNG_WRITE_FILTER_SUPPORTED */ +} + + +/* Do the actual writing of a previously filtered row. */ +void /* PRIVATE */ +png_write_filtered_row(png_structp png_ptr, png_bytep filtered_row) +{ + png_debug(1, "in png_write_filtered_row"); + + png_debug1(2, "filter = %d", filtered_row[0]); + /* Set up the zlib input buffer */ + + png_ptr->zstream.next_in = filtered_row; + png_ptr->zstream.avail_in = (uInt)png_ptr->row_info.rowbytes + 1; + /* Repeat until we have compressed all the data */ + do + { + int ret; /* Return of zlib */ + + /* Compress the data */ + ret = deflate(&png_ptr->zstream, Z_NO_FLUSH); + /* Check for compression errors */ + if (ret != Z_OK) + { + if (png_ptr->zstream.msg != NULL) + png_error(png_ptr, png_ptr->zstream.msg); + else + png_error(png_ptr, "zlib error"); + } + + /* See if it is time to write another IDAT */ + if (!(png_ptr->zstream.avail_out)) + { + /* Write the IDAT and reset the zlib output buffer */ + png_write_IDAT(png_ptr, png_ptr->zbuf, png_ptr->zbuf_size); + png_ptr->zstream.next_out = png_ptr->zbuf; + png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size; + } + /* Repeat until all data has been compressed */ + } while (png_ptr->zstream.avail_in); + + /* Swap the current and previous rows */ + if (png_ptr->prev_row != NULL) + { + png_bytep tptr; + + tptr = png_ptr->prev_row; + png_ptr->prev_row = png_ptr->row_buf; + png_ptr->row_buf = tptr; + } + + /* Finish row - updates counters and flushes zlib if last row */ + png_write_finish_row(png_ptr); + +#ifdef PNG_WRITE_FLUSH_SUPPORTED + png_ptr->flush_rows++; + + if (png_ptr->flush_dist > 0 && + png_ptr->flush_rows >= png_ptr->flush_dist) + { + png_write_flush(png_ptr); + } +#endif +} +#endif /* PNG_WRITE_SUPPORTED */ diff --git a/src/myth.cpp b/src/myth.cpp new file mode 100644 index 00000000..65472b6e --- /dev/null +++ b/src/myth.cpp @@ -0,0 +1,901 @@ +/* This code comes from MythTV. +For now, integration with ccextractor is a quick hack. It could get better with time. */ + +#include "ccextractor.h" +#include "608.h" +#ifdef _WIN32 +#include +#else +#include +#endif +#include +#include +#include +#include + +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() +{ + unsigned char a,b; + buffered_read_byte (&a); + past++; + buffered_read_byte (&b); + past++; + return (a<<8) | b; +} + +int get_byte () +{ + unsigned char b; + buffered_read_byte(&b); + if (result==1) + { + past++; + return b; + } + else + return 0; +} + +unsigned int get_be32() +{ + unsigned int val; + val = get_be16() << 16; + val |= get_be16(); + return val; +} + + +static LLONG get_pts(int c) +{ + LLONG pts; + int val; + + if (c < 0) + c = get_byte(); + pts = LLONG((c >> 1) & 0x07) << 30; + val = get_be16(); + pts |= LLONG (val >> 1) << 15; + val = get_be16(); + pts |= LLONG (val >> 1); + return pts; +} + +static int find_next_start_code(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; + buffered_read_byte (&cx); + if (result!=1) + break; + 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 (int length) +{ + buffered_seek (length); + past+=length; +} + +static long mpegps_psm_parse(void) +{ + int psm_length, ps_info_length, es_map_length; + + psm_length = get_be16(); + get_byte(); + get_byte(); + ps_info_length = get_be16(); + + /* skip program_stream_info */ + url_fskip(ps_info_length); + es_map_length = get_be16(); + + /* at least one es available? */ + while (es_map_length >= 4) + { + unsigned char type = (unsigned char) get_byte(); + unsigned char es_id =(unsigned char) get_byte(); + unsigned int es_info_length = get_be16(); + /* remember mapping from stream id to stream type */ + psm_es_type[es_id] = type; + /* skip program_stream_info */ + url_fskip(es_info_length); + es_map_length -= 4 + es_info_length; + } + get_be32(); /* crc32 */ + return 2 + psm_length; +} + + +static int mpegps_read_pes_header(int *pstart_code, + LLONG *ppts, LLONG *pdts) +{ + int len, size, startcode, c, flags, header_len; + LLONG pts, dts, last_pos; + + last_pos = -1; +redo: + /* next start code (should be immediately after) */ + header_state = 0xff; + size = MAX_SYNC_SIZE; + startcode = find_next_start_code(&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(); + // url_fskip(len); + goto redo; + } + position_sanity_check(); + if (startcode == PROGRAM_STREAM_MAP) + { + mpegps_psm_parse(); + goto redo; + } + + /* find matching stream */ + if (!((startcode >= 0x1c0 && startcode <= 0x1df) || + (startcode >= 0x1e0 && startcode <= 0x1ef) || + (startcode == 0x1bd))) + goto redo; + + len = get_be16(); + pts = AV_NOPTS_VALUE; + dts = AV_NOPTS_VALUE; + position_sanity_check(); + /* stuffing */ + for(;;) { + if (len < 1) + goto redo; + c = get_byte(); + 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(); + c = get_byte(); + len -= 2; + } + position_sanity_check(); + if ((c & 0xf0) == 0x20) { + if (len < 4) + goto redo; + dts = pts = get_pts( c); + len -= 4; + } else if ((c & 0xf0) == 0x30) { + if (len < 9) + goto redo; + pts = get_pts(c); + dts = get_pts(-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(); + header_len = get_byte(); + len -= 2; + if (header_len > len) + goto redo; + if ((flags & 0xc0) == 0x80) { + dts = pts = get_pts(-1); + if (header_len < 5) + goto redo; + header_len -= 5; + len -= 5; + } if ((flags & 0xc0) == 0xc0) { + pts = get_pts( -1); + dts = get_pts( -1); + if (header_len < 10) + goto redo; + header_len -= 10; + len -= 10; + } + len -= header_len; + while (header_len > 0) { + get_byte(); + 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(); + len--; + if (startcode >= 0x80 && startcode <= 0xbf) { + /* audio: skip header */ + if (len < 3) + goto redo; + get_byte(); + get_byte(); + get_byte(); + 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() +{ + + const unsigned char *meat = av.data; + if (meat==NULL) + { + mprint ("Warning: ProcessVBIDataPacket called with NULL data, ignoring.\n"); + return; + } + + LLONG linemask = 0; + // 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(ccdata); +// processed_ccblocks++; // Not sure this is accurate + } + else + { + ccdata[0]=0x05; // Field 1 + ccdata[1]=meat[1]; + ccdata[2]=meat[2]; + do_cb(ccdata); + } + } + // 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(void) +{ + LLONG pts, dts; + + int len, startcode, type, codec_id = 0, es_type; +redo: + len = mpegps_read_pes_header(&startcode, &pts, &dts); + if (len < 0) + return len; + position_sanity_check(); + /* now find stream */ + /* + for(i=0;inb_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 (buf,8); + past+=8; + // get_buffer(&s->pb, buf, 8); + buffered_seek (-8); + 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(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(); // emphasis (1), muse(1), reserved(1), frame number(5) + get_byte(); // quant (2), freq(2), reserved(1), channels(3) + get_byte(); // 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 (av.data,av.size); + 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(void) +{ + int rc; + int has_vbi=0; + + av.data=NULL; + ccx_options.buffer_input = 1; + 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"); + LLONG saved=0; + + while (!processed_enough && (rc=mpegps_read_packet ())==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 (); + } + /* 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(desp,length); + 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 (last_reported_progress!=th) + { + activity_progress (-1, cur_sec/60, cur_sec%60); + last_reported_progress = th; + } + } + else + { + if (total_inputsize>0 ) + { + int progress = (int) ((((total_past+past)>>8)*100)/(total_inputsize>>8)); + if (last_reported_progress != progress) + { + int cur_sec = (int) (get_fts() / 1000); + activity_progress (progress, cur_sec/60, cur_sec%60); + + fflush (stdout); + last_reported_progress = progress; + } + } + } + } + if (desp) + free (desp); + free (av.data); +} diff --git a/src/output.cpp b/src/output.cpp new file mode 100644 index 00000000..bd0878e8 --- /dev/null +++ b/src/output.cpp @@ -0,0 +1,240 @@ +#include "ccextractor.h" +#ifdef _WIN32 +#include +#else +#include +#endif + + +void init_write (struct ccx_s_write *wb, int field) +{ + memset(wb, 0, sizeof(struct ccx_s_write)); + wb->fh=-1; + wb->filename=NULL; + wb->data608=(struct eia608 *) malloc (sizeof (struct eia608)); + wb->bytes_processed_608=0; + wb->my_field=field; + init_eia608 (wb->data608); +} + +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 ccx_s_write *wb) +{ + // Don't do anything for empty data + if (data==NULL) + return; + + if (ccx_options.write_format==CCX_OF_RAW || ccx_options.write_format==CCX_OF_DVDRAW) + { + if (wb) + writeraw (data,length,wb); + } + 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,wb); + else + fatal(EXIT_BUG_BUG, "Should not be reached!"); +} + +void flushbuffer (struct ccx_s_write *wb, int closefile) +{ + if (closefile && wb!=NULL && wb->fh!=-1 && !cc_to_stdout) + close (wb->fh); +} + +void writeDVDraw (const unsigned char *data1, int length1, + const unsigned char *data2, int length2, + ccx_s_write *wb) +{ + /* these are only used by DVD raw mode: */ + static int loopcount = 1; /* loop 1: 5 elements, loop 2: 8 elements, + loop 3: 11 elements, rest: 15 elements */ + static int datacount = 0; /* counts within loop */ + + if (datacount==0) + { + write (wb->fh,DVD_HEADER,sizeof (DVD_HEADER)); + if (loopcount==1) + write (wb->fh,lc1,sizeof (lc1)); + if (loopcount==2) + write (wb->fh,lc2,sizeof (lc2)); + if (loopcount==3) + { + write (wb->fh,lc3,sizeof (lc3)); + if (data2 && length2) + write (wb->fh,data2,length2); + } + if (loopcount>3) + { + write (wb->fh,lc4,sizeof (lc4)); + if (data2 && length2) + write (wb->fh,data2,length2); + } + } + datacount++; + write (wb->fh,lc5,sizeof (lc5)); + if (data1 && length1) + write (wb->fh,data1,length1); + if (((loopcount == 1) && (datacount < 5)) || ((loopcount == 2) && + (datacount < 8)) || (( loopcount == 3) && (datacount < 11)) || + ((loopcount > 3) && (datacount < 15))) + { + write (wb->fh,lc6,sizeof (lc6)); + if (data2 && length2) + write (wb->fh,data2,length2); + } + else + { + if (loopcount==1) + { + write (wb->fh,lc6,sizeof (lc6)); + 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) +{ + if (ccx_options.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,&wbout1); + } + if (length2) + { + if (ccx_options.extract!=1) + writedata (data2,length2,&wbout2); + else // User doesn't want field 2 data, but we want XDS. + writedata (data2,length2,NULL); + } + } +} + +/* Buffer data with the same FTS and write when a new FTS or data==NULL + * is encountered */ +void writercwtdata (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]; + + if ( (prevfts != currfts && prevfts != -1) + || data == NULL + || cbcount == 0xFFFF) + { + // Remove trailing empty or 608 padding caption blocks + if ( cbcount != 0xFFFF) + { + unsigned char cc_valid; + unsigned char cc_type; + int storecbcount=cbcount; + + for( int cb = cbcount-1; cb >= 0 ; cb-- ) + { + cc_valid = (*(cbbuffer+3*cb) & 4) >>2; + cc_type = *(cbbuffer+3*cb) & 3; + + // The -fullbin option disables pruning of 608 padding blocks + if ( (cc_valid && cc_type <= 1 // Only skip NTSC padding packets + && !ccx_options.fullbin // Unless we want to keep them + && *(cbbuffer+3*cb+1)==0x80 + && *(cbbuffer+3*cb+2)==0x80) + || !(cc_valid || cc_type==3) ) // or unused packets + { + cbcount--; + } + else + { + cb = -1; + } + } + dbg_print(CCX_DMT_CBRAW, "%s Write %d RCWT blocks - skipped %d padding / %d unused blocks.\n", + print_mstime(prevfts), cbcount, storecbcount - cbcount, cbempty); + } + + // New FTS, write data header + // RCWT data header (10 bytes): + //byte(s) value description + //0-7 FTS int64_t number with current FTS + //8-9 blocks Number of 3 byte data blocks with the same FTS that are + // following this header + memcpy(cbheader,&prevfts,8); + memcpy(cbheader+8,&cbcount,2); + + if (cbcount > 0) + { + writeraw(cbheader,10,&wbout1); + writeraw(cbbuffer,3*cbcount, &wbout1); + } + cbcount = 0; + cbempty = 0; + } + + if ( data ) + { + // Store the data while the FTS is unchanged + + unsigned char cc_valid = (*data & 4) >> 2; + unsigned char cc_type = *data & 3; + // Store only non-empty packets + if (cc_valid || cc_type==3) + { + // Store in buffer until we know how many blocks come with + // this FTS. + memcpy(cbbuffer+cbcount*3, data, 3); + cbcount++; + } + else + { + cbempty++; + } + } + else + { + // Write a padding block for field 1 and 2 data if this is the final + // call to this function. This forces the RCWT file to have the + // same length as the source video file. + + // currfts currently holds the end time, subtract one block length + // so that the FTS corresponds to the time before the last block is + // written + currfts -= 1001/30; + + memcpy(cbheader,&currfts,8); + cbcount = 2; + memcpy(cbheader+8,&cbcount,2); + + memcpy(cbbuffer, "\x04\x80\x80", 3); // Field 1 padding + memcpy(cbbuffer+3, "\x05\x80\x80", 3); // Field 2 padding + + writeraw(cbheader,10,&wbout1); + writeraw(cbbuffer,3*cbcount, &wbout1); + + cbcount = 0; + cbempty = 0; + + dbg_print(CCX_DMT_CBRAW, "%s Write final padding RCWT blocks.\n", + print_mstime(currfts)); + } + + prevfts = currfts; +} diff --git a/src/params.cpp b/src/params.cpp new file mode 100644 index 00000000..6aa0a148 --- /dev/null +++ b/src/params.cpp @@ -0,0 +1,1416 @@ +#include "ccextractor.h" + +static int inputfile_capacity=0; +static int spell_builtin_added=0; // so we don't do it twice + +static const char *DEF_VAL_STARTCREDITSNOTBEFORE="0"; +static const char *DEF_VAL_STARTCREDITSNOTAFTER="5:00"; // To catch the theme after the teaser in TV shows +static const char *DEF_VAL_STARTCREDITSFORATLEAST="2"; +static const char *DEF_VAL_STARTCREDITSFORATMOST="5"; +static const char *DEF_VAL_ENDCREDITSFORATLEAST="2"; +static const char *DEF_VAL_ENDCREDITSFORATMOST="5"; + +// 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 stringztoms (const char *s, ccx_boundary_time *bt) +{ + unsigned ss=0, mm=0, hh=0; + int value=-1; + int colons=0; + const char *c=s; + while (*c) + { + if (*c==':') + { + if (value==-1) // : at the start, or ::, etc + return -1; + colons++; + if (colons>2) // Max 2, for HH:MM:SS + return -1; + hh=mm; + mm=ss; + ss=value; + value=-1; + } + else + { + if (!isdigit (*c)) // Only : or digits, so error + return -1; + if (value==-1) + value=*c-'0'; + else + value=value*10+*c-'0'; + } + c++; + } + hh=mm; + mm=ss; + ss=value; + if (mm>59 || ss>59) + return -1; + bt->set=1; + bt->hh=hh; + bt->mm=mm; + bt->ss=ss; + LLONG secs=(hh*3600+mm*60+ss); + bt->time_in_ms=secs*1000; + return 0; +} + + +int add_word (const char *word) +{ + char *new_lower; + char *new_correct; + if (spell_words==spell_capacity) + { + // Time to grow + spell_capacity+=50; + spell_lower=(char **) realloc (spell_lower, sizeof (char *) * + spell_capacity); + spell_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 (spell_lower==NULL || spell_correct==NULL || + new_lower==NULL || new_correct==NULL) + { + return -1; + } + strcpy (new_correct, word); + for (size_t i=0; i=line && (*c==0xd || *c==0xa)) + { + *c=0; + c--; + } + if (strlen (line)>32) + { + mprint ("Word in line %d too long, max = 32 characters.\n",num); + fclose (fi); + return -1; + } + if (strlen (line)>0) + { + if (add_word (line)) + return -1; + } + } + fclose (fi); + return 0; +} + +int isanumber (char *s) +{ + while (*s) + { + if (!isdigit (*s)) + return 0; + s++; + } + return 1; +} + +int parsedelay (char *par) +{ + int sign=0; + char *c=par; + while (*c) + { + if (*c=='-' || *c=='+') + { + if (c!=par) // Sign only at the beginning + return 1; + if (*c=='-') + sign=1; + } + else + { + if (!isdigit (*c)) + return 1; + subs_delay=subs_delay*10 + (*c-'0'); + } + c++; + } + if (sign) + subs_delay=-subs_delay; + return 0; +} + +int append_file_to_queue (char *filename) +{ + char *c=(char *) malloc (strlen (filename)+1); + if (c==NULL) + return -1; + strcpy (c,filename); + if (inputfile_capacity<=num_input_files) + { + inputfile_capacity+=10; + inputfile=(char **) realloc (inputfile,sizeof (char *) * inputfile_capacity); + if (inputfile==NULL) + return -1; + } + inputfile[num_input_files]=c; + num_input_files++; + return 0; +} + +int add_file_sequence (char *filename) +{ + int m,n; + n=strlen (filename)-1; + // Look for the last digit in filename + while (n>=0 && !isdigit (filename[n])) + n--; + if (n==-1) // None. No expansion needed + return append_file_to_queue(filename); + m=n; + while (m>=0 && isdigit (filename[m])) + m--; + m++; + // Here: Significant digits go from filename[m] to filename[n] + char *num=(char *) malloc (n-m+2); + strncpy (num,filename+m, n-m+1); + num[n-m+1]=0; + int i = atoi (num); + char *temp=(char *) malloc (n-m+3); // For overflows + // printf ("Expanding %d to %d, initial value=%d\n",m,n,i); + for (;;) + { + FILE *f=fopen (filename,"r"); + if (f==NULL) // Doesn't exist or we can't read it. We're done + break; + fclose (f); + if (append_file_to_queue (filename)) // Memory panic + return -1; + i++; + sprintf (temp,"%d",i); + if (strlen (temp)>strlen (num)) // From 999 to 1000, etc. + break; + strncpy (filename+m+(strlen (num)-strlen (temp)),temp,strlen (temp)); + memset (filename+m,'0',strlen (num)-strlen (temp)); + } + return 0; +} + +void set_output_format (const char *format) +{ + while (*format=='-') + format++; + if (strcmp (format,"srt")==0) + ccx_options.write_format=CCX_OF_SRT; + else if (strcmp (format,"sami")==0 || strcmp (format,"smi")==0) + ccx_options.write_format=CCX_OF_SAMI; + else if (strcmp (format,"transcript")==0 || strcmp (format,"txt")==0) + { + ccx_options.write_format=CCX_OF_TRANSCRIPT; + timestamps_on_transcript=0; + } + else if (strcmp (format,"timedtranscript")==0 || strcmp (format,"ttxt")==0) + { + ccx_options.write_format=CCX_OF_TRANSCRIPT; + if (ccx_options.date_format==ODF_NONE) + ccx_options.date_format=ODF_HHMMSSMS; + timestamps_on_transcript=1; + } + else if (strcmp (format,"raw")==0) + ccx_options.write_format=CCX_OF_RAW; + else if (strcmp (format, "smptett")==0) + ccx_options.write_format=CCX_OF_SMPTETT ; + else if (strcmp (format,"bin")==0) + ccx_options.write_format=CCX_OF_RCWT; + else if (strcmp (format,"null")==0) + ccx_options.write_format=CCX_OF_NULL; + else if (strcmp (format,"dvdraw")==0) + ccx_options.write_format=CCX_OF_DVDRAW; + else if (strcmp (format,"spupng")==0) + ccx_options.write_format=CCX_OF_SPUPNG; + else + fatal (EXIT_MALFORMED_PARAMETER, "Unknown output file format: %s\n", format); +} + +void set_input_format (const char *format) +{ + while (*format=='-') + format++; + if (strcmp (format,"es")==0) // Does this actually do anything? + auto_stream = CCX_SM_ELEMENTARY_OR_NOT_FOUND; + else if (strcmp (format,"ts")==0) + auto_stream = CCX_SM_TRANSPORT; + else if (strcmp (format,"ps")==0 || strcmp (format,"nots")==0) + auto_stream = CCX_SM_PROGRAM; + else if (strcmp (format,"asf")==0 || strcmp (format,"dvr-ms")==0) + auto_stream = CCX_SM_ASF; + else if (strcmp (format,"wtv")==0) + auto_stream = CCX_SM_WTV; + else if (strcmp (format,"raw")==0) + auto_stream = CCX_SM_MCPOODLESRAW; + else if (strcmp (format,"bin")==0) + auto_stream = CCX_SM_RCWT; + else if (strcmp (format,"mp4")==0) + auto_stream = CCX_SM_MP4; + else if (strcmp (format,"hex")==0) + auto_stream = CCX_SM_HEX_DUMP; + else + fatal (EXIT_MALFORMED_PARAMETER, "Unknown input file format: %s\n", format); +} + +void usage (void) +{ + mprint ("Originally based on McPoodle's tools. Check his page for lots of information\n"); + mprint ("on closed captions technical details.\n"); + mprint ("(http://www.theneitherworld.com/mcpoodle/SCC_TOOLS/DOCS/SCC_TOOLS.HTML)\n\n"); + mprint ("This tool home page:\n"); + mprint ("http://www.ccextractor.org\n"); + mprint (" Extracts closed captions and teletext subtitles from video streams.\n"); + mprint (" (DVB, .TS, ReplayTV 4000 and 5000, dvr-ms, bttv, Tivo, Dish Network,\n"); + mprint (" .mp4, HDHomeRun are known to work).\n\n"); + mprint (" Syntax:\n"); + mprint (" ccextractor [options] inputfile1 [inputfile2...] [-o outputfilename]\n"); + mprint (" [-o1 outputfilename1] [-o2 outputfilename2]\n\n"); + mprint ("File name related options:\n"); + mprint (" inputfile: file(s) to process\n"); + mprint (" -o outputfilename: Use -o parameters to define output filename if you don't\n"); + mprint (" like the default ones (same as infile plus _1 or _2 when\n"); + mprint (" needed and file extension, e.g. .srt).\n"); + mprint (" -o or -o1 -> Name of the first (maybe only) output\n"); + mprint (" file.\n"); + mprint (" -o2 -> Name of the second output file, when\n"); + mprint (" it applies.\n"); + mprint (" -cf filename: Write 'clean' data to a file. Cleans means the ES\n"); + mprint (" without TS or PES headers.\n"); + mprint (" -stdout: Write output to stdout (console) instead of file. If\n"); + mprint (" stdout is used, then -o, -o1 and -o2 can't be used. Also\n"); + mprint (" -stdout will redirect all messages to stderr (error).\n\n"); + mprint ("You can pass as many input files as you need. They will be processed in order.\n"); + mprint ("If a file name is suffixed by +, ccextractor will try to follow a numerical\n"); + mprint ("sequence. For example, DVD001.VOB+ means DVD001.VOB, DVD002.VOB and so on\n"); + mprint ("until there are no more files.\n"); + mprint ("Output will be one single file (either raw or srt). Use this if you made your\n"); + mprint ("recording in several cuts (to skip commercials for example) but you want one\n"); + mprint ("subtitle file with contiguous timing.\n\n"); + mprint ("Network support:\n"); + mprint (" -udp port: Read the input via UDP (listening in the specified port)\n"); + mprint (" instead of reading a file.\n\n"); + mprint (" -udp [host:]port: Read the input via UDP (listening in the specified\n"); + mprint (" port) instead of reading a file. Host can be a\n"); + mprint (" hostname or IPv4 address. If host is not specified\n"); + mprint (" then listens on the local host.\n\n"); + mprint ("Options that affect what will be processed:\n"); + mprint (" -1, -2, -12: Output Field 1 data, Field 2 data, or both\n"); + mprint (" (DEFAULT is -1)\n"); + mprint (" -cc2: When in srt/sami mode, process captions in channel 2\n"); + mprint (" instead channel 1.\n"); + mprint ("-svc --service N,N...: Enabled CEA-708 captions processing for the listed\n"); + mprint (" services. The parameter is a command delimited list\n"); + mprint (" of services numbers, such as \"1,2\" to process the\n"); + mprint (" primary and secondary language services.\n"); + mprint ("In general, if you want English subtitles you don't need to use these options\n"); + mprint ("as they are broadcast in field 1, channel 1. If you want the second language\n"); + mprint ("(usually Spanish) you may need to try -2, or -cc2, or both.\n\n"); + mprint ("Input formats:\n"); + mprint (" With the exception of McPoodle's raw format, which is just the closed\n"); + mprint (" caption data with no other info, CCExtractor can usually detect the\n"); + mprint (" input format correctly. To force a specific format:\n\n"); + mprint (" -in=format\n\n"); + mprint (" where format is one of these:\n"); + mprint (" ts -> For Transport Streams.\n"); + mprint (" ps -> For Program Streams.\n"); + mprint (" es -> For Elementary Streams.\n"); + mprint (" asf -> ASF container (such as DVR-MS).\n"); + mprint (" wtv -> Windows Television (WTV)\n"); + mprint (" bin -> CCExtractor's own binary format.\n"); + mprint (" raw -> For McPoodle's raw files.\n"); + mprint (" mp4 -> MP4/MOV/M4V and similar.\n"); + mprint (" hex -> Hexadecimal dump as generated by wtvccdump.\n"); + mprint (" -ts, -ps, -es, -mp4, -wtv and -asf (or --dvr-ms) can be used as shorts.\n\n"); + mprint ("Output formats:\n\n"); + mprint (" -out=format\n\n"); + mprint (" where format is one of these:\n"); + mprint (" srt -> SubRip (default, so not actually needed).\n"); + mprint (" sami -> MS Synchronized Accesible Media Interface.\n"); + mprint (" bin -> CC data in CCExtractor's own binary format.\n"); + mprint (" raw -> CC data in McPoodle's Broadcast format.\n"); + mprint (" dvdraw -> CC data in McPoodle's DVD format.\n"); + mprint (" txt -> Transcript (no time codes, no roll-up\n"); + mprint (" captions, just the plain transcription.\n"); + mprint (" ttxt -> Timed Transcript (transcription with time\n"); + mprint (" info)\n"); + mprint (" smptett -> SMPTE Timed Text (W3C TTML) format.\n"); + mprint (" spupng -> Set of .xml and .png files for use with\n"); + mprint (" dvdauthor's spumux.\n"); + mprint (" See \"Notes on spupng output format\"\n"); + + mprint (" null -> Don't produce any file output\n\n"); + mprint (" Note: Teletext output can only be srt, txt or ttxt for now.\n\n"); + + mprint ("Options that affect how input files will be processed.\n"); + + mprint (" -gt --goptime: Use GOP for timing instead of PTS. This only applies\n"); + mprint (" to Program or Transport Streams with MPEG2 data and\n"); + mprint (" overrides the default PTS timing.\n"); + mprint (" GOP timing is always used for Elementary Streams.\n"); + mprint (" -nogt --nogoptime: Never use GOP timing (use PTS), even if ccextractor\n"); + mprint (" detects GOP timing is the reasonable choice.\n"); + mprint (" -fp --fixpadding: Fix padding - some cards (or providers, or whatever)\n"); + mprint (" seem to send 0000 as CC padding instead of 8080. If you\n"); + mprint (" get bad timing, this might solve it.\n"); + mprint (" -90090: Use 90090 (instead of 90000) as MPEG clock frequency.\n"); + mprint (" (reported to be needed at least by Panasonic DMR-ES15\n"); + mprint (" DVD Recorder)\n"); + mprint (" -ve --videoedited: By default, ccextractor will process input files in\n"); + mprint (" sequence as if they were all one large file (i.e.\n"); + mprint (" split by a generic, non video-aware tool. If you\n"); + mprint (" are processing video hat was split with a editing\n"); + mprint (" tool, use -ve so ccextractor doesn't try to rebuild\n"); + mprint (" the original timing.\n"); + mprint (" -s --stream [secs]: Consider the file as a continuous stream that is\n"); + mprint (" growing as ccextractor processes it, so don't try\n"); + mprint (" to figure out its size and don't terminate processing\n"); + mprint (" when reaching the current end (i.e. wait for more\n"); + mprint (" data to arrive). If the optional parameter secs is\n"); + mprint (" present, it means the number of seconds without any\n"); + mprint (" new data after which ccextractor should exit. Use\n"); + mprint (" this parameter if you want to process a live stream\n"); + mprint (" but not kill ccextractor externally.\n"); + mprint (" Note: If -s is used then only one input file is\n"); + mprint (" allowed.\n"); + mprint (" -poc --usepicorder: Use the pic_order_cnt_lsb in AVC/H.264 data streams\n"); + mprint (" to order the CC information. The default way is to\n"); + mprint (" use the PTS information. Use this switch only when\n"); + mprint (" needed.\n"); + mprint (" -myth: Force MythTV code branch.\n"); + mprint (" -nomyth: Disable MythTV code branch.\n"); + mprint (" The MythTV branch is needed for analog captures where\n"); + mprint (" the closed caption data is stored in the VBI, such as\n"); + mprint (" those with bttv cards (Hauppage 250 for example). This\n"); + mprint (" is detected automatically so you don't need to worry\n"); + mprint (" about this unless autodetection doesn't work for you.\n"); + mprint (" -wtvconvertfix: This switch works around a bug in Windows 7's built in\n"); + mprint (" software to convert *.wtv to *.dvr-ms. For analog NTSC\n"); + mprint (" recordings the CC information is marked as digital\n"); + mprint (" captions. Use this switch only when needed.\n"); + mprint (" -wtvmpeg2: Read the captions from the MPEG2 video stream rather\n"); + mprint (" than the captions stream in WTV files\n"); + mprint (" -pn --program-number: In TS mode, specifically select a program to process.\n"); + mprint (" Not needed if the TS only has one. If this parameter\n"); + mprint (" is not specified and CCExtractor detects more than one\n"); + mprint (" program in the input, it will list the programs found\n"); + mprint (" and terminate without doing anything, unless\n"); + mprint (" -autoprogram (see below) is used.\n"); + mprint (" -autoprogram: If there's more than one program in the stream, just use\n"); + mprint (" the first one we find that contains a suitable stream.\n"); + mprint (" -datapid: Don't try to find out the stream for caption/teletext\n"); + mprint (" data, just use this one instead.\n"); + mprint (" -datastreamtype: Instead of selecting the stream by its PID, select it\n"); + mprint (" by its type (pick the stream that has this type in\n"); + mprint (" the PMT)\n"); + mprint (" -streamtype: Assume the data is of this type, don't autodetect. This\n"); + mprint (" parameter may be needed if -datapid or -datastreamtype\n"); + mprint (" is used and CCExtractor cannot determine how to process\n"); + mprint (" the stream. The value will usually be 2 (MPEG video) or\n"); + mprint (" 6 (MPEG private data).\n"); + mprint (" -haup --hauppauge: If the video was recorder using a Hauppauge card, it\n"); + mprint (" might need special processing. This parameter will\n"); + mprint (" force the special treatment.\n"); + mprint (" -mp4vidtrack: In MP4 files the closed caption data can be embedded in\n"); + mprint (" the video track or in a dedicated CC track. If a\n"); + mprint (" dedicated track is detected it will be processed instead\n"); + mprint (" of the video track. If you need to force the video track\n"); + mprint (" to be processed instead use this option.\n"); + mprint (" -noautotimeref: Some streams come with broadcast date information. When\n"); + mprint (" such data is available, CCExtractor will set its time\n"); + mprint (" reference to the received data. Use this parameter if\n"); + mprint (" you prefer your own reference. Note: Current this only\n"); + mprint (" affects Teletext in timed transcript with -datets.\n"); + mprint ("\n"); + mprint ("Options that affect what kind of output will be produced:\n"); + mprint (" -unicode: Encode subtitles in Unicode instead of Latin-1.\n"); + mprint (" -utf8: Encode subtitles in UTF-8 (no longer needed.\n"); + mprint (" because UTF-8 is now the default).\n"); + mprint (" -latin1: Encode subtitles in UTF-8 instead of Latin-1\n"); + mprint (" -nofc --nofontcolor: For .srt/.sami, don't add font color tags.\n"); + mprint ("-nots --notypesetting: For .srt/.sami, don't add typesetting tags.\n"); + mprint (" -trim: Trim lines.\n"); + mprint (" -dc --defaultcolor: Select a different default color (instead of\n"); + mprint (" white). This causes all output in .srt/.smi\n"); + mprint (" files to have a font tag, which makes the files\n"); + mprint (" larger. Add the color you want in RGB, such as\n"); + mprint (" -dc #FF0000 for red.\n"); + mprint (" -sc --sentencecap: Sentence capitalization. Use if you hate\n"); + mprint (" ALL CAPS in subtitles.\n"); + mprint (" --capfile -caf file: Add the contents of 'file' to the list of words\n"); + mprint (" that must be capitalized. For example, if file\n"); + mprint (" is a plain text file that contains\n\n"); + mprint (" Tony\n"); + mprint (" Alan\n\n"); + mprint (" Whenever those words are found they will be written\n"); + mprint (" exactly as they appear in the file.\n"); + mprint (" Use one line per word. Lines starting with # are\n"); + mprint (" considered comments and discarded.\n\n"); + mprint (" -unixts REF: For timed transcripts that have an absolute date\n"); + mprint (" instead of a timestamp relative to the file start), use\n"); + mprint (" this time reference (UNIX timestamp). 0 => Use current\n"); + mprint (" system time.\n"); + mprint (" ccextractor will automatically switch to transport\n"); + mprint (" stream UTC timestamps when available.\n"); + mprint (" -datets: In transcripts, write time as YYYYMMDDHHMMss,ms.\n"); + mprint (" -sects: In transcripts, write time as ss,ms\n"); + mprint (" -UCLA: Transcripts are generated with a specific format\n"); + mprint (" that is convenient for a specific project, feel\n"); + mprint (" free to play with it but be aware that this format\n"); + mprint (" is really live - don't rely on its output format\n"); + mprint (" not changing between versions.\n"); + mprint (" -lf: Use LF (UNIX) instead of CRLF (DOS, Windows) as line\n"); + mprint (" terminator.\n\n"); + mprint (" -autodash: Based on position on screen, attempt to determine\n"); + mprint (" the different speakers and a dash (-) when each\n"); + mprint (" of them talks (.srt only, -trim required)."); + mprint ("Options that affect how ccextractor reads and writes (buffering):\n"); + mprint (" -bi --bufferinput: Forces input buffering.\n"); + mprint (" -nobi -nobufferinput: Disables input buffering.\n"); + mprint (" -bs --buffersize val: Specify a size for reading, in bytes (suffix with K or\n"); + mprint (" or M for kilobytes and megabytes). Default is 16M.\n"); + mprint ("\n"); + mprint ("Note: -bo is only used when writing raw files, not .srt or .sami\n\n"); + mprint ("Options that affect the built-in closed caption decoder:\n"); + mprint (" -dru: Direct Roll-Up. When in roll-up mode, write character by\n"); + mprint (" character instead of line by line. Note that this\n"); + mprint (" produces (much) larger files.\n"); + mprint (" -noru --norollup: If you hate the repeated lines caused by the roll-up\n"); + mprint (" emulation, you can have ccextractor write only one\n"); + mprint (" line at a time, getting rid of these repeated lines.\n"); + mprint (" -ru1 / ru2 / ru3: roll-up captions can consist of 2, 3 or 4 visible\n"); + mprint (" lines at any time (the number of lines is part of\n"); + mprint (" the transmission). If having 3 or 4 lines annoys\n"); + mprint (" you you can use -ru to force the decoder to always\n"); + mprint (" use 1, 2 or 3 lines. Note that 1 line is not\n"); + mprint (" a real mode rollup mode, so CCExtractor does what\n"); + mprint (" it can.\n"); + mprint (" In -ru1 the start timestamp is actually the timestamp\n"); + mprint (" of the first character received which is possibly more\n"); + mprint (" accurate.\n"); + mprint ("\n"); + mprint ("Options that affect timing:\n"); + mprint (" -delay ms: For srt/sami, add this number of milliseconds to\n"); + mprint (" all times. For example, -delay 400 makes subtitles\n"); + mprint (" appear 400ms late. You can also use negative numbers\n"); + mprint (" to make subs appear early.\n"); + mprint ("Notes on times: -startat and -endat times are used first, then -delay.\n"); + mprint ("So if you use -srt -startat 3:00 -endat 5:00 -delay 120000, ccextractor will\n"); + mprint ("generate a .srt file, with only data from 3:00 to 5:00 in the input file(s)\n"); + mprint ("and then add that (huge) delay, which would make the final file start at\n"); + mprint ("5:00 and end at 7:00.\n\n"); + mprint ("Options that affect what segment of the input file(s) to process:\n"); + mprint (" -startat time: Only write caption information that starts after the\n"); + mprint (" given time.\n"); + mprint (" Time can be seconds, MM:SS or HH:MM:SS.\n"); + mprint (" For example, -startat 3:00 means 'start writing from\n"); + mprint (" minute 3.\n"); + mprint (" -endat time: Stop processing after the given time (same format as\n"); + mprint (" -startat).\n"); + mprint (" The -startat and -endat options are honored in all\n"); + mprint (" output formats. In all formats with timing information\n"); + mprint (" the times are unchanged.\n"); + mprint ("-scr --screenfuls num: Write 'num' screenfuls and terminate processing.\n\n"); + mprint ("Adding start and end credits:\n"); + mprint (" CCExtractor can _try_ to add a custom message (for credits for example) at\n"); + mprint (" the start and end of the file, looking for a window where there are no\n"); + mprint (" captions. If there is no such window, then no text will be added.\n"); + mprint (" The start window must be between the times given and must have enough time\n"); + mprint (" to display the message for at least the specified time.\n"); + mprint (" --startcreditstext txt: Write this text as start credits. If there are\n"); + mprint (" several lines, separate them with the\n"); + mprint (" characters \\n, for example Line1\\nLine 2.\n"); + mprint (" --startcreditsnotbefore time: Don't display the start credits before this\n"); + mprint (" time (S, or MM:SS). Default: %s\n", DEF_VAL_STARTCREDITSNOTBEFORE); + mprint (" --startcreditsnotafter time: Don't display the start credits after this\n"); + mprint (" time (S, or MM:SS). Default: %s\n", DEF_VAL_STARTCREDITSNOTAFTER); + mprint (" --startcreditsforatleast time: Start credits need to be displayed for at least\n"); + mprint (" this time (S, or MM:SS). Default: %s\n", DEF_VAL_STARTCREDITSFORATLEAST); + mprint (" --startcreditsforatmost time: Start credits should be displayed for at most\n"); + mprint (" this time (S, or MM:SS). Default: %s\n", DEF_VAL_STARTCREDITSFORATMOST); + mprint (" --endcreditstext txt: Write this text as end credits. If there are\n"); + mprint (" several lines, separate them with the\n"); + mprint (" characters \\n, for example Line1\\nLine 2.\n"); + mprint (" --endcreditsforatleast time: End credits need to be displayed for at least\n"); + mprint (" this time (S, or MM:SS). Default: %s\n", DEF_VAL_ENDCREDITSFORATLEAST); + mprint (" --endcreditsforatmost time: End credits should be displayed for at most\n"); + mprint (" this time (S, or MM:SS). Default: %s\n", DEF_VAL_ENDCREDITSFORATMOST); + mprint ("\n"); + mprint ("Options that affect debug data:\n"); + mprint (" -debug: Show lots of debugging output.\n"); + mprint (" -608: Print debug traces from the EIA-608 decoder.\n"); + mprint (" If you need to submit a bug report, please send\n"); + mprint (" the output from this option.\n"); + mprint (" -708: Print debug information from the (currently\n"); + mprint (" in development) EIA-708 (DTV) decoder.\n"); + mprint (" -goppts: Enable lots of time stamp output.\n"); + mprint (" -xdsdebug: Enable XDS debug data (lots of it).\n"); + mprint (" -vides: Print debug info about the analysed elementary\n"); + mprint (" video stream.\n"); + mprint (" -cbraw: Print debug trace with the raw 608/708 data with\n"); + mprint (" time stamps.\n"); + mprint (" -nosync: Disable the syncing code. Only useful for debugging\n"); + mprint (" purposes.\n"); + mprint (" -fullbin: Disable the removal of trailing padding blocks\n"); + mprint (" when exporting to bin format. Only useful for\n"); + mprint (" for debugging purposes.\n"); + mprint (" -parsedebug: Print debug info about the parsed container\n"); + mprint (" file. (Only for TS/ASF files at the moment.)\n"); + mprint (" -parsePAT: Print Program Association Table dump.\n"); + mprint (" -parsePMT: Print Program Map Table dump.\n"); + mprint (" -investigate_packets: If no CC packets are detected based on the PMT, try\n"); + mprint (" to find data in all packets by scanning.\n\n"); + mprint ("Teletext related options:\n"); + mprint (" -tpage page: Use this page for subtitles (if this parameter\n"); + mprint (" is not used, try to autodetect). In Spain the\n"); + mprint (" page is always 888, may vary in other countries.\n"); + mprint (" -tverbose: Enable verbose mode in the teletext decoder.\n\n"); + mprint (" -teletext: Force teletext mode even if teletext is not detected.\n"); + mprint (" If used, you should also pass -datapid to specify\n"); + mprint (" the stream ID you want to process.\n"); + mprint (" -noteletext: Disable teletext processing. This might be needed\n"); + mprint (" for video streams that have both teletext packets\n"); + mprint (" and CEA-608/708 packets (if teletext is processed\n"); + mprint (" then CEA-608/708 processing is disabled).\n"); + mprint ("\n"); + mprint ("Communication with other programs and console output:\n"); + mprint (" --gui_mode_reports: Report progress and interesting events to stderr\n"); + mprint (" in a easy to parse format. This is intended to be\n"); + mprint (" used by other programs. See docs directory for.\n"); + mprint (" details.\n"); + mprint (" --no_progress_bar: Suppress the output of the progress bar\n"); + mprint (" -quiet: Don't write any message.\n"); + mprint ("\n"); + mprint ("Notes on the CEA-708 decoder: While it is starting to be useful, it's\n"); + mprint ("a work in progress. A number of things don't work yet in the decoder\n"); + mprint ("itself, and many of the auxiliary tools (case conversion to name one)\n"); + mprint ("won't do anything yet. Feel free to submit samples that cause problems\n"); + mprint ("and feature requests.\n"); + mprint ("\n"); + mprint("Notes on spupng output format:\n"); + mprint("One .xml file is created per output field. A set of .png files are created in\n"); + mprint("a directory with the same base name as the corresponding .xml file(s), but with\n"); + mprint("a .d extension. Each .png file will contain an image representing one caption\n"); + mprint("and named subNNNN.png, starting with sub0000.png.\n"); + mprint("For example, the command:\n"); + mprint(" ccextractor -out=spupng input.mpg\n"); + mprint("will create the files:\n"); + mprint(" input.xml\n"); + mprint(" input.d/sub0000.png\n"); + mprint(" input.d/sub0001.png\n"); + mprint(" ...\n"); + mprint("The command:\n"); + mprint(" ccextractor -out=spupng -o /tmp/output -12 input.mpg\n"); + mprint("will create the files:\n"); + mprint(" /tmp/output_1.xml\n"); + mprint(" /tmp/output_1.d/sub0000.png\n"); + mprint(" /tmp/output_1.d/sub0001.png\n"); + mprint(" ...\n"); + mprint(" /tmp/output_2.xml\n"); + mprint(" /tmp/output_2.d/sub0000.png\n"); + mprint(" /tmp/output_2.d/sub0001.png\n"); + mprint(" ...\n"); +} + +void parse_708services (char *s) +{ + char *c, *e, *l; + if (s==NULL) + return; + l=s+strlen (s); + for (c=s; c63) + fatal (EXIT_MALFORMED_PARAMETER, "Invalid service number (%d), valid range is 1-63."); + cea708services[svc-1]=1; + do_cea708=1; + c=e+1; + } +} + +long atol_size (char *s) +{ + long val=atoi (s); + if (toupper (s[strlen (s)-1])=='M') + val=val*1024*1024; + else if (toupper (s[strlen (s)-1])=='K') + val=val*1024; + return val; +} + +int atoi_hex (char *s) +{ + if (strlen (s)>2 && s[0]=='0' && (s[1]=='x' || s[1]=='X')) + { + // Hexadecimal + return strtol(s+2, NULL, 16); + } + else + { + return atoi (s); + } +} + +void parse_parameters (int argc, char *argv[]) +{ + char *cea708_service_list=NULL; // List CEA-708 services + + // Sensible default values for credits + stringztoms (DEF_VAL_STARTCREDITSNOTBEFORE, &ccx_options.startcreditsnotbefore); + stringztoms (DEF_VAL_STARTCREDITSNOTAFTER, &ccx_options.startcreditsnotafter); + stringztoms (DEF_VAL_STARTCREDITSFORATLEAST, &ccx_options.startcreditsforatleast); + stringztoms (DEF_VAL_STARTCREDITSFORATMOST, &ccx_options.startcreditsforatmost); + stringztoms (DEF_VAL_ENDCREDITSFORATLEAST, &ccx_options.endcreditsforatleast); + stringztoms (DEF_VAL_ENDCREDITSFORATMOST, &ccx_options.endcreditsforatmost); + + // Parse parameters + for (int i=1; ih_addrtype != AF_INET) + { + fatal(EXIT_MALFORMED_PARAMETER, "No support for non-IPv4 network addresses: %s\n", + argv[i+1]); + } + ccx_options.udpaddr = ntohl(((struct in_addr *)host->h_addr_list[0])->s_addr); + ccx_options.udpport = atoi_hex(colon + 1); + } + else + { + ccx_options.udpaddr = INADDR_ANY; + ccx_options.udpport = atoi_hex(argv[i+1]); + } + ccx_options.input_source=CCX_DS_NETWORK; + i++; + continue; + } + fatal (EXIT_INCOMPATIBLE_PARAMETERS, "Error: Parameter %s not understood.\n", argv[i]); + // Unrecognized switches are silently ignored + } +} + diff --git a/src/params_dump.cpp b/src/params_dump.cpp new file mode 100644 index 00000000..a4720df5 --- /dev/null +++ b/src/params_dump.cpp @@ -0,0 +1,202 @@ +#include "ccextractor.h" + +void params_dump(void) +{ + // Display parsed parameters + mprint ("Input: "); + switch (ccx_options.input_source) + { + case CCX_DS_FILE: + for (int i=0;i +#include +#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 uint32_t in_addr_t; +#define IN_CLASSD(i) (((INT32)(i) & 0xf0000000) == 0xe0000000) +#define IN_MULTICAST(i) IN_CLASSD(i) + +#else // _WIN32 + +#include +#define __STDC_LIMIT_MACROS +#include +#include +#include +#include +#include +#include + +#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 +#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 +#ifdef __CYGWIN__ +// Cygwin internally maps these functions to 64bit usage, but there are +// no explicit xxxx64 functions. +#define FOPEN64 fopen +#define OPEN open +#define LSEEK lseek +#else +#define FOPEN64 fopen64 +#define OPEN open64 +#define LSEEK lseek64 +#endif +#define FSEEK fseek +#define FTELL ftell +#define FSTAT fstat +#define TELL tell +#include +#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 diff --git a/src/sequencing.cpp b/src/sequencing.cpp new file mode 100644 index 00000000..a01b97d1 --- /dev/null +++ b/src/sequencing.cpp @@ -0,0 +1,315 @@ +#include "ccextractor.h" +#include "708.h" + +// Defined by the maximum number of B-Frames per anchor frame. +//#define MAXBFRAMES 50 - from ccextractor.h +// They can be (temporally) before or after the anchor. Reserve +// enough space. +//#define SORTBUF (2*MAXBFRAMES+1) - from ccextractor.h +// B-Frames can be (temporally) before or after the anchor +int cc_data_count[SORTBUF]; +// Store fts; +static LLONG cc_fts[SORTBUF]; +// Store HD CC packets +unsigned char cc_data_pkts[SORTBUF][10*31*3+1]; // *10, because MP4 seems to have different limits + +// Set to true if data is buffered +int has_ccdata_buffered = 0; +// The sequence number of the current anchor frame. All currently read +// B-Frames belong to this I- or P-frame. +static int anchor_seq_number = -1; + +// Remember the current field for 608 decoding +int current_field=1; // 1 = field 1, 2 = field 2, 3 = 708 + +static int in_xds_mode ; // Stolen from 608.cpp. We need this for debug + +void init_hdcc (void) +{ + for (int j=0; j 2*MAXBFRAMES) + { + // Maybe missing an anchor frame - try to recover + dbg_print(CCX_DMT_VERBOSE, "Too many B-frames, or missing anchor frame. Trying to recover ..\n"); + + process_hdcc(); + anchor_hdcc( sequence_number); + seq_index = sequence_number - anchor_seq_number + MAXBFRAMES; + } + + has_ccdata_buffered = 1; + + // In GOP mode the fts is set only once for the whole GOP. Recreate + // the right time according to the sequence number. + if (ccx_options.use_gop_as_pts==1) + { + current_fts_now += LLONG(sequence_number*1000.0/current_fps); + } + + if (cc_count) + { + if (cc_data) + { + // Changed by CFS to concat, i.e. don't assume there's no data already for this seq_index. + // Needed at least for MP4 samples. // TODO: make sure we don't overflow + cc_fts[seq_index] = current_fts_now; // CFS: Maybe do even if there's no data? + if (stream_mode!=CCX_SM_MP4) // CFS: Very ugly hack, but looks like overwriting is needed for at least some ES + cc_data_count[seq_index] = 0; + memcpy(cc_data_pkts[seq_index]+cc_data_count[seq_index]*3, cc_data, cc_count*3+1); + } + cc_data_count[seq_index] += cc_count; + } + // DEBUG STUFF +/* + printf("\nCC blocks, channel 0:\n"); + for ( int i=0; i < cc_count*3; i+=3) + { + printf("%s", debug_608toASC( cc_data+i, 0) ); + } + printf("\n"); +*/ +} + +// Set a new anchor frame that new B-frames refer to. +void anchor_hdcc(int seq) +{ + // Re-init the index + anchor_seq_number = seq; +} + +// Sort/flash caption block buffer +void process_hdcc (void) +{ + // Remember the current value + LLONG store_fts_now = fts_now; + + dbg_print(CCX_DMT_VERBOSE, "Flush HD caption blocks\n"); + + int reset_cb = -1; + + for (int seq=0; seq>2; + unsigned char cc_type = (*(cc_data_pkts[seq]+j)) & 3; + + if (cc_valid && (cc_type==0 || cc_type==1)) + { + // For EIA-608 data we verify parity. + if (!cc608_parity_table[cc_data_pkts[seq][j+2]]) + { + // If the second byte doesn't pass parity, ignore pair + continue; + } + if (!cc608_parity_table[cc_data_pkts[seq][j+1]]) + { + // The first byte doesn't pass parity, we replace it with a solid blank + // and process the pair. + cc_data_pkts[seq][j+1]=0x7F; + } + } + do_cb(cc_data_pkts[seq]+j); + + } // for loop over packets + } + + // Restore the value + fts_now = store_fts_now; + + // Now that we are done, clean up. + init_hdcc(); +} + + +int do_cb (unsigned char *cc_block) +{ + unsigned char cc_valid = (*cc_block & 4) >>2; + unsigned char cc_type = *cc_block & 3; + + int timeok = 1; + + if ( ccx_options.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 ( ccx_options.write_format!=CCX_OF_RAW && // In raw we cannot skip padding because timing depends on it + ccx_options.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:%02X:%02X", print_mstime(fts_now + fts_global),in_xds_mode, + cc_block[0], cc_block[1], 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) + { + cc_stats[cc_type]++; + + switch (cc_type) + { + case 0: + dbg_print(CCX_DMT_CBRAW, " %s .. ..\n", debug_608toASC( cc_block, 0)); + + current_field=1; + saw_caption_block = 1; + + if (ccx_options.extraction_start.set && + get_fts() < ccx_options.extraction_start.time_in_ms) + timeok = 0; + if (ccx_options.extraction_end.set && + get_fts() > ccx_options.extraction_end.time_in_ms) + { + timeok = 0; + processed_enough=1; + } + if (timeok) + { + if(ccx_options.write_format!=CCX_OF_RCWT) + printdata (cc_block+1,2,0,0); + else + writercwtdata(cc_block); + } + cb_field1++; + break; + case 1: + dbg_print(CCX_DMT_CBRAW, " .. %s ..\n", debug_608toASC( cc_block, 1)); + + current_field=2; + saw_caption_block = 1; + + if (ccx_options.extraction_start.set && + get_fts() < ccx_options.extraction_start.time_in_ms) + timeok = 0; + if (ccx_options.extraction_end.set && + get_fts() > ccx_options.extraction_end.time_in_ms) + { + timeok = 0; + processed_enough=1; + } + if (timeok) + { + if(ccx_options.write_format!=CCX_OF_RCWT) + printdata (0,0,cc_block+1,2); + else + writercwtdata(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 (ccx_options.extraction_start.set && + get_fts() < ccx_options.extraction_start.time_in_ms) + timeok = 0; + if (ccx_options.extraction_end.set && + get_fts() > ccx_options.extraction_end.time_in_ms) + { + timeok = 0; + 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(ccx_options.write_format!=CCX_OF_RCWT) + do_708 ((const unsigned char *) temp, 4); + else + writercwtdata(cc_block); + } + cb_708++; + // Check for bytes read + // printf ("Warning: Losing EIA-708 data!\n"); + break; + default: + fatal(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; +} diff --git a/src/stdint.h b/src/stdint.h new file mode 100644 index 00000000..d02608a5 --- /dev/null +++ b/src/stdint.h @@ -0,0 +1,247 @@ +// ISO C9x compliant stdint.h for Microsoft Visual Studio +// Based on ISO/IEC 9899:TC2 Committee draft (May 6, 2005) WG14/N1124 +// +// Copyright (c) 2006-2008 Alexander Chemeris +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// 3. The name of the author may be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED +// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +/////////////////////////////////////////////////////////////////////////////// + +#ifndef _MSC_VER // [ +#error "Use this header only with Microsoft Visual C++ compilers!" +#endif // _MSC_VER ] + +#ifndef _MSC_STDINT_H_ // [ +#define _MSC_STDINT_H_ + +#if _MSC_VER > 1000 +#pragma once +#endif + +#include + +// For Visual Studio 6 in C++ mode and for many Visual Studio versions when +// compiling for ARM we should wrap include with 'extern "C++" {}' +// or compiler give many errors like this: +// error C2733: second C linkage of overloaded function 'wmemchr' not allowed +#ifdef __cplusplus +extern "C" { +#endif +# include +#ifdef __cplusplus +} +#endif + +// Define _W64 macros to mark types changing their size, like intptr_t. +#ifndef _W64 +# if !defined(__midl) && (defined(_X86_) || defined(_M_IX86)) && _MSC_VER >= 1300 +# define _W64 __w64 +# else +# define _W64 +# endif +#endif + + +// 7.18.1 Integer types + +// 7.18.1.1 Exact-width integer types + +// Visual Studio 6 and Embedded Visual C++ 4 doesn't +// realize that, e.g. char has the same size as __int8 +// so we give up on __intX for them. +#if (_MSC_VER < 1300) + typedef signed char int8_t; + typedef signed short int16_t; + typedef signed int int32_t; + typedef unsigned char uint8_t; + typedef unsigned short uint16_t; + typedef unsigned int uint32_t; +#else + typedef signed __int8 int8_t; + typedef signed __int16 int16_t; + typedef signed __int32 int32_t; + typedef unsigned __int8 uint8_t; + typedef unsigned __int16 uint16_t; + typedef unsigned __int32 uint32_t; +#endif +typedef signed __int64 int64_t; +typedef unsigned __int64 uint64_t; + + +// 7.18.1.2 Minimum-width integer types +typedef int8_t int_least8_t; +typedef int16_t int_least16_t; +typedef int32_t int_least32_t; +typedef int64_t int_least64_t; +typedef uint8_t uint_least8_t; +typedef uint16_t uint_least16_t; +typedef uint32_t uint_least32_t; +typedef uint64_t uint_least64_t; + +// 7.18.1.3 Fastest minimum-width integer types +typedef int8_t int_fast8_t; +typedef int16_t int_fast16_t; +typedef int32_t int_fast32_t; +typedef int64_t int_fast64_t; +typedef uint8_t uint_fast8_t; +typedef uint16_t uint_fast16_t; +typedef uint32_t uint_fast32_t; +typedef uint64_t uint_fast64_t; + +// 7.18.1.4 Integer types capable of holding object pointers +#ifdef _WIN64 // [ + typedef signed __int64 intptr_t; + typedef unsigned __int64 uintptr_t; +#else // _WIN64 ][ + typedef _W64 signed int intptr_t; + typedef _W64 unsigned int uintptr_t; +#endif // _WIN64 ] + +// 7.18.1.5 Greatest-width integer types +typedef int64_t intmax_t; +typedef uint64_t uintmax_t; + + +// 7.18.2 Limits of specified-width integer types + +#if !defined(__cplusplus) || defined(__STDC_LIMIT_MACROS) // [ See footnote 220 at page 257 and footnote 221 at page 259 + +// 7.18.2.1 Limits of exact-width integer types +#define INT8_MIN ((int8_t)_I8_MIN) +#define INT8_MAX _I8_MAX +#define INT16_MIN ((int16_t)_I16_MIN) +#define INT16_MAX _I16_MAX +#define INT32_MIN ((int32_t)_I32_MIN) +#define INT32_MAX _I32_MAX +#define INT64_MIN ((int64_t)_I64_MIN) +#define INT64_MAX _I64_MAX +#define UINT8_MAX _UI8_MAX +#define UINT16_MAX _UI16_MAX +#define UINT32_MAX _UI32_MAX +#define UINT64_MAX _UI64_MAX + +// 7.18.2.2 Limits of minimum-width integer types +#define INT_LEAST8_MIN INT8_MIN +#define INT_LEAST8_MAX INT8_MAX +#define INT_LEAST16_MIN INT16_MIN +#define INT_LEAST16_MAX INT16_MAX +#define INT_LEAST32_MIN INT32_MIN +#define INT_LEAST32_MAX INT32_MAX +#define INT_LEAST64_MIN INT64_MIN +#define INT_LEAST64_MAX INT64_MAX +#define UINT_LEAST8_MAX UINT8_MAX +#define UINT_LEAST16_MAX UINT16_MAX +#define UINT_LEAST32_MAX UINT32_MAX +#define UINT_LEAST64_MAX UINT64_MAX + +// 7.18.2.3 Limits of fastest minimum-width integer types +#define INT_FAST8_MIN INT8_MIN +#define INT_FAST8_MAX INT8_MAX +#define INT_FAST16_MIN INT16_MIN +#define INT_FAST16_MAX INT16_MAX +#define INT_FAST32_MIN INT32_MIN +#define INT_FAST32_MAX INT32_MAX +#define INT_FAST64_MIN INT64_MIN +#define INT_FAST64_MAX INT64_MAX +#define UINT_FAST8_MAX UINT8_MAX +#define UINT_FAST16_MAX UINT16_MAX +#define UINT_FAST32_MAX UINT32_MAX +#define UINT_FAST64_MAX UINT64_MAX + +// 7.18.2.4 Limits of integer types capable of holding object pointers +#ifdef _WIN64 // [ +# define INTPTR_MIN INT64_MIN +# define INTPTR_MAX INT64_MAX +# define UINTPTR_MAX UINT64_MAX +#else // _WIN64 ][ +# define INTPTR_MIN INT32_MIN +# define INTPTR_MAX INT32_MAX +# define UINTPTR_MAX UINT32_MAX +#endif // _WIN64 ] + +// 7.18.2.5 Limits of greatest-width integer types +#define INTMAX_MIN INT64_MIN +#define INTMAX_MAX INT64_MAX +#define UINTMAX_MAX UINT64_MAX + +// 7.18.3 Limits of other integer types + +#ifdef _WIN64 // [ +# define PTRDIFF_MIN _I64_MIN +# define PTRDIFF_MAX _I64_MAX +#else // _WIN64 ][ +# define PTRDIFF_MIN _I32_MIN +# define PTRDIFF_MAX _I32_MAX +#endif // _WIN64 ] + +#define SIG_ATOMIC_MIN INT_MIN +#define SIG_ATOMIC_MAX INT_MAX + +#ifndef SIZE_MAX // [ +# ifdef _WIN64 // [ +# define SIZE_MAX _UI64_MAX +# else // _WIN64 ][ +# define SIZE_MAX _UI32_MAX +# endif // _WIN64 ] +#endif // SIZE_MAX ] + +// WCHAR_MIN and WCHAR_MAX are also defined in +#ifndef WCHAR_MIN // [ +# define WCHAR_MIN 0 +#endif // WCHAR_MIN ] +#ifndef WCHAR_MAX // [ +# define WCHAR_MAX _UI16_MAX +#endif // WCHAR_MAX ] + +#define WINT_MIN 0 +#define WINT_MAX _UI16_MAX + +#endif // __STDC_LIMIT_MACROS ] + + +// 7.18.4 Limits of other integer types + +#if !defined(__cplusplus) || defined(__STDC_CONSTANT_MACROS) // [ See footnote 224 at page 260 + +// 7.18.4.1 Macros for minimum-width integer constants + +#define INT8_C(val) val##i8 +#define INT16_C(val) val##i16 +#define INT32_C(val) val##i32 +#define INT64_C(val) val##i64 + +#define UINT8_C(val) val##ui8 +#define UINT16_C(val) val##ui16 +#define UINT32_C(val) val##ui32 +#define UINT64_C(val) val##ui64 + +// 7.18.4.2 Macros for greatest-width integer constants +#define INTMAX_C INT64_C +#define UINTMAX_C UINT64_C + +#endif // __STDC_CONSTANT_MACROS ] + + +#endif // _MSC_STDINT_H_ ] diff --git a/src/stdintmsc.h b/src/stdintmsc.h new file mode 100644 index 00000000..e032ff16 --- /dev/null +++ b/src/stdintmsc.h @@ -0,0 +1,232 @@ +// ISO C9x compliant stdint.h for Microsoft Visual Studio +// Based on ISO/IEC 9899:TC2 Committee draft (May 6, 2005) WG14/N1124 +// +// Copyright (c) 2006-2008 Alexander Chemeris +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// 3. The name of the author may be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED +// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +/////////////////////////////////////////////////////////////////////////////// + +#ifndef _MSC_VER // [ +#error "Use this header only with Microsoft Visual C++ compilers!" +#endif // _MSC_VER ] + +#ifndef _MSC_STDINT_H_ // [ +#define _MSC_STDINT_H_ + +#if _MSC_VER > 1000 +#pragma once +#endif + +#include + +// For Visual Studio 6 in C++ mode wrap include with 'extern "C++" {}' +// or compiler give many errors like this: +// error C2733: second C linkage of overloaded function 'wmemchr' not allowed +#if (_MSC_VER < 1300) && defined(__cplusplus) + extern "C++" { +#endif +# include +#if (_MSC_VER < 1300) && defined(__cplusplus) + } +#endif + +// Define _W64 macros to mark types changing their size, like intptr_t. +#ifndef _W64 +# if !defined(__midl) && (defined(_X86_) || defined(_M_IX86)) && _MSC_VER >= 1300 +# define _W64 __w64 +# else +# define _W64 +# endif +#endif + + +// 7.18.1 Integer types + +// 7.18.1.1 Exact-width integer types +typedef __int8 int8_t; +typedef __int16 int16_t; +typedef __int32 int32_t; +typedef __int64 int64_t; +typedef unsigned __int8 uint8_t; +typedef unsigned __int16 uint16_t; +typedef unsigned __int32 uint32_t; +typedef unsigned __int64 uint64_t; + +// 7.18.1.2 Minimum-width integer types +typedef int8_t int_least8_t; +typedef int16_t int_least16_t; +typedef int32_t int_least32_t; +typedef int64_t int_least64_t; +typedef uint8_t uint_least8_t; +typedef uint16_t uint_least16_t; +typedef uint32_t uint_least32_t; +typedef uint64_t uint_least64_t; + +// 7.18.1.3 Fastest minimum-width integer types +typedef int8_t int_fast8_t; +typedef int16_t int_fast16_t; +typedef int32_t int_fast32_t; +typedef int64_t int_fast64_t; +typedef uint8_t uint_fast8_t; +typedef uint16_t uint_fast16_t; +typedef uint32_t uint_fast32_t; +typedef uint64_t uint_fast64_t; + +// 7.18.1.4 Integer types capable of holding object pointers +#ifdef _WIN64 // [ + typedef __int64 intptr_t; + typedef unsigned __int64 uintptr_t; +#else // _WIN64 ][ + typedef _W64 int intptr_t; + typedef _W64 unsigned int uintptr_t; +#endif // _WIN64 ] + +// 7.18.1.5 Greatest-width integer types +typedef int64_t intmax_t; +typedef uint64_t uintmax_t; + + +// 7.18.2 Limits of specified-width integer types + +#if !defined(__cplusplus) || defined(__STDC_LIMIT_MACROS) // [ See footnote 220 at page 257 and footnote 221 at page 259 + +// 7.18.2.1 Limits of exact-width integer types +#define INT8_MIN ((int8_t)_I8_MIN) +#define INT8_MAX _I8_MAX +#define INT16_MIN ((int16_t)_I16_MIN) +#define INT16_MAX _I16_MAX +#define INT32_MIN ((int32_t)_I32_MIN) +#define INT32_MAX _I32_MAX +#define INT64_MIN ((int64_t)_I64_MIN) +#define INT64_MAX _I64_MAX +#define UINT8_MAX _UI8_MAX +#define UINT16_MAX _UI16_MAX +#define UINT32_MAX _UI32_MAX +#define UINT64_MAX _UI64_MAX + +// 7.18.2.2 Limits of minimum-width integer types +#define INT_LEAST8_MIN INT8_MIN +#define INT_LEAST8_MAX INT8_MAX +#define INT_LEAST16_MIN INT16_MIN +#define INT_LEAST16_MAX INT16_MAX +#define INT_LEAST32_MIN INT32_MIN +#define INT_LEAST32_MAX INT32_MAX +#define INT_LEAST64_MIN INT64_MIN +#define INT_LEAST64_MAX INT64_MAX +#define UINT_LEAST8_MAX UINT8_MAX +#define UINT_LEAST16_MAX UINT16_MAX +#define UINT_LEAST32_MAX UINT32_MAX +#define UINT_LEAST64_MAX UINT64_MAX + +// 7.18.2.3 Limits of fastest minimum-width integer types +#define INT_FAST8_MIN INT8_MIN +#define INT_FAST8_MAX INT8_MAX +#define INT_FAST16_MIN INT16_MIN +#define INT_FAST16_MAX INT16_MAX +#define INT_FAST32_MIN INT32_MIN +#define INT_FAST32_MAX INT32_MAX +#define INT_FAST64_MIN INT64_MIN +#define INT_FAST64_MAX INT64_MAX +#define UINT_FAST8_MAX UINT8_MAX +#define UINT_FAST16_MAX UINT16_MAX +#define UINT_FAST32_MAX UINT32_MAX +#define UINT_FAST64_MAX UINT64_MAX + +// 7.18.2.4 Limits of integer types capable of holding object pointers +#ifdef _WIN64 // [ +# define INTPTR_MIN INT64_MIN +# define INTPTR_MAX INT64_MAX +# define UINTPTR_MAX UINT64_MAX +#else // _WIN64 ][ +# define INTPTR_MIN INT32_MIN +# define INTPTR_MAX INT32_MAX +# define UINTPTR_MAX UINT32_MAX +#endif // _WIN64 ] + +// 7.18.2.5 Limits of greatest-width integer types +#define INTMAX_MIN INT64_MIN +#define INTMAX_MAX INT64_MAX +#define UINTMAX_MAX UINT64_MAX + +// 7.18.3 Limits of other integer types + +#ifdef _WIN64 // [ +# define PTRDIFF_MIN _I64_MIN +# define PTRDIFF_MAX _I64_MAX +#else // _WIN64 ][ +# define PTRDIFF_MIN _I32_MIN +# define PTRDIFF_MAX _I32_MAX +#endif // _WIN64 ] + +#define SIG_ATOMIC_MIN INT_MIN +#define SIG_ATOMIC_MAX INT_MAX + +#ifndef SIZE_MAX // [ +# ifdef _WIN64 // [ +# define SIZE_MAX _UI64_MAX +# else // _WIN64 ][ +# define SIZE_MAX _UI32_MAX +# endif // _WIN64 ] +#endif // SIZE_MAX ] + +// WCHAR_MIN and WCHAR_MAX are also defined in +#ifndef WCHAR_MIN // [ +# define WCHAR_MIN 0 +#endif // WCHAR_MIN ] +#ifndef WCHAR_MAX // [ +# define WCHAR_MAX _UI16_MAX +#endif // WCHAR_MAX ] + +#define WINT_MIN 0 +#define WINT_MAX _UI16_MAX + +#endif // __STDC_LIMIT_MACROS ] + + +// 7.18.4 Limits of other integer types + +#if !defined(__cplusplus) || defined(__STDC_CONSTANT_MACROS) // [ See footnote 224 at page 260 + +// 7.18.4.1 Macros for minimum-width integer constants + +#define INT8_C(val) val##i8 +#define INT16_C(val) val##i16 +#define INT32_C(val) val##i32 +#define INT64_C(val) val##i64 + +#define UINT8_C(val) val##ui8 +#define UINT16_C(val) val##ui16 +#define UINT32_C(val) val##ui32 +#define UINT64_C(val) val##ui64 + +// 7.18.4.2 Macros for greatest-width integer constants +#define INTMAX_C INT64_C +#define UINTMAX_C UINT64_C + +#endif // __STDC_CONSTANT_MACROS ] + + +#endif // _MSC_STDINT_H_ ] diff --git a/src/stream_functions.cpp b/src/stream_functions.cpp new file mode 100644 index 00000000..fb7d79cd --- /dev/null +++ b/src/stream_functions.cpp @@ -0,0 +1,337 @@ +#include "ccextractor.h" +#ifdef _WIN32 +#include +#else +#include +#endif + +void detect_stream_type (void) +{ + stream_mode=CCX_SM_ELEMENTARY_OR_NOT_FOUND; // Not found + startbytes_avail = (int) buffered_read_opt (startbytes,STARTBYTESLENGTH); + + if( startbytes_avail == -1) + fatal (EXIT_READ_ERROR, "Error reading input file!\n"); + + if (startbytes_avail>=4) + { + // Check for ASF magic bytes + if (startbytes[0]==0x30 && + startbytes[1]==0x26 && + startbytes[2]==0xb2 && + startbytes[3]==0x75) + stream_mode=CCX_SM_ASF; + } + if(startbytes_avail>=4) + { + if(startbytes[0]==0xb7 && + startbytes[1]==0xd8 && + startbytes[2]==0x00 && + startbytes[3]==0x20) + stream_mode=CCX_SM_WTV; + } + if (stream_mode==CCX_SM_ELEMENTARY_OR_NOT_FOUND && startbytes_avail>=6) + { + // Check for hexadecimal dump generated by wtvccdump + // ; CCHD + if (startbytes[0]==';' && + startbytes[1]==' ' && + startbytes[2]=='C' && + startbytes[3]=='C' && + startbytes[4]=='H' && + startbytes[5]=='D') + stream_mode= CCX_SM_HEX_DUMP; + } + + if (stream_mode==CCX_SM_ELEMENTARY_OR_NOT_FOUND && startbytes_avail>=11) + { + // Check for CCExtractor magic bytes + if (startbytes[0]==0xCC && + startbytes[1]==0xCC && + startbytes[2]==0xED && + startbytes[8]==0 && + startbytes[9]==0 && + startbytes[10]==0) + stream_mode=CCX_SM_RCWT; + } + if (stream_mode==CCX_SM_ELEMENTARY_OR_NOT_FOUND) // Still not found + { + if (startbytes_avail > 188*8) // Otherwise, assume no TS + { + // First check for TS + for (unsigned i=0; i<188;i++) + { + if (startbytes[i]==0x47 && startbytes[i+188]==0x47 && + startbytes[i+188*2]==0x47 && startbytes[i+188*3]==0x47 && + startbytes[i+188*4]==0x47 && startbytes[i+188*5]==0x47 && + startbytes[i+188*6]==0x47 && startbytes[i+188*7]==0x47 + ) + { + // Eight sync bytes, that's good enough + startbytes_pos=i; + stream_mode=CCX_SM_TRANSPORT; + break; + } + } + // Now check for PS (Needs PACK header) + for (unsigned i=0; + i < unsigned(startbytes_avail<50000?startbytes_avail-3:49997); + i++) + { + if (startbytes[i]==0x00 && startbytes[i+1]==0x00 && + startbytes[i+2]==0x01 && startbytes[i+3]==0xBA) + { + // If we find a PACK header it is not an ES + startbytes_pos=i; + stream_mode=CCX_SM_PROGRAM; + break; + } + } + // TiVo is also a PS + if (startbytes[0]=='T' && startbytes[1]=='i' && + startbytes[2]=='V' && startbytes[3]=='o') + { + // The TiVo header is longer, but the PS loop will find the beginning + startbytes_pos=187; + stream_mode=CCX_SM_PROGRAM; + strangeheader=1; // Avoid message about unrecognized header + } + } + else + { + startbytes_pos=0; + stream_mode=CCX_SM_ELEMENTARY_OR_NOT_FOUND; + } + } + if (stream_mode==CCX_SM_ELEMENTARY_OR_NOT_FOUND && startbytes_avail>=4) // Still not found + { + // Try for MP4 by looking for box signatures - this should happen very + // early in the file according to specs + for (int i=0;i10) // Too much coincidence + return 1; + + return 0; +} + +/* Read and evaluate the current video PES header. The function returns + * the length of the payload if successful, otherwise -1 is returned + * indicating a premature end of file / too small buffer. + * If sbuflen is + * 0 .. Read from file into nextheader + * >0 .. Use data in nextheader with the length of sbuflen + */ +int read_video_pes_header (unsigned char *nextheader, int *headerlength, int sbuflen) +{ + // Read the next video PES + // ((nextheader[3]&0xf0)==0xe0) + unsigned peslen=nextheader[4]<<8 | nextheader[5]; + unsigned payloadlength = 0; // Length of packet data bytes + + if ( !sbuflen ) + { + // Extension present, get it + buffered_read (nextheader+6,3); + past=past+result; + if (result!=3) { + // Consider this the end of the show. + return -1; + } + } + else + { + // We need at least 9 bytes to continue + if( sbuflen < 9 ) + return -1; + } + *headerlength = 6+3; + + unsigned hskip=0; + + // Assume header[8] is right, but double check + if ( !sbuflen ) + { + if (nextheader[8] > 0) { + buffered_read (nextheader+9,nextheader[8]); + past=past+result; + if (result!=nextheader[8]) { + return -1; + } + } + } + else + { + // See if the buffer is big enough + if( sbuflen < *headerlength + (int)nextheader[8] ) + return -1; + } + *headerlength += (int) nextheader[8]; + int falsepes = 0; + int pesext = 0; + + // Avoid false positives, check --- not really needed + if ( (nextheader[7]&0xC0) == 0x80 ) { + // PTS only + hskip += 5; + if( (nextheader[9]&0xF1) != 0x21 || (nextheader[11]&0x01) != 0x01 + || (nextheader[13]&0x01) != 0x01 ) { + falsepes = 1; + mprint("False PTS\n"); + } + } else if ( (nextheader[7]&0xC0) == 0xC0 ) { + // PTS and DTS + hskip += 10; + if( (nextheader[9]&0xF1) != 0x31 || (nextheader[11]&0x01) != 0x01 + || (nextheader[13]&0x01) != 0x01 + || (nextheader[14]&0xF1) != 0x11 || (nextheader[16]&0x01) != 0x01 + || (nextheader[18]&0x01) != 0x01 ) { + falsepes = 1; + mprint("False PTS/DTS\n"); + } + } else if ( (nextheader[7]&0xC0) == 0x40 ) { + // Forbidden + falsepes = 1; + mprint("False PTS/DTS flag\n"); + } + if ( !falsepes && nextheader[7]&0x20 ) { // ESCR + if ((nextheader[9+hskip]&0xC4) != 0x04 || !(nextheader[11+hskip]&0x04) + || !(nextheader[13+hskip]&0x04) || !(nextheader[14+hskip]&0x01) ) { + falsepes = 1; + mprint("False ESCR\n"); + } + hskip += 6; + } + if ( !falsepes && nextheader[7]&0x10 ) { // ES + if ( !(nextheader[9+hskip]&0x80) || !(nextheader[11+hskip]&0x01) ) { + mprint("False ES\n"); + falsepes = 1; + } + hskip += 3; + } + if ( !falsepes && nextheader[7]&0x04) { // add copy info + if ( !(nextheader[9+hskip]&0x80) ) { + mprint("False add copy info\n"); + falsepes = 1; + } + hskip += 1; + } + if ( !falsepes && nextheader[7]&0x02) { // PES CRC + hskip += 2; + } + if ( !falsepes && nextheader[7]&0x01) { // PES extension + if ( (nextheader[9+hskip]&0x0E)!=0x0E ) { + mprint("False PES ext\n"); + falsepes = 1; + } + hskip += 1; + pesext = 1; + } + + if ( !falsepes ) { + hskip = nextheader[8]; + } + + if ( !falsepes && nextheader[7]&0x80 ) { + // Read PTS from byte 9,10,11,12,13 + + LLONG bits_9 = ((LLONG) nextheader[9] & 0x0E)<<29; // PTS 32..30 - Must be LLONG to prevent overflow + unsigned bits_10 = nextheader[10] << 22; // PTS 29..22 + unsigned bits_11 = (nextheader[11] & 0xFE) << 14; // PTS 21..15 + unsigned bits_12 = nextheader[12] << 7; // PTS 14-7 + unsigned bits_13 = nextheader[13] >> 1; // PTS 6-0 + + if (!bits_9 && ((current_pts>>30)&7)==7) // PTS about to rollover + rollover_bits++; + if (bits_9==7 && ((current_pts>>30)&7)==0) // PTS rollback? Rare and if happens it would mean out of order frames + rollover_bits--; + + + current_pts = bits_9 | bits_10 | bits_11 | bits_12 | bits_13; + current_pts = (LLONG) rollover_bits<<33 | current_pts; + + if (pts_set==0) + pts_set=1; + + dbg_print(CCX_DMT_VERBOSE, "Set PTS: %s (%u)\n", + print_mstime((current_pts)/(MPEG_CLOCK_FREQ/1000)), + unsigned(current_pts) ); + /* The user data holding the captions seems to come between GOP and + * the first frame. The sync PTS (sync_pts) (set at picture 0) + * corresponds to the first frame of the current GOP. */ + } + + // This might happen in PES packets in TS stream. It seems that when the + // packet length is unkown it is set to 0. + if (peslen+6 >= hskip+9) + { + payloadlength = peslen - (hskip + 3); // for [6], [7] and [8] + } + + // Use a save default if this is not a real PES header + if (falsepes) { + mprint("False PES header\n"); + } + + dbg_print(CCX_DMT_VERBOSE, "PES header length: %d (%d verified) Data length: %d\n", + *headerlength, hskip+9, payloadlength); + + return payloadlength; +} diff --git a/src/teletext.h b/src/teletext.h new file mode 100644 index 00000000..97b1a2f0 --- /dev/null +++ b/src/teletext.h @@ -0,0 +1,253 @@ +/*! +(c) 2011-2013 Forers, s. r. o.: telxcc +*/ + +#ifndef teletext_h_included +#define teletext_h_included + +//#include + +typedef enum { + LATIN = 0, + CYRILLIC1, + CYRILLIC2, + CYRILLIC3, + GREEK, + ARABIC, + HEBREW +} g0_charsets_t; + +// Note: All characters are encoded in UCS-2 + +// --- G0 ---------------------------------------------------------------------- + +// G0 charsets +uint16_t G0[5][96] = { + { // Latin G0 Primary Set + 0x0020, 0x0021, 0x0022, 0x00a3, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f, + 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f, + 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f, + 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005a, 0x00ab, 0x00bd, 0x00bb, 0x005e, 0x0023, + 0x002d, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f, + 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007a, 0x00bc, 0x00a6, 0x00be, 0x00f7, 0x007f + }, + { // Cyrillic G0 Primary Set - Option 1 - Serbian/Croatian + 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x044b, 0x0027, 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f, + 0x0030, 0x0031, 0x3200, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f, + 0x0427, 0x0410, 0x0411, 0x0426, 0x0414, 0x0415, 0x0424, 0x0413, 0x0425, 0x0418, 0x0408, 0x041a, 0x041b, 0x041c, 0x041d, 0x041e, + 0x041f, 0x040c, 0x0420, 0x0421, 0x0422, 0x0423, 0x0412, 0x0403, 0x0409, 0x040a, 0x0417, 0x040b, 0x0416, 0x0402, 0x0428, 0x040f, + 0x0447, 0x0430, 0x0431, 0x0446, 0x0434, 0x0435, 0x0444, 0x0433, 0x0445, 0x0438, 0x0428, 0x043a, 0x043b, 0x043c, 0x043d, 0x043e, + 0x043f, 0x042c, 0x0440, 0x0441, 0x0442, 0x0443, 0x0432, 0x0423, 0x0429, 0x042a, 0x0437, 0x042b, 0x0436, 0x0422, 0x0448, 0x042f + }, + { // Cyrillic G0 Primary Set - Option 2 - Russian/Bulgarian + 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x044b, 0x0027, 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f, + 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f, + 0x042e, 0x0410, 0x0411, 0x0426, 0x0414, 0x0415, 0x0424, 0x0413, 0x0425, 0x0418, 0x0419, 0x041a, 0x041b, 0x041c, 0x041d, 0x041e, + 0x041f, 0x042f, 0x0420, 0x0421, 0x0422, 0x0423, 0x0416, 0x0412, 0x042c, 0x042a, 0x0417, 0x0428, 0x042d, 0x0429, 0x0427, 0x042b, + 0x044e, 0x0430, 0x0431, 0x0446, 0x0434, 0x0435, 0x0444, 0x0433, 0x0445, 0x0438, 0x0439, 0x043a, 0x043b, 0x043c, 0x043d, 0x043e, + 0x043f, 0x044f, 0x0440, 0x0441, 0x0442, 0x0443, 0x0436, 0x0432, 0x044c, 0x044a, 0x0437, 0x0448, 0x044d, 0x0449, 0x0447, 0x044b + }, + { // Cyrillic G0 Primary Set - Option 3 - Ukrainian + 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x00ef, 0x0027, 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f, + 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f, + 0x042e, 0x0410, 0x0411, 0x0426, 0x0414, 0x0415, 0x0424, 0x0413, 0x0425, 0x0418, 0x0419, 0x041a, 0x041b, 0x041c, 0x041d, 0x041e, + 0x041f, 0x042f, 0x0420, 0x0421, 0x0422, 0x0423, 0x0416, 0x0412, 0x042c, 0x0049, 0x0417, 0x0428, 0x042d, 0x0429, 0x0427, 0x00cf, + 0x044e, 0x0430, 0x0431, 0x0446, 0x0434, 0x0435, 0x0444, 0x0433, 0x0445, 0x0438, 0x0439, 0x043a, 0x043b, 0x043c, 0x043d, 0x043e, + 0x043f, 0x044f, 0x0440, 0x0441, 0x0442, 0x0443, 0x0436, 0x0432, 0x044c, 0x0069, 0x0437, 0x0448, 0x044d, 0x0449, 0x0447, 0x00ff + }, + { // Greek G0 Primary Set + 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f, + 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f, + 0x0390, 0x0391, 0x0392, 0x0393, 0x0394, 0x0395, 0x0396, 0x0397, 0x0398, 0x0399, 0x039a, 0x039b, 0x039c, 0x039d, 0x039e, 0x039f, + 0x03a0, 0x03a1, 0x03a2, 0x03a3, 0x03a4, 0x03a5, 0x03a6, 0x03a7, 0x03a8, 0x03a9, 0x03aa, 0x03ab, 0x03ac, 0x03ad, 0x03ae, 0x03af, + 0x03b0, 0x03b1, 0x03b2, 0x03b3, 0x03b4, 0x03b5, 0x03b6, 0x03b7, 0x03b8, 0x03b9, 0x03ba, 0x03bb, 0x03bc, 0x03bd, 0x03be, 0x03bf, + 0x03c0, 0x03c1, 0x03c2, 0x03c3, 0x03c4, 0x03c5, 0x03c6, 0x03c7, 0x03c8, 0x03c9, 0x03ca, 0x03cb, 0x03cc, 0x03cd, 0x03ce, 0x03cf + } + //{ // Arabic G0 Primary Set + //}, + //{ // Hebrew G0 Primary Set + //} +}; + +// array positions where chars from G0_LATIN_NATIONAL_SUBSETS are injected into G0[LATIN] +const uint8_t G0_LATIN_NATIONAL_SUBSETS_POSITIONS[13] = { + 0x03, 0x04, 0x20, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x5b, 0x5c, 0x5d, 0x5e +}; + +// ETS 300 706, chapter 15.2, table 32: Function of Default G0 and G2 Character Set Designation +// and National Option Selection bits in packets X/28/0 Format 1, X/28/4, M/29/0 and M/29/4 + +// Latin National Option Sub-sets +struct { + const char *language; + uint16_t characters[13]; +} const G0_LATIN_NATIONAL_SUBSETS[14] = { + { // 0 + "English", + { 0x00a3, 0x0024, 0x0040, 0x00ab, 0x00bd, 0x00bb, 0x005e, 0x0023, 0x002d, 0x00bc, 0x00a6, 0x00be, 0x00f7 } + }, + { // 1 + "French", + { 0x00e9, 0x00ef, 0x00e0, 0x00eb, 0x00ea, 0x00f9, 0x00ee, 0x0023, 0x00e8, 0x00e2, 0x00f4, 0x00fb, 0x00e7 } + }, + { // 2 + "Swedish, Finnish, Hungarian", + { 0x0023, 0x00a4, 0x00c9, 0x00c4, 0x00d6, 0x00c5, 0x00dc, 0x005f, 0x00e9, 0x00e4, 0x00f6, 0x00e5, 0x00fc } + }, + { // 3 + "Czech, Slovak", + { 0x0023, 0x016f, 0x010d, 0x0165, 0x017e, 0x00fd, 0x00ed, 0x0159, 0x00e9, 0x00e1, 0x011b, 0x00fa, 0x0161 } + }, + { // 4 + "German", + { 0x0023, 0x0024, 0x00a7, 0x00c4, 0x00d6, 0x00dc, 0x005e, 0x005f, 0x00b0, 0x00e4, 0x00f6, 0x00fc, 0x00df } + }, + { // 5 + "Portuguese, Spanish", + { 0x00e7, 0x0024, 0x00a1, 0x00e1, 0x00e9, 0x00ed, 0x00f3, 0x00fa, 0x00bf, 0x00fc, 0x00f1, 0x00e8, 0x00e0 } + }, + { // 6 + "Italian", + { 0x00a3, 0x0024, 0x00e9, 0x00b0, 0x00e7, 0x00bb, 0x005e, 0x0023, 0x00f9, 0x00e0, 0x00f2, 0x00e8, 0x00ec } + }, + { // 7 + "Rumanian", + { 0x0023, 0x00a4, 0x0162, 0x00c2, 0x015e, 0x0102, 0x00ce, 0x0131, 0x0163, 0x00e2, 0x015f, 0x0103, 0x00ee } + }, + { // 8 + "Polish", + { 0x0023, 0x0144, 0x0105, 0x017b, 0x015a, 0x0141, 0x0107, 0x00f3, 0x0119, 0x017c, 0x015b, 0x0142, 0x017a } + }, + { // 9 + "Turkish", + { 0x0054, 0x011f, 0x0130, 0x015e, 0x00d6, 0x00c7, 0x00dc, 0x011e, 0x0131, 0x015f, 0x00f6, 0x00e7, 0x00fc } + }, + { // a + "Serbian, Croatian, Slovenian", + { 0x0023, 0x00cb, 0x010c, 0x0106, 0x017d, 0x0110, 0x0160, 0x00eb, 0x010d, 0x0107, 0x017e, 0x0111, 0x0161 } + }, + { // b + "Estonian", + { 0x0023, 0x00f5, 0x0160, 0x00c4, 0x00d6, 0x017e, 0x00dc, 0x00d5, 0x0161, 0x00e4, 0x00f6, 0x017e, 0x00fc } + }, + { // c + "Lettish, Lithuanian", + { 0x0023, 0x0024, 0x0160, 0x0117, 0x0119, 0x017d, 0x010d, 0x016b, 0x0161, 0x0105, 0x0173, 0x017e, 0x012f } + } +}; + +// References to the G0_LATIN_NATIONAL_SUBSETS array +const uint8_t G0_LATIN_NATIONAL_SUBSETS_MAP[56] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x01, 0x02, 0x03, 0x04, 0xff, 0x06, 0xff, + 0x00, 0x01, 0x02, 0x09, 0x04, 0x05, 0x06, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0x0a, 0xff, 0x07, + 0xff, 0xff, 0x0b, 0x03, 0x04, 0xff, 0x0c, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0x09, 0xff, 0xff, 0xff, 0xff +}; + +// --- G2 ---------------------------------------------------------------------- + +const uint16_t G2[1][96] = { + { // Latin G2 Supplementary Set + 0x0020, 0x00a1, 0x00a2, 0x00a3, 0x0024, 0x00a5, 0x0023, 0x00a7, 0x00a4, 0x2018, 0x201c, 0x00ab, 0x2190, 0x2191, 0x2192, 0x2193, + 0x00b0, 0x00b1, 0x00b2, 0x00b3, 0x00d7, 0x00b5, 0x00b6, 0x00b7, 0x00f7, 0x2019, 0x201d, 0x00bb, 0x00bc, 0x00bd, 0x00be, 0x00bf, + 0x0020, 0x0300, 0x0301, 0x0302, 0x0303, 0x0304, 0x0306, 0x0307, 0x0308, 0x0000, 0x030a, 0x0327, 0x005f, 0x030b, 0x0328, 0x030c, + 0x2015, 0x00b9, 0x00ae, 0x00a9, 0x2122, 0x266a, 0x20ac, 0x2030, 0x03B1, 0x0000, 0x0000, 0x0000, 0x215b, 0x215c, 0x215d, 0x215e, + 0x03a9, 0x00c6, 0x0110, 0x00aa, 0x0126, 0x0000, 0x0132, 0x013f, 0x0141, 0x00d8, 0x0152, 0x00ba, 0x00de, 0x0166, 0x014a, 0x0149, + 0x0138, 0x00e6, 0x0111, 0x00f0, 0x0127, 0x0131, 0x0133, 0x0140, 0x0142, 0x00f8, 0x0153, 0x00df, 0x00fe, 0x0167, 0x014b, 0x0020 + } +// { // Cyrillic G2 Supplementary Set +// }, +// { // Greek G2 Supplementary Set +// }, +// { // Arabic G2 Supplementary Set +// } +}; + +const uint16_t G2_ACCENTS[15][52] = { + // A B C D E F G H I J K L M N O P Q R S T U V W X Y Z a b c d e f g h i j k l m n o p q r s t u v w x y z + { // grave + 0x00c0, 0x0000, 0x0000, 0x0000, 0x00c8, 0x0000, 0x0000, 0x0000, 0x00cc, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x00d2, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x00d9, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x00e0, 0x0000, 0x0000, 0x0000, 0x00e8, 0x0000, + 0x0000, 0x0000, 0x00ec, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x00f2, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x00f9, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000 + }, + { // acute + 0x00c1, 0x0000, 0x0106, 0x0000, 0x00c9, 0x0000, 0x0000, 0x0000, 0x00cd, 0x0000, 0x0000, 0x0139, 0x0000, 0x0143, 0x00d3, 0x0000, + 0x0000, 0x0154, 0x015a, 0x0000, 0x00da, 0x0000, 0x0000, 0x0000, 0x00dd, 0x0179, 0x00e1, 0x0000, 0x0107, 0x0000, 0x00e9, 0x0000, + 0x0123, 0x0000, 0x00ed, 0x0000, 0x0000, 0x013a, 0x0000, 0x0144, 0x00f3, 0x0000, 0x0000, 0x0155, 0x015b, 0x0000, 0x00fa, 0x0000, + 0x0000, 0x0000, 0x00fd, 0x017a + }, + { // circumflex + 0x00c2, 0x0000, 0x0108, 0x0000, 0x00ca, 0x0000, 0x011c, 0x0124, 0x00ce, 0x0134, 0x0000, 0x0000, 0x0000, 0x0000, 0x00d4, 0x0000, + 0x0000, 0x0000, 0x015c, 0x0000, 0x00db, 0x0000, 0x0174, 0x0000, 0x0176, 0x0000, 0x00e2, 0x0000, 0x0109, 0x0000, 0x00ea, 0x0000, + 0x011d, 0x0125, 0x00ee, 0x0135, 0x0000, 0x0000, 0x0000, 0x0000, 0x00f4, 0x0000, 0x0000, 0x0000, 0x015d, 0x0000, 0x00fb, 0x0000, + 0x0175, 0x0000, 0x0177, 0x0000 + }, + { // tilde + 0x00c3, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0128, 0x0000, 0x0000, 0x0000, 0x0000, 0x00d1, 0x00d5, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0168, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x00e3, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0129, 0x0000, 0x0000, 0x0000, 0x0000, 0x00f1, 0x00f5, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0169, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000 + }, + { // macron + 0x0100, 0x0000, 0x0000, 0x0000, 0x0112, 0x0000, 0x0000, 0x0000, 0x012a, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x014c, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x016a, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0101, 0x0000, 0x0000, 0x0000, 0x0113, 0x0000, + 0x0000, 0x0000, 0x012b, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x014d, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x016b, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000 + }, + { // breve + 0x0102, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x011e, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x016c, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0103, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x011f, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x016d, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000 + }, + { // dot + 0x0000, 0x0000, 0x010a, 0x0000, 0x0116, 0x0000, 0x0120, 0x0000, 0x0130, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x017b, 0x0000, 0x0000, 0x010b, 0x0000, 0x0117, 0x0000, + 0x0121, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x017c + }, + { // umlaut + 0x00c4, 0x0000, 0x0000, 0x0000, 0x00cb, 0x0000, 0x0000, 0x0000, 0x00cf, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x00d6, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x00dc, 0x0000, 0x0000, 0x0000, 0x0178, 0x0000, 0x00e4, 0x0000, 0x0000, 0x0000, 0x00eb, 0x0000, + 0x0000, 0x0000, 0x00ef, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x00f6, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x00fc, 0x0000, + 0x0000, 0x0000, 0x00ff, 0x0000 + }, + { 0 }, + { // ring + 0x00c5, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x016e, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x00e5, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x016f, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000 + }, + { // cedilla + 0x0000, 0x0000, 0x00c7, 0x0000, 0x0000, 0x0000, 0x0122, 0x0000, 0x0000, 0x0000, 0x0136, 0x013b, 0x0000, 0x0145, 0x0000, 0x0000, + 0x0000, 0x0156, 0x015e, 0x0162, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x00e7, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0137, 0x013c, 0x0000, 0x0146, 0x0000, 0x0000, 0x0000, 0x0157, 0x015f, 0x0163, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000 + }, + { 0 }, + { // double acute + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0150, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0170, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0151, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0171, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000 + }, + { // ogonek + 0x0104, 0x0000, 0x0000, 0x0000, 0x0118, 0x0000, 0x0000, 0x0000, 0x012e, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0172, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0105, 0x0000, 0x0000, 0x0000, 0x0119, 0x0000, + 0x0000, 0x0000, 0x012f, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0173, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000 + }, + { // caron + 0x0000, 0x0000, 0x010c, 0x010e, 0x011a, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x013d, 0x0000, 0x0147, 0x0000, 0x0000, + 0x0000, 0x0158, 0x0160, 0x0164, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x017d, 0x0000, 0x0000, 0x010d, 0x010f, 0x011b, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x013e, 0x0000, 0x0148, 0x0000, 0x0000, 0x0000, 0x0159, 0x0161, 0x0165, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x017e + } +}; + +#endif diff --git a/src/telxcc.cpp b/src/telxcc.cpp new file mode 100644 index 00000000..d04b83e1 --- /dev/null +++ b/src/telxcc.cpp @@ -0,0 +1,1286 @@ +/*! +(c) 2011-2013 Forers, s. r. o.: telxcc + +telxcc conforms to ETSI 300 706 Presentation Level 1.5: Presentation Level 1 defines the basic Teletext page, +characterised by the use of spacing attributes only and a limited alphanumeric and mosaics repertoire. +Presentation Level 1.5 decoder responds as Level 1 but the character repertoire is extended via packets X/26. +Selection of national option sub-sets related features from Presentation Level 2.5 feature set have been implemented, too. +(X/28/0 Format 1, X/28/4, M/29/0 and M/29/4 packets) + +Further documentation: +ETSI TS 101 154 V1.9.1 (2009-09), Technical Specification + Digital Video Broadcasting (DVB); Specification for the use of Video and Audio Coding in Broadcasting Applications based on the MPEG-2 Transport Stream +ETSI EN 300 231 V1.3.1 (2003-04), European Standard (Telecommunications series) + Television systems; Specification of the domestic video Programme Delivery Control system (PDC) +ETSI EN 300 472 V1.3.1 (2003-05), European Standard (Telecommunications series) + Digital Video Broadcasting (DVB); Specification for conveying ITU-R System B Teletext in DVB bitstreams +ETSI EN 301 775 V1.2.1 (2003-05), European Standard (Telecommunications series) + Digital Video Broadcasting (DVB); Specification for the carriage of Vertical Blanking Information (VBI) data in DVB bitstreams +ETS 300 706 (May 1997) + Enhanced Teletext Specification +ETS 300 708 (March 1997) + Television systems; Data transmission within Teletext +ISO/IEC STANDARD 13818-1 Second edition (2000-12-01) + Information technology — Generic coding of moving pictures and associated audio information: Systems +ISO/IEC STANDARD 6937 Third edition (2001-12-15) + Information technology — Coded graphic character set for text communication — Latin alphabet +Werner Brückner -- Teletext in digital television +*/ + + +#include "ccextractor.h" +#include "hamming.h" +#include "teletext.h" +#include +#ifdef I18N +#include +#include +#define _(STRING) gettext(STRING) +#endif + +#define TELXCC_VERSION "2.4.4" + +#ifdef __MINGW32__ +// switch stdin and all normal files into binary mode -- needed for Windows +#include +int _CRT_fmode = _O_BINARY; + +// for better UX in Windows we want to detect that app is not run by "double-clicking" in Explorer +#define WIN32_LEAN_AND_MEAN +#define _WIN32_WINNT 0x0502 +#define _WIN32_IE 0x0400 +#include +#include +#endif + +// size of a TS packet in bytes +#define TS_PACKET_SIZE 188 + +// size of a TS packet payload in bytes +#define TS_PACKET_PAYLOAD_SIZE (TS_PACKET_SIZE - 4) + +// size of a packet payload buffer +#define PAYLOAD_BUFFER_SIZE 4096 + +typedef struct { + uint8_t sync; + uint8_t transport_error; + uint8_t payload_unit_start; + uint8_t transport_priority; + uint16_t pid; + uint8_t scrambling_control; + uint8_t adaptation_field_exists; + uint8_t continuity_counter; +} ts_packet_t; + +typedef struct { + uint16_t program_num; + uint16_t program_pid; +} pat_section_t; + +typedef struct { + uint8_t pointer_field; + uint8_t table_id; + uint16_t section_length; + uint8_t current_next_indicator; +} pat_t; + +typedef struct { + uint8_t ccx_stream_type; + uint16_t elementary_pid; + uint16_t es_info_length; +} pmt_program_descriptor_t; + +typedef struct { + uint8_t pointer_field; + uint8_t table_id; + uint16_t section_length; + uint16_t program_num; + uint8_t current_next_indicator; + uint16_t pcr_pid; + uint16_t program_info_length; +} pmt_t; + +typedef enum { + DATA_UNIT_EBU_TELETEXT_NONSUBTITLE = 0x02, + DATA_UNIT_EBU_TELETEXT_SUBTITLE = 0x03, + DATA_UNIT_EBU_TELETEXT_INVERTED = 0x0c, + DATA_UNIT_VPS = 0xc3, + DATA_UNIT_CLOSED_CAPTIONS = 0xc5 +} data_unit_t; + +typedef enum { + TRANSMISSION_MODE_PARALLEL = 0, + TRANSMISSION_MODE_SERIAL = 1 +} transmission_mode_t; + +static const char* TTXT_COLOURS[8] = { + //black, red, green, yellow, blue, magenta, cyan, white + "#000000", "#ff0000", "#00ff00", "#ffff00", "#0000ff", "#ff00ff", "#00ffff", "#ffffff" +}; + +#define MAX_TLT_PAGES 1000 + +static short int seen_sub_page[MAX_TLT_PAGES]; + +// 1-byte alignment; just to be sure, this struct is being used for explicit type conversion +// FIXME: remove explicit type conversion from buffer to structs +#pragma pack(push) +#pragma pack(1) +typedef struct { + uint8_t _clock_in; // clock run in + uint8_t _framing_code; // framing code, not needed, ETSI 300 706: const 0xe4 + uint8_t address[2]; + uint8_t data[40]; +} teletext_packet_payload_t; +#pragma pack(pop) + +typedef struct { + uint64_t show_timestamp; // show at timestamp (in ms) + uint64_t hide_timestamp; // hide at timestamp (in ms) + uint16_t text[25][40]; // 25 lines x 40 cols (1 screen/page) of wide chars + uint8_t tainted; // 1 = text variable contains any data +} teletext_page_t; + +// application config global variable +struct ccx_s_teletext_config tlt_config = { NO, 0, 0, 0, NO, NO, 0 }; + +// macro -- output only when increased verbosity was turned on +#define VERBOSE_ONLY if (tlt_config.verbose == YES) + +// application states -- flags for notices that should be printed only once +static struct s_states { + uint8_t programme_info_processed; + uint8_t pts_initialized; +} states = { NO, NO }; + +// SRT frames produced +uint32_t tlt_frames_produced = 0; + +// subtitle type pages bitmap, 2048 bits = 2048 possible pages in teletext (excl. subpages) +static uint8_t cc_map[256] = { 0 }; + +// last timestamp computed +static uint64_t last_timestamp = 0; + +// working teletext page buffer +teletext_page_t page_buffer = { 0 }; + +// teletext transmission mode +static transmission_mode_t transmission_mode = TRANSMISSION_MODE_SERIAL; + +// flag indicating if incoming data should be processed or ignored +static uint8_t receiving_data = NO; + +// current charset (charset can be -- and always is -- changed during transmission) +struct s_primary_charset { + uint8_t current; + uint8_t g0_m29; + uint8_t g0_x28; +} primary_charset = { + 0x00, UNDEF, UNDEF +}; + +// entities, used in colour mode, to replace unsafe HTML tag chars +struct { + uint16_t character; + const char *entity; +} const ENTITIES[] = { + { '<', "<" }, + { '>', ">" }, + { '&', "&" } +}; + +// PMTs table +#define TS_PMT_MAP_SIZE 128 +static uint16_t pmt_map[TS_PMT_MAP_SIZE] = { 0 }; +static uint16_t pmt_map_count = 0; + +// TTXT streams table +#define TS_PMT_TTXT_MAP_SIZE 128 +static uint16_t pmt_ttxt_map[TS_PMT_MAP_SIZE] = { 0 }; +static uint16_t pmt_ttxt_map_count = 0; + +// FYI, packet counter +uint32_t tlt_packet_counter = 0; + +static int telxcc_inited=0; + +#define array_length(a) (sizeof(a)/sizeof(a[0])) + +// extracts magazine number from teletext page +#define MAGAZINE(p) ((p >> 8) & 0xf) + +// extracts page number from teletext page +#define PAGE(p) (p & 0xff) + +// Current and previous page buffers. This is the output written to file when +// the time comes. +static char *page_buffer_prev=NULL; +static char *page_buffer_cur=NULL; +static unsigned page_buffer_cur_size=0; +static unsigned page_buffer_cur_used=0; +static unsigned page_buffer_prev_size=0; +static unsigned page_buffer_prev_used=0; +// Current and previous page compare strings. This is plain text (no colors, +// tags, etc) in UCS2 (fixed length), so we can compare easily. +static uint64_t *ucs2_buffer_prev=NULL; +static uint64_t *ucs2_buffer_cur=NULL; +static unsigned ucs2_buffer_cur_size=0; +static unsigned ucs2_buffer_cur_used=0; +static unsigned ucs2_buffer_prev_size=0; +static unsigned ucs2_buffer_prev_used=0; +// Buffer timestamp +static uint64_t prev_hide_timestamp; +static uint64_t prev_show_timestamp; + +void page_buffer_add_string (const char *s) +{ + if (page_buffer_cur_size<(page_buffer_cur_used+strlen (s)+1)) + { + int add=strlen (s)+4096; // So we don't need to realloc often + page_buffer_cur_size=page_buffer_cur_size+add; + page_buffer_cur=(char *) realloc (page_buffer_cur,page_buffer_cur_size); + if (!page_buffer_cur) + fatal (EXIT_NOT_ENOUGH_MEMORY, "Not enough memory to process teletext page.\n"); + } + memcpy (page_buffer_cur+page_buffer_cur_used, s, strlen (s)); + page_buffer_cur_used+=strlen (s); + page_buffer_cur[page_buffer_cur_used]=0; +} + +void ucs2_buffer_add_char (uint64_t c) +{ + if (ucs2_buffer_cur_size<(ucs2_buffer_cur_used+2)) + { + int add=4096; // So we don't need to realloc often + ucs2_buffer_cur_size=ucs2_buffer_cur_size+add; + ucs2_buffer_cur=(uint64_t *) realloc (ucs2_buffer_cur,ucs2_buffer_cur_size*sizeof (uint64_t)); + if (!ucs2_buffer_cur) + fatal (EXIT_NOT_ENOUGH_MEMORY, "Not enough memory to process teletext page.\n"); + } + ucs2_buffer_cur[ucs2_buffer_cur_used++]=c; + ucs2_buffer_cur[ucs2_buffer_cur_used]=0; +} + +void page_buffer_add_char (char c) +{ + char t[2]; + t[0]=c; + t[1]=0; + page_buffer_add_string (t); +} + +// ETS 300 706, chapter 8.2 +uint8_t unham_8_4(uint8_t a) { + uint8_t r = UNHAM_8_4[a]; + if (r == 0xff) { + dbg_print (CCX_DMT_TELETEXT, "- Unrecoverable data error; UNHAM8/4(%02x)\n", a); + } + return (r & 0x0f); +} + +// ETS 300 706, chapter 8.3 +uint32_t unham_24_18(uint32_t a) { + uint8_t test = 0; + + //Tests A-F correspond to bits 0-6 respectively in 'test'. + for (uint8_t i = 0; i < 23; i++) test ^= ((a >> i) & 0x01) * (i + 33); + //Only parity bit is tested for bit 24 + test ^= ((a >> 23) & 0x01) * 32; + + if ((test & 0x1f) != 0x1f) { + //Not all tests A-E correct + if ((test & 0x20) == 0x20) { + //F correct: Double error + return 0xffffffff; + } + //Test F incorrect: Single error + a ^= 1 << (30 - test); + } + + return (a & 0x000004) >> 2 | (a & 0x000070) >> 3 | (a & 0x007f00) >> 4 | (a & 0x7f0000) >> 5; +} + +void remap_g0_charset(uint8_t c) { + if (c != primary_charset.current) { + uint8_t m = G0_LATIN_NATIONAL_SUBSETS_MAP[c]; + if (m == 0xff) { + fprintf(stderr, "- G0 Latin National Subset ID 0x%1x.%1x is not implemented\n", (c >> 3), (c & 0x7)); + } + else { + for (uint8_t j = 0; j < 13; j++) G0[LATIN][G0_LATIN_NATIONAL_SUBSETS_POSITIONS[j]] = G0_LATIN_NATIONAL_SUBSETS[m].characters[j]; + VERBOSE_ONLY fprintf(stderr, "- Using G0 Latin National Subset ID 0x%1x.%1x (%s)\n", (c >> 3), (c & 0x7), G0_LATIN_NATIONAL_SUBSETS[m].language); + primary_charset.current = c; + } + } +} + + + +// wide char (16 bits) to utf-8 conversion +void ucs2_to_utf8(char *r, uint16_t ch) { + if (ch < 0x80) { + r[0] = ch & 0x7f; + r[1] = 0; + r[2] = 0; + } + else if (ch < 0x800) { + r[0] = (ch >> 6) | 0xc0; + r[1] = (ch & 0x3f) | 0x80; + r[2] = 0; + } + else { + r[0] = (ch >> 12) | 0xe0; + r[1] = ((ch >> 6) & 0x3f) | 0x80; + r[2] = (ch & 0x3f) | 0x80; + } + r[3] = 0; +} + +// check parity and translate any reasonable teletext character into ucs2 +uint16_t telx_to_ucs2(uint8_t c) { + if (PARITY_8[c] == 0) { + dbg_print (CCX_DMT_TELETEXT, "- Unrecoverable data error; PARITY(%02x)\n", c); + return 0x20; + } + + uint16_t r = c & 0x7f; + if (r >= 0x20) r = G0[LATIN][r - 0x20]; + return r; +} + +uint16_t bcd_page_to_int (uint16_t bcd) +{ + return ((bcd&0xf00)>>8)*100 + ((bcd&0xf0)>>4)*10 + (bcd&0xf); +} + +void telxcc_dump_prev_page (void) +{ + char c_temp1[80],c_temp2[80]; // For timing + if (!page_buffer_prev) + return; + if (ccx_options.date_format!=ODF_NONE) + { + millis_to_date (prev_show_timestamp, c_temp1); // Note: Delay not added here because it was already accounted for + millis_to_date (prev_hide_timestamp, c_temp2); + fdprintf(wbout1.fh,"%s|%s|", c_temp1,c_temp2); + } + if (ccx_options.ucla_settings) + fdprintf(wbout1.fh, "TLT|%.3u|", bcd_page_to_int (tlt_config.page)); + + if (wbout1.fh!=-1) fdprintf(wbout1.fh, "%s",page_buffer_prev); + fdprintf(wbout1.fh,"%s",encoded_crlf); + if (page_buffer_prev) free (page_buffer_prev); + if (ucs2_buffer_prev) free (ucs2_buffer_prev); + // Switch "dump" buffers + page_buffer_prev_used=page_buffer_cur_used; + page_buffer_prev_size=page_buffer_cur_size; + page_buffer_prev=page_buffer_cur; + page_buffer_cur_size=0; + page_buffer_cur_used=0; + page_buffer_cur=NULL; + // Also switch compare buffers + ucs2_buffer_prev_used=ucs2_buffer_cur_used; + ucs2_buffer_prev_size=ucs2_buffer_cur_size; + ucs2_buffer_prev=ucs2_buffer_cur; + ucs2_buffer_cur_size=0; + ucs2_buffer_cur_used=0; + ucs2_buffer_cur=NULL; +} + +// Note: c1 and c2 are just used for debug output, not for the actual comparison +int fuzzy_memcmp (const char *c1, const char *c2, const uint64_t *ucs2_buf1, unsigned ucs2_buf1_len, + const uint64_t *ucs2_buf2, unsigned ucs2_buf2_len) +{ + size_t short_len=ucs2_buf1_lenmax); + dbg_print(CCX_DMT_LEVENSHTEIN, "\rLEV | %s | %s | Max: %d | Calc: %d | Match: %d\n", c1,c2,max,l,!res); + return res; +} + +void process_page(teletext_page_t *page) { +#ifdef DEBUG + for (uint8_t row = 1; row < 25; row++) { + fprintf(stdout, "DEBUG[%02u]: ", row); + for (uint8_t col = 0; col < 40; col++) fprintf(stdout, "%3x ", page->text[row][col]); + fprintf(stdout, "\n"); + } + fprintf(stdout, "\n"); +#endif + char u[4] = {0, 0, 0, 0}; + + // optimization: slicing column by column -- higher probability we could find boxed area start mark sooner + uint8_t page_is_empty = YES; + for (uint8_t col = 0; col < 40; col++) { + for (uint8_t row = 1; row < 25; row++) { + if (page->text[row][col] == 0x0b) { + page_is_empty = NO; + goto page_is_empty; + } + } + } + page_is_empty: + if (page_is_empty == YES) return; + + if (page->show_timestamp > page->hide_timestamp) + page->hide_timestamp = page->show_timestamp; + + char timecode_show[24] = { 0 }, timecode_hide[24] = { 0 }; + + int time_reported=0; + ++tlt_frames_produced; + char c_tempb[256]; // For buffering + + timestamp_to_srttime(page->show_timestamp, timecode_show); + timecode_show[12] = 0; + timestamp_to_srttime(page->hide_timestamp, timecode_hide); + timecode_hide[12] = 0; + + // process data + for (uint8_t row = 1; row < 25; row++) { + // anchors for string trimming purpose + uint8_t col_start = 40; + uint8_t col_stop = 40; + + for (int8_t col = 39; col >= 0; col--) { + if (page->text[row][col] == 0xb) { + col_start = col; + break; + } + } + // line is empty + if (col_start > 39) continue; + + for (uint8_t col = col_start + 1; col <= 39; col++) { + if (page->text[row][col] > 0x20) { + if (col_stop > 39) col_start = col; + col_stop = col; + } + if (page->text[row][col] == 0xa) break; + } + // line is empty + if (col_stop > 39) continue; + + // ETS 300 706, chapter 12.2: Alpha White ("Set-After") - Start-of-row default condition. + // used for colour changes _before_ start box mark + // white is default as stated in ETS 300 706, chapter 12.2 + // black(0), red(1), green(2), yellow(3), blue(4), magenta(5), cyan(6), white(7) + uint8_t foreground_color = 0x7; + uint8_t font_tag_opened = NO; + + if (ccx_options.gui_mode_reports) + { + fprintf (stderr, "###SUBTITLE#"); + if (!time_reported) + { + char timecode_show_mmss[6], timecode_hide_mmss[6]; + memcpy (timecode_show_mmss, timecode_show+3, 5); + memcpy (timecode_hide_mmss, timecode_hide+3, 5); + timecode_show_mmss[5]=0; + timecode_hide_mmss[5]=0; + // Note, only MM:SS here as we need to save space in the preview window + fprintf (stderr, "%s#%s#", + timecode_show_mmss, timecode_hide_mmss); + time_reported=1; + } + else + fprintf (stderr, "##"); + } + + for (uint8_t col = 0; col <= col_stop; col++) { + // v is just a shortcut + uint16_t v = page->text[row][col]; + + if (col < col_start) { + if (v <= 0x7) foreground_color = (uint8_t) v; + } + + if (col == col_start) { + if ((foreground_color != 0x7) && !ccx_options.nofontcolor) { + sprintf (c_tempb, "", TTXT_COLOURS[foreground_color]); + page_buffer_add_string (c_tempb); + // if (wbout1.fh!=-1) fdprintf(wbout1.fh, "", TTXT_COLOURS[foreground_color]); + font_tag_opened = YES; + } + } + + if (col >= col_start) { + if (v <= 0x7) { + // ETS 300 706, chapter 12.2: Unless operating in "Hold Mosaics" mode, + // each character space occupied by a spacing attribute is displayed as a SPACE. + if (!ccx_options.nofontcolor) { + if (font_tag_opened == YES) { + page_buffer_add_string (""); + // if (wbout1.fh!=-1) fdprintf(wbout1.fh, " "); + font_tag_opened = NO; + } + + // black is considered as white for telxcc purpose + // telxcc writes tags only when needed + if ((v > 0x0) && (v < 0x7)) { + sprintf (c_tempb, "", TTXT_COLOURS[v]); + page_buffer_add_string (c_tempb); + // if (wbout1.fh!=-1) fdprintf(wbout1.fh, "", TTXT_COLOURS[v]); + font_tag_opened = YES; + } + } + else v = 0x20; + } + + if (v >= 0x20) { + ucs2_to_utf8(u, v); + uint64_t ucs2_char=(u[0]<<24) | (u[1]<<16) | (u[2]<<8) | u[3]; + ucs2_buffer_add_char(ucs2_char); + } + + if (v >= 0x20) { + // translate some chars into entities, if in colour mode + if (!ccx_options.nofontcolor) { + for (uint8_t i = 0; i < array_length(ENTITIES); i++) + if (v == ENTITIES[i].character) { + //if (wbout1.fh!=-1) fdprintf(wbout1.fh, "%s", ENTITIES[i].entity); + page_buffer_add_string (ENTITIES[i].entity); + // v < 0x20 won't be printed in next block + v = 0; + break; + } + } + } + + + if (v >= 0x20) { + //if (wbout1.fh!=-1) fdprintf(wbout1.fh, "%s", u); + page_buffer_add_string (u); + if (ccx_options.gui_mode_reports) // For now we just handle the easy stuff + fprintf (stderr,"%s",u); + } + } + } + + // no tag will left opened! + if ((!ccx_options.nofontcolor) && (font_tag_opened == YES)) { + //if (wbout1.fh!=-1) fdprintf(wbout1.fh, ""); + page_buffer_add_string (""); + font_tag_opened = NO; + } + + page_buffer_add_string ((ccx_options.write_format == CCX_OF_TRANSCRIPT) ? " " : "\r\n"); + if (ccx_options.gui_mode_reports) + { + fprintf (stderr,"\n"); + } + } + time_reported=0; + + switch (ccx_options.write_format) + { + case CCX_OF_TRANSCRIPT: + if (page_buffer_prev_used==0) + prev_show_timestamp=page->show_timestamp; + if (page_buffer_prev_used==0 || + fuzzy_memcmp (page_buffer_prev, page_buffer_cur, + ucs2_buffer_prev, ucs2_buffer_prev_used, + ucs2_buffer_cur, ucs2_buffer_cur_used + )==0) + { + // If empty previous buffer, we just start one with the + // current page and do nothing. Wait until we see more. + if (page_buffer_prev) free (page_buffer_prev); + page_buffer_prev_used=page_buffer_cur_used; + page_buffer_prev_size=page_buffer_cur_size; + page_buffer_prev=page_buffer_cur; + page_buffer_cur_size=0; + page_buffer_cur_used=0; + page_buffer_cur=NULL; + if (ucs2_buffer_prev) free (ucs2_buffer_prev); + ucs2_buffer_prev_used=ucs2_buffer_cur_used; + ucs2_buffer_prev_size=ucs2_buffer_cur_size; + ucs2_buffer_prev=ucs2_buffer_cur; + ucs2_buffer_cur_size=0; + ucs2_buffer_cur_used=0; + ucs2_buffer_cur=NULL; + prev_hide_timestamp=page->hide_timestamp; + break; + } + else + { + // OK, the old and new buffer don't match. So write the old + telxcc_dump_prev_page(); + prev_hide_timestamp=page->hide_timestamp; + prev_show_timestamp=page->show_timestamp; + } + break; + default: // Yes, this means everything else is .srt for now + page_buffer_add_string ("\r\n"); + if (wbout1.fh!=-1) fdprintf(wbout1.fh,"%"PRIu32"\r\n%s --> %s\r\n", tlt_frames_produced, timecode_show, timecode_hide); + if (wbout1.fh!=-1) fdprintf(wbout1.fh, "%s",page_buffer_cur); + } + + // Also update GUI... + + page_buffer_cur_used=0; + if (page_buffer_cur) + page_buffer_cur[0]=0; + if (ccx_options.gui_mode_reports) + fflush (stderr); +} + +void process_telx_packet(data_unit_t data_unit_id, teletext_packet_payload_t *packet, uint64_t timestamp) { + // variable names conform to ETS 300 706, chapter 7.1.2 + uint8_t address = (unham_8_4(packet->address[1]) << 4) | unham_8_4(packet->address[0]); + uint8_t m = address & 0x7; + if (m == 0) m = 8; + uint8_t y = (address >> 3) & 0x1f; + uint8_t designation_code = (y > 25) ? unham_8_4(packet->data[0]) : 0x00; + + if (y == 0) { + // CC map + uint8_t i = (unham_8_4(packet->data[1]) << 4) | unham_8_4(packet->data[0]); + uint8_t flag_subtitle = (unham_8_4(packet->data[5]) & 0x08) >> 3; + cc_map[i] |= flag_subtitle << (m - 1); + + if ((flag_subtitle == YES) && (i < 0xff)) + { + int thisp= (m << 8) | (unham_8_4(packet->data[1]) << 4) | unham_8_4(packet->data[0]); + char t1[10]; + sprintf (t1,"%x",thisp); // Example: 1928 -> 788 + thisp=atoi (t1); + if (!seen_sub_page[thisp]) + { + seen_sub_page[thisp]=1; + mprint ("\rNotice: Teletext page with possible subtitles detected: %03d\n",thisp); + } + } + if ((tlt_config.page == 0) && (flag_subtitle == YES) && (i < 0xff)) { + tlt_config.page = (m << 8) | (unham_8_4(packet->data[1]) << 4) | unham_8_4(packet->data[0]); + mprint ("- No teletext page specified, first received suitable page is %03x, not guaranteed\n", tlt_config.page); + } + + // Page number and control bits + uint16_t page_number = (m << 8) | (unham_8_4(packet->data[1]) << 4) | unham_8_4(packet->data[0]); + uint8_t charset = ((unham_8_4(packet->data[7]) & 0x08) | (unham_8_4(packet->data[7]) & 0x04) | (unham_8_4(packet->data[7]) & 0x02)) >> 1; + //uint8_t flag_suppress_header = unham_8_4(packet->data[6]) & 0x01; + //uint8_t flag_inhibit_display = (unham_8_4(packet->data[6]) & 0x08) >> 3; + + // ETS 300 706, chapter 9.3.1.3: + // When set to '1' the service is designated to be in Serial mode and the transmission of a page is terminated + // by the next page header with a different page number. + // When set to '0' the service is designated to be in Parallel mode and the transmission of a page is terminated + // by the next page header with a different page number but the same magazine number. + // The same setting shall be used for all page headers in the service. + // ETS 300 706, chapter 7.2.1: Page is terminated by and excludes the next page header packet + // having the same magazine address in parallel transmission mode, or any magazine address in serial transmission mode. + transmission_mode = (transmission_mode_t) (unham_8_4(packet->data[7]) & 0x01); + + // FIXME: Well, this is not ETS 300 706 kosher, however we are interested in DATA_UNIT_EBU_TELETEXT_SUBTITLE only + if ((transmission_mode == TRANSMISSION_MODE_PARALLEL) && (data_unit_id != DATA_UNIT_EBU_TELETEXT_SUBTITLE)) return; + + if ((receiving_data == YES) && ( + ((transmission_mode == TRANSMISSION_MODE_SERIAL) && (PAGE(page_number) != PAGE(tlt_config.page))) || + ((transmission_mode == TRANSMISSION_MODE_PARALLEL) && (PAGE(page_number) != PAGE(tlt_config.page)) && (m == MAGAZINE(tlt_config.page))) + )) { + receiving_data = NO; + return; + } + + // Page transmission is terminated, however now we are waiting for our new page + if (page_number != tlt_config.page) return; + + // Now we have the begining of page transmission; if there is page_buffer pending, process it + if (page_buffer.tainted == YES) { + // it would be nice, if subtitle hides on previous video frame, so we contract 40 ms (1 frame @25 fps) + page_buffer.hide_timestamp = timestamp - 40; + process_page(&page_buffer); + } + + page_buffer.show_timestamp = timestamp; + page_buffer.hide_timestamp = 0; + memset(page_buffer.text, 0x00, sizeof(page_buffer.text)); + page_buffer.tainted = NO; + receiving_data = YES; + primary_charset.g0_x28 = UNDEF; + + uint8_t c = (primary_charset.g0_m29 != UNDEF) ? primary_charset.g0_m29 : charset; + remap_g0_charset(c); + + /* + // I know -- not needed; in subtitles we will never need disturbing teletext page status bar + // displaying tv station name, current time etc. + if (flag_suppress_header == NO) { + for (uint8_t i = 14; i < 40; i++) page_buffer.text[y][i] = telx_to_ucs2(packet->data[i]); + //page_buffer.tainted = YES; + } + */ + } + else if ((m == MAGAZINE(tlt_config.page)) && (y >= 1) && (y <= 23) && (receiving_data == YES)) { + // ETS 300 706, chapter 9.4.1: Packets X/26 at presentation Levels 1.5, 2.5, 3.5 are used for addressing + // a character location and overwriting the existing character defined on the Level 1 page + // ETS 300 706, annex B.2.2: Packets with Y = 26 shall be transmitted before any packets with Y = 1 to Y = 25; + // so page_buffer.text[y][i] may already contain any character received + // in frame number 26, skip original G0 character + for (uint8_t i = 0; i < 40; i++) if (page_buffer.text[y][i] == 0x00) page_buffer.text[y][i] = telx_to_ucs2(packet->data[i]); + page_buffer.tainted = YES; + } + else if ((m == MAGAZINE(tlt_config.page)) && (y == 26) && (receiving_data == YES)) { + // ETS 300 706, chapter 12.3.2: X/26 definition + uint8_t x26_row = 0; + uint8_t x26_col = 0; + + uint32_t triplets[13] = { 0 }; + for (uint8_t i = 1, j = 0; i < 40; i += 3, j++) triplets[j] = unham_24_18((packet->data[i + 2] << 16) | (packet->data[i + 1] << 8) | packet->data[i]); + + for (uint8_t j = 0; j < 13; j++) { + // invalid data (HAM24/18 uncorrectable error detected), skip group + if (triplets[j] == 0xffffffff) { + dbg_print (CCX_DMT_TELETEXT, "- Unrecoverable data error; UNHAM24/18()=%04x\n", triplets[j]); + continue; + } + + uint8_t data = (triplets[j] & 0x3f800) >> 11; + uint8_t mode = (triplets[j] & 0x7c0) >> 6; + uint8_t address = triplets[j] & 0x3f; + uint8_t row_address_group = (address >= 40) && (address <= 63); + + // ETS 300 706, chapter 12.3.1, table 27: set active position + if ((mode == 0x04) && (row_address_group == YES)) { + x26_row = address - 40; + if (x26_row == 0) x26_row = 24; + x26_col = 0; + } + + // ETS 300 706, chapter 12.3.1, table 27: termination marker + if ((mode >= 0x11) && (mode <= 0x1f) && (row_address_group == YES)) break; + + // ETS 300 706, chapter 12.3.1, table 27: character from G2 set + if ((mode == 0x0f) && (row_address_group == NO)) { + x26_col = address; + if (data > 31) page_buffer.text[x26_row][x26_col] = G2[0][data - 0x20]; + } + + // ETS 300 706, chapter 12.3.1, table 27: G0 character with diacritical mark + if ((mode >= 0x11) && (mode <= 0x1f) && (row_address_group == NO)) { + x26_col = address; + + // A - Z + if ((data >= 65) && (data <= 90)) page_buffer.text[x26_row][x26_col] = G2_ACCENTS[mode - 0x11][data - 65]; + // a - z + else if ((data >= 97) && (data <= 122)) page_buffer.text[x26_row][x26_col] = G2_ACCENTS[mode - 0x11][data - 71]; + // other + else page_buffer.text[x26_row][x26_col] = telx_to_ucs2(data); + } + } + } + else if ((m == MAGAZINE(tlt_config.page)) && (y == 28) && (receiving_data == YES)) { + // TODO: + // ETS 300 706, chapter 9.4.7: Packet X/28/4 + // Where packets 28/0 and 28/4 are both transmitted as part of a page, packet 28/0 takes precedence over 28/4 for all but the colour map entry coding. + if ((designation_code == 0) || (designation_code == 4)) { + // ETS 300 706, chapter 9.4.2: Packet X/28/0 Format 1 + // ETS 300 706, chapter 9.4.7: Packet X/28/4 + uint32_t triplet0 = unham_24_18((packet->data[3] << 16) | (packet->data[2] << 8) | packet->data[1]); + + if (triplet0 == 0xffffffff) { + // invalid data (HAM24/18 uncorrectable error detected), skip group + dbg_print (CCX_DMT_TELETEXT, "! Unrecoverable data error; UNHAM24/18()=%04x\n", triplet0); + } + else { + // ETS 300 706, chapter 9.4.2: Packet X/28/0 Format 1 only + if ((triplet0 & 0x0f) == 0x00) { + primary_charset.g0_x28 = (triplet0 & 0x3f80) >> 7; + remap_g0_charset(primary_charset.g0_x28); + } + } + } + } + else if ((m == MAGAZINE(tlt_config.page)) && (y == 29)) { + // TODO: + // ETS 300 706, chapter 9.5.1 Packet M/29/0 + // Where M/29/0 and M/29/4 are transmitted for the same magazine, M/29/0 takes precedence over M/29/4. + if ((designation_code == 0) || (designation_code == 4)) { + // ETS 300 706, chapter 9.5.1: Packet M/29/0 + // ETS 300 706, chapter 9.5.3: Packet M/29/4 + uint32_t triplet0 = unham_24_18((packet->data[3] << 16) | (packet->data[2] << 8) | packet->data[1]); + + if (triplet0 == 0xffffffff) { + // invalid data (HAM24/18 uncorrectable error detected), skip group + dbg_print (CCX_DMT_TELETEXT, "! Unrecoverable data error; UNHAM24/18()=%04x\n", triplet0); + } + else { + // ETS 300 706, table 11: Coding of Packet M/29/0 + // ETS 300 706, table 13: Coding of Packet M/29/4 + if ((triplet0 & 0xff) == 0x00) { + primary_charset.g0_m29 = (triplet0 & 0x3f80) >> 7; + // X/28 takes precedence over M/29 + if (primary_charset.g0_x28 == UNDEF) { + remap_g0_charset(primary_charset.g0_m29); + } + } + } + } + } + else if ((m == 8) && (y == 30)) { + // ETS 300 706, chapter 9.8: Broadcast Service Data Packets + if (states.programme_info_processed == NO) { + // ETS 300 706, chapter 9.8.1: Packet 8/30 Format 1 + if (unham_8_4(packet->data[0]) < 2) { + mprint ("- Programme Identification Data = "); + for (uint8_t i = 20; i < 40; i++) { + uint8_t c = telx_to_ucs2(packet->data[i]); + // strip any control codes from PID, eg. TVP station + if (c < 0x20) continue; + + char u[4] = { 0, 0, 0, 0 }; + ucs2_to_utf8(u, c); + mprint ( "%s", u); + } + mprint ("\n"); + + // OMG! ETS 300 706 stores timestamp in 7 bytes in Modified Julian Day in BCD format + HH:MM:SS in BCD format + // + timezone as 5-bit count of half-hours from GMT with 1-bit sign + // In addition all decimals are incremented by 1 before transmission. + uint32_t t = 0; + // 1st step: BCD to Modified Julian Day + t += (packet->data[10] & 0x0f) * 10000; + t += ((packet->data[11] & 0xf0) >> 4) * 1000; + t += (packet->data[11] & 0x0f) * 100; + t += ((packet->data[12] & 0xf0) >> 4) * 10; + t += (packet->data[12] & 0x0f); + t -= 11111; + // 2nd step: conversion Modified Julian Day to unix timestamp + t = (t - 40587) * 86400; + // 3rd step: add time + t += 3600 * ( ((packet->data[13] & 0xf0) >> 4) * 10 + (packet->data[13] & 0x0f) ); + t += 60 * ( ((packet->data[14] & 0xf0) >> 4) * 10 + (packet->data[14] & 0x0f) ); + t += ( ((packet->data[15] & 0xf0) >> 4) * 10 + (packet->data[15] & 0x0f) ); + t -= 40271; + // 4th step: conversion to time_t + time_t t0 = (time_t)t; + // ctime output itself is \n-ended + mprint ("- Universal Time Co-ordinated = %s", ctime(&t0)); + + dbg_print (CCX_DMT_TELETEXT, "- Transmission mode = %s\n", (transmission_mode == TRANSMISSION_MODE_SERIAL ? "serial" : "parallel")); + + if (ccx_options.write_format == CCX_OF_TRANSCRIPT && ccx_options.date_format==ODF_DATE && !ccx_options.noautotimeref) { + mprint ("- Broadcast Service Data Packet received, resetting UTC referential value to %s", ctime(&t0)); + utc_refvalue = t; + states.pts_initialized = NO; + } + + states.programme_info_processed = YES; + } + } + } +} + +void tlt_process_pes_packet(uint8_t *buffer, uint16_t size) { + tlt_packet_counter++; + if (size < 6) return; + + // Packetized Elementary Stream (PES) 32-bit start code + uint64_t pes_prefix = (buffer[0] << 16) | (buffer[1] << 8) | buffer[2]; + uint8_t pes_stream_id = buffer[3]; + + // check for PES header + if (pes_prefix != 0x000001) return; + + // stream_id is not "Private Stream 1" (0xbd) + if (pes_stream_id != 0xbd) return; + + // PES packet length + // ETSI EN 301 775 V1.2.1 (2003-05) chapter 4.3: (N x 184) - 6 + 6 B header + uint16_t pes_packet_length = 6 + ((buffer[4] << 8) | buffer[5]); + // Can be zero. If the "PES packet length" is set to zero, the PES packet can be of any length. + // A value of zero for the PES packet length can be used only when the PES packet payload is a video elementary stream. + if (pes_packet_length == 6) return; + + // truncate incomplete PES packets + if (pes_packet_length > size) pes_packet_length = size; + + uint8_t optional_pes_header_included = NO; + uint16_t optional_pes_header_length = 0; + // optional PES header marker bits (10.. ....) + if ((buffer[6] & 0xc0) == 0x80) { + optional_pes_header_included = YES; + optional_pes_header_length = buffer[8]; + } + + // should we use PTS or PCR? + static uint8_t using_pts = UNDEF; + if (using_pts == UNDEF) { + if ((optional_pes_header_included == YES) && ((buffer[7] & 0x80) > 0)) { + using_pts = YES; + dbg_print (CCX_DMT_TELETEXT, "- PID 0xbd PTS available\n"); + } else { + using_pts = NO; + dbg_print (CCX_DMT_TELETEXT, "- PID 0xbd PTS unavailable, using TS PCR\n"); + } + } + + uint32_t t = 0; + // If there is no PTS available, use global PCR + if (using_pts == NO) { + t = global_timestamp; + } + // if (using_pts == NO) t = get_pts(); + else { + // PTS is 33 bits wide, however, timestamp in ms fits into 32 bits nicely (PTS/90) + // presentation and decoder timestamps use the 90 KHz clock, hence PTS/90 = [ms] + uint64_t pts = 0; + // __MUST__ assign value to uint64_t and __THEN__ rotate left by 29 bits + // << is defined for signed int (as in "C" spec.) and overflow occures + pts = (buffer[9] & 0x0e); + pts <<= 29; + pts |= (buffer[10] << 22); + pts |= ((buffer[11] & 0xfe) << 14); + pts |= (buffer[12] << 7); + pts |= ((buffer[13] & 0xfe) >> 1); + t = (uint32_t) (pts / 90); + } + + static int64_t delta = 0; + static uint32_t t0 = 0; + if (states.pts_initialized == NO) { + if (utc_refvalue == UINT64_MAX) + delta = (uint64_t) (subs_delay - t); + else + delta = (uint64_t) (subs_delay + 1000 * utc_refvalue - t); + t0 = t; + states.pts_initialized = YES; + if ((using_pts == NO) && (global_timestamp == 0)) { + // We are using global PCR, nevertheless we still have not received valid PCR timestamp yet + states.pts_initialized = NO; + } + } + if (t < t0) delta = last_timestamp; + last_timestamp = t + delta; + t0 = t; + + // skip optional PES header and process each 46-byte teletext packet + uint16_t i = 7; + if (optional_pes_header_included == YES) i += 3 + optional_pes_header_length; + while (i <= pes_packet_length - 6) { + uint8_t data_unit_id = buffer[i++]; + uint8_t data_unit_len = buffer[i++]; + + if ((data_unit_id == DATA_UNIT_EBU_TELETEXT_NONSUBTITLE) || (data_unit_id == DATA_UNIT_EBU_TELETEXT_SUBTITLE)) { + // teletext payload has always size 44 bytes + if (data_unit_len == 44) { + // reverse endianess (via lookup table), ETS 300 706, chapter 7.1 + for (uint8_t j = 0; j < data_unit_len; j++) buffer[i + j] = REVERSE_8[buffer[i + j]]; + + // FIXME: This explicit type conversion could be a problem some day -- do not need to be platform independant + process_telx_packet((data_unit_t) data_unit_id, (teletext_packet_payload_t *)&buffer[i], last_timestamp); + } + } + + i += data_unit_len; + } +} + +void analyze_pat(uint8_t *buffer, uint8_t size) { + if (size < 7) return; + + pat_t pat = { 0 }; + pat.pointer_field = buffer[0]; + +//! +if (pat.pointer_field > 0) { + fatal (EXIT_UNSUPPORTED, "! pat.pointer_field > 0 (0x%02x)\n\n", pat.pointer_field); +} + + pat.table_id = buffer[1]; + if (pat.table_id == 0x00) { + pat.section_length = ((buffer[2] & 0x03) << 8) | buffer[3]; + pat.current_next_indicator = buffer[6] & 0x01; + // already valid PAT + if (pat.current_next_indicator == 1) { + uint16_t i = 9; + while ((i < 9 + (pat.section_length - 5 - 4)) && (i < size)) { + pat_section_t section = { 0 }; + section.program_num = (buffer[i] << 8) | buffer[i + 1]; + section.program_pid = ((buffer[i + 2] & 0x1f) << 8) | buffer[i + 3]; + + if (in_array(pmt_map, pmt_map_count, section.program_pid) == NO) { + if (pmt_map_count < TS_PMT_MAP_SIZE) { + pmt_map[pmt_map_count++] = section.program_pid; +#ifdef DEBUG + fprintf(stderr, "- Found PMT for SID %"PRIu16" (0x%x)\n", section.program_num, section.program_num); +#endif + } + } + + i += 4; + } + } + } +} + +void analyze_pmt(uint8_t *buffer, uint8_t size) { + if (size < 7) return; + + pmt_t pmt = { 0 }; + pmt.pointer_field = buffer[0]; + +//! +if (pmt.pointer_field > 0) { + fatal (EXIT_UNSUPPORTED, "! pmt.pointer_field > 0 (0x%02x)\n\n", pmt.pointer_field); +} + + pmt.table_id = buffer[1]; + if (pmt.table_id == 0x02) { + pmt.section_length = ((buffer[2] & 0x03) << 8) | buffer[3]; + pmt.program_num = (buffer[4] << 8) | buffer[5]; + pmt.current_next_indicator = buffer[6] & 0x01; + pmt.pcr_pid = ((buffer[9] & 0x1f) << 8) | buffer[10]; + pmt.program_info_length = ((buffer[11] & 0x03) << 8) | buffer[12]; + // already valid PMT + if (pmt.current_next_indicator == 1) { + uint16_t i = 13 + pmt.program_info_length; + while ((i < 13 + (pmt.program_info_length + pmt.section_length - 4 - 9)) && (i < size)) { + pmt_program_descriptor_t desc = { 0 }; + desc.ccx_stream_type = buffer[i]; + desc.elementary_pid = ((buffer[i + 1] & 0x1f) << 8) | buffer[i + 2]; + desc.es_info_length = ((buffer[i + 3] & 0x03) << 8) | buffer[i + 4]; + + uint8_t descriptor_tag = buffer[i + 5]; + // descriptor_tag: 0x45 = VBI_data_descriptor, 0x46 = VBI_teletext_descriptor, 0x56 = teletext_descriptor + if ((desc.ccx_stream_type == 0x06) && ((descriptor_tag == 0x45) || (descriptor_tag == 0x46) || (descriptor_tag == 0x56))) { + if (in_array(pmt_ttxt_map, pmt_ttxt_map_count, desc.elementary_pid) == NO) { + if (pmt_ttxt_map_count < TS_PMT_TTXT_MAP_SIZE) { + pmt_ttxt_map[pmt_ttxt_map_count++] = desc.elementary_pid; + if (tlt_config.tid == 0) tlt_config.tid = desc.elementary_pid; + mprint ("- Found VBI/teletext stream ID %"PRIu16" (0x%x) for SID %"PRIu16" (0x%x)\n", desc.elementary_pid, desc.elementary_pid, pmt.program_num, pmt.program_num); + } + } + } + + i += 5 + desc.es_info_length; + } + } + } +} + +// graceful exit support +uint8_t exit_request = NO; + +// Called only when teletext is detected or forced and it's going to be used for extraction. +void telxcc_init(void) +{ + if (!telxcc_inited) + { + telxcc_inited=1; + if (wbout1.fh!=-1 && ccx_options.encoding!=CCX_ENC_UTF_8) // If encoding it UTF8 then this was already done + fdprintf(wbout1.fh, "\xef\xbb\xbf"); + memset (seen_sub_page,0,MAX_TLT_PAGES*sizeof (short int)); + } +} + +// Close output +void telxcc_close(void) +{ + if (telxcc_inited) + { + // output any pending close caption + if (page_buffer.tainted == YES) { + // this time we do not subtract any frames, there will be no more frames + page_buffer.hide_timestamp = last_timestamp; + process_page(&page_buffer); + } + telxcc_dump_prev_page(); + + if ((tlt_frames_produced == 0) && (tlt_config.nonempty == YES)) { + if (wbout1.fh!=-1) fdprintf(wbout1.fh, "1\r\n00:00:00,000 --> 00:00:10,000\r\n(no closed captions available)\r\n\r\n"); + tlt_frames_produced++; + } + } +} + +int main_telxcc (int argc, char *argv[]) { +/* + // command line params parsing + for (uint8_t i = 1; i < argc; i++) { + if (strcmp(argv[i], "-h") == 0) { + fprintf(stderr, "Usage: telxcc [-h] [-v] [-p PAGE] [-t TID] [-o OFFSET] [-n] [-1] [-c]\n"); + fprintf(stderr, " STDIN transport stream\n"); + fprintf(stderr, " STDOUT subtitles in SubRip SRT file format (UTF-8 encoded)\n"); + fprintf(stderr, " -h this help text\n"); + fprintf(stderr, " -v be verbose\n"); + fprintf(stderr, " -p PAGE teletext page number carrying closed captions\n"); + fprintf(stderr, " -t TID transport stream PID of teletext data sub-stream\n"); + fprintf(stderr, " -o OFFSET subtitles offset in seconds\n"); + fprintf(stderr, " -n do not print UTF-8 BOM characters to the file\n"); + fprintf(stderr, " -1 produce at least one (dummy) frame\n"); + fprintf(stderr, " -c output colour information in font HTML tags\n"); + fprintf(stderr, "\n"); + exit(EXIT_SUCCESS); + } + else if ((strcmp(argv[i], "-t") == 0) && (argc > i + 1)) + tlt_config.tid = atoi(argv[++i]); + else if ((strcmp(argv[i], "-o") == 0) && (argc > i + 1)) + tlt_config.offset = atof(argv[++i]); + else if (strcmp(argv[i], "-n") == 0) + tlt_config.bom = NO; + else if (strcmp(argv[i], "-1") == 0) + tlt_config.nonempty = YES; + else if (strcmp(argv[i], "-c") == 0) + tlt_config.colours = YES; + else { + fprintf(stderr, "- Unknown option %s\n", argv[i]); + exit(EXIT_FAILURE); + } + } +*/ + + FILE *infile=fopen ("F:\\ConBackup\\Closed captions\\BrokenSamples\\RDVaughan\\uk_dvb_example.mpg","rb"); + + // full buffering -- disables flushing after CR/FL, we will flush manually whole SRT frames + // setvbuf(stdout, (char*)NULL, _IOFBF, 0); + + // print UTF-8 BOM chars + if (tlt_config.bom == YES) { + if (wbout1.fh!=-1) fdprintf(wbout1.fh, "\xef\xbb\xbf"); + } + + // TS packet buffer + uint8_t ts_buffer[TS_PACKET_SIZE] = { 0 }; + + // 255 means not set yet + uint8_t continuity_counter = 255; + + // PES packet buffer + uint8_t payload_buffer[PAYLOAD_BUFFER_SIZE] = { 0 }; + uint16_t payload_counter = 0; + + // reading input + while ((exit_request == NO) && (fread(&ts_buffer, 1, TS_PACKET_SIZE, infile) == TS_PACKET_SIZE)) { + // Transport Stream Header + // We do not use buffer to struct loading (e.g. ts_packet_t *header = (ts_packet_t *)buffer;) + // -- struct packing is platform dependant and not performing well. + ts_packet_t header = { 0 }; + header.sync = ts_buffer[0]; + header.transport_error = (ts_buffer[1] & 0x80) >> 7; + header.payload_unit_start = (ts_buffer[1] & 0x40) >> 6; + header.transport_priority = (ts_buffer[1] & 0x20) >> 5; + header.pid = ((ts_buffer[1] & 0x1f) << 8) | ts_buffer[2]; + header.scrambling_control = (ts_buffer[3] & 0xc0) >> 6; + header.adaptation_field_exists = (ts_buffer[3] & 0x20) >> 5; + header.continuity_counter = ts_buffer[3] & 0x0f; + //uint8_t ts_payload_exists = (ts_buffer[3] & 0x10) >> 4; + + uint8_t af_discontinuity = 0; + if (header.adaptation_field_exists > 0) { + af_discontinuity = (ts_buffer[5] & 0x80) >> 7; + } + + // not TS packet? + if (header.sync != 0x47) + fatal (EXIT_UNSUPPORTED, "- Invalid TS packet header\n- telxcc does not work with unaligned TS.\n\n"); + + // uncorrectable error? + if (header.transport_error > 0) { + dbg_print (CCX_DMT_TELETEXT, "- Uncorrectable TS packet error (received CC %1x)\n", header.continuity_counter); + continue; + } + + // if available, calculate current PCR + if (header.adaptation_field_exists > 0) { + // PCR in adaptation field + uint8_t af_pcr_exists = (ts_buffer[5] & 0x10) >> 4; + if (af_pcr_exists > 0) { + uint64_t pts = 0; + pts |= (ts_buffer[6] << 25); + pts |= (ts_buffer[7] << 17); + pts |= (ts_buffer[8] << 9); + pts |= (ts_buffer[9] << 1); + pts |= (ts_buffer[10] >> 7); + global_timestamp = (uint32_t) pts / 90; + pts = ((ts_buffer[10] & 0x01) << 8); + pts |= ts_buffer[11]; + global_timestamp += (uint32_t) (pts / 27000); + if (!global_timestamp_inited) + { + min_global_timestamp = global_timestamp; + global_timestamp_inited = 1; + } + } + } + + // null packet + if (header.pid == 0x1fff) continue; + + // TID not specified, autodetect via PAT/PMT + if (tlt_config.tid == 0) { + // process PAT + if (header.pid == 0x0000) { + analyze_pat(&ts_buffer[4], TS_PACKET_PAYLOAD_SIZE); + continue; + } + + // process PMT + if (in_array(pmt_map, pmt_map_count, header.pid) == YES) { + analyze_pmt(&ts_buffer[4], TS_PACKET_PAYLOAD_SIZE); + continue; + } + } + + if (tlt_config.tid == header.pid) { + // TS continuity check + if (continuity_counter == 255) continuity_counter = header.continuity_counter; + else { + if (af_discontinuity == 0) { + continuity_counter = (continuity_counter + 1) % 16; + if (header.continuity_counter != continuity_counter) { + dbg_print (CCX_DMT_TELETEXT, "- Missing TS packet, flushing pes_buffer (expected CC %1x, received CC %1x, TS discontinuity %s, TS priority %s)\n", + continuity_counter, header.continuity_counter, (af_discontinuity ? "YES" : "NO"), (header.transport_priority ? "YES" : "NO")); + payload_counter = 0; + continuity_counter = 255; + } + } + } + + // waiting for first payload_unit_start indicator + if ((header.payload_unit_start == 0) && (payload_counter == 0)) continue; + + // proceed with payload buffer + if ((header.payload_unit_start > 0) && (payload_counter > 0)) tlt_process_pes_packet(payload_buffer, payload_counter); + + // new payload frame start + if (header.payload_unit_start > 0) payload_counter = 0; + + // add payload data to buffer + if (payload_counter < (PAYLOAD_BUFFER_SIZE - TS_PACKET_PAYLOAD_SIZE)) { + memcpy(&payload_buffer[payload_counter], &ts_buffer[4], TS_PACKET_PAYLOAD_SIZE); + payload_counter += TS_PACKET_PAYLOAD_SIZE; + tlt_packet_counter++; + } + else + dbg_print (CCX_DMT_TELETEXT, "- packet payload size exceeds payload_buffer size, probably not teletext stream\n"); + } + } + + + VERBOSE_ONLY { + if (tlt_config.tid == 0) dbg_print (CCX_DMT_TELETEXT, "- No teletext PID specified, no suitable PID found in PAT/PMT tables. Please specify teletext PID via -t parameter.\n"); + if (tlt_frames_produced == 0) dbg_print (CCX_DMT_TELETEXT, "- No frames produced. CC teletext page number was probably wrong.\n"); + dbg_print (CCX_DMT_TELETEXT, "- There were some CC data carried via pages = "); + // We ignore i = 0xff, because 0xffs are teletext ending frames + for (uint16_t i = 0; i < 255; i++) + for (uint8_t j = 0; j < 8; j++) { + uint8_t v = cc_map[i] & (1 << j); + if (v > 0) fprintf(stderr, "%03x ", ((j + 1) << 8) | i); + } + fprintf(stderr, "\n"); + } + + + return EXIT_SUCCESS; +} + \ No newline at end of file diff --git a/src/timing.cpp b/src/timing.cpp new file mode 100644 index 00000000..f2c23f96 --- /dev/null +++ b/src/timing.cpp @@ -0,0 +1,288 @@ +#include "ccextractor.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) + +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) + return; + + // 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; + + // 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 >=5 ) + { + // 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); + pts_jump = 1; + pts_big_change=1; + + // Discard the gap if it is not on an I-frame or temporal reference + // zero. + if(current_tref != 0 && current_picture_coding_type != CCX_FRAME_TYPE_I_FRAME) + { + fts_now = fts_max; + mprint ("Change did not occur on first frame - probably a broken GOP\n"); + return; + } + } + } + + // Set min_pts, fts_offset + if (pts_set!=0) + { + pts_set=2; + + // Use this part only the first time min_pts is set. Later treat + // it as a reference clock change + if (current_pts fts_max ) + { + fts_max = fts_now; + } +} + + +LLONG get_fts(void) +{ + LLONG fts; + + switch (current_field) + { + case 1: + fts = fts_now + fts_global + cb_field1*1001/30; + break; + case 2: + fts = fts_now + fts_global + cb_field2*1001/30; + break; + case 3: + fts = fts_now + fts_global + cb_708*1001/30; + break; + default: + fatal(EXIT_BUG_BUG, "Cannot be reached!"); + } + + return fts; +} + +LLONG get_fts_max(void) +{ + // This returns the maximum FTS that belonged to a frame. Caption block + // counters are not applicable. + return fts_max + fts_global; +} + +/* Fill a static buffer with a time string (hh:mm:ss:ms) corresponding +to the microsecond value in mstime. */ +char *print_mstime2buf( LLONG mstime , char *buf ) +{ + unsigned hh,mm,ss,ms; + int signoffset = (mstime < 0 ? 1 : 0); + + if (mstime<0) // Avoid loss of data warning with abs() + mstime=-mstime; + hh = (unsigned) (mstime/1000/60/60); + mm = (unsigned) (mstime/1000/60 - 60*hh); + ss = (unsigned) (mstime/1000 - 60*(mm + 60*hh)); + ms = (int) (mstime - 1000*(ss + 60*(mm + 60*hh))); + + buf[0]='-'; + sprintf (buf+signoffset, "%02u:%02u:%02u:%03u",hh,mm,ss,ms); + + return buf; +} + +/* Fill a static buffer with a time string (hh:mm:ss:ms) corresponding +to the microsecond value in mstime. */ +char *print_mstime( LLONG mstime ) +{ + static char buf[15]; // 14 should be long enough + return print_mstime2buf (mstime, buf); +} + +/* Helper function for to display debug timing info. */ +void print_debug_timing( void ) +{ + // Avoid wrong "Calc. difference" and "Asynchronous by" numbers + // for uninitialized min_pts + LLONG tempmin_pts = (min_pts==0x01FFFFFFFFLL ? sync_pts : min_pts); + + mprint("Sync time stamps: PTS: %s ", + print_mstime((sync_pts)/(MPEG_CLOCK_FREQ/1000)) ); + mprint("GOP: %s \n", print_mstime(gop_time.ms)); + + // Length first GOP to last GOP + LLONG goplenms = LLONG(gop_time.ms - first_gop_time.ms); + // Length at last sync point + LLONG ptslenms = unsigned((sync_pts-tempmin_pts)/(MPEG_CLOCK_FREQ/1000) + + fts_offset); + + mprint("Last FTS: %s", + print_mstime(get_fts_max())); + mprint(" 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", + get_fts_max()+LLONG(1000.0/current_fps)-ptslenms, + get_fts_max()+LLONG(1000.0/current_fps)-goplenms); +} + +void calculate_ms_gop_time (struct gop_time_code *g) +{ + int seconds=(g->time_code_hours*3600)+(g->time_code_minutes*60)+g->time_code_seconds; + g->ms = LLONG( 1000*(seconds + g->time_code_pictures/current_fps) ); + if (gop_rollover) + g->ms += 24*60*60*1000; +} + +int gop_accepted(struct gop_time_code* g ) +{ + if (! ((g->time_code_hours <= 23) + && (g->time_code_minutes <= 59) + && (g->time_code_seconds <= 59) + && (g->time_code_pictures <= 59))) + return 0; + + if (gop_time.time_code_hours==23 && gop_time.time_code_minutes==59 && + g->time_code_hours==0 && g->time_code_minutes==0) + { + gop_rollover = 1; + return 1; + } + if (gop_time.inited) + { + if (gop_time.ms > g->ms) + { + // We are going back in time but it's not a complete day rollover + return 0; + } + } + return 1; +} diff --git a/src/ts_functions.cpp b/src/ts_functions.cpp new file mode 100644 index 00000000..7e844bdb --- /dev/null +++ b/src/ts_functions.cpp @@ -0,0 +1,620 @@ +#include "ccextractor.h" + +unsigned char tspacket[188]; // Current packet + +struct ts_payload payload; +unsigned char *last_pat_payload=NULL; +unsigned last_pat_length = 0; + + +static long capbufsize = 20000; +static unsigned char *capbuf = (unsigned char*)malloc(capbufsize); +long capbuflen = 0; // Bytes read in capbuf +static unsigned char *haup_capbuf = NULL; +static long haup_capbufsize = 0; +static long haup_capbuflen = 0; // Bytes read in haup_capbuf + +unsigned TS_program_number = 0; // Identifier for current program +unsigned pmtpid = 0; // PID for Program Map Table +unsigned cap_stream_type=CCX_STREAM_TYPE_UNKNOWNSTREAM; // Stream type for cappid + + +// Descriptions for ts ccx_stream_type +const char *desc[256]; + +void init_ts_constants(void) +{ + desc[CCX_STREAM_TYPE_UNKNOWNSTREAM] = "Unknown"; + desc[CCX_STREAM_TYPE_VIDEO_MPEG1] = "MPEG-1 video"; + desc[CCX_STREAM_TYPE_VIDEO_MPEG2] = "MPEG-2 video"; + desc[CCX_STREAM_TYPE_AUDIO_MPEG1] = "MPEG-1 audio"; + desc[CCX_STREAM_TYPE_AUDIO_MPEG2] = "MPEG-2 audio"; + desc[CCX_STREAM_TYPE_MHEG_PACKETS] = "MHEG Packets"; + desc[CCX_STREAM_TYPE_PRIVATE_TABLE_MPEG2] = "MPEG-2 private table sections"; + desc[CCX_STREAM_TYPE_PRIVATE_MPEG2] ="MPEG-2 private data"; + desc[CCX_STREAM_TYPE_MPEG2_ANNEX_A_DSM_CC] = "MPEG-2 Annex A DSM CC"; + desc[CCX_STREAM_TYPE_ITU_T_H222_1] = "ITU-T Rec. H.222.1"; + desc[CCX_STREAM_TYPE_AUDIO_AAC] = "AAC audio"; + desc[CCX_STREAM_TYPE_VIDEO_MPEG4] = "MPEG-4 video"; + desc[CCX_STREAM_TYPE_VIDEO_H264] = "H.264 video"; + desc[CCX_STREAM_TYPE_PRIVATE_USER_MPEG2] = "MPEG-2 User Private"; + desc[CCX_STREAM_TYPE_AUDIO_AC3] = "AC3 audio"; + desc[CCX_STREAM_TYPE_AUDIO_DTS] = "DTS audio"; + desc[CCX_STREAM_TYPE_AUDIO_HDMV_DTS]="HDMV audio"; + desc[CCX_STREAM_TYPE_ISO_IEC_13818_6_TYPE_A] = "ISO/IEC 13818-6 type A"; + desc[CCX_STREAM_TYPE_ISO_IEC_13818_6_TYPE_B] = "ISO/IEC 13818-6 type B"; + desc[CCX_STREAM_TYPE_ISO_IEC_13818_6_TYPE_C] = "ISO/IEC 13818-6 type C"; + desc[CCX_STREAM_TYPE_ISO_IEC_13818_6_TYPE_D] = "ISO/IEC 13818-6 type D"; +} + + +// Return 1 for sucessfully read ts packet +int ts_readpacket(void) +{ + buffered_read(tspacket,188); + past+=result; + if (result!=188) + { + if (result>0) + mprint("Premature end of file!\n"); + end_of_file=1; + return 0; + } + + int printtsprob = 1; + while (tspacket[0]!=0x47) + { + if (printtsprob) + { + mprint ("\nProblem: No TS header mark (filepos=%lld). Received bytes:\n", past); + dump (CCX_DMT_GENERIC_NOTICES, tspacket,4, 0, 0); + + mprint ("Skip forward to the next TS header mark.\n"); + printtsprob = 0; + } + + unsigned char *tstemp; + // The amount of bytes read into tspacket + int tslen = 188; + + // Check for 0x47 in the remaining bytes of tspacket + tstemp = (unsigned char *) memchr (tspacket+1, 0x47, tslen-1); + if (tstemp != NULL ) + { + // Found it + int atpos = tstemp-tspacket; + + memmove (tspacket,tstemp,(size_t)(tslen-atpos)); + buffered_read(tspacket+(tslen-atpos),atpos); + past+=result; + if (result!=atpos) + { + mprint("Premature end of file!\n"); + end_of_file=1; + return 0; + } + } + else + { + // Read the next 188 bytes. + buffered_read(tspacket,tslen); + past+=result; + if (result!=tslen) + { + mprint("Premature end of file!\n"); + end_of_file=1; + return 0; + } + } + } + + unsigned char *payload_start = tspacket + 4; + unsigned payload_length = 188 - 4; + + unsigned transport_error_indicator = (tspacket[1]&0x80)>>7; + unsigned payload_start_indicator = (tspacket[1]&0x40)>>6; + // unsigned transport_priority = (tspacket[1]&0x20)>>5; + unsigned pid = (((tspacket[1] & 0x1F) << 8) | tspacket[2]) & 0x1FFF; + // unsigned transport_scrambling_control = (tspacket[3]&0xC0)>>6; + unsigned adaptation_field_control = (tspacket[3]&0x30)>>4; + unsigned ccounter = tspacket[3] & 0xF; + + if (transport_error_indicator) + { + mprint ("Warning: Defective (error indicator on) TS packet (filepos=%lld):\n", past); + dump (CCX_DMT_GENERIC_NOTICES, tspacket, 188, 0, 0); + } + + unsigned adaptation_field_length = 0; + if ( adaptation_field_control & 2 ) + { + // Take the PCR (Program Clock Reference) from here, in case PTS is not available (copied from telxcc). + adaptation_field_length = tspacket[4]; + + uint8_t af_pcr_exists = (tspacket[5] & 0x10) >> 4; + if (af_pcr_exists > 0) { + uint64_t pts = 0; + pts |= (tspacket[6] << 25); + pts |= (tspacket[7] << 17); + pts |= (tspacket[8] << 9); + pts |= (tspacket[9] << 1); + pts |= (tspacket[10] >> 7); + global_timestamp = (uint32_t) pts / 90; + pts = ((tspacket[10] & 0x01) << 8); + pts |= tspacket[11]; + global_timestamp += (uint32_t) (pts / 27000); + if (!global_timestamp_inited) + { + min_global_timestamp = global_timestamp; + global_timestamp_inited = 1; + } + } + + + payload_start = payload_start + adaptation_field_length + 1; + payload_length = tspacket+188-payload_start; + } + + dbg_print(CCX_DMT_PARSE, "TS pid: %d PES start: %d counter: %u payload length: %u adapt length: %d\n", + pid, payload_start_indicator, ccounter, payload_length, + int(adaptation_field_length)); + + // Catch bad packages with adaptation_field_length > 184 and + // the unsigned nature of payload_length leading to huge numbers. + if (payload_length > 184) + { + // This renders the package invalid + payload_length = 0; + dbg_print(CCX_DMT_PARSE, " Reject package - set length to zero.\n"); + } + + // Save data in global struct + payload.start = payload_start; + payload.length = payload_length; + payload.pesstart = payload_start_indicator; + payload.pid = pid; + payload.counter = ccounter; + payload.transport_error = transport_error_indicator; + if (payload_length == 0) + { + dbg_print(CCX_DMT_PARSE, " No payload in package.\n"); + } + + // Store packet information + return 1; +} + + + +void look_for_caption_data (void) +{ + // See if we find the usual CC data marker (GA94) in this packet. + if (payload.length<4 || PIDs_seen[payload.pid]==3) // Second thing means we already inspected this PID + return; + for (unsigned i=0;iprintable_stream_type], + PIDs_programs[payload.pid]->program_number); + PIDs_seen[payload.pid]=2; + } + else + { + dbg_print(CCX_DMT_PARSE, "\nNew PID found: %u, program number still unknown\n", payload.pid); + PIDs_seen[payload.pid]=1; + } + break; + case 1: // Saw it before but we didn't know what program it belonged to. Luckier now? + if (PIDs_programs[payload.pid]) + { + dbg_print(CCX_DMT_PARSE, "\nProgram for PID: %u (previously unknown) is: %u (%s)\n", payload.pid, + PIDs_programs[payload.pid]->program_number, + desc[PIDs_programs[payload.pid]->printable_stream_type] + ); + PIDs_seen[payload.pid]=2; + } + break; + case 2: // Already seen and reported with correct program + break; + case 3: // Already seen, reported, and inspected for CC data (and found some) + break; + } + + + if (payload.pid==1003 && !hauppauge_warning_shown && !ccx_options.hauppauge_mode) + { + // TODO: Change this very weak test for something more decent such as size. + mprint ("\n\nNote: This TS could be a recording from a Hauppage card. If no captions are detected, try --hauppauge\n\n"); + hauppauge_warning_shown=1; + } + + // No caption stream PID defined yet, continue searching. + if ( !ccx_options.ts_cappid ) + { + if (!packet_analysis_mode) + dbg_print(CCX_DMT_PARSE, "Packet (pid %u) skipped - no stream with captions identified yet.\n", + payload.pid); + else + look_for_caption_data (); + continue; + } + + if (ccx_options.hauppauge_mode && payload.pid==HAUPPAGE_CCPID) + { + // Haup packets processed separately, because we can't mix payloads. So they go in their own buffer + // copy payload to capbuf + int haup_newcapbuflen = haup_capbuflen + payload.length; + if ( haup_newcapbuflen > haup_capbufsize) { + haup_capbuf = (unsigned char*)realloc(haup_capbuf, haup_newcapbuflen); + if (!haup_capbuf) + fatal(EXIT_NOT_ENOUGH_MEMORY, "Out of memory"); + haup_capbufsize = haup_newcapbuflen; + } + memcpy(haup_capbuf+haup_capbuflen, payload.start, payload.length); + haup_capbuflen = haup_newcapbuflen; + + } + + // Check for PID with captions. Note that in Hauppauge mode we also process the video stream because + // we need the timing from its PES header, which isn't included in Hauppauge's packets + if( payload.pid == ccx_options.ts_cappid) + { // Now we got a payload + + // Video PES start + if (payload.pesstart) + { + // Pretend the previous was smaller + prev_ccounter=payload.counter-1; + + saw_pesstart = 1; + } + + // Discard packets when no pesstart was found. + if ( !saw_pesstart ) + { + dbg_print(CCX_DMT_PARSE, "Packet (pid %u) skipped - Did not see pesstart.\n", + payload.pid); + continue; + } + + // If the buffer is empty we just started this function + if (payload.pesstart && capbuflen > 0) + { + dbg_print(CCX_DMT_PARSE, "\nPES finished (%ld bytes/%ld PES packets/%ld total packets)\n", + capbuflen, pespcount, pcount); + + // Keep the data in capbuf to be worked on + + prev_packet = 1; + gotpes = 1; + break; + } + + if ( (prev_ccounter==15 ? 0 : prev_ccounter+1) != payload.counter ) + { + mprint("TS continuity counter not incremented prev/curr %u/%u\n", + prev_ccounter, payload.counter); + } + prev_ccounter = payload.counter; + + + pespcount++; + // copy payload to capbuf + int newcapbuflen = capbuflen + payload.length; + if ( newcapbuflen > capbufsize) { + capbuf = (unsigned char*)realloc(capbuf, newcapbuflen); + if (!capbuf) + fatal(EXIT_NOT_ENOUGH_MEMORY, "Out of memory"); + capbufsize = newcapbuflen; + } + memcpy(capbuf+capbuflen, payload.start, payload.length); + capbuflen = newcapbuflen; + } + //else + // if(debug_verbose) + // printf("Packet (pid %u) skipped - unused.\n", + // payload.pid); + + // Nothing suitable found, start over + } + while( !gotpes ); // gotpes==1 never arrives here because of the breaks + + return capbuflen; +} + + +// TS specific data grabber +LLONG ts_getmoredata(void) +{ + long payload_read = 0; + const char *tstr; // Temporary string to describe the stream type + + do + { + if( !ts_readstream() ) + { // If we didn't get data, try again + mprint("(no CC data extracted)\n"); + continue; + } + // Handle obscure case where we didn't find a PMT (so + // cap_stream_type wasn't set) but the user told us what kind + // of stream to look for, so we move forward anyway. This + // happens with MPEG-2 sources from ZeeVee HDbridge. + if (cap_stream_type == CCX_STREAM_TYPE_UNKNOWNSTREAM && ccx_options.ts_forced_streamtype != CCX_STREAM_TYPE_UNKNOWNSTREAM) + { + cap_stream_type = ccx_options.ts_forced_streamtype; + } + + // Separate MPEG-2 and H.264 video streams + if( cap_stream_type == CCX_STREAM_TYPE_VIDEO_MPEG2) + { + ccx_bufferdatatype = CCX_PES; + tstr = "MPG"; + } + else if( cap_stream_type == CCX_STREAM_TYPE_VIDEO_H264 ) + { + ccx_bufferdatatype = CCX_H264; + tstr = "H.264"; + } + else if ( cap_stream_type == CCX_STREAM_TYPE_UNKNOWNSTREAM && ccx_options.hauppauge_mode) + { + ccx_bufferdatatype = CCX_HAUPPAGE; + tstr = "Hauppage"; + } + else if ( cap_stream_type == CCX_STREAM_TYPE_PRIVATE_MPEG2 && ccx_options.teletext_mode==CCX_TXT_IN_USE) + { + ccx_bufferdatatype = CCX_TELETEXT; + tstr = "Teletext"; + } + else if ( cap_stream_type == CCX_STREAM_TYPE_PRIVATE_MPEG2 && ccx_options.teletext_mode==CCX_TXT_FORBIDDEN) + { + ccx_bufferdatatype = CCX_PRIVATE_MPEG2_CC; + tstr = "CC in private MPEG packet"; + } + else + { + if (ccx_options.ts_forced_cappid) + fatal (EXIT_UNSUPPORTED, "Unable to determine stream type of selected PID."); + else + fatal(EXIT_BUG_BUG, "Not reachable!"); + } + // We read a video PES + + if (capbuf[0]!=0x00 || capbuf[1]!=0x00 || + capbuf[2]!=0x01) + { + // ??? Shouldn't happen. Complain and try again. + mprint("Missing PES header!\n"); + dump(CCX_DMT_GENERIC_NOTICES, capbuf,256, 0, 0); + continue; + } + unsigned stream_id = capbuf[3]; + + if (ccx_options.teletext_mode == CCX_TXT_IN_USE) + { + if (ccx_options.ts_cappid==0) + { // If here, the user forced teletext mode but didn't supply a PID, and we haven't found it yet. + continue; + } + memcpy(buffer+inbuf, capbuf, capbuflen); + payload_read = capbuflen; + inbuf += capbuflen; + break; + } + if (ccx_bufferdatatype == CCX_PRIVATE_MPEG2_CC) + { + dump (CCX_DMT_GENERIC_NOTICES, capbuf, capbuflen,0, 1); + // Bogus data, so we return something + buffer[inbuf++]=0xFA; + buffer[inbuf++]=0x80; + buffer[inbuf++]=0x80; + payload_read+=3; + break; + } + if (ccx_options.hauppauge_mode) + { + if (haup_capbuflen%12 != 0) + mprint ("Warning: Inconsistent Hauppage's buffer length\n"); + if (!haup_capbuflen) + { + // Do this so that we always return something until EOF. This will be skipped. + buffer[inbuf++]=0xFA; + buffer[inbuf++]=0x80; + buffer[inbuf++]=0x80; + payload_read+=3; + } + + for (int i=0; i BUFSIZE - inbuf) + { + fatal(EXIT_BUG_BUG, + "Remaining buffer (%lld) not enough to hold the 3 Hauppage bytes.\n" + "Please send bug report!", + BUFSIZE - inbuf); + } + if (haup_capbuf[i+9]==1 || haup_capbuf[i+9]==2) // Field match. // TODO: If extract==12 this won't work! + { + if (haup_capbuf[i+9]==1) + buffer[inbuf++]=4; // Field 1 + cc_valid=1 + else + buffer[inbuf++]=5; // Field 2 + cc_valid=1 + buffer[inbuf++]=haup_capbuf[i+10]; + buffer[inbuf++]=haup_capbuf[i+11]; + payload_read+=3; + } + /* + if (inbuf>1024) // Just a way to send the bytes to the decoder from time to time, otherwise the buffer will fill up. + break; + else + continue; */ + } + } + haup_capbuflen=0; + } + + if( !((stream_id&0xf0)==0xe0)) // 0xBD = private stream + { + // ??? Shouldn't happen. Complain and try again. + mprint("Not a video PES header!\n"); + continue; + } + + dbg_print(CCX_DMT_VERBOSE, "TS payload start video PES id: %d len: %ld\n", + stream_id, capbuflen); + + int pesheaderlen; + int vpesdatalen = read_video_pes_header(capbuf, &pesheaderlen, capbuflen); + + if (vpesdatalen < 0) + { // Seems to be a broken PES + end_of_file=1; + break; + } + + unsigned char *databuf = capbuf + pesheaderlen; + long databuflen = capbuflen - pesheaderlen; + + // If the package length is unknown vpesdatalen is zero. + // If we know he package length, use it to quit + dbg_print(CCX_DMT_VERBOSE, "Read PES-%s (databuffer %ld/PES data %d) ", + tstr, databuflen, vpesdatalen); + // We got the whole PES in buffer + if( vpesdatalen && (databuflen >= vpesdatalen) ) + dbg_print(CCX_DMT_VERBOSE, " - complete"); + dbg_print(CCX_DMT_VERBOSE, "\n"); + + + if (databuflen > BUFSIZE - inbuf) + { + fatal(EXIT_BUG_BUG, + "PES data packet (%ld) larger than remaining buffer (%lld).\n" + "Please send bug report!", + databuflen, BUFSIZE - inbuf); + } + + if (!ccx_options.hauppauge_mode) // in Haup mode the buffer is filled somewhere else + { + memcpy(buffer+inbuf, databuf, databuflen); + payload_read = databuflen; + inbuf += databuflen; + } + break; + } + while ( !end_of_file ); + + return payload_read; +} diff --git a/src/ts_tables.cpp b/src/ts_tables.cpp new file mode 100644 index 00000000..4862bf96 --- /dev/null +++ b/src/ts_tables.cpp @@ -0,0 +1,567 @@ +/* Process Program Map Table - The PMT contains a list of streams in a program. + Input: pos => Index in the PAT array + Returns: Changes in the selected PID=1, No changes=0, if changes then if the + buffer had anything it should be flushed. + PMT specs: ISO13818-1 / table 2-28 + */ + +#include "ccextractor.h" + +static unsigned pmt_warning_shown=0; // Only display warning once + +// PMTs table +PAT_entry pmt_array[TS_PMT_MAP_SIZE] = { 0 }; +uint16_t pmt_array_length = 0; + +void process_ccx_mpeg_descriptor (unsigned char *data, unsigned length); + +unsigned get_printable_stream_type (unsigned ccx_stream_type) +{ + unsigned tmp_stream_type=ccx_stream_type; + switch (ccx_stream_type) + { + case CCX_STREAM_TYPE_VIDEO_MPEG2: + case CCX_STREAM_TYPE_VIDEO_H264: + case CCX_STREAM_TYPE_PRIVATE_MPEG2: + case CCX_STREAM_TYPE_PRIVATE_TABLE_MPEG2: + case CCX_STREAM_TYPE_MHEG_PACKETS: + case CCX_STREAM_TYPE_MPEG2_ANNEX_A_DSM_CC: + case CCX_STREAM_TYPE_ISO_IEC_13818_6_TYPE_A: + case CCX_STREAM_TYPE_ISO_IEC_13818_6_TYPE_B: + case CCX_STREAM_TYPE_ISO_IEC_13818_6_TYPE_C: + case CCX_STREAM_TYPE_ISO_IEC_13818_6_TYPE_D: + case CCX_STREAM_TYPE_ITU_T_H222_1: + case CCX_STREAM_TYPE_VIDEO_MPEG1: + case CCX_STREAM_TYPE_AUDIO_MPEG1: + case CCX_STREAM_TYPE_AUDIO_MPEG2: + case CCX_STREAM_TYPE_AUDIO_AAC: + case CCX_STREAM_TYPE_VIDEO_MPEG4: + case CCX_STREAM_TYPE_AUDIO_AC3: + case CCX_STREAM_TYPE_AUDIO_DTS: + case CCX_STREAM_TYPE_AUDIO_HDMV_DTS: + break; + default: + if (ccx_stream_type>=0x80 && ccx_stream_type<=0xFF) + tmp_stream_type=CCX_STREAM_TYPE_PRIVATE_USER_MPEG2; + else + tmp_stream_type = CCX_STREAM_TYPE_UNKNOWNSTREAM; + break; + } + return tmp_stream_type; +} + +void clear_PMT_array (void) +{ + for (int i=0;i> 1; + unsigned current_next_indicator = payload_start[5] & 0x01; + if (!current_next_indicator) + // This table is not active, no need to evaluate + return 0; + unsigned section_number = payload_start[6]; + unsigned last_section_number = payload_start[7]; + if ( last_section_number > 0 ) + { + mprint("Long PMTs are not supported - skipped.\n"); + return 0; + } + unsigned PCR_PID = (((payload_start[8] & 0x1F) << 8) + | payload_start[9]); + unsigned pi_length = (((payload_start[10] & 0x0F) << 8) + | payload_start[11]); + + if( 12 + pi_length > payload_length ) + { + // If we would support long PMTs, this would be wrong. + mprint("program_info_length cannot be longer than the payload_length - skipped\n"); + return 0; + } + payload_start += 12 + pi_length; + payload_length = tspacket+188-payload_start; + + unsigned stream_data = section_length - 9 - pi_length - 4; // prev. bytes and CRC + + dbg_print(CCX_DMT_PARSE, "Read PMT packet (id: %u) program number: %u\n", + table_id, program_number); + dbg_print(CCX_DMT_PARSE, " section length: %u number: %u last: %u\n", + section_length, section_number, last_section_number); + dbg_print(CCX_DMT_PARSE, " version_number: %u current_next_indicator: %u\n", + version_number, current_next_indicator); + dbg_print(CCX_DMT_PARSE, " PCR_PID: %u data length: %u payload_length: %u\n", + PCR_PID, stream_data, payload_length); + + if (!pmt_warning_shown && stream_data+4 > payload_length ) + { + dbg_print (CCX_DMT_GENERIC_NOTICES, "\rWarning: Probably parsing incomplete PMT, expected data longer than available payload.\n"); + pmt_warning_shown=1; + } + dbg_print(CCX_DMT_PMT, "\nProgram Map Table for program %u, PMT PID: %u\n", + program_number,payload.pid); + // Make a note of the program number for all PIDs, so we can report it later + for( unsigned i=0; i < stream_data && (i+4)elementary_PID=elementary_PID; + PIDs_programs[elementary_PID]->ccx_stream_type=ccx_stream_type; + PIDs_programs[elementary_PID]->program_number=program_number; + PIDs_programs[elementary_PID]->PMT_PID=payload.pid; + PIDs_programs[elementary_PID]->printable_stream_type=get_printable_stream_type (ccx_stream_type); + dbg_print(CCX_DMT_PMT, "%6u | %3X (%3u) | %s\n",elementary_PID,ccx_stream_type,ccx_stream_type, + desc[PIDs_programs[elementary_PID]->printable_stream_type]); + process_ccx_mpeg_descriptor (payload_start+i+5,ES_info_length); + i += ES_info_length; + } + dbg_print(CCX_DMT_PMT, "---\n"); + if (TS_program_number || !ccx_options.ts_autoprogram) + { + if( payload.pid != pmtpid) + { + // This isn't the PMT we are interested in (note: If TS_program_number=0 && + // autoprogram then we need to check this PMT in case there's a suitable + // stream) + return 0; + } + if (program_number != TS_program_number) // Is this the PMT of the program we want? + { + // Only use PMTs with matching program number + dbg_print(CCX_DMT_PARSE,"Reject this PMT packet (pid: %u) program number: %u\n", + pmtpid, program_number); + return 0; + } + } + + unsigned newcappid = 0; + unsigned newcap_stream_type = 0; + dbg_print(CCX_DMT_VERBOSE, "\nProgram map section (PMT)\n"); + + for( unsigned i=0; i < stream_data && (i+4)=0x80 && ccx_stream_type<=0xFF) + { + if (ccx_options.ts_forced_streamtype==CCX_STREAM_TYPE_UNKNOWNSTREAM) + { + mprint ("I can't tell the stream type of the manually selected PID.\n"); + mprint ("Please pass -streamtype to select manually.\n"); + fatal (EXIT_FAILURE, "(user assistance needed)"); + } + else + cap_stream_type = newcap_stream_type = ccx_options.ts_forced_streamtype; + } + else + cap_stream_type = newcap_stream_type = ccx_stream_type; + continue; + } + + if ((ccx_stream_type==CCX_STREAM_TYPE_VIDEO_H264 || ccx_stream_type==CCX_STREAM_TYPE_VIDEO_MPEG2) + && ccx_options.teletext_mode != CCX_TXT_IN_USE) + { + newcappid = elementary_PID; + newcap_stream_type = ccx_stream_type; + } + + // For the print command below + unsigned tmp_stream_type = get_printable_stream_type (ccx_stream_type); + dbg_print(CCX_DMT_VERBOSE, " %s stream [0x%02x] - PID: %u\n", + desc[tmp_stream_type], + ccx_stream_type, elementary_PID); + i += ES_info_length; + } + if (!newcappid && !ccx_options.ts_forced_cappid) + { + if (!ccx_options.ts_autoprogram) + { + mprint("No supported stream with caption data found, won't be able to process\n"); + mprint("unless a PID is provided manually or packet inspection is enabled.\n"); + } + else + { + mprint("No supported stream with caption data found in this program.\n"); + } + return 0; + } + if (newcappid != ccx_options.ts_cappid && !ccx_options.ts_forced_cappid) + { + ccx_options.ts_cappid = newcappid; + cap_stream_type = newcap_stream_type; + mprint ("Decode captions from program %d - %s stream [0x%02x] - PID: %u\n", + program_number , desc[cap_stream_type], cap_stream_type, ccx_options.ts_cappid); + if (ccx_options.ts_autoprogram) // Make our program selection official + { + pmtpid=payload.pid; + TS_program_number = program_number; + } + // If we have data flush it + if( capbuflen > 0 ) + must_flush=1; + } + return must_flush; +} + +/* Program Allocation Table. It contains a list of all programs and the + PIDs of their Program Map Table. + Returns: gotpes */ + +int parse_PAT (void) +{ + int gotpes=0; + int is_multiprogram=0; + static int warning_program_not_found_shown=0; + + /* if ((forced_cappid || telext_mode==CCX_TXT_IN_USE) && cap_stream_type!=CCX_STREAM_TYPE_UNKNOWNSTREAM) // Already know what we need, skip + return 0; */ + + if (!payload.pesstart) + // Not the first entry. Ignore it, it should not be here. + return 0; + + unsigned pointer_field = *(payload.start); + unsigned char *payload_start = payload.start + pointer_field + 1; + if (tspacket-payload_start+188<0) // Negative length? Seen it, but impossible + return 0; + unsigned payload_length = tspacket+188-payload_start; + unsigned section_number = payload_start[6]; + unsigned last_section_number = payload_start[7]; + if (section_number > last_section_number) // Impossible: Defective PAT + { + dbg_print(CCX_DMT_PAT, "Skipped defective PAT packet, section_number=%u but last_section_number=%u\n", + section_number, last_section_number); + return gotpes; + } + if ( last_section_number > 0 ) + { + dbg_print(CCX_DMT_PAT, "Long PAT packet (%u / %u), skipping.\n", + section_number, last_section_number); + return gotpes; + /* fatal(EXIT_BUG_BUG, + "Sorry, long PATs not yet supported!\n"); */ + } + + if (last_pat_payload!=NULL && payload_length == last_pat_length && + !memcmp (payload_start, last_pat_payload, payload_length)) + { + // dbg_print(CCX_DMT_PAT, "PAT hasn't changed, skipping.\n"); + return 0; + } + + if (last_pat_payload!=NULL) + { + mprint ("Notice: PAT changed, clearing all variables.\n"); + clear_PMT_array(); + if (ccx_options.teletext_mode==CCX_TXT_IN_USE) + ccx_options.teletext_mode=CCX_TXT_AUTO_NOT_YET_FOUND; + ccx_options.ts_cappid=0; + cap_stream_type=CCX_STREAM_TYPE_UNKNOWNSTREAM; + memset (PIDs_seen,0,sizeof (int) *65536); // Forget all we saw + if (!tlt_config.user_page) // If the user didn't select a page... + tlt_config.page=0; // ..forget whatever we detected. + + gotpes=1; + } + + last_pat_payload=(unsigned char *) realloc (last_pat_payload, payload_length+8); // Extra 8 in case memcpy copies dwords, etc + if (last_pat_payload==NULL) + fatal (EXIT_NOT_ENOUGH_MEMORY, "Not enough memory to process PAT.\n"); + memcpy (last_pat_payload, payload_start, payload_length); + last_pat_length = payload_length; + + + unsigned table_id = payload_start[0]; + unsigned section_length = (((payload_start[1] & 0x0F) << 8) + | payload_start[2]); + unsigned transport_stream_id = ((payload_start[3] << 8) + | payload_start[4]); + unsigned version_number = (payload_start[5] & 0x3E) >> 1; + + // Means current OR next (so you can build a long PAT before it + // actually is to be in use). + unsigned current_next_indicator = payload_start[5] & 0x01; + + + + if (!current_next_indicator) + // This table is not active, no need to evaluate + return 0; + + payload_start += 8; + payload_length = tspacket+188-payload_start; + + unsigned programm_data = section_length - 5 - 4; // prev. bytes and CRC + + dbg_print(CCX_DMT_PAT, "Read PAT packet (id: %u) ts-id: 0x%04x\n", + table_id, transport_stream_id); + dbg_print(CCX_DMT_PAT, " section length: %u number: %u last: %u\n", + section_length, section_number, last_section_number); + dbg_print(CCX_DMT_PAT, " version_number: %u current_next_indicator: %u\n", + version_number, current_next_indicator); + + if ( programm_data+4 > payload_length ) + { + fatal(EXIT_BUG_BUG, + "Sorry, PAT too long!\n"); + } + + unsigned ts_prog_num = 0; + unsigned ts_prog_map_pid = 0; + dbg_print(CCX_DMT_PAT, "\nProgram association section (PAT)\n"); + + int temp=0; + for( unsigned i=0; i < programm_data; i+=4) + { + unsigned program_number = ((payload_start[i] << 8) + | payload_start[i+1]); + if( !program_number ) + continue; + temp++; + if (temp>=2) // Found 2 programs, we don't need more + break; + } + + is_multiprogram = (temp>1); + + for( unsigned i=0; i < programm_data; i+=4) + { + unsigned program_number = ((payload_start[i] << 8) + | payload_start[i+1]); + unsigned prog_map_pid = ((payload_start[i+2] << 8) + | payload_start[i+3]) & 0x1FFF; + + dbg_print(CCX_DMT_PAT, " Program number: %u -> PMTPID: %u\n", + program_number, prog_map_pid); + + if( !program_number ) + continue; + + if (!is_multiprogram || (ccx_options.ts_forced_program_selected && program_number == ccx_options.ts_forced_program)) + { + // If there's just one program we select it unless the user selected + // something else anyway. + ts_prog_num = program_number; + ts_prog_map_pid = prog_map_pid; + } + + // Having an array for PMTs comes from telxcc. + int found=0,j; + for (j=0;j 0 ) + gotpes = 1; + } + return gotpes; +} + +void process_ccx_mpeg_descriptor (unsigned char *data, unsigned length) +{ + const char *txt_teletext_type[]={"Reserved", "Initial page", "Subtitle page", "Additional information page", "Programme schedule page", + "Subtitle page for hearing impaired people"}; + int i,l; + if (!data || !length) + return; + switch (data[0]) + { + case CCX_MPEG_DSC_ISO639_LANGUAGE: + if (length<2) + return; + l=data[1]; + if (l+2=0x20?c1:' ', + c2>=0x20?c2:' ', + c3>=0x20?c3:' '); + } + break; + case CCX_MPEG_DSC_VBI_DATA_DESCRIPTOR: + dbg_print(CCX_DMT_PMT, "DVB VBI data descriptor (not implemented)\n"); + break; + case CCX_MPEG_DSC_VBI_TELETEXT_DESCRIPTOR: + dbg_print(CCX_DMT_PMT, "DVB VBI teletext descriptor\n"); + break; + case CCX_MPEG_DSC_TELETEXT_DESCRIPTOR: + dbg_print(CCX_DMT_PMT, " DVB teletext descriptor\n"); + if (length<2) + return; + l=data[1]; + if (l+2>3; // 5 MSB + unsigned magazine_number=data[i+5]&0x7; // 3 LSB + unsigned teletext_page_number=data[i+6]; + dbg_print (CCX_DMT_PMT, " ISO639: %c%c%c\n",c1>=0x20?c1:' ', + c2>=0x20?c2:' ', + c3>=0x20?c3:' '); + dbg_print (CCX_DMT_PMT, " Teletext type: %s (%02X)\n", (teletext_type<6? txt_teletext_type[teletext_type]:"Reserved for future use"), + teletext_type); + dbg_print (CCX_DMT_PMT, " Initial page: %02X\n",teletext_page_number); + } + break; + default: + if (data[0]==CCX_MPEG_DSC_REGISTRATION) // Registration descriptor, could be useful eventually + break; + if (data[0]==CCX_MPEG_DSC_DATA_STREAM_ALIGNMENT) // Data stream alignment descriptor + break; + if (data[0]>=0x13 && data[0]<=0x3F) // Reserved + break; + if (data[0]>=0x40 && data[0]<=0xFF) // User private + break; + mprint ("Still unsupported MPEG descriptor type=%d (%02X)\n",data[0],data[0]); + break; + } +} + diff --git a/src/utility.cpp b/src/utility.cpp new file mode 100644 index 00000000..ee853e53 --- /dev/null +++ b/src/utility.cpp @@ -0,0 +1,295 @@ +#include "ccextractor.h" + +static char *text; +static int text_size=0; + +void timestamp_to_srttime(uint64_t timestamp, char *buffer) { + uint64_t p = timestamp; + uint8_t h = (uint8_t) (p / 3600000); + uint8_t m = (uint8_t) (p / 60000 - 60 * h); + uint8_t s = (uint8_t) (p / 1000 - 3600 * h - 60 * m); + uint16_t u = (uint16_t) (p - 3600000 * h - 60000 * m - 1000 * s); + sprintf(buffer, "%02"PRIu8":%02"PRIu8":%02"PRIu8",%03"PRIu16, h, m, s, u); +} +#define MIN3(a, b, c) ((a) < (b) ? ((a) < (c) ? (a) : (c)) : ((b) < (c) ? (b) : (c))) + +int levenshtein_dist (const uint64_t *s1, const uint64_t *s2, unsigned s1len, unsigned s2len) { + unsigned int x, y, lastdiag, olddiag; + unsigned int *column=(unsigned *) malloc ((s1len+1)*sizeof (unsigned int)); + for (y = 1; y <= s1len; y++) + column[y] = y; + for (x = 1; x <= s2len; x++) { + column[0] = x; + for (y = 1, lastdiag = x-1; y <= s1len; y++) { + olddiag = column[y]; + column[y] = MIN3(column[y] + 1, column[y-1] + 1, lastdiag + (s1[y-1] == s2[x-1] ? 0 : 1)); + lastdiag = olddiag; + } + } + int v=column[s1len]; + free (column); + return v; +} + +void millis_to_date (uint64_t timestamp, char *buffer) +{ + time_t secs; + unsigned int millis; + char c_temp[80]; + struct tm *time_struct=NULL; + switch (ccx_options.date_format) + { + case ODF_NONE: + buffer[0]=0; + break; + case ODF_HHMMSS: + timestamp_to_srttime (timestamp, buffer); + buffer[8]=0; + break; + case ODF_HHMMSSMS: + timestamp_to_srttime (timestamp, buffer); + break; + case ODF_SECONDS: + secs=(time_t) (timestamp/1000); + millis=(time_t) (timestamp%1000); + sprintf (buffer, "%lu%c%03u", (unsigned long) secs, + ccx_options.millis_separator,(unsigned) millis); + break; + case ODF_DATE: + secs=(time_t) (timestamp/1000); + millis=(unsigned int) (timestamp%1000); + time_struct = gmtime(&secs); + strftime(c_temp, sizeof(c_temp), "%Y%m%d%H%M%S", time_struct); + sprintf (buffer,"%s%c%03u",c_temp, + ccx_options.millis_separator,millis); + break; + + default: + fatal (EXIT_BUG_BUG, "Invalid value for date_format in millis_to_date()\n"); + } +} + +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; +} + +bool_t in_array(uint16_t *array, uint16_t length, uint16_t element) { + bool_t r = NO; + for (uint16_t i = 0; i < length; i++) + if (array[i] == element) { + r = YES; + break; + } + return r; +} + +/* Alloc text space */ +void resize_text() +{ + text_size=(!text_size)?1024:text_size*2; + if (text) + free (text); + text=(char *) malloc (text_size); + if (!text) + fatal (EXIT_NOT_ENOUGH_MEMORY, "Not enough memory for text buffer."); + memset (text,0,text_size); +} + +/* Write formatted message to stderr and then exit. */ +void fatal(int exit_code, const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + if (ccx_options.gui_mode_reports) + fprintf(stderr,"###MESSAGE#"); + else + fprintf(stderr, "\rError: "); + vfprintf(stderr, fmt, args); + fprintf(stderr, "\n"); + va_end(args); + exit(exit_code); +} + +/* 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 ((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; + } + } +} + +/* General output, replacement for printf so we can control globally where messages go. + mprint => Message print */ +void mprint (const char *fmt, ...) +{ + va_list args; + if (!ccx_options.messages_target) + return; + activity_header(); // Brag about writing it :-) + va_start(args, fmt); + if (ccx_options.messages_target==CCX_MESSAGES_STDOUT) + { + vfprintf(stdout, fmt, args); + fflush (stdout); + } + else + { + vfprintf(stderr, fmt, args); + fflush (stderr); + } + va_end(args); +} + +/* Shorten some debug output code. */ +void dbg_print(LLONG mask, const char *fmt, ...) +{ + va_list args; + if (!ccx_options.messages_target) + return; + LLONG t=temp_debug ? (ccx_options.debug_mask_on_debug | ccx_options.debug_mask) : ccx_options.debug_mask; // Mask override? + + if(mask & t) + { + va_start(args, fmt); + if (ccx_options.messages_target==CCX_MESSAGES_STDOUT) + { + vfprintf(stdout, fmt, args); + fflush (stdout); + } + else + { + vfprintf(stderr, fmt, args); + fflush (stderr); + } + va_end(args); + } +} + + +/* Shorten some debug output code. */ +void dvprint(const char *fmt, ...) +{ + va_list args; + if (!ccx_options.messages_target) + return; + if(! (ccx_options.debug_mask & CCX_DMT_VIDES )) + return; + + va_start(args, fmt); + if (ccx_options.messages_target==CCX_MESSAGES_STDOUT) + { + vfprintf(stdout, fmt, args); + fflush (stdout); + } + else + { + vfprintf(stderr, fmt, args); + fflush (stderr); + } + va_end(args); +} + +void dump (LLONG mask, unsigned char *start, int l, unsigned long abs_start, unsigned clear_high_bit) +{ + LLONG t=temp_debug ? (ccx_options.debug_mask_on_debug | ccx_options.debug_mask) : ccx_options.debug_mask; // Mask override? + if(! (mask & t)) + return; + + for (int x=0; x=' ') + mprint ("%c",start[x+j] & (clear_high_bit?0x7F:0xFF)); // 0x7F < remove high bit, convenient for visual CC inspection + else + mprint (" "); + } + mprint ("\n"); + } +} + +void init_boundary_time (ccx_boundary_time *bt) +{ + bt->hh=0; + bt->mm=0; + bt->ss=0; + bt->set=0; + bt->time_in_ms=0; +} + + +void sleep_secs (int secs) +{ +#ifdef _WIN32 + Sleep(secs * 1000); +#else + sleep(secs); +#endif +} + +int hex2int (char high, char low) +{ + unsigned char h,l; + if (high>='0' && high<='9') + h=high-'0'; + else if (high>='a' && high<='f') + h=high-'a'+10; + else + return -1; + if (low>='0' && low<='9') + l=low-'0'; + else if (low>='a' && low<='f') + l=low-'a'+10; + else + return -1; + return h*16+l; +} diff --git a/src/wtv_constants.h b/src/wtv_constants.h new file mode 100644 index 00000000..14cbabb1 --- /dev/null +++ b/src/wtv_constants.h @@ -0,0 +1,29 @@ +//WTV GUIDS +#define WTV_HEADER "\xB7\xD8\x00\x20\x37\x49\xDA\x11\xA6\x4E\x00\x07\xE9\x5E\xAD\x8D" +#define WTV_STREAM "\xED\xA4\x13\x23\x2D\xBF\x4F\x45\xAD\x8A\xD9\x5B\xA7\xF9\x1F\xEE" +#define WTV_STREAM2 "\xA2\xC3\xD2\xC2\x7E\x9A\xDA\x11\x8B\xF7\x00\x07\xE9\x5E\xAD\x8D" +#define WTV_DATA "\x95\xC3\xD2\xC2\x7E\x9A\xDA\x11\x8B\xF7\x00\x07\xE9\x5E\xAD\x8D" +#define WTV_STREAM_VIDEO "\x76\x69\x64\x73\x00\x00\x10\x00\x80\x00\x00\xAA\x00\x38\x9B\x71" +#define WTV_STREAM_MSTVCAPTION "\x89\x8A\x8B\xB8\x49\xB0\x80\x4C\xAD\xCF\x58\x98\x98\x5E\x22\xC1" +#define WTV_EOF "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +#define WTV_TIMING "\x5B\x05\xE6\x1B\x97\xA9\x49\x43\x88\x17\x1A\x65\x5A\x29\x8A\x97" + +#define WTV_CHUNK_SIZE 0x1000 +#define WTV_META_CHUNK_SIZE 0x40000 + +#define WTV_META_CHUNK_MASK 0xfffffffffffc0000 + +#define WTV_TABLE_ENTRIES ".entries." + +//Maximum size we will try and malloc for chunked_buffer. 100MB +#define WTV_MAX_ALLOC 1024*1024*100 + +struct wtv_chunked_buffer +{ + uint64_t skip_chunks[256]; + uint32_t chunk; + uint8_t count; + uint8_t* buffer; + uint32_t buffer_size; + uint64_t filepos; +}; diff --git a/src/wtv_functions.cpp b/src/wtv_functions.cpp new file mode 100644 index 00000000..6c395c6c --- /dev/null +++ b/src/wtv_functions.cpp @@ -0,0 +1,432 @@ +#include "ccextractor.h" +#include "wtv_constants.h" + +int check_stream_id(int stream_id, int video_streams, int num_streams); +int add_skip_chunks(wtv_chunked_buffer *cb, uint32_t offset, uint32_t flag); +void init_chunked_buffer(wtv_chunked_buffer *cb); +uint64_t get_meta_chunk_start(uint64_t offset); +uint64_t time_to_pes_time(uint64_t time); +void add_chunk(wtv_chunked_buffer *cb, uint64_t value); +int qsort_cmpint (const void * a, const void * b); +void get_sized_buffer(wtv_chunked_buffer *cb, uint32_t size); +void skip_sized_buffer(wtv_chunked_buffer *cb, uint32_t size); +int read_header(wtv_chunked_buffer *cb); +LLONG get_data(wtv_chunked_buffer *cb); + +// Helper function for qsort (64bit int sort) +int qsort_cmpint (const void * a, const void * b) +{ + if(ab) return 1; + return 0; +} + +// Check if the passed stream_id is in the list of stream_ids we care about +// Return true if it is. +int check_stream_id(int stream_id, int video_streams[], int num_streams) { + int x; + for(x=0; xcount=0; + cb->buffer=NULL; + cb->buffer_size=0; + int x; + for(x=0; x<256; x++) + cb->skip_chunks[x]=-1; +} + +// Calculate the actual file offset of the passed skip value. +uint64_t get_meta_chunk_start(uint64_t offset) { + return offset*WTV_CHUNK_SIZE&WTV_META_CHUNK_MASK; +} + +// Convert WTV time to PES time +uint64_t time_to_pes_time(uint64_t time) +{ + return ((time/10000)*90); +} + +// Read the actual values of the passed lookup offset and add them to +// the list of chunks to skip as nessasary. Returns false on error. +int add_skip_chunks(wtv_chunked_buffer *cb, uint32_t offset, uint32_t flag) +{ + + uint64_t start = filebuffer_pos; //Not sure this is the best way to do this + buffered_seek((int)((offset*WTV_CHUNK_SIZE)-start)); + uint64_t seek_back=0-((offset*WTV_CHUNK_SIZE)-start); + + uint32_t value; + buffered_read((unsigned char*)&value, 4); + if(result!=4) + return 0; + seek_back-=4; + while(value!=0) { + dbg_print(CCX_DMT_PARSE, "value: %llx\n", get_meta_chunk_start(value)); + buffered_read((unsigned char*)&value, 4); + if(result!=4) + return 0; + add_chunk(cb, get_meta_chunk_start(value)); + seek_back-=4; + } + buffered_seek((int)seek_back); + dbg_print(CCX_DMT_PARSE, "filebuffer_pos: %x\n", filebuffer_pos); + return 1; +} + +void add_chunk(wtv_chunked_buffer *cb, uint64_t value) +{ + int x; + for(x=0; xcount; x++) + if(cb->skip_chunks[x]==value) + return; + cb->skip_chunks[cb->count]=value; + cb->count++; +} + +// skip_sized_buffer. Same as get_sized_buffer, only without actually copying any data +// in to the buffer. +void skip_sized_buffer(wtv_chunked_buffer *cb, uint32_t size) { + if(cb->buffer!=NULL && cb->buffer_size>0) { + free(cb->buffer); + } + cb->buffer=NULL; + cb->buffer_size=0; + uint64_t start = cb->filepos; + if(cb->skip_chunks[cb->chunk]!=-1 && start+size>cb->skip_chunks[cb->chunk]) { + buffered_seek((int)((cb->skip_chunks[cb->chunk]-start)+(WTV_META_CHUNK_SIZE)+(size-(cb->skip_chunks[cb->chunk]-start)))); + cb->filepos+=(cb->skip_chunks[cb->chunk]-start)+(WTV_META_CHUNK_SIZE)+(size-(cb->skip_chunks[cb->chunk]-start)); + cb->chunk++; + } + else { + buffered_seek(size); + cb->filepos+=size; + } + past=cb->filepos; +} + +// get_sized_buffer will alloc and set a buffer in the passed wtv_chunked_buffer struct +// it will handle any meta data chunks that need to be skipped in the file +// Will print error messages and return a null buffer on error. +void get_sized_buffer(wtv_chunked_buffer *cb, uint32_t size) { + if(cb->buffer!=NULL && cb->buffer_size>0) { + free(cb->buffer); + } + if(size>WTV_MAX_ALLOC) { + mprint("\nRequested buffer of %i > %i bytes (WTV_MAX_ALLOC)!\n", size, WTV_MAX_ALLOC); + cb->buffer=NULL; + return; + } + cb->buffer = (uint8_t*)malloc(size); + cb->buffer_size=size; + uint64_t start = cb->filepos; + + if(cb->skip_chunks[cb->chunk]!=-1 && start+size>cb->skip_chunks[cb->chunk]) { + buffered_read(cb->buffer, (int)(cb->skip_chunks[cb->chunk]-start)); + cb->filepos+=cb->skip_chunks[cb->chunk]-start; + buffered_seek(WTV_META_CHUNK_SIZE); + cb->filepos+=WTV_META_CHUNK_SIZE; + buffered_read(cb->buffer+(cb->skip_chunks[cb->chunk]-start), (int)(size-(cb->skip_chunks[cb->chunk]-start))); + cb->filepos+=size-(cb->skip_chunks[cb->chunk]-start); + cb->chunk++; + } + else { + buffered_read(cb->buffer, size); + cb->filepos+=size; + if(result!=size) { + free(cb->buffer); + cb->buffer_size=0; + past=cb->filepos; + cb->buffer=NULL; + mprint("\nPremature end of file!\n"); + return; + } + } + past=cb->filepos; + return; +} + +// read_header will read all the required information from +// the wtv header/root sections and calculate the skip_chunks. +// If successful, will return with the file positioned +// at the start of the data dir +int read_header(wtv_chunked_buffer *cb) { + uint8_t *parsebuf; + parsebuf = (uint8_t*)malloc(1024); + buffered_read(parsebuf,0x42); + past+=result; + if (result!=0x42) + { + mprint("\nPremature end of file!\n"); + end_of_file=1; + return 0; + } + // Expecting WTV header + if( !memcmp(parsebuf, WTV_HEADER, 16 ) ) + { + dbg_print(CCX_DMT_PARSE, "\nWTV header\n"); + } + else + { + mprint("\nMissing WTV header. Abort.\n"); + end_of_file=1; + return 0; + } + + //Next read just enough to get the location of the root directory + uint32_t filelen; + uint32_t root_dir; + memcpy(&filelen, parsebuf+0x30, 4); + dbg_print(CCX_DMT_PARSE, "filelen: %x\n", filelen); + memcpy(&root_dir, parsebuf+0x38, 4); + dbg_print(CCX_DMT_PARSE, "root_dir: %x\n", root_dir); + + //Seek to start of the root dir. Typically 0x1100 + buffered_skip((root_dir*WTV_CHUNK_SIZE)-0x42); + past+=(root_dir*WTV_CHUNK_SIZE)-0x42; + + if (result!=(root_dir*WTV_CHUNK_SIZE)-0x42) + { + end_of_file=1; + return 0; + } + + // Read and calculate the meta data chunks in the file we need to skip over + // while parsing the file. + int end=0; + while(!end) + { + buffered_read(parsebuf, 32); + int x; + for(x=0; x<16; x++) + dbg_print(CCX_DMT_PARSE, "%02X ", parsebuf[x]); + dbg_print(CCX_DMT_PARSE, "\n"); + + if (result!=32) + { + mprint("\nPremature end of file!\n"); + end_of_file=1; + free(parsebuf); + return 0; + } + past+=32; + if( !memcmp(parsebuf, WTV_EOF, 16 )) + { + dbg_print(CCX_DMT_PARSE, "WTV EOF\n"); + end=1; + } + else { + + uint16_t len; + uint64_t file_length; + memcpy(&len, parsebuf+16, 2); + dbg_print(CCX_DMT_PARSE, "len: %x\n", len); + memcpy(&file_length, parsebuf+24, 8); + dbg_print(CCX_DMT_PARSE, "file_length: %x\n", file_length); + if(len>1024) + { + mprint("Too large for buffer!\n"); + end_of_file=1; + free(parsebuf); + return 0; + } + buffered_read(parsebuf, len-32); + if (result!=len-32) + { + mprint("Premature end of file!\n"); + end_of_file=1; + free(parsebuf); + return 0; + } + past+=len-32; + // Read a unicode string + uint32_t text_len; + memcpy(&text_len, parsebuf, 4); //text_len is number of unicode chars, not bytes. + dbg_print(CCX_DMT_PARSE, "text_len: %x\n", text_len); + char *string; + string = (char*)malloc(text_len+1); // alloc for ascii + string[text_len]='\0'; + for(x=0; xskip_chunks, cb->count, sizeof(uint64_t), qsort_cmpint); + dbg_print(CCX_DMT_PARSE, "skip_chunks: "); + int x; + for(x=0; xcount; x++) + dbg_print(CCX_DMT_PARSE, "%llx, ", cb->skip_chunks[x]); + dbg_print(CCX_DMT_PARSE, "\n"); + + // Seek forward to the start of the data dir + // Typically 0x40000 + buffered_skip((int)((cb->skip_chunks[cb->chunk]+WTV_META_CHUNK_SIZE)-past)); + cb->filepos=(cb->skip_chunks[cb->chunk]+WTV_META_CHUNK_SIZE); + cb->chunk++; + past=cb->filepos; + free(parsebuf); + return 1; +} + +LLONG get_data(wtv_chunked_buffer *cb) { + static int video_streams[32]; + static int num_streams=0; + while(1) + { + int bytesread = 0; + // Read the 32 bytes containing the GUID and length and stream_id info + get_sized_buffer(cb, 32); + + if(cb->buffer==NULL) {end_of_file=1; return 0; } //Make this a macro? + + uint8_t guid[16]; + memcpy(&guid, cb->buffer, 16); // Read the GUID + int x; + for(x=0; x<16; x++) + dbg_print(CCX_DMT_PARSE, "%02X ", guid[x]); + dbg_print(CCX_DMT_PARSE, "\n"); + uint32_t len; + memcpy(&len, cb->buffer+16, 4); // Read the length + len-=32; + dbg_print(CCX_DMT_PARSE, "len %X\n", len); + uint32_t pad; + pad = len%8==0 ? 0 : 8- (len % 8); // Calculate the padding to add to the length + // to get to the next GUID + dbg_print(CCX_DMT_PARSE, "pad %X\n", pad); + uint32_t stream_id; + memcpy(&stream_id, cb->buffer+20, 4); + stream_id = stream_id & 0x7f; // Read and calculate the stream_id + dbg_print(CCX_DMT_PARSE, "stream_id: %X\n", stream_id); + + for(x=0; xbuffer); + cb->buffer=NULL; + //return one more byte so the final percentage is shown correctly + *(buffer+inbuf)=0x00; + end_of_file=1; + inbuf++; + return 1; + } + if( !memcmp(guid, WTV_STREAM2, 16 ) ) + { + // The WTV_STREAM2 GUID appares near the start of the data dir + // It maps stream_ids to the type of stream + dbg_print(CCX_DMT_PARSE, "WTV STREAM2\n"); + get_sized_buffer(cb, 0xc+16); + if(cb->buffer==NULL) {end_of_file=1; return 0; } + static unsigned char stream_type[16]; + memcpy(&stream_type, cb->buffer+0xc, 16); //Read the stream type GUID + const void *stream_guid; + if(ccx_options.wtvmpeg2) + stream_guid = WTV_STREAM_VIDEO; // We want mpeg2 data if the user set -wtvmpeg2 + else + stream_guid = WTV_STREAM_MSTVCAPTION; // Otherwise we want the MSTV captions stream + if(!memcmp(stream_type, stream_guid, 16 ) ) + { + video_streams[num_streams]=stream_id; // We keep a list of stream ids + num_streams++; // Even though there should only be 1 + } + len-=28; + } + if( !memcmp(guid, WTV_TIMING, 16 ) && check_stream_id(stream_id, video_streams, num_streams)) + { + // The WTV_TIMING GUID contains a timestamp for the given stream_id + dbg_print(CCX_DMT_PARSE, "WTV TIMING\n"); + get_sized_buffer(cb, 0x8+0x8); + if(cb->buffer==NULL) {end_of_file=1; return 0; } + int64_t time; + memcpy(&time, cb->buffer+0x8, 8); // Read the timestamp + dbg_print(CCX_DMT_PARSE, "TIME: %ld\n", time); + if(time!=-1) { // Ignore -1 timestamps + current_pts = time_to_pes_time(time); // Convert from WTV to PES time + pts_set = 1; + frames_since_ref_time = 0; + set_fts(); + } + len-=16; + } + if( !memcmp(guid, WTV_DATA, 16 ) + && check_stream_id(stream_id, video_streams, num_streams) && current_pts!=0 + && (ccx_options.wtvmpeg2 || (!ccx_options.wtvmpeg2 && len==2))) + { + // This is the data for a stream we want to process + dbg_print(CCX_DMT_PARSE, "\nWTV DATA\n"); + get_sized_buffer(cb, len); + if(cb->buffer==NULL) {end_of_file=1; return 0; } + memcpy(buffer+inbuf, cb->buffer, len); + inbuf+=result; + bytesread+=(int) len; + frames_since_ref_time++; + set_fts(); + if(pad>0) { //Make sure we skip any padding too, since we are returning here + skip_sized_buffer(cb, pad); + } + return bytesread; + } + if(len+pad>0) { + // skip any remaining data + // For any unhandled GUIDs this will be len+pad + // For others it will just be pad + skip_sized_buffer(cb, len+pad); + } + } +} + +LLONG wtv_getmoredata(void) +{ + static long parsebufsize = 1024; + static wtv_chunked_buffer cb; + if(firstcall) + { + init_chunked_buffer(&cb); + if(ccx_options.wtvmpeg2) + ccx_bufferdatatype=CCX_PES; + else + ccx_bufferdatatype=CCX_RAW; + if(read_header(&cb)==0) + // read_header returned an error + // read_header will have printed the error message + return 0; + firstcall=0; + } + return get_data(&cb); +} diff --git a/src/xds.cpp b/src/xds.cpp new file mode 100644 index 00000000..eb581722 --- /dev/null +++ b/src/xds.cpp @@ -0,0 +1,909 @@ +#include "ccextractor.h" + +// Program Identification Number (Start Time) for current program +static int current_xds_min=-1; +static int current_xds_hour=-1; +static int current_xds_date=-1; +static int current_xds_month=-1; +static int current_program_type_reported=0; // No. +static int xds_start_time_shown=0; +static int xds_program_length_shown=0; +static int xds_program_type_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_call_letters[7]; +static char current_xds_program_type[33]; + +static const char *XDSclasses[]= +{ + "Current", + "Future", + "Channel", + "Miscellaneous", + "Public service", + "Reserved", + "Private data", + "End" +}; + +static const char *XDSclasses_short[]= +{ + "CUR", + "FUT", + "CHN", + "MIS", + "PUB", + "RES", + "PRV", + "END" +}; + +static const char *XDSProgramTypes[]= +{ + "Education","Entertainment", "Movie", "News", "Religious", + "Sports", "Other", "Action","Advertisement", "Animated", + "Anthology","Automobile","Awards","Baseball","Basketball", + "Bulletin","Business","Classical","College","Combat", + "Comedy","Commentary","Concert","Consumer","Contemporary", + "Crime","Dance","Documentary","Drama","Elementary", + "Erotica","Exercise","Fantasy","Farm","Fashion", + "Fiction","Food","Football","Foreign","Fund-Raiser", + "Game/Quiz","Garden","Golf","Government","Health", + "High_School","History","Hobby","Hockey","Home", + "Horror","Information","Instruction","International","Interview", + "Language","Legal","Live","Local","Math", + "Medical","Meeting","Military","Mini-Series","Music", + "Mystery","National","Nature","Police","Politics", + "Premiere","Pre-Recorded","Product","Professional","Public", + "Racing","Reading","Repair","Repeat","Review", + "Romance","Science","Series","Service","Shopping", + "Soap_Opera","Special","Suspense","Talk","Technical", + "Tennis","Travel","Variety","Video","Weather", + "Western" +}; + +#define XDS_CLASS_CURRENT 0 +#define XDS_CLASS_FUTURE 1 +#define XDS_CLASS_CHANNEL 2 +#define XDS_CLASS_MISC 3 +#define XDS_CLASS_PUBLIC 4 +#define XDS_CLASS_RESERVED 5 +#define XDS_CLASS_PRIVATE 6 +#define XDS_CLASS_END 7 + +// Types for the classes current and future +#define XDS_TYPE_PIN_START_TIME 1 +#define XDS_TYPE_LENGH_AND_CURRENT_TIME 2 +#define XDS_TYPE_PROGRAM_NAME 3 +#define XDS_TYPE_PROGRAM_TYPE 4 +#define XDS_TYPE_CONTENT_ADVISORY 5 +#define XDS_TYPE_AUDIO_SERVICES 6 +#define XDS_TYPE_CGMS 8 // Copy Generation Management System +#define XDS_TYPE_PROGRAM_DESC_1 0x10 +#define XDS_TYPE_PROGRAM_DESC_2 0x11 +#define XDS_TYPE_PROGRAM_DESC_3 0x12 +#define XDS_TYPE_PROGRAM_DESC_4 0x13 +#define XDS_TYPE_PROGRAM_DESC_5 0x14 +#define XDS_TYPE_PROGRAM_DESC_6 0x15 +#define XDS_TYPE_PROGRAM_DESC_7 0x16 +#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_TSID 4 // Transmission Signal Identifier + +// Types for miscellaneous packets +#define XDS_TYPE_TIME_OF_DAY 1 +#define XDS_TYPE_LOCAL_TIME_ZONE 4 + +#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 + +struct xds_buffer +{ + unsigned in_use; + int xds_class; + 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]; + +static int cur_xds_buffer_idx=-1; +static int cur_xds_packet_class=-1; +static unsigned char *cur_xds_payload; +static int cur_xds_payload_length; +static int cur_xds_packet_type; + + +void xds_init() +{ + for (int i=0;ifh==-1) + return; + write (wb->fh, encoded_crlf, encoded_crlf_length); +} + +void xds_write_transcript_line_prefix (struct ccx_s_write *wb) +{ + unsigned h1,m1,s1,ms1; + unsigned h2,m2,s2,ms2; + if (!wb || wb->fh==-1) + return; + + if (timestamps_on_transcript) + { + const char *mode="XDS"; + if (ts_start_of_xds == -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."); + ; + } + if (ccx_options.ucla_settings) + { + mstotime (ts_start_of_xds+subs_delay,&h1,&m1,&s1,&ms1); + mstotime (get_fts()+subs_delay,&h2,&m2,&s2,&ms2); + + // SSC-1182 BEGIN + // Changed output format to be more like ZVBI + + char buffer[80]; + time_t start_time_int = (ts_start_of_xds+subs_delay)/1000; + int start_time_dec = (ts_start_of_xds+subs_delay)%1000; + struct tm *start_time_struct = gmtime(&start_time_int); + strftime(buffer, sizeof(buffer), "%Y%m%d%H%M%S", start_time_struct); + fdprintf(wb->fh, "%s.%03d|", buffer, start_time_dec); + + time_t end_time_int = (get_fts()+subs_delay)/1000; + int end_time_dec = (get_fts()+subs_delay)%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.%03d|", buffer, end_time_dec); + fdprintf(wb->fh, "%s|%s|", mode,XDSclasses_short[cur_xds_packet_class]); + // SSC-1182 END + } + else + { + if (utc_refvalue==UINT64_MAX) + { + mstotime (ts_start_of_xds+subs_delay,&h1,&m1,&s1,&ms1); + mstotime (get_fts()+subs_delay,&h2,&m2,&s2,&ms2); + char timeline[256]; + sprintf (timeline, "%02u:%02u:%02u,%03u | %02u:%02u:%02u,%03u | %s | ", + h1,m1,s1,ms1,h2,m2,s2,ms2,mode); + enc_buffer_used=encode_line (enc_buffer,(unsigned char *) timeline); + write (wb->fh, enc_buffer,enc_buffer_used); + } + else + { + fdprintf(wb->fh, "%lld.%03d|", (ts_start_of_xds+subs_delay)/1000,(ts_start_of_xds+subs_delay)%1000 ); + fdprintf(wb->fh, "%lld.%03d|", (get_fts()+subs_delay)/1000,(get_fts()+subs_delay)%1000 ); + } + } + } +} + +void xdsprint (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; + va_list ap; + + 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 (wbxdsout->fh, p, n); + free (p); + xds_write_transcript_line_suffix (wbxdsout); + 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); + xds_write_transcript_line_suffix (wbxdsout); + return ; + } else { + p = np; + } + } +} + +void xds_debug_test() +{ + process_xds_bytes (0x05,0x02); + process_xds_bytes (0x20,0x20); + do_end_of_xds (0x2a); + +} + +void xds_cea608_test() +{ + /* 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. */ + process_xds_bytes (0x01,0x03); + process_xds_bytes (0x53,0x74); + process_xds_bytes (0x61,0x72); + process_xds_bytes (0x20,0x54); + process_xds_bytes (0x72,0x65); + process_xds_bytes (0x02,0x03); + process_xds_bytes (0x02,0x03); + process_xds_bytes (0x6b,0x00); + do_end_of_xds (0x1d); +} + +int how_many_used() +{ + int c=0; + for (int i=0;i=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()); + int first_free_buf=-1; + int matching_buf=-1; + for (int i=0;i0 && hi<=0x1f) || (lo>0 && lo<=0x1f)) + { + mprint ("\rNote: Illegal XDS data"); + return; + } + } + if (xds_buffers[cur_xds_buffer_idx].used_bytes<=32) + { + // 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; + } +} + +void xds_do_copy_generation_management_system (unsigned c1, unsigned c2) +{ + static unsigned last_c1=-1, last_c2=-1; + static char copy_permited[256]; + static char aps[256]; + static char rcd[256]; + int changed=0; + unsigned c1_6=(c1&0x40)>>6; + unsigned unused1=(c1&0x20)>>5; + unsigned cgms_a_b4=(c1&0x10)>>4; + unsigned cgms_a_b3=(c1&0x8)>>3; + unsigned aps_b2=(c1&0x4)>>2; + unsigned aps_b1=(c1&0x2)>>1; + unsigned asb_0=(c1&0x1); + unsigned c2_6=(c2&0x40)>>6; + unsigned c2_5=(c2&0x20)>>5; + unsigned c2_4=(c2&0x10)>>4; + unsigned c2_3=(c2&0x8)>>3; + unsigned c2_2=(c2&0x4)>>2; + unsigned c2_1=(c2&0x2)>>1; + unsigned rcd0=(c2&0x1); + if (!c1_6 || !c2_6) // These must be high. If not, not following specs + return; + if (last_c1!=c1 || last_c2!=c2) + { + changed=1; + 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)", + "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]); + sprintf (aps,"APS: %s", apstext[aps_b2*2+aps_b1]); + sprintf (rcd,"Redistribution Control Descriptor: %d", rcd0); + + } + + xdsprint(copy_permited); + xdsprint(aps); + xdsprint(rcd); + if (changed) + { + mprint ("\rXDS: %s\n",copy_permited); + mprint ("\rXDS: %s\n",aps); + mprint ("\rXDS: %s\n",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); +} + +void xds_do_content_advisory (unsigned c1, unsigned c2) +{ + static unsigned last_c1=-1, last_c2=-1; + static char age[256]; + static char content[256]; + static char rating[256]; + int changed=0; + // Insane encoding + unsigned c1_6=(c1&0x40)>>6; + unsigned Da2=(c1&0x20)>>5; + unsigned a1=(c1&0x10)>>4; + unsigned a0=(c1&0x8)>>3; + unsigned r2=(c1&0x4)>>2; + unsigned r1=(c1&0x2)>>1; + unsigned r0=(c1&0x1); + unsigned c2_6=(c2&0x40)>>6; + unsigned FV=(c2&0x20)>>5; + unsigned S=(c2&0x10)>>4; + unsigned La3=(c2&0x8)>>3; + unsigned g2=(c2&0x4)>>2; + unsigned g1=(c2&0x2)>>1; + unsigned g0=(c2&0x1); + unsigned supported=0; + if (!c1_6 || !c2_6) // These must be high. If not, not following specs + return; + if (last_c1!=c1 || last_c2!=c2) + { + changed=1; + 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)", + "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; + if (!g2 && g1 && !g0) // For TV-Y7 (Older chidren), the Violence bit is "fantasy violence" + { + if (FV) + strcpy (content, "[Fantasy Violence] "); + } + else // For all others, is real + { + if (FV) + strcpy (content, "[Violence] "); + } + if (S) + strcat (content, "[Sexual Situations] "); + if (La3) + strcat (content, "[Adult Language] "); + if (Da2) + strcat (content, "[Sexually Suggestive Dialog] "); + supported=1; + } + if (!a0) // MPA + { + const char *ratingtext[8]={"N/A", "G", "PG", "PG-13", "R", "NC-17", "X", "Not Rated"}; + sprintf (rating,"ContentAdvisory: MPA Rating: %s", ratingtext[r2*4+r1*2+r0]); + supported=1; + } + 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", + "Viewers 14 years and older", "Adult Programming", "[undefined]"}; + sprintf (rating,"ContentAdvisory: Canadian English Rating: %s", ratingtext[g2*4+g1*2+g0]); + supported=1; + } + + } + // 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) + { + mprint ("\rXDS: %s\n ",age); + mprint ("\rXDS: %s\n ",content); + } + dbg_print(CCX_DMT_XDS, "\rXDS: %s\n",age); + dbg_print(CCX_DMT_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 (changed && !supported) + mprint ("XDS: Unsupported ContentAdvisory encoding, please submit sample.\n"); + + +} + +int xds_do_current_and_future () +{ + int was_proc=0; + switch (cur_xds_packet_type) + { + case XDS_TYPE_PIN_START_TIME: + { + was_proc=1; + if (cur_xds_payload_length<7) // We need 4 data bytes + break; + int min=cur_xds_payload[2] & 0x3f; // 6 bits + 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 changed=0; + if (current_xds_min!=min || + current_xds_hour!=hour || + current_xds_date!=date || + current_xds_month!=month) + { + changed=1; + xds_start_time_shown=0; + current_xds_min=min; + current_xds_hour=hour; + current_xds_date=date; + current_xds_month=month; + } + + dbg_print(CCX_DMT_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", + (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); + xds_start_time_shown=1; + } + } + break; + case XDS_TYPE_LENGH_AND_CURRENT_TIME: + { + was_proc=1; + if (cur_xds_payload_length<5) // We need 2 data bytes + 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); + else + dbg_print(CCX_DMT_XDS, "\rXDS: Program length (HH:MM): %02d:%02d ",hour,min); + + xdsprint("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); + 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); + + } + if (cur_xds_payload_length>8) // Next two bytes (optional) available + { + 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); + } + if (!xds_program_length_shown) + printf ("\n"); + else + dbg_print(CCX_DMT_XDS, "\n"); + xds_program_length_shown=1; + } + break; + case XDS_TYPE_PROGRAM_NAME: + { + was_proc=1; + char xds_program_name[33]; + int i; + for (i=2;ifh!=-1) + fdprintf (wbxdsout->fh,"Program type "); + + for (int i=2;ifh!=-1) + { + if (cur_xds_payload[i]>=0x20 && cur_xds_payload[i]<0x7F) + fdprintf (wbxdsout->fh,"[%s] ",XDSProgramTypes[cur_xds_payload[i]-0x20]); + } + if (!current_program_type_reported) + { + if (cur_xds_payload[i]>=0x20 && cur_xds_payload[i]<0x7F) + mprint ("%s",XDSProgramTypes[cur_xds_payload[i]-0x20]); + else + mprint ("ILLEGAL VALUE"); + mprint ("] "); + } + } + xds_write_transcript_line_suffix(wbxdsout); + if (!current_program_type_reported) + mprint ("\n"); + current_program_type_reported=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]); + break; + 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]); + break; + case XDS_TYPE_PROGRAM_DESC_1: + case XDS_TYPE_PROGRAM_DESC_2: + case XDS_TYPE_PROGRAM_DESC_3: + case XDS_TYPE_PROGRAM_DESC_4: + case XDS_TYPE_PROGRAM_DESC_5: + case XDS_TYPE_PROGRAM_DESC_6: + case XDS_TYPE_PROGRAM_DESC_7: + case XDS_TYPE_PROGRAM_DESC_8: + { + was_proc=1; + int changed=0; + char xds_desc[33]; + int i; + for (i=2;ifh, "%02X ",cur_xds_payload[i]); + + xds_write_transcript_line_suffix (wbxdsout); + return 1; +} + +int xds_do_misc () +{ + int was_proc=0; + switch (cur_xds_packet_type) + { + case XDS_TYPE_TIME_OF_DAY: + { + was_proc=1; + if (cur_xds_payload_length<9) // We need 6 data bytes + break; + int min=cur_xds_payload[2] & 0x3f; // 6 bits + 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 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); + break; + } + case XDS_TYPE_LOCAL_TIME_ZONE: + { + was_proc=1; + if (cur_xds_payload_length<5) // We need 2 data bytes + break; + 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); + break; + } + default: + was_proc=0; + break; + } + return was_proc; +} + +void do_end_of_xds (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_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=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", + cur_xds_packet_class,XDSclasses[cur_xds_packet_class], + cur_xds_payload_length, + 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); + 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. */ + + 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 + { + was_proc=1; + break; + } + case XDS_CLASS_CURRENT: // Info on current program + was_proc = xds_do_current_and_future(); + break; + case XDS_CLASS_CHANNEL: + was_proc = xds_do_channel(); + 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(); + break; + } + + if (!was_proc) + { + mprint ("Note: We found an currently unsupported XDS packet.\n"); + } + clear_xds_buffer (cur_xds_buffer_idx); + +} diff --git a/src/zlib/adler32.c b/src/zlib/adler32.c new file mode 100644 index 00000000..67337d1e --- /dev/null +++ b/src/zlib/adler32.c @@ -0,0 +1,167 @@ +/* adler32.c -- compute the Adler-32 checksum of a data stream + * Copyright (C) 1995-2011 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* @(#) $Id: adler32.c,v 1.2 2013/12/18 09:48:32 cfsmp3 Exp $ */ + +#include "zutil.h" + +#define local static + +local uLong adler32_combine_ OF((uLong adler1, uLong adler2, z_off64_t len2)); + +#define BASE 65521 /* largest prime smaller than 65536 */ +#define NMAX 5552 +/* NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 */ + +#define DO1(buf,i) {adler += (buf)[i]; sum2 += adler;} +#define DO2(buf,i) DO1(buf,i); DO1(buf,i+1); +#define DO4(buf,i) DO2(buf,i); DO2(buf,i+2); +#define DO8(buf,i) DO4(buf,i); DO4(buf,i+4); +#define DO16(buf) DO8(buf,0); DO8(buf,8); + +/* use NO_DIVIDE if your processor does not do division in hardware -- + try it both ways to see which is faster */ +#ifdef NO_DIVIDE +/* note that this assumes BASE is 65521, where 65536 % 65521 == 15 + (thank you to John Reiser for pointing this out) */ +# define CHOP(a) \ + do { \ + unsigned long tmp = a >> 16; \ + a &= 0xffffUL; \ + a += (tmp << 4) - tmp; \ + } while (0) +# define MOD28(a) \ + do { \ + CHOP(a); \ + if (a >= BASE) a -= BASE; \ + } while (0) +# define MOD(a) \ + do { \ + CHOP(a); \ + MOD28(a); \ + } while (0) +# define MOD63(a) \ + do { /* this assumes a is not negative */ \ + z_off64_t tmp = a >> 32; \ + a &= 0xffffffffL; \ + a += (tmp << 8) - (tmp << 5) + tmp; \ + tmp = a >> 16; \ + a &= 0xffffL; \ + a += (tmp << 4) - tmp; \ + tmp = a >> 16; \ + a &= 0xffffL; \ + a += (tmp << 4) - tmp; \ + if (a >= BASE) a -= BASE; \ + } while (0) +#else +# define MOD(a) a %= BASE +# define MOD28(a) a %= BASE +# define MOD63(a) a %= BASE +#endif + +/* ========================================================================= */ +uLong ZEXPORT adler32(uLong adler, const Bytef *buf, uInt len) +{ + unsigned long sum2; + unsigned n; + + /* split Adler-32 into component sums */ + sum2 = (adler >> 16) & 0xffff; + adler &= 0xffff; + + /* in case user likes doing a byte at a time, keep it fast */ + if (len == 1) { + adler += buf[0]; + if (adler >= BASE) + adler -= BASE; + sum2 += adler; + if (sum2 >= BASE) + sum2 -= BASE; + return adler | (sum2 << 16); + } + + /* initial Adler-32 value (deferred check for len == 1 speed) */ + if (buf == Z_NULL) + return 1L; + + /* in case short lengths are provided, keep it somewhat fast */ + if (len < 16) { + while (len--) { + adler += *buf++; + sum2 += adler; + } + if (adler >= BASE) + adler -= BASE; + MOD28(sum2); /* only added so many BASE's */ + return adler | (sum2 << 16); + } + + /* do length NMAX blocks -- requires just one modulo operation */ + while (len >= NMAX) { + len -= NMAX; + n = NMAX / 16; /* NMAX is divisible by 16 */ + do { + DO16(buf); /* 16 sums unrolled */ + buf += 16; + } while (--n); + MOD(adler); + MOD(sum2); + } + + /* do remaining bytes (less than NMAX, still just one modulo) */ + if (len) { /* avoid modulos if none remaining */ + while (len >= 16) { + len -= 16; + DO16(buf); + buf += 16; + } + while (len--) { + adler += *buf++; + sum2 += adler; + } + MOD(adler); + MOD(sum2); + } + + /* return recombined sums */ + return adler | (sum2 << 16); +} + +/* ========================================================================= */ +local uLong adler32_combine_(uLong adler1, uLong adler2, z_off64_t len2) +{ + unsigned long sum1; + unsigned long sum2; + unsigned rem; + + /* for negative len, return invalid adler32 as a clue for debugging */ + if (len2 < 0) + return 0xffffffffUL; + + /* the derivation of this formula is left as an exercise for the reader */ + MOD63(len2); /* assumes len2 >= 0 */ + rem = (unsigned)len2; + sum1 = adler1 & 0xffff; + sum2 = rem * sum1; + MOD(sum2); + sum1 += (adler2 & 0xffff) + BASE - 1; + sum2 += ((adler1 >> 16) & 0xffff) + ((adler2 >> 16) & 0xffff) + BASE - rem; + if (sum1 >= BASE) sum1 -= BASE; + if (sum1 >= BASE) sum1 -= BASE; + if (sum2 >= (BASE << 1)) sum2 -= (BASE << 1); + if (sum2 >= BASE) sum2 -= BASE; + return sum1 | (sum2 << 16); +} + +/* ========================================================================= */ +uLong ZEXPORT adler32_combine(uLong adler1, uLong adler2, z_off_t len2) +{ + return adler32_combine_(adler1, adler2, len2); +} + +uLong ZEXPORT adler32_combine64(uLong adler1, uLong adler2, z_off64_t len2) +{ + return adler32_combine_(adler1, adler2, len2); +} diff --git a/src/zlib/crc32.c b/src/zlib/crc32.c new file mode 100644 index 00000000..369f9817 --- /dev/null +++ b/src/zlib/crc32.c @@ -0,0 +1,403 @@ +/* crc32.c -- compute the CRC-32 of a data stream + * Copyright (C) 1995-2006, 2010, 2011, 2012 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + * + * Thanks to Rodney Brown for his contribution of faster + * CRC methods: exclusive-oring 32 bits of data at a time, and pre-computing + * tables for updating the shift register in one step with three exclusive-ors + * instead of four steps with four exclusive-ors. This results in about a + * factor of two increase in speed on a Power PC G4 (PPC7455) using gcc -O3. + */ + +/* @(#) $Id: crc32.c,v 1.2 2013/12/18 09:48:32 cfsmp3 Exp $ */ + +/* + Note on the use of DYNAMIC_CRC_TABLE: there is no mutex or semaphore + protection on the static variables used to control the first-use generation + of the crc tables. Therefore, if you #define DYNAMIC_CRC_TABLE, you should + first call get_crc_table() to initialize the tables before allowing more than + one thread to use crc32(). + + DYNAMIC_CRC_TABLE and MAKECRCH can be #defined to write out crc32.h. + */ + +#ifdef MAKECRCH +# include +# ifndef DYNAMIC_CRC_TABLE +# define DYNAMIC_CRC_TABLE +# endif /* !DYNAMIC_CRC_TABLE */ +#endif /* MAKECRCH */ + +#include "zutil.h" /* for STDC and FAR definitions */ + +#define local static + +/* Definitions for doing the crc four data bytes at a time. */ +#if !defined(NOBYFOUR) && defined(Z_U4) +# define BYFOUR +#endif +#ifdef BYFOUR + local unsigned long crc32_little OF((unsigned long, + const unsigned char FAR *, unsigned)); + local unsigned long crc32_big OF((unsigned long, + const unsigned char FAR *, unsigned)); +# define TBLS 8 +#else +# define TBLS 1 +#endif /* BYFOUR */ + +/* Local functions for crc concatenation */ +local unsigned long gf2_matrix_times OF((unsigned long *mat, + unsigned long vec)); +local void gf2_matrix_square OF((unsigned long *square, unsigned long *mat)); +local uLong crc32_combine_ OF((uLong crc1, uLong crc2, z_off64_t len2)); + + +#ifdef DYNAMIC_CRC_TABLE + +local volatile int crc_table_empty = 1; +local z_crc_t FAR crc_table[TBLS][256]; +local void make_crc_table OF((void)); +#ifdef MAKECRCH + local void write_table OF((FILE *, const z_crc_t FAR *)); +#endif /* MAKECRCH */ +/* + Generate tables for a byte-wise 32-bit CRC calculation on the polynomial: + x^32+x^26+x^23+x^22+x^16+x^12+x^11+x^10+x^8+x^7+x^5+x^4+x^2+x+1. + + Polynomials over GF(2) are represented in binary, one bit per coefficient, + with the lowest powers in the most significant bit. Then adding polynomials + is just exclusive-or, and multiplying a polynomial by x is a right shift by + one. If we call the above polynomial p, and represent a byte as the + polynomial q, also with the lowest power in the most significant bit (so the + byte 0xb1 is the polynomial x^7+x^3+x+1), then the CRC is (q*x^32) mod p, + where a mod b means the remainder after dividing a by b. + + This calculation is done using the shift-register method of multiplying and + taking the remainder. The register is initialized to zero, and for each + incoming bit, x^32 is added mod p to the register if the bit is a one (where + x^32 mod p is p+x^32 = x^26+...+1), and the register is multiplied mod p by + x (which is shifting right by one and adding x^32 mod p if the bit shifted + out is a one). We start with the highest power (least significant bit) of + q and repeat for all eight bits of q. + + The first table is simply the CRC of all possible eight bit values. This is + all the information needed to generate CRCs on data a byte at a time for all + combinations of CRC register values and incoming bytes. The remaining tables + allow for word-at-a-time CRC calculation for both big-endian and little- + endian machines, where a word is four bytes. +*/ +local void make_crc_table() +{ + z_crc_t c; + int n, k; + z_crc_t poly; /* polynomial exclusive-or pattern */ + /* terms of polynomial defining this crc (except x^32): */ + static volatile int first = 1; /* flag to limit concurrent making */ + static const unsigned char p[] = {0,1,2,4,5,7,8,10,11,12,16,22,23,26}; + + /* See if another task is already doing this (not thread-safe, but better + than nothing -- significantly reduces duration of vulnerability in + case the advice about DYNAMIC_CRC_TABLE is ignored) */ + if (first) { + first = 0; + + /* make exclusive-or pattern from polynomial (0xedb88320UL) */ + poly = 0; + for (n = 0; n < (int)(sizeof(p)/sizeof(unsigned char)); n++) + poly |= (z_crc_t)1 << (31 - p[n]); + + /* generate a crc for every 8-bit value */ + for (n = 0; n < 256; n++) { + c = (z_crc_t)n; + for (k = 0; k < 8; k++) + c = c & 1 ? poly ^ (c >> 1) : c >> 1; + crc_table[0][n] = c; + } + +#ifdef BYFOUR + /* generate crc for each value followed by one, two, and three zeros, + and then the byte reversal of those as well as the first table */ + for (n = 0; n < 256; n++) { + c = crc_table[0][n]; + crc_table[4][n] = ZSWAP32(c); + for (k = 1; k < 4; k++) { + c = crc_table[0][c & 0xff] ^ (c >> 8); + crc_table[k][n] = c; + crc_table[k + 4][n] = ZSWAP32(c); + } + } +#endif /* BYFOUR */ + + crc_table_empty = 0; + } + else { /* not first */ + /* wait for the other guy to finish (not efficient, but rare) */ + while (crc_table_empty) + ; + } + +#ifdef MAKECRCH + /* write out CRC tables to crc32.h */ + { + FILE *out; + + out = fopen("crc32.h", "w"); + if (out == NULL) return; + fprintf(out, "/* crc32.h -- tables for rapid CRC calculation\n"); + fprintf(out, " * Generated automatically by crc32.c\n */\n\n"); + fprintf(out, "local const z_crc_t FAR "); + fprintf(out, "crc_table[TBLS][256] =\n{\n {\n"); + write_table(out, crc_table[0]); +# ifdef BYFOUR + fprintf(out, "#ifdef BYFOUR\n"); + for (k = 1; k < 8; k++) { + fprintf(out, " },\n {\n"); + write_table(out, crc_table[k]); + } + fprintf(out, "#endif\n"); +# endif /* BYFOUR */ + fprintf(out, " }\n};\n"); + fclose(out); + } +#endif /* MAKECRCH */ +} + +#ifdef MAKECRCH +local void write_table(out, table) + FILE *out; + const z_crc_t FAR *table; +{ + int n; + + for (n = 0; n < 256; n++) + fprintf(out, "%s0x%08lxUL%s", n % 5 ? "" : " ", + (unsigned long)(table[n]), + n == 255 ? "\n" : (n % 5 == 4 ? ",\n" : ", ")); +} +#endif /* MAKECRCH */ + +#else /* !DYNAMIC_CRC_TABLE */ +/* ======================================================================== + * Tables of CRC-32s of all single-byte values, made by make_crc_table(). + */ +#include "crc32.h" +#endif /* DYNAMIC_CRC_TABLE */ + +/* ========================================================================= + * This function can be used by asm versions of crc32() + */ +const z_crc_t FAR * ZEXPORT get_crc_table() +{ +#ifdef DYNAMIC_CRC_TABLE + if (crc_table_empty) + make_crc_table(); +#endif /* DYNAMIC_CRC_TABLE */ + return (const z_crc_t FAR *)crc_table; +} + +/* ========================================================================= */ +#define DO1 crc = crc_table[0][((int)crc ^ (*buf++)) & 0xff] ^ (crc >> 8) +#define DO8 DO1; DO1; DO1; DO1; DO1; DO1; DO1; DO1 + +/* ========================================================================= */ +unsigned long ZEXPORT crc32(unsigned long crc, const unsigned char FAR *buf, uInt len) +{ + if (buf == Z_NULL) return 0UL; + +#ifdef DYNAMIC_CRC_TABLE + if (crc_table_empty) + make_crc_table(); +#endif /* DYNAMIC_CRC_TABLE */ + +#ifdef BYFOUR + if (sizeof(void *) == sizeof(ptrdiff_t)) { + z_crc_t endian; + + endian = 1; + if (*((unsigned char *)(&endian))) + return crc32_little(crc, buf, len); + else + return crc32_big(crc, buf, len); + } +#endif /* BYFOUR */ + crc = crc ^ 0xffffffffUL; + while (len >= 8) { + DO8; + len -= 8; + } + if (len) do { + DO1; + } while (--len); + return crc ^ 0xffffffffUL; +} + +#ifdef BYFOUR + +/* ========================================================================= */ +#define DOLIT4 c ^= *buf4++; \ + c = crc_table[3][c & 0xff] ^ crc_table[2][(c >> 8) & 0xff] ^ \ + crc_table[1][(c >> 16) & 0xff] ^ crc_table[0][c >> 24] +#define DOLIT32 DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4 + +/* ========================================================================= */ +local unsigned long crc32_little(unsigned long crc, const unsigned char FAR *buf, unsigned len) +{ + register z_crc_t c; + register const z_crc_t FAR *buf4; + + c = (z_crc_t)crc; + c = ~c; + while (len && ((ptrdiff_t)buf & 3)) { + c = crc_table[0][(c ^ *buf++) & 0xff] ^ (c >> 8); + len--; + } + + buf4 = (const z_crc_t FAR *)(const void FAR *)buf; + while (len >= 32) { + DOLIT32; + len -= 32; + } + while (len >= 4) { + DOLIT4; + len -= 4; + } + buf = (const unsigned char FAR *)buf4; + + if (len) do { + c = crc_table[0][(c ^ *buf++) & 0xff] ^ (c >> 8); + } while (--len); + c = ~c; + return (unsigned long)c; +} + +/* ========================================================================= */ +#define DOBIG4 c ^= *++buf4; \ + c = crc_table[4][c & 0xff] ^ crc_table[5][(c >> 8) & 0xff] ^ \ + crc_table[6][(c >> 16) & 0xff] ^ crc_table[7][c >> 24] +#define DOBIG32 DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4 + +/* ========================================================================= */ +local unsigned long crc32_big(unsigned long crc, const unsigned char FAR *buf, unsigned len) +{ + register z_crc_t c; + register const z_crc_t FAR *buf4; + + c = ZSWAP32((z_crc_t)crc); + c = ~c; + while (len && ((ptrdiff_t)buf & 3)) { + c = crc_table[4][(c >> 24) ^ *buf++] ^ (c << 8); + len--; + } + + buf4 = (const z_crc_t FAR *)(const void FAR *)buf; + buf4--; + while (len >= 32) { + DOBIG32; + len -= 32; + } + while (len >= 4) { + DOBIG4; + len -= 4; + } + buf4++; + buf = (const unsigned char FAR *)buf4; + + if (len) do { + c = crc_table[4][(c >> 24) ^ *buf++] ^ (c << 8); + } while (--len); + c = ~c; + return (unsigned long)(ZSWAP32(c)); +} + +#endif /* BYFOUR */ + +#define GF2_DIM 32 /* dimension of GF(2) vectors (length of CRC) */ + +/* ========================================================================= */ +local unsigned long gf2_matrix_times(unsigned long *mat, unsigned long vec) +{ + unsigned long sum; + + sum = 0; + while (vec) { + if (vec & 1) + sum ^= *mat; + vec >>= 1; + mat++; + } + return sum; +} + +/* ========================================================================= */ +local void gf2_matrix_square(unsigned long *square, unsigned long *mat) +{ + int n; + + for (n = 0; n < GF2_DIM; n++) + square[n] = gf2_matrix_times(mat, mat[n]); +} + +/* ========================================================================= */ +local uLong crc32_combine_(uLong crc1, uLong crc2, z_off64_t len2) +{ + int n; + unsigned long row; + unsigned long even[GF2_DIM]; /* even-power-of-two zeros operator */ + unsigned long odd[GF2_DIM]; /* odd-power-of-two zeros operator */ + + /* degenerate case (also disallow negative lengths) */ + if (len2 <= 0) + return crc1; + + /* put operator for one zero bit in odd */ + odd[0] = 0xedb88320UL; /* CRC-32 polynomial */ + row = 1; + for (n = 1; n < GF2_DIM; n++) { + odd[n] = row; + row <<= 1; + } + + /* put operator for two zero bits in even */ + gf2_matrix_square(even, odd); + + /* put operator for four zero bits in odd */ + gf2_matrix_square(odd, even); + + /* apply len2 zeros to crc1 (first square will put the operator for one + zero byte, eight zero bits, in even) */ + do { + /* apply zeros operator for this bit of len2 */ + gf2_matrix_square(even, odd); + if (len2 & 1) + crc1 = gf2_matrix_times(even, crc1); + len2 >>= 1; + + /* if no more bits set, then done */ + if (len2 == 0) + break; + + /* another iteration of the loop with odd and even swapped */ + gf2_matrix_square(odd, even); + if (len2 & 1) + crc1 = gf2_matrix_times(odd, crc1); + len2 >>= 1; + + /* if no more bits set, then done */ + } while (len2 != 0); + + /* return combined crc */ + crc1 ^= crc2; + return crc1; +} + +/* ========================================================================= */ +uLong ZEXPORT crc32_combine(uLong crc1, uLong crc2, z_off_t len2) +{ + return crc32_combine_(crc1, crc2, len2); +} + +uLong ZEXPORT crc32_combine64(uLong crc1, uLong crc2, z_off64_t len2) +{ + return crc32_combine_(crc1, crc2, len2); +} diff --git a/src/zlib/crc32.h b/src/zlib/crc32.h new file mode 100644 index 00000000..9e0c7781 --- /dev/null +++ b/src/zlib/crc32.h @@ -0,0 +1,441 @@ +/* crc32.h -- tables for rapid CRC calculation + * Generated automatically by crc32.c + */ + +local const z_crc_t FAR crc_table[TBLS][256] = +{ + { + 0x00000000UL, 0x77073096UL, 0xee0e612cUL, 0x990951baUL, 0x076dc419UL, + 0x706af48fUL, 0xe963a535UL, 0x9e6495a3UL, 0x0edb8832UL, 0x79dcb8a4UL, + 0xe0d5e91eUL, 0x97d2d988UL, 0x09b64c2bUL, 0x7eb17cbdUL, 0xe7b82d07UL, + 0x90bf1d91UL, 0x1db71064UL, 0x6ab020f2UL, 0xf3b97148UL, 0x84be41deUL, + 0x1adad47dUL, 0x6ddde4ebUL, 0xf4d4b551UL, 0x83d385c7UL, 0x136c9856UL, + 0x646ba8c0UL, 0xfd62f97aUL, 0x8a65c9ecUL, 0x14015c4fUL, 0x63066cd9UL, + 0xfa0f3d63UL, 0x8d080df5UL, 0x3b6e20c8UL, 0x4c69105eUL, 0xd56041e4UL, + 0xa2677172UL, 0x3c03e4d1UL, 0x4b04d447UL, 0xd20d85fdUL, 0xa50ab56bUL, + 0x35b5a8faUL, 0x42b2986cUL, 0xdbbbc9d6UL, 0xacbcf940UL, 0x32d86ce3UL, + 0x45df5c75UL, 0xdcd60dcfUL, 0xabd13d59UL, 0x26d930acUL, 0x51de003aUL, + 0xc8d75180UL, 0xbfd06116UL, 0x21b4f4b5UL, 0x56b3c423UL, 0xcfba9599UL, + 0xb8bda50fUL, 0x2802b89eUL, 0x5f058808UL, 0xc60cd9b2UL, 0xb10be924UL, + 0x2f6f7c87UL, 0x58684c11UL, 0xc1611dabUL, 0xb6662d3dUL, 0x76dc4190UL, + 0x01db7106UL, 0x98d220bcUL, 0xefd5102aUL, 0x71b18589UL, 0x06b6b51fUL, + 0x9fbfe4a5UL, 0xe8b8d433UL, 0x7807c9a2UL, 0x0f00f934UL, 0x9609a88eUL, + 0xe10e9818UL, 0x7f6a0dbbUL, 0x086d3d2dUL, 0x91646c97UL, 0xe6635c01UL, + 0x6b6b51f4UL, 0x1c6c6162UL, 0x856530d8UL, 0xf262004eUL, 0x6c0695edUL, + 0x1b01a57bUL, 0x8208f4c1UL, 0xf50fc457UL, 0x65b0d9c6UL, 0x12b7e950UL, + 0x8bbeb8eaUL, 0xfcb9887cUL, 0x62dd1ddfUL, 0x15da2d49UL, 0x8cd37cf3UL, + 0xfbd44c65UL, 0x4db26158UL, 0x3ab551ceUL, 0xa3bc0074UL, 0xd4bb30e2UL, + 0x4adfa541UL, 0x3dd895d7UL, 0xa4d1c46dUL, 0xd3d6f4fbUL, 0x4369e96aUL, + 0x346ed9fcUL, 0xad678846UL, 0xda60b8d0UL, 0x44042d73UL, 0x33031de5UL, + 0xaa0a4c5fUL, 0xdd0d7cc9UL, 0x5005713cUL, 0x270241aaUL, 0xbe0b1010UL, + 0xc90c2086UL, 0x5768b525UL, 0x206f85b3UL, 0xb966d409UL, 0xce61e49fUL, + 0x5edef90eUL, 0x29d9c998UL, 0xb0d09822UL, 0xc7d7a8b4UL, 0x59b33d17UL, + 0x2eb40d81UL, 0xb7bd5c3bUL, 0xc0ba6cadUL, 0xedb88320UL, 0x9abfb3b6UL, + 0x03b6e20cUL, 0x74b1d29aUL, 0xead54739UL, 0x9dd277afUL, 0x04db2615UL, + 0x73dc1683UL, 0xe3630b12UL, 0x94643b84UL, 0x0d6d6a3eUL, 0x7a6a5aa8UL, + 0xe40ecf0bUL, 0x9309ff9dUL, 0x0a00ae27UL, 0x7d079eb1UL, 0xf00f9344UL, + 0x8708a3d2UL, 0x1e01f268UL, 0x6906c2feUL, 0xf762575dUL, 0x806567cbUL, + 0x196c3671UL, 0x6e6b06e7UL, 0xfed41b76UL, 0x89d32be0UL, 0x10da7a5aUL, + 0x67dd4accUL, 0xf9b9df6fUL, 0x8ebeeff9UL, 0x17b7be43UL, 0x60b08ed5UL, + 0xd6d6a3e8UL, 0xa1d1937eUL, 0x38d8c2c4UL, 0x4fdff252UL, 0xd1bb67f1UL, + 0xa6bc5767UL, 0x3fb506ddUL, 0x48b2364bUL, 0xd80d2bdaUL, 0xaf0a1b4cUL, + 0x36034af6UL, 0x41047a60UL, 0xdf60efc3UL, 0xa867df55UL, 0x316e8eefUL, + 0x4669be79UL, 0xcb61b38cUL, 0xbc66831aUL, 0x256fd2a0UL, 0x5268e236UL, + 0xcc0c7795UL, 0xbb0b4703UL, 0x220216b9UL, 0x5505262fUL, 0xc5ba3bbeUL, + 0xb2bd0b28UL, 0x2bb45a92UL, 0x5cb36a04UL, 0xc2d7ffa7UL, 0xb5d0cf31UL, + 0x2cd99e8bUL, 0x5bdeae1dUL, 0x9b64c2b0UL, 0xec63f226UL, 0x756aa39cUL, + 0x026d930aUL, 0x9c0906a9UL, 0xeb0e363fUL, 0x72076785UL, 0x05005713UL, + 0x95bf4a82UL, 0xe2b87a14UL, 0x7bb12baeUL, 0x0cb61b38UL, 0x92d28e9bUL, + 0xe5d5be0dUL, 0x7cdcefb7UL, 0x0bdbdf21UL, 0x86d3d2d4UL, 0xf1d4e242UL, + 0x68ddb3f8UL, 0x1fda836eUL, 0x81be16cdUL, 0xf6b9265bUL, 0x6fb077e1UL, + 0x18b74777UL, 0x88085ae6UL, 0xff0f6a70UL, 0x66063bcaUL, 0x11010b5cUL, + 0x8f659effUL, 0xf862ae69UL, 0x616bffd3UL, 0x166ccf45UL, 0xa00ae278UL, + 0xd70dd2eeUL, 0x4e048354UL, 0x3903b3c2UL, 0xa7672661UL, 0xd06016f7UL, + 0x4969474dUL, 0x3e6e77dbUL, 0xaed16a4aUL, 0xd9d65adcUL, 0x40df0b66UL, + 0x37d83bf0UL, 0xa9bcae53UL, 0xdebb9ec5UL, 0x47b2cf7fUL, 0x30b5ffe9UL, + 0xbdbdf21cUL, 0xcabac28aUL, 0x53b39330UL, 0x24b4a3a6UL, 0xbad03605UL, + 0xcdd70693UL, 0x54de5729UL, 0x23d967bfUL, 0xb3667a2eUL, 0xc4614ab8UL, + 0x5d681b02UL, 0x2a6f2b94UL, 0xb40bbe37UL, 0xc30c8ea1UL, 0x5a05df1bUL, + 0x2d02ef8dUL +#ifdef BYFOUR + }, + { + 0x00000000UL, 0x191b3141UL, 0x32366282UL, 0x2b2d53c3UL, 0x646cc504UL, + 0x7d77f445UL, 0x565aa786UL, 0x4f4196c7UL, 0xc8d98a08UL, 0xd1c2bb49UL, + 0xfaefe88aUL, 0xe3f4d9cbUL, 0xacb54f0cUL, 0xb5ae7e4dUL, 0x9e832d8eUL, + 0x87981ccfUL, 0x4ac21251UL, 0x53d92310UL, 0x78f470d3UL, 0x61ef4192UL, + 0x2eaed755UL, 0x37b5e614UL, 0x1c98b5d7UL, 0x05838496UL, 0x821b9859UL, + 0x9b00a918UL, 0xb02dfadbUL, 0xa936cb9aUL, 0xe6775d5dUL, 0xff6c6c1cUL, + 0xd4413fdfUL, 0xcd5a0e9eUL, 0x958424a2UL, 0x8c9f15e3UL, 0xa7b24620UL, + 0xbea97761UL, 0xf1e8e1a6UL, 0xe8f3d0e7UL, 0xc3de8324UL, 0xdac5b265UL, + 0x5d5daeaaUL, 0x44469febUL, 0x6f6bcc28UL, 0x7670fd69UL, 0x39316baeUL, + 0x202a5aefUL, 0x0b07092cUL, 0x121c386dUL, 0xdf4636f3UL, 0xc65d07b2UL, + 0xed705471UL, 0xf46b6530UL, 0xbb2af3f7UL, 0xa231c2b6UL, 0x891c9175UL, + 0x9007a034UL, 0x179fbcfbUL, 0x0e848dbaUL, 0x25a9de79UL, 0x3cb2ef38UL, + 0x73f379ffUL, 0x6ae848beUL, 0x41c51b7dUL, 0x58de2a3cUL, 0xf0794f05UL, + 0xe9627e44UL, 0xc24f2d87UL, 0xdb541cc6UL, 0x94158a01UL, 0x8d0ebb40UL, + 0xa623e883UL, 0xbf38d9c2UL, 0x38a0c50dUL, 0x21bbf44cUL, 0x0a96a78fUL, + 0x138d96ceUL, 0x5ccc0009UL, 0x45d73148UL, 0x6efa628bUL, 0x77e153caUL, + 0xbabb5d54UL, 0xa3a06c15UL, 0x888d3fd6UL, 0x91960e97UL, 0xded79850UL, + 0xc7cca911UL, 0xece1fad2UL, 0xf5facb93UL, 0x7262d75cUL, 0x6b79e61dUL, + 0x4054b5deUL, 0x594f849fUL, 0x160e1258UL, 0x0f152319UL, 0x243870daUL, + 0x3d23419bUL, 0x65fd6ba7UL, 0x7ce65ae6UL, 0x57cb0925UL, 0x4ed03864UL, + 0x0191aea3UL, 0x188a9fe2UL, 0x33a7cc21UL, 0x2abcfd60UL, 0xad24e1afUL, + 0xb43fd0eeUL, 0x9f12832dUL, 0x8609b26cUL, 0xc94824abUL, 0xd05315eaUL, + 0xfb7e4629UL, 0xe2657768UL, 0x2f3f79f6UL, 0x362448b7UL, 0x1d091b74UL, + 0x04122a35UL, 0x4b53bcf2UL, 0x52488db3UL, 0x7965de70UL, 0x607eef31UL, + 0xe7e6f3feUL, 0xfefdc2bfUL, 0xd5d0917cUL, 0xcccba03dUL, 0x838a36faUL, + 0x9a9107bbUL, 0xb1bc5478UL, 0xa8a76539UL, 0x3b83984bUL, 0x2298a90aUL, + 0x09b5fac9UL, 0x10aecb88UL, 0x5fef5d4fUL, 0x46f46c0eUL, 0x6dd93fcdUL, + 0x74c20e8cUL, 0xf35a1243UL, 0xea412302UL, 0xc16c70c1UL, 0xd8774180UL, + 0x9736d747UL, 0x8e2de606UL, 0xa500b5c5UL, 0xbc1b8484UL, 0x71418a1aUL, + 0x685abb5bUL, 0x4377e898UL, 0x5a6cd9d9UL, 0x152d4f1eUL, 0x0c367e5fUL, + 0x271b2d9cUL, 0x3e001cddUL, 0xb9980012UL, 0xa0833153UL, 0x8bae6290UL, + 0x92b553d1UL, 0xddf4c516UL, 0xc4eff457UL, 0xefc2a794UL, 0xf6d996d5UL, + 0xae07bce9UL, 0xb71c8da8UL, 0x9c31de6bUL, 0x852aef2aUL, 0xca6b79edUL, + 0xd37048acUL, 0xf85d1b6fUL, 0xe1462a2eUL, 0x66de36e1UL, 0x7fc507a0UL, + 0x54e85463UL, 0x4df36522UL, 0x02b2f3e5UL, 0x1ba9c2a4UL, 0x30849167UL, + 0x299fa026UL, 0xe4c5aeb8UL, 0xfdde9ff9UL, 0xd6f3cc3aUL, 0xcfe8fd7bUL, + 0x80a96bbcUL, 0x99b25afdUL, 0xb29f093eUL, 0xab84387fUL, 0x2c1c24b0UL, + 0x350715f1UL, 0x1e2a4632UL, 0x07317773UL, 0x4870e1b4UL, 0x516bd0f5UL, + 0x7a468336UL, 0x635db277UL, 0xcbfad74eUL, 0xd2e1e60fUL, 0xf9ccb5ccUL, + 0xe0d7848dUL, 0xaf96124aUL, 0xb68d230bUL, 0x9da070c8UL, 0x84bb4189UL, + 0x03235d46UL, 0x1a386c07UL, 0x31153fc4UL, 0x280e0e85UL, 0x674f9842UL, + 0x7e54a903UL, 0x5579fac0UL, 0x4c62cb81UL, 0x8138c51fUL, 0x9823f45eUL, + 0xb30ea79dUL, 0xaa1596dcUL, 0xe554001bUL, 0xfc4f315aUL, 0xd7626299UL, + 0xce7953d8UL, 0x49e14f17UL, 0x50fa7e56UL, 0x7bd72d95UL, 0x62cc1cd4UL, + 0x2d8d8a13UL, 0x3496bb52UL, 0x1fbbe891UL, 0x06a0d9d0UL, 0x5e7ef3ecUL, + 0x4765c2adUL, 0x6c48916eUL, 0x7553a02fUL, 0x3a1236e8UL, 0x230907a9UL, + 0x0824546aUL, 0x113f652bUL, 0x96a779e4UL, 0x8fbc48a5UL, 0xa4911b66UL, + 0xbd8a2a27UL, 0xf2cbbce0UL, 0xebd08da1UL, 0xc0fdde62UL, 0xd9e6ef23UL, + 0x14bce1bdUL, 0x0da7d0fcUL, 0x268a833fUL, 0x3f91b27eUL, 0x70d024b9UL, + 0x69cb15f8UL, 0x42e6463bUL, 0x5bfd777aUL, 0xdc656bb5UL, 0xc57e5af4UL, + 0xee530937UL, 0xf7483876UL, 0xb809aeb1UL, 0xa1129ff0UL, 0x8a3fcc33UL, + 0x9324fd72UL + }, + { + 0x00000000UL, 0x01c26a37UL, 0x0384d46eUL, 0x0246be59UL, 0x0709a8dcUL, + 0x06cbc2ebUL, 0x048d7cb2UL, 0x054f1685UL, 0x0e1351b8UL, 0x0fd13b8fUL, + 0x0d9785d6UL, 0x0c55efe1UL, 0x091af964UL, 0x08d89353UL, 0x0a9e2d0aUL, + 0x0b5c473dUL, 0x1c26a370UL, 0x1de4c947UL, 0x1fa2771eUL, 0x1e601d29UL, + 0x1b2f0bacUL, 0x1aed619bUL, 0x18abdfc2UL, 0x1969b5f5UL, 0x1235f2c8UL, + 0x13f798ffUL, 0x11b126a6UL, 0x10734c91UL, 0x153c5a14UL, 0x14fe3023UL, + 0x16b88e7aUL, 0x177ae44dUL, 0x384d46e0UL, 0x398f2cd7UL, 0x3bc9928eUL, + 0x3a0bf8b9UL, 0x3f44ee3cUL, 0x3e86840bUL, 0x3cc03a52UL, 0x3d025065UL, + 0x365e1758UL, 0x379c7d6fUL, 0x35dac336UL, 0x3418a901UL, 0x3157bf84UL, + 0x3095d5b3UL, 0x32d36beaUL, 0x331101ddUL, 0x246be590UL, 0x25a98fa7UL, + 0x27ef31feUL, 0x262d5bc9UL, 0x23624d4cUL, 0x22a0277bUL, 0x20e69922UL, + 0x2124f315UL, 0x2a78b428UL, 0x2bbade1fUL, 0x29fc6046UL, 0x283e0a71UL, + 0x2d711cf4UL, 0x2cb376c3UL, 0x2ef5c89aUL, 0x2f37a2adUL, 0x709a8dc0UL, + 0x7158e7f7UL, 0x731e59aeUL, 0x72dc3399UL, 0x7793251cUL, 0x76514f2bUL, + 0x7417f172UL, 0x75d59b45UL, 0x7e89dc78UL, 0x7f4bb64fUL, 0x7d0d0816UL, + 0x7ccf6221UL, 0x798074a4UL, 0x78421e93UL, 0x7a04a0caUL, 0x7bc6cafdUL, + 0x6cbc2eb0UL, 0x6d7e4487UL, 0x6f38fadeUL, 0x6efa90e9UL, 0x6bb5866cUL, + 0x6a77ec5bUL, 0x68315202UL, 0x69f33835UL, 0x62af7f08UL, 0x636d153fUL, + 0x612bab66UL, 0x60e9c151UL, 0x65a6d7d4UL, 0x6464bde3UL, 0x662203baUL, + 0x67e0698dUL, 0x48d7cb20UL, 0x4915a117UL, 0x4b531f4eUL, 0x4a917579UL, + 0x4fde63fcUL, 0x4e1c09cbUL, 0x4c5ab792UL, 0x4d98dda5UL, 0x46c49a98UL, + 0x4706f0afUL, 0x45404ef6UL, 0x448224c1UL, 0x41cd3244UL, 0x400f5873UL, + 0x4249e62aUL, 0x438b8c1dUL, 0x54f16850UL, 0x55330267UL, 0x5775bc3eUL, + 0x56b7d609UL, 0x53f8c08cUL, 0x523aaabbUL, 0x507c14e2UL, 0x51be7ed5UL, + 0x5ae239e8UL, 0x5b2053dfUL, 0x5966ed86UL, 0x58a487b1UL, 0x5deb9134UL, + 0x5c29fb03UL, 0x5e6f455aUL, 0x5fad2f6dUL, 0xe1351b80UL, 0xe0f771b7UL, + 0xe2b1cfeeUL, 0xe373a5d9UL, 0xe63cb35cUL, 0xe7fed96bUL, 0xe5b86732UL, + 0xe47a0d05UL, 0xef264a38UL, 0xeee4200fUL, 0xeca29e56UL, 0xed60f461UL, + 0xe82fe2e4UL, 0xe9ed88d3UL, 0xebab368aUL, 0xea695cbdUL, 0xfd13b8f0UL, + 0xfcd1d2c7UL, 0xfe976c9eUL, 0xff5506a9UL, 0xfa1a102cUL, 0xfbd87a1bUL, + 0xf99ec442UL, 0xf85cae75UL, 0xf300e948UL, 0xf2c2837fUL, 0xf0843d26UL, + 0xf1465711UL, 0xf4094194UL, 0xf5cb2ba3UL, 0xf78d95faUL, 0xf64fffcdUL, + 0xd9785d60UL, 0xd8ba3757UL, 0xdafc890eUL, 0xdb3ee339UL, 0xde71f5bcUL, + 0xdfb39f8bUL, 0xddf521d2UL, 0xdc374be5UL, 0xd76b0cd8UL, 0xd6a966efUL, + 0xd4efd8b6UL, 0xd52db281UL, 0xd062a404UL, 0xd1a0ce33UL, 0xd3e6706aUL, + 0xd2241a5dUL, 0xc55efe10UL, 0xc49c9427UL, 0xc6da2a7eUL, 0xc7184049UL, + 0xc25756ccUL, 0xc3953cfbUL, 0xc1d382a2UL, 0xc011e895UL, 0xcb4dafa8UL, + 0xca8fc59fUL, 0xc8c97bc6UL, 0xc90b11f1UL, 0xcc440774UL, 0xcd866d43UL, + 0xcfc0d31aUL, 0xce02b92dUL, 0x91af9640UL, 0x906dfc77UL, 0x922b422eUL, + 0x93e92819UL, 0x96a63e9cUL, 0x976454abUL, 0x9522eaf2UL, 0x94e080c5UL, + 0x9fbcc7f8UL, 0x9e7eadcfUL, 0x9c381396UL, 0x9dfa79a1UL, 0x98b56f24UL, + 0x99770513UL, 0x9b31bb4aUL, 0x9af3d17dUL, 0x8d893530UL, 0x8c4b5f07UL, + 0x8e0de15eUL, 0x8fcf8b69UL, 0x8a809decUL, 0x8b42f7dbUL, 0x89044982UL, + 0x88c623b5UL, 0x839a6488UL, 0x82580ebfUL, 0x801eb0e6UL, 0x81dcdad1UL, + 0x8493cc54UL, 0x8551a663UL, 0x8717183aUL, 0x86d5720dUL, 0xa9e2d0a0UL, + 0xa820ba97UL, 0xaa6604ceUL, 0xaba46ef9UL, 0xaeeb787cUL, 0xaf29124bUL, + 0xad6fac12UL, 0xacadc625UL, 0xa7f18118UL, 0xa633eb2fUL, 0xa4755576UL, + 0xa5b73f41UL, 0xa0f829c4UL, 0xa13a43f3UL, 0xa37cfdaaUL, 0xa2be979dUL, + 0xb5c473d0UL, 0xb40619e7UL, 0xb640a7beUL, 0xb782cd89UL, 0xb2cddb0cUL, + 0xb30fb13bUL, 0xb1490f62UL, 0xb08b6555UL, 0xbbd72268UL, 0xba15485fUL, + 0xb853f606UL, 0xb9919c31UL, 0xbcde8ab4UL, 0xbd1ce083UL, 0xbf5a5edaUL, + 0xbe9834edUL + }, + { + 0x00000000UL, 0xb8bc6765UL, 0xaa09c88bUL, 0x12b5afeeUL, 0x8f629757UL, + 0x37def032UL, 0x256b5fdcUL, 0x9dd738b9UL, 0xc5b428efUL, 0x7d084f8aUL, + 0x6fbde064UL, 0xd7018701UL, 0x4ad6bfb8UL, 0xf26ad8ddUL, 0xe0df7733UL, + 0x58631056UL, 0x5019579fUL, 0xe8a530faUL, 0xfa109f14UL, 0x42acf871UL, + 0xdf7bc0c8UL, 0x67c7a7adUL, 0x75720843UL, 0xcdce6f26UL, 0x95ad7f70UL, + 0x2d111815UL, 0x3fa4b7fbUL, 0x8718d09eUL, 0x1acfe827UL, 0xa2738f42UL, + 0xb0c620acUL, 0x087a47c9UL, 0xa032af3eUL, 0x188ec85bUL, 0x0a3b67b5UL, + 0xb28700d0UL, 0x2f503869UL, 0x97ec5f0cUL, 0x8559f0e2UL, 0x3de59787UL, + 0x658687d1UL, 0xdd3ae0b4UL, 0xcf8f4f5aUL, 0x7733283fUL, 0xeae41086UL, + 0x525877e3UL, 0x40edd80dUL, 0xf851bf68UL, 0xf02bf8a1UL, 0x48979fc4UL, + 0x5a22302aUL, 0xe29e574fUL, 0x7f496ff6UL, 0xc7f50893UL, 0xd540a77dUL, + 0x6dfcc018UL, 0x359fd04eUL, 0x8d23b72bUL, 0x9f9618c5UL, 0x272a7fa0UL, + 0xbafd4719UL, 0x0241207cUL, 0x10f48f92UL, 0xa848e8f7UL, 0x9b14583dUL, + 0x23a83f58UL, 0x311d90b6UL, 0x89a1f7d3UL, 0x1476cf6aUL, 0xaccaa80fUL, + 0xbe7f07e1UL, 0x06c36084UL, 0x5ea070d2UL, 0xe61c17b7UL, 0xf4a9b859UL, + 0x4c15df3cUL, 0xd1c2e785UL, 0x697e80e0UL, 0x7bcb2f0eUL, 0xc377486bUL, + 0xcb0d0fa2UL, 0x73b168c7UL, 0x6104c729UL, 0xd9b8a04cUL, 0x446f98f5UL, + 0xfcd3ff90UL, 0xee66507eUL, 0x56da371bUL, 0x0eb9274dUL, 0xb6054028UL, + 0xa4b0efc6UL, 0x1c0c88a3UL, 0x81dbb01aUL, 0x3967d77fUL, 0x2bd27891UL, + 0x936e1ff4UL, 0x3b26f703UL, 0x839a9066UL, 0x912f3f88UL, 0x299358edUL, + 0xb4446054UL, 0x0cf80731UL, 0x1e4da8dfUL, 0xa6f1cfbaUL, 0xfe92dfecUL, + 0x462eb889UL, 0x549b1767UL, 0xec277002UL, 0x71f048bbUL, 0xc94c2fdeUL, + 0xdbf98030UL, 0x6345e755UL, 0x6b3fa09cUL, 0xd383c7f9UL, 0xc1366817UL, + 0x798a0f72UL, 0xe45d37cbUL, 0x5ce150aeUL, 0x4e54ff40UL, 0xf6e89825UL, + 0xae8b8873UL, 0x1637ef16UL, 0x048240f8UL, 0xbc3e279dUL, 0x21e91f24UL, + 0x99557841UL, 0x8be0d7afUL, 0x335cb0caUL, 0xed59b63bUL, 0x55e5d15eUL, + 0x47507eb0UL, 0xffec19d5UL, 0x623b216cUL, 0xda874609UL, 0xc832e9e7UL, + 0x708e8e82UL, 0x28ed9ed4UL, 0x9051f9b1UL, 0x82e4565fUL, 0x3a58313aUL, + 0xa78f0983UL, 0x1f336ee6UL, 0x0d86c108UL, 0xb53aa66dUL, 0xbd40e1a4UL, + 0x05fc86c1UL, 0x1749292fUL, 0xaff54e4aUL, 0x322276f3UL, 0x8a9e1196UL, + 0x982bbe78UL, 0x2097d91dUL, 0x78f4c94bUL, 0xc048ae2eUL, 0xd2fd01c0UL, + 0x6a4166a5UL, 0xf7965e1cUL, 0x4f2a3979UL, 0x5d9f9697UL, 0xe523f1f2UL, + 0x4d6b1905UL, 0xf5d77e60UL, 0xe762d18eUL, 0x5fdeb6ebUL, 0xc2098e52UL, + 0x7ab5e937UL, 0x680046d9UL, 0xd0bc21bcUL, 0x88df31eaUL, 0x3063568fUL, + 0x22d6f961UL, 0x9a6a9e04UL, 0x07bda6bdUL, 0xbf01c1d8UL, 0xadb46e36UL, + 0x15080953UL, 0x1d724e9aUL, 0xa5ce29ffUL, 0xb77b8611UL, 0x0fc7e174UL, + 0x9210d9cdUL, 0x2aacbea8UL, 0x38191146UL, 0x80a57623UL, 0xd8c66675UL, + 0x607a0110UL, 0x72cfaefeUL, 0xca73c99bUL, 0x57a4f122UL, 0xef189647UL, + 0xfdad39a9UL, 0x45115eccUL, 0x764dee06UL, 0xcef18963UL, 0xdc44268dUL, + 0x64f841e8UL, 0xf92f7951UL, 0x41931e34UL, 0x5326b1daUL, 0xeb9ad6bfUL, + 0xb3f9c6e9UL, 0x0b45a18cUL, 0x19f00e62UL, 0xa14c6907UL, 0x3c9b51beUL, + 0x842736dbUL, 0x96929935UL, 0x2e2efe50UL, 0x2654b999UL, 0x9ee8defcUL, + 0x8c5d7112UL, 0x34e11677UL, 0xa9362eceUL, 0x118a49abUL, 0x033fe645UL, + 0xbb838120UL, 0xe3e09176UL, 0x5b5cf613UL, 0x49e959fdUL, 0xf1553e98UL, + 0x6c820621UL, 0xd43e6144UL, 0xc68bceaaUL, 0x7e37a9cfUL, 0xd67f4138UL, + 0x6ec3265dUL, 0x7c7689b3UL, 0xc4caeed6UL, 0x591dd66fUL, 0xe1a1b10aUL, + 0xf3141ee4UL, 0x4ba87981UL, 0x13cb69d7UL, 0xab770eb2UL, 0xb9c2a15cUL, + 0x017ec639UL, 0x9ca9fe80UL, 0x241599e5UL, 0x36a0360bUL, 0x8e1c516eUL, + 0x866616a7UL, 0x3eda71c2UL, 0x2c6fde2cUL, 0x94d3b949UL, 0x090481f0UL, + 0xb1b8e695UL, 0xa30d497bUL, 0x1bb12e1eUL, 0x43d23e48UL, 0xfb6e592dUL, + 0xe9dbf6c3UL, 0x516791a6UL, 0xccb0a91fUL, 0x740cce7aUL, 0x66b96194UL, + 0xde0506f1UL + }, + { + 0x00000000UL, 0x96300777UL, 0x2c610eeeUL, 0xba510999UL, 0x19c46d07UL, + 0x8ff46a70UL, 0x35a563e9UL, 0xa395649eUL, 0x3288db0eUL, 0xa4b8dc79UL, + 0x1ee9d5e0UL, 0x88d9d297UL, 0x2b4cb609UL, 0xbd7cb17eUL, 0x072db8e7UL, + 0x911dbf90UL, 0x6410b71dUL, 0xf220b06aUL, 0x4871b9f3UL, 0xde41be84UL, + 0x7dd4da1aUL, 0xebe4dd6dUL, 0x51b5d4f4UL, 0xc785d383UL, 0x56986c13UL, + 0xc0a86b64UL, 0x7af962fdUL, 0xecc9658aUL, 0x4f5c0114UL, 0xd96c0663UL, + 0x633d0ffaUL, 0xf50d088dUL, 0xc8206e3bUL, 0x5e10694cUL, 0xe44160d5UL, + 0x727167a2UL, 0xd1e4033cUL, 0x47d4044bUL, 0xfd850dd2UL, 0x6bb50aa5UL, + 0xfaa8b535UL, 0x6c98b242UL, 0xd6c9bbdbUL, 0x40f9bcacUL, 0xe36cd832UL, + 0x755cdf45UL, 0xcf0dd6dcUL, 0x593dd1abUL, 0xac30d926UL, 0x3a00de51UL, + 0x8051d7c8UL, 0x1661d0bfUL, 0xb5f4b421UL, 0x23c4b356UL, 0x9995bacfUL, + 0x0fa5bdb8UL, 0x9eb80228UL, 0x0888055fUL, 0xb2d90cc6UL, 0x24e90bb1UL, + 0x877c6f2fUL, 0x114c6858UL, 0xab1d61c1UL, 0x3d2d66b6UL, 0x9041dc76UL, + 0x0671db01UL, 0xbc20d298UL, 0x2a10d5efUL, 0x8985b171UL, 0x1fb5b606UL, + 0xa5e4bf9fUL, 0x33d4b8e8UL, 0xa2c90778UL, 0x34f9000fUL, 0x8ea80996UL, + 0x18980ee1UL, 0xbb0d6a7fUL, 0x2d3d6d08UL, 0x976c6491UL, 0x015c63e6UL, + 0xf4516b6bUL, 0x62616c1cUL, 0xd8306585UL, 0x4e0062f2UL, 0xed95066cUL, + 0x7ba5011bUL, 0xc1f40882UL, 0x57c40ff5UL, 0xc6d9b065UL, 0x50e9b712UL, + 0xeab8be8bUL, 0x7c88b9fcUL, 0xdf1ddd62UL, 0x492dda15UL, 0xf37cd38cUL, + 0x654cd4fbUL, 0x5861b24dUL, 0xce51b53aUL, 0x7400bca3UL, 0xe230bbd4UL, + 0x41a5df4aUL, 0xd795d83dUL, 0x6dc4d1a4UL, 0xfbf4d6d3UL, 0x6ae96943UL, + 0xfcd96e34UL, 0x468867adUL, 0xd0b860daUL, 0x732d0444UL, 0xe51d0333UL, + 0x5f4c0aaaUL, 0xc97c0dddUL, 0x3c710550UL, 0xaa410227UL, 0x10100bbeUL, + 0x86200cc9UL, 0x25b56857UL, 0xb3856f20UL, 0x09d466b9UL, 0x9fe461ceUL, + 0x0ef9de5eUL, 0x98c9d929UL, 0x2298d0b0UL, 0xb4a8d7c7UL, 0x173db359UL, + 0x810db42eUL, 0x3b5cbdb7UL, 0xad6cbac0UL, 0x2083b8edUL, 0xb6b3bf9aUL, + 0x0ce2b603UL, 0x9ad2b174UL, 0x3947d5eaUL, 0xaf77d29dUL, 0x1526db04UL, + 0x8316dc73UL, 0x120b63e3UL, 0x843b6494UL, 0x3e6a6d0dUL, 0xa85a6a7aUL, + 0x0bcf0ee4UL, 0x9dff0993UL, 0x27ae000aUL, 0xb19e077dUL, 0x44930ff0UL, + 0xd2a30887UL, 0x68f2011eUL, 0xfec20669UL, 0x5d5762f7UL, 0xcb676580UL, + 0x71366c19UL, 0xe7066b6eUL, 0x761bd4feUL, 0xe02bd389UL, 0x5a7ada10UL, + 0xcc4add67UL, 0x6fdfb9f9UL, 0xf9efbe8eUL, 0x43beb717UL, 0xd58eb060UL, + 0xe8a3d6d6UL, 0x7e93d1a1UL, 0xc4c2d838UL, 0x52f2df4fUL, 0xf167bbd1UL, + 0x6757bca6UL, 0xdd06b53fUL, 0x4b36b248UL, 0xda2b0dd8UL, 0x4c1b0aafUL, + 0xf64a0336UL, 0x607a0441UL, 0xc3ef60dfUL, 0x55df67a8UL, 0xef8e6e31UL, + 0x79be6946UL, 0x8cb361cbUL, 0x1a8366bcUL, 0xa0d26f25UL, 0x36e26852UL, + 0x95770cccUL, 0x03470bbbUL, 0xb9160222UL, 0x2f260555UL, 0xbe3bbac5UL, + 0x280bbdb2UL, 0x925ab42bUL, 0x046ab35cUL, 0xa7ffd7c2UL, 0x31cfd0b5UL, + 0x8b9ed92cUL, 0x1daede5bUL, 0xb0c2649bUL, 0x26f263ecUL, 0x9ca36a75UL, + 0x0a936d02UL, 0xa906099cUL, 0x3f360eebUL, 0x85670772UL, 0x13570005UL, + 0x824abf95UL, 0x147ab8e2UL, 0xae2bb17bUL, 0x381bb60cUL, 0x9b8ed292UL, + 0x0dbed5e5UL, 0xb7efdc7cUL, 0x21dfdb0bUL, 0xd4d2d386UL, 0x42e2d4f1UL, + 0xf8b3dd68UL, 0x6e83da1fUL, 0xcd16be81UL, 0x5b26b9f6UL, 0xe177b06fUL, + 0x7747b718UL, 0xe65a0888UL, 0x706a0fffUL, 0xca3b0666UL, 0x5c0b0111UL, + 0xff9e658fUL, 0x69ae62f8UL, 0xd3ff6b61UL, 0x45cf6c16UL, 0x78e20aa0UL, + 0xeed20dd7UL, 0x5483044eUL, 0xc2b30339UL, 0x612667a7UL, 0xf71660d0UL, + 0x4d476949UL, 0xdb776e3eUL, 0x4a6ad1aeUL, 0xdc5ad6d9UL, 0x660bdf40UL, + 0xf03bd837UL, 0x53aebca9UL, 0xc59ebbdeUL, 0x7fcfb247UL, 0xe9ffb530UL, + 0x1cf2bdbdUL, 0x8ac2bacaUL, 0x3093b353UL, 0xa6a3b424UL, 0x0536d0baUL, + 0x9306d7cdUL, 0x2957de54UL, 0xbf67d923UL, 0x2e7a66b3UL, 0xb84a61c4UL, + 0x021b685dUL, 0x942b6f2aUL, 0x37be0bb4UL, 0xa18e0cc3UL, 0x1bdf055aUL, + 0x8def022dUL + }, + { + 0x00000000UL, 0x41311b19UL, 0x82623632UL, 0xc3532d2bUL, 0x04c56c64UL, + 0x45f4777dUL, 0x86a75a56UL, 0xc796414fUL, 0x088ad9c8UL, 0x49bbc2d1UL, + 0x8ae8effaUL, 0xcbd9f4e3UL, 0x0c4fb5acUL, 0x4d7eaeb5UL, 0x8e2d839eUL, + 0xcf1c9887UL, 0x5112c24aUL, 0x1023d953UL, 0xd370f478UL, 0x9241ef61UL, + 0x55d7ae2eUL, 0x14e6b537UL, 0xd7b5981cUL, 0x96848305UL, 0x59981b82UL, + 0x18a9009bUL, 0xdbfa2db0UL, 0x9acb36a9UL, 0x5d5d77e6UL, 0x1c6c6cffUL, + 0xdf3f41d4UL, 0x9e0e5acdUL, 0xa2248495UL, 0xe3159f8cUL, 0x2046b2a7UL, + 0x6177a9beUL, 0xa6e1e8f1UL, 0xe7d0f3e8UL, 0x2483dec3UL, 0x65b2c5daUL, + 0xaaae5d5dUL, 0xeb9f4644UL, 0x28cc6b6fUL, 0x69fd7076UL, 0xae6b3139UL, + 0xef5a2a20UL, 0x2c09070bUL, 0x6d381c12UL, 0xf33646dfUL, 0xb2075dc6UL, + 0x715470edUL, 0x30656bf4UL, 0xf7f32abbUL, 0xb6c231a2UL, 0x75911c89UL, + 0x34a00790UL, 0xfbbc9f17UL, 0xba8d840eUL, 0x79dea925UL, 0x38efb23cUL, + 0xff79f373UL, 0xbe48e86aUL, 0x7d1bc541UL, 0x3c2ade58UL, 0x054f79f0UL, + 0x447e62e9UL, 0x872d4fc2UL, 0xc61c54dbUL, 0x018a1594UL, 0x40bb0e8dUL, + 0x83e823a6UL, 0xc2d938bfUL, 0x0dc5a038UL, 0x4cf4bb21UL, 0x8fa7960aUL, + 0xce968d13UL, 0x0900cc5cUL, 0x4831d745UL, 0x8b62fa6eUL, 0xca53e177UL, + 0x545dbbbaUL, 0x156ca0a3UL, 0xd63f8d88UL, 0x970e9691UL, 0x5098d7deUL, + 0x11a9ccc7UL, 0xd2fae1ecUL, 0x93cbfaf5UL, 0x5cd76272UL, 0x1de6796bUL, + 0xdeb55440UL, 0x9f844f59UL, 0x58120e16UL, 0x1923150fUL, 0xda703824UL, + 0x9b41233dUL, 0xa76bfd65UL, 0xe65ae67cUL, 0x2509cb57UL, 0x6438d04eUL, + 0xa3ae9101UL, 0xe29f8a18UL, 0x21cca733UL, 0x60fdbc2aUL, 0xafe124adUL, + 0xeed03fb4UL, 0x2d83129fUL, 0x6cb20986UL, 0xab2448c9UL, 0xea1553d0UL, + 0x29467efbUL, 0x687765e2UL, 0xf6793f2fUL, 0xb7482436UL, 0x741b091dUL, + 0x352a1204UL, 0xf2bc534bUL, 0xb38d4852UL, 0x70de6579UL, 0x31ef7e60UL, + 0xfef3e6e7UL, 0xbfc2fdfeUL, 0x7c91d0d5UL, 0x3da0cbccUL, 0xfa368a83UL, + 0xbb07919aUL, 0x7854bcb1UL, 0x3965a7a8UL, 0x4b98833bUL, 0x0aa99822UL, + 0xc9fab509UL, 0x88cbae10UL, 0x4f5def5fUL, 0x0e6cf446UL, 0xcd3fd96dUL, + 0x8c0ec274UL, 0x43125af3UL, 0x022341eaUL, 0xc1706cc1UL, 0x804177d8UL, + 0x47d73697UL, 0x06e62d8eUL, 0xc5b500a5UL, 0x84841bbcUL, 0x1a8a4171UL, + 0x5bbb5a68UL, 0x98e87743UL, 0xd9d96c5aUL, 0x1e4f2d15UL, 0x5f7e360cUL, + 0x9c2d1b27UL, 0xdd1c003eUL, 0x120098b9UL, 0x533183a0UL, 0x9062ae8bUL, + 0xd153b592UL, 0x16c5f4ddUL, 0x57f4efc4UL, 0x94a7c2efUL, 0xd596d9f6UL, + 0xe9bc07aeUL, 0xa88d1cb7UL, 0x6bde319cUL, 0x2aef2a85UL, 0xed796bcaUL, + 0xac4870d3UL, 0x6f1b5df8UL, 0x2e2a46e1UL, 0xe136de66UL, 0xa007c57fUL, + 0x6354e854UL, 0x2265f34dUL, 0xe5f3b202UL, 0xa4c2a91bUL, 0x67918430UL, + 0x26a09f29UL, 0xb8aec5e4UL, 0xf99fdefdUL, 0x3accf3d6UL, 0x7bfde8cfUL, + 0xbc6ba980UL, 0xfd5ab299UL, 0x3e099fb2UL, 0x7f3884abUL, 0xb0241c2cUL, + 0xf1150735UL, 0x32462a1eUL, 0x73773107UL, 0xb4e17048UL, 0xf5d06b51UL, + 0x3683467aUL, 0x77b25d63UL, 0x4ed7facbUL, 0x0fe6e1d2UL, 0xccb5ccf9UL, + 0x8d84d7e0UL, 0x4a1296afUL, 0x0b238db6UL, 0xc870a09dUL, 0x8941bb84UL, + 0x465d2303UL, 0x076c381aUL, 0xc43f1531UL, 0x850e0e28UL, 0x42984f67UL, + 0x03a9547eUL, 0xc0fa7955UL, 0x81cb624cUL, 0x1fc53881UL, 0x5ef42398UL, + 0x9da70eb3UL, 0xdc9615aaUL, 0x1b0054e5UL, 0x5a314ffcUL, 0x996262d7UL, + 0xd85379ceUL, 0x174fe149UL, 0x567efa50UL, 0x952dd77bUL, 0xd41ccc62UL, + 0x138a8d2dUL, 0x52bb9634UL, 0x91e8bb1fUL, 0xd0d9a006UL, 0xecf37e5eUL, + 0xadc26547UL, 0x6e91486cUL, 0x2fa05375UL, 0xe836123aUL, 0xa9070923UL, + 0x6a542408UL, 0x2b653f11UL, 0xe479a796UL, 0xa548bc8fUL, 0x661b91a4UL, + 0x272a8abdUL, 0xe0bccbf2UL, 0xa18dd0ebUL, 0x62defdc0UL, 0x23efe6d9UL, + 0xbde1bc14UL, 0xfcd0a70dUL, 0x3f838a26UL, 0x7eb2913fUL, 0xb924d070UL, + 0xf815cb69UL, 0x3b46e642UL, 0x7a77fd5bUL, 0xb56b65dcUL, 0xf45a7ec5UL, + 0x370953eeUL, 0x763848f7UL, 0xb1ae09b8UL, 0xf09f12a1UL, 0x33cc3f8aUL, + 0x72fd2493UL + }, + { + 0x00000000UL, 0x376ac201UL, 0x6ed48403UL, 0x59be4602UL, 0xdca80907UL, + 0xebc2cb06UL, 0xb27c8d04UL, 0x85164f05UL, 0xb851130eUL, 0x8f3bd10fUL, + 0xd685970dUL, 0xe1ef550cUL, 0x64f91a09UL, 0x5393d808UL, 0x0a2d9e0aUL, + 0x3d475c0bUL, 0x70a3261cUL, 0x47c9e41dUL, 0x1e77a21fUL, 0x291d601eUL, + 0xac0b2f1bUL, 0x9b61ed1aUL, 0xc2dfab18UL, 0xf5b56919UL, 0xc8f23512UL, + 0xff98f713UL, 0xa626b111UL, 0x914c7310UL, 0x145a3c15UL, 0x2330fe14UL, + 0x7a8eb816UL, 0x4de47a17UL, 0xe0464d38UL, 0xd72c8f39UL, 0x8e92c93bUL, + 0xb9f80b3aUL, 0x3cee443fUL, 0x0b84863eUL, 0x523ac03cUL, 0x6550023dUL, + 0x58175e36UL, 0x6f7d9c37UL, 0x36c3da35UL, 0x01a91834UL, 0x84bf5731UL, + 0xb3d59530UL, 0xea6bd332UL, 0xdd011133UL, 0x90e56b24UL, 0xa78fa925UL, + 0xfe31ef27UL, 0xc95b2d26UL, 0x4c4d6223UL, 0x7b27a022UL, 0x2299e620UL, + 0x15f32421UL, 0x28b4782aUL, 0x1fdeba2bUL, 0x4660fc29UL, 0x710a3e28UL, + 0xf41c712dUL, 0xc376b32cUL, 0x9ac8f52eUL, 0xada2372fUL, 0xc08d9a70UL, + 0xf7e75871UL, 0xae591e73UL, 0x9933dc72UL, 0x1c259377UL, 0x2b4f5176UL, + 0x72f11774UL, 0x459bd575UL, 0x78dc897eUL, 0x4fb64b7fUL, 0x16080d7dUL, + 0x2162cf7cUL, 0xa4748079UL, 0x931e4278UL, 0xcaa0047aUL, 0xfdcac67bUL, + 0xb02ebc6cUL, 0x87447e6dUL, 0xdefa386fUL, 0xe990fa6eUL, 0x6c86b56bUL, + 0x5bec776aUL, 0x02523168UL, 0x3538f369UL, 0x087faf62UL, 0x3f156d63UL, + 0x66ab2b61UL, 0x51c1e960UL, 0xd4d7a665UL, 0xe3bd6464UL, 0xba032266UL, + 0x8d69e067UL, 0x20cbd748UL, 0x17a11549UL, 0x4e1f534bUL, 0x7975914aUL, + 0xfc63de4fUL, 0xcb091c4eUL, 0x92b75a4cUL, 0xa5dd984dUL, 0x989ac446UL, + 0xaff00647UL, 0xf64e4045UL, 0xc1248244UL, 0x4432cd41UL, 0x73580f40UL, + 0x2ae64942UL, 0x1d8c8b43UL, 0x5068f154UL, 0x67023355UL, 0x3ebc7557UL, + 0x09d6b756UL, 0x8cc0f853UL, 0xbbaa3a52UL, 0xe2147c50UL, 0xd57ebe51UL, + 0xe839e25aUL, 0xdf53205bUL, 0x86ed6659UL, 0xb187a458UL, 0x3491eb5dUL, + 0x03fb295cUL, 0x5a456f5eUL, 0x6d2fad5fUL, 0x801b35e1UL, 0xb771f7e0UL, + 0xeecfb1e2UL, 0xd9a573e3UL, 0x5cb33ce6UL, 0x6bd9fee7UL, 0x3267b8e5UL, + 0x050d7ae4UL, 0x384a26efUL, 0x0f20e4eeUL, 0x569ea2ecUL, 0x61f460edUL, + 0xe4e22fe8UL, 0xd388ede9UL, 0x8a36abebUL, 0xbd5c69eaUL, 0xf0b813fdUL, + 0xc7d2d1fcUL, 0x9e6c97feUL, 0xa90655ffUL, 0x2c101afaUL, 0x1b7ad8fbUL, + 0x42c49ef9UL, 0x75ae5cf8UL, 0x48e900f3UL, 0x7f83c2f2UL, 0x263d84f0UL, + 0x115746f1UL, 0x944109f4UL, 0xa32bcbf5UL, 0xfa958df7UL, 0xcdff4ff6UL, + 0x605d78d9UL, 0x5737bad8UL, 0x0e89fcdaUL, 0x39e33edbUL, 0xbcf571deUL, + 0x8b9fb3dfUL, 0xd221f5ddUL, 0xe54b37dcUL, 0xd80c6bd7UL, 0xef66a9d6UL, + 0xb6d8efd4UL, 0x81b22dd5UL, 0x04a462d0UL, 0x33cea0d1UL, 0x6a70e6d3UL, + 0x5d1a24d2UL, 0x10fe5ec5UL, 0x27949cc4UL, 0x7e2adac6UL, 0x494018c7UL, + 0xcc5657c2UL, 0xfb3c95c3UL, 0xa282d3c1UL, 0x95e811c0UL, 0xa8af4dcbUL, + 0x9fc58fcaUL, 0xc67bc9c8UL, 0xf1110bc9UL, 0x740744ccUL, 0x436d86cdUL, + 0x1ad3c0cfUL, 0x2db902ceUL, 0x4096af91UL, 0x77fc6d90UL, 0x2e422b92UL, + 0x1928e993UL, 0x9c3ea696UL, 0xab546497UL, 0xf2ea2295UL, 0xc580e094UL, + 0xf8c7bc9fUL, 0xcfad7e9eUL, 0x9613389cUL, 0xa179fa9dUL, 0x246fb598UL, + 0x13057799UL, 0x4abb319bUL, 0x7dd1f39aUL, 0x3035898dUL, 0x075f4b8cUL, + 0x5ee10d8eUL, 0x698bcf8fUL, 0xec9d808aUL, 0xdbf7428bUL, 0x82490489UL, + 0xb523c688UL, 0x88649a83UL, 0xbf0e5882UL, 0xe6b01e80UL, 0xd1dadc81UL, + 0x54cc9384UL, 0x63a65185UL, 0x3a181787UL, 0x0d72d586UL, 0xa0d0e2a9UL, + 0x97ba20a8UL, 0xce0466aaUL, 0xf96ea4abUL, 0x7c78ebaeUL, 0x4b1229afUL, + 0x12ac6fadUL, 0x25c6adacUL, 0x1881f1a7UL, 0x2feb33a6UL, 0x765575a4UL, + 0x413fb7a5UL, 0xc429f8a0UL, 0xf3433aa1UL, 0xaafd7ca3UL, 0x9d97bea2UL, + 0xd073c4b5UL, 0xe71906b4UL, 0xbea740b6UL, 0x89cd82b7UL, 0x0cdbcdb2UL, + 0x3bb10fb3UL, 0x620f49b1UL, 0x55658bb0UL, 0x6822d7bbUL, 0x5f4815baUL, + 0x06f653b8UL, 0x319c91b9UL, 0xb48adebcUL, 0x83e01cbdUL, 0xda5e5abfUL, + 0xed3498beUL + }, + { + 0x00000000UL, 0x6567bcb8UL, 0x8bc809aaUL, 0xeeafb512UL, 0x5797628fUL, + 0x32f0de37UL, 0xdc5f6b25UL, 0xb938d79dUL, 0xef28b4c5UL, 0x8a4f087dUL, + 0x64e0bd6fUL, 0x018701d7UL, 0xb8bfd64aUL, 0xddd86af2UL, 0x3377dfe0UL, + 0x56106358UL, 0x9f571950UL, 0xfa30a5e8UL, 0x149f10faUL, 0x71f8ac42UL, + 0xc8c07bdfUL, 0xada7c767UL, 0x43087275UL, 0x266fcecdUL, 0x707fad95UL, + 0x1518112dUL, 0xfbb7a43fUL, 0x9ed01887UL, 0x27e8cf1aUL, 0x428f73a2UL, + 0xac20c6b0UL, 0xc9477a08UL, 0x3eaf32a0UL, 0x5bc88e18UL, 0xb5673b0aUL, + 0xd00087b2UL, 0x6938502fUL, 0x0c5fec97UL, 0xe2f05985UL, 0x8797e53dUL, + 0xd1878665UL, 0xb4e03addUL, 0x5a4f8fcfUL, 0x3f283377UL, 0x8610e4eaUL, + 0xe3775852UL, 0x0dd8ed40UL, 0x68bf51f8UL, 0xa1f82bf0UL, 0xc49f9748UL, + 0x2a30225aUL, 0x4f579ee2UL, 0xf66f497fUL, 0x9308f5c7UL, 0x7da740d5UL, + 0x18c0fc6dUL, 0x4ed09f35UL, 0x2bb7238dUL, 0xc518969fUL, 0xa07f2a27UL, + 0x1947fdbaUL, 0x7c204102UL, 0x928ff410UL, 0xf7e848a8UL, 0x3d58149bUL, + 0x583fa823UL, 0xb6901d31UL, 0xd3f7a189UL, 0x6acf7614UL, 0x0fa8caacUL, + 0xe1077fbeUL, 0x8460c306UL, 0xd270a05eUL, 0xb7171ce6UL, 0x59b8a9f4UL, + 0x3cdf154cUL, 0x85e7c2d1UL, 0xe0807e69UL, 0x0e2fcb7bUL, 0x6b4877c3UL, + 0xa20f0dcbUL, 0xc768b173UL, 0x29c70461UL, 0x4ca0b8d9UL, 0xf5986f44UL, + 0x90ffd3fcUL, 0x7e5066eeUL, 0x1b37da56UL, 0x4d27b90eUL, 0x284005b6UL, + 0xc6efb0a4UL, 0xa3880c1cUL, 0x1ab0db81UL, 0x7fd76739UL, 0x9178d22bUL, + 0xf41f6e93UL, 0x03f7263bUL, 0x66909a83UL, 0x883f2f91UL, 0xed589329UL, + 0x546044b4UL, 0x3107f80cUL, 0xdfa84d1eUL, 0xbacff1a6UL, 0xecdf92feUL, + 0x89b82e46UL, 0x67179b54UL, 0x027027ecUL, 0xbb48f071UL, 0xde2f4cc9UL, + 0x3080f9dbUL, 0x55e74563UL, 0x9ca03f6bUL, 0xf9c783d3UL, 0x176836c1UL, + 0x720f8a79UL, 0xcb375de4UL, 0xae50e15cUL, 0x40ff544eUL, 0x2598e8f6UL, + 0x73888baeUL, 0x16ef3716UL, 0xf8408204UL, 0x9d273ebcUL, 0x241fe921UL, + 0x41785599UL, 0xafd7e08bUL, 0xcab05c33UL, 0x3bb659edUL, 0x5ed1e555UL, + 0xb07e5047UL, 0xd519ecffUL, 0x6c213b62UL, 0x094687daUL, 0xe7e932c8UL, + 0x828e8e70UL, 0xd49eed28UL, 0xb1f95190UL, 0x5f56e482UL, 0x3a31583aUL, + 0x83098fa7UL, 0xe66e331fUL, 0x08c1860dUL, 0x6da63ab5UL, 0xa4e140bdUL, + 0xc186fc05UL, 0x2f294917UL, 0x4a4ef5afUL, 0xf3762232UL, 0x96119e8aUL, + 0x78be2b98UL, 0x1dd99720UL, 0x4bc9f478UL, 0x2eae48c0UL, 0xc001fdd2UL, + 0xa566416aUL, 0x1c5e96f7UL, 0x79392a4fUL, 0x97969f5dUL, 0xf2f123e5UL, + 0x05196b4dUL, 0x607ed7f5UL, 0x8ed162e7UL, 0xebb6de5fUL, 0x528e09c2UL, + 0x37e9b57aUL, 0xd9460068UL, 0xbc21bcd0UL, 0xea31df88UL, 0x8f566330UL, + 0x61f9d622UL, 0x049e6a9aUL, 0xbda6bd07UL, 0xd8c101bfUL, 0x366eb4adUL, + 0x53090815UL, 0x9a4e721dUL, 0xff29cea5UL, 0x11867bb7UL, 0x74e1c70fUL, + 0xcdd91092UL, 0xa8beac2aUL, 0x46111938UL, 0x2376a580UL, 0x7566c6d8UL, + 0x10017a60UL, 0xfeaecf72UL, 0x9bc973caUL, 0x22f1a457UL, 0x479618efUL, + 0xa939adfdUL, 0xcc5e1145UL, 0x06ee4d76UL, 0x6389f1ceUL, 0x8d2644dcUL, + 0xe841f864UL, 0x51792ff9UL, 0x341e9341UL, 0xdab12653UL, 0xbfd69aebUL, + 0xe9c6f9b3UL, 0x8ca1450bUL, 0x620ef019UL, 0x07694ca1UL, 0xbe519b3cUL, + 0xdb362784UL, 0x35999296UL, 0x50fe2e2eUL, 0x99b95426UL, 0xfcdee89eUL, + 0x12715d8cUL, 0x7716e134UL, 0xce2e36a9UL, 0xab498a11UL, 0x45e63f03UL, + 0x208183bbUL, 0x7691e0e3UL, 0x13f65c5bUL, 0xfd59e949UL, 0x983e55f1UL, + 0x2106826cUL, 0x44613ed4UL, 0xaace8bc6UL, 0xcfa9377eUL, 0x38417fd6UL, + 0x5d26c36eUL, 0xb389767cUL, 0xd6eecac4UL, 0x6fd61d59UL, 0x0ab1a1e1UL, + 0xe41e14f3UL, 0x8179a84bUL, 0xd769cb13UL, 0xb20e77abUL, 0x5ca1c2b9UL, + 0x39c67e01UL, 0x80fea99cUL, 0xe5991524UL, 0x0b36a036UL, 0x6e511c8eUL, + 0xa7166686UL, 0xc271da3eUL, 0x2cde6f2cUL, 0x49b9d394UL, 0xf0810409UL, + 0x95e6b8b1UL, 0x7b490da3UL, 0x1e2eb11bUL, 0x483ed243UL, 0x2d596efbUL, + 0xc3f6dbe9UL, 0xa6916751UL, 0x1fa9b0ccUL, 0x7ace0c74UL, 0x9461b966UL, + 0xf10605deUL +#endif + } +}; diff --git a/src/zlib/deflate.c b/src/zlib/deflate.c new file mode 100644 index 00000000..b7fd3036 --- /dev/null +++ b/src/zlib/deflate.c @@ -0,0 +1,1903 @@ +/* deflate.c -- compress data using the deflation algorithm + * Copyright (C) 1995-2013 Jean-loup Gailly and Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* + * ALGORITHM + * + * The "deflation" process depends on being able to identify portions + * of the input text which are identical to earlier input (within a + * sliding window trailing behind the input currently being processed). + * + * The most straightforward technique turns out to be the fastest for + * most input files: try all possible matches and select the longest. + * The key feature of this algorithm is that insertions into the string + * dictionary are very simple and thus fast, and deletions are avoided + * completely. Insertions are performed at each input character, whereas + * string matches are performed only when the previous match ends. So it + * is preferable to spend more time in matches to allow very fast string + * insertions and avoid deletions. The matching algorithm for small + * strings is inspired from that of Rabin & Karp. A brute force approach + * is used to find longer strings when a small match has been found. + * A similar algorithm is used in comic (by Jan-Mark Wams) and freeze + * (by Leonid Broukhis). + * A previous version of this file used a more sophisticated algorithm + * (by Fiala and Greene) which is guaranteed to run in linear amortized + * time, but has a larger average cost, uses more memory and is patented. + * However the F&G algorithm may be faster for some highly redundant + * files if the parameter max_chain_length (described below) is too large. + * + * ACKNOWLEDGEMENTS + * + * The idea of lazy evaluation of matches is due to Jan-Mark Wams, and + * I found it in 'freeze' written by Leonid Broukhis. + * Thanks to many people for bug reports and testing. + * + * REFERENCES + * + * Deutsch, L.P.,"DEFLATE Compressed Data Format Specification". + * Available in http://tools.ietf.org/html/rfc1951 + * + * A description of the Rabin and Karp algorithm is given in the book + * "Algorithms" by R. Sedgewick, Addison-Wesley, p252. + * + * Fiala,E.R., and Greene,D.H. + * Data Compression with Finite Windows, Comm.ACM, 32,4 (1989) 490-595 + * + */ + +/* @(#) $Id: deflate.c,v 1.2 2013/12/18 09:48:32 cfsmp3 Exp $ */ + +#include "deflate.h" + +const char deflate_copyright[] = + " deflate 1.2.8 Copyright 1995-2013 Jean-loup Gailly and Mark Adler "; +/* + If you use the zlib library in a product, an acknowledgment is welcome + in the documentation of your product. If for some reason you cannot + include such an acknowledgment, I would appreciate that you keep this + copyright string in the executable of your product. + */ + +/* =========================================================================== + * Function prototypes. + */ +typedef enum { + need_more, /* block not completed, need more input or more output */ + block_done, /* block flush performed */ + finish_started, /* finish started, need only more output at next deflate */ + finish_done /* finish done, accept no more input or output */ +} block_state; + +typedef block_state (*compress_func) OF((deflate_state *s, int flush)); +/* Compression function. Returns the block state after the call. */ + +local void fill_window OF((deflate_state *s)); +local block_state deflate_stored OF((deflate_state *s, int flush)); +local block_state deflate_fast OF((deflate_state *s, int flush)); +#ifndef FASTEST +local block_state deflate_slow OF((deflate_state *s, int flush)); +#endif +local block_state deflate_rle OF((deflate_state *s, int flush)); +local block_state deflate_huff OF((deflate_state *s, int flush)); +local void lm_init OF((deflate_state *s)); +local void putShortMSB OF((deflate_state *s, uInt b)); +local void flush_pending OF((z_streamp strm)); +local int read_buf OF((z_streamp strm, Bytef *buf, unsigned size)); +#ifdef ASMV + void match_init OF((void)); /* asm code initialization */ + uInt longest_match OF((deflate_state *s, IPos cur_match)); +#else +local uInt longest_match OF((deflate_state *s, IPos cur_match)); +#endif + +#ifdef DEBUG +local void check_match OF((deflate_state *s, IPos start, IPos match, + int length)); +#endif + +/* =========================================================================== + * Local data + */ + +#define NIL 0 +/* Tail of hash chains */ + +#ifndef TOO_FAR +# define TOO_FAR 4096 +#endif +/* Matches of length 3 are discarded if their distance exceeds TOO_FAR */ + +/* Values for max_lazy_match, good_match and max_chain_length, depending on + * the desired pack level (0..9). The values given below have been tuned to + * exclude worst case performance for pathological files. Better values may be + * found for specific files. + */ +typedef struct config_s { + ush good_length; /* reduce lazy search above this match length */ + ush max_lazy; /* do not perform lazy search above this match length */ + ush nice_length; /* quit search above this match length */ + ush max_chain; + compress_func func; +} config; + +#ifdef FASTEST +local const config configuration_table[2] = { +/* good lazy nice chain */ +/* 0 */ {0, 0, 0, 0, deflate_stored}, /* store only */ +/* 1 */ {4, 4, 8, 4, deflate_fast}}; /* max speed, no lazy matches */ +#else +local const config configuration_table[10] = { +/* good lazy nice chain */ +/* 0 */ {0, 0, 0, 0, deflate_stored}, /* store only */ +/* 1 */ {4, 4, 8, 4, deflate_fast}, /* max speed, no lazy matches */ +/* 2 */ {4, 5, 16, 8, deflate_fast}, +/* 3 */ {4, 6, 32, 32, deflate_fast}, + +/* 4 */ {4, 4, 16, 16, deflate_slow}, /* lazy matches */ +/* 5 */ {8, 16, 32, 32, deflate_slow}, +/* 6 */ {8, 16, 128, 128, deflate_slow}, +/* 7 */ {8, 32, 128, 256, deflate_slow}, +/* 8 */ {32, 128, 258, 1024, deflate_slow}, +/* 9 */ {32, 258, 258, 4096, deflate_slow}}; /* max compression */ +#endif + +/* Note: the deflate() code requires max_lazy >= MIN_MATCH and max_chain >= 4 + * For deflate_fast() (levels <= 3) good is ignored and lazy has a different + * meaning. + */ + +#define EQUAL 0 +/* result of memcmp for equal strings */ + +#ifndef NO_DUMMY_DECL +struct static_tree_desc_s {int dummy;}; /* for buggy compilers */ +#endif + +/* rank Z_BLOCK between Z_NO_FLUSH and Z_PARTIAL_FLUSH */ +#define RANK(f) (((f) << 1) - ((f) > 4 ? 9 : 0)) + +/* =========================================================================== + * Update a hash value with the given input byte + * IN assertion: all calls to to UPDATE_HASH are made with consecutive + * input characters, so that a running hash key can be computed from the + * previous key instead of complete recalculation each time. + */ +#define UPDATE_HASH(s,h,c) (h = (((h)<hash_shift) ^ (c)) & s->hash_mask) + + +/* =========================================================================== + * Insert string str in the dictionary and set match_head to the previous head + * of the hash chain (the most recent string with same hash key). Return + * the previous length of the hash chain. + * If this file is compiled with -DFASTEST, the compression level is forced + * to 1, and no hash chains are maintained. + * IN assertion: all calls to to INSERT_STRING are made with consecutive + * input characters and the first MIN_MATCH bytes of str are valid + * (except for the last MIN_MATCH-1 bytes of the input file). + */ +#ifdef FASTEST +#define INSERT_STRING(s, str, match_head) \ + (UPDATE_HASH(s, s->ins_h, s->window[(str) + (MIN_MATCH-1)]), \ + match_head = s->head[s->ins_h], \ + s->head[s->ins_h] = (Pos)(str)) +#else +#define INSERT_STRING(s, str, match_head) \ + (UPDATE_HASH(s, s->ins_h, s->window[(str) + (MIN_MATCH-1)]), \ + match_head = s->prev[(str) & s->w_mask] = s->head[s->ins_h], \ + s->head[s->ins_h] = (Pos)(str)) +#endif + +/* =========================================================================== + * Initialize the hash table (avoiding 64K overflow for 16 bit systems). + * prev[] will be initialized on the fly. + */ +#define CLEAR_HASH(s) \ + s->head[s->hash_size-1] = NIL; \ + zmemzero((Bytef *)s->head, (unsigned)(s->hash_size-1)*sizeof(*s->head)); + +/* ========================================================================= */ +int ZEXPORT deflateInit_(z_streamp strm, int level, const char * version, int stream_size) +{ + return deflateInit2_(strm, level, Z_DEFLATED, MAX_WBITS, DEF_MEM_LEVEL, + Z_DEFAULT_STRATEGY, version, stream_size); + /* To do: ignore strm->next_in if we use it as window */ +} + +/* ========================================================================= */ +int ZEXPORT deflateInit2_(z_streamp strm, int level, int method, int windowBits, + int memLevel, int strategy, const char * version, int stream_size) +{ + deflate_state *s; + int wrap = 1; + static const char my_version[] = ZLIB_VERSION; + + ushf *overlay; + /* We overlay pending_buf and d_buf+l_buf. This works since the average + * output size for (length,distance) codes is <= 24 bits. + */ + + if (version == Z_NULL || version[0] != my_version[0] || + stream_size != sizeof(z_stream)) { + return Z_VERSION_ERROR; + } + if (strm == Z_NULL) return Z_STREAM_ERROR; + + strm->msg = Z_NULL; + if (strm->zalloc == (alloc_func)0) { +#ifdef Z_SOLO + return Z_STREAM_ERROR; +#else + strm->zalloc = zcalloc; + strm->opaque = (voidpf)0; +#endif + } + if (strm->zfree == (free_func)0) +#ifdef Z_SOLO + return Z_STREAM_ERROR; +#else + strm->zfree = zcfree; +#endif + +#ifdef FASTEST + if (level != 0) level = 1; +#else + if (level == Z_DEFAULT_COMPRESSION) level = 6; +#endif + + if (windowBits < 0) { /* suppress zlib wrapper */ + wrap = 0; + windowBits = -windowBits; + } +#ifdef GZIP + else if (windowBits > 15) { + wrap = 2; /* write gzip wrapper instead */ + windowBits -= 16; + } +#endif + if (memLevel < 1 || memLevel > MAX_MEM_LEVEL || method != Z_DEFLATED || + windowBits < 8 || windowBits > 15 || level < 0 || level > 9 || + strategy < 0 || strategy > Z_FIXED) { + return Z_STREAM_ERROR; + } + if (windowBits == 8) windowBits = 9; /* until 256-byte window bug fixed */ + s = (deflate_state *) ZALLOC(strm, 1, sizeof(deflate_state)); + if (s == Z_NULL) return Z_MEM_ERROR; + strm->state = (struct internal_state FAR *)s; + s->strm = strm; + + s->wrap = wrap; + s->gzhead = Z_NULL; + s->w_bits = windowBits; + s->w_size = 1 << s->w_bits; + s->w_mask = s->w_size - 1; + + s->hash_bits = memLevel + 7; + s->hash_size = 1 << s->hash_bits; + s->hash_mask = s->hash_size - 1; + s->hash_shift = ((s->hash_bits+MIN_MATCH-1)/MIN_MATCH); + + s->window = (Bytef *) ZALLOC(strm, s->w_size, 2*sizeof(Byte)); + s->prev = (Posf *) ZALLOC(strm, s->w_size, sizeof(Pos)); + s->head = (Posf *) ZALLOC(strm, s->hash_size, sizeof(Pos)); + + s->high_water = 0; /* nothing written to s->window yet */ + + s->lit_bufsize = 1 << (memLevel + 6); /* 16K elements by default */ + + overlay = (ushf *) ZALLOC(strm, s->lit_bufsize, sizeof(ush)+2); + s->pending_buf = (uchf *) overlay; + s->pending_buf_size = (ulg)s->lit_bufsize * (sizeof(ush)+2L); + + if (s->window == Z_NULL || s->prev == Z_NULL || s->head == Z_NULL || + s->pending_buf == Z_NULL) { + s->status = FINISH_STATE; + strm->msg = ERR_MSG(Z_MEM_ERROR); + deflateEnd (strm); + return Z_MEM_ERROR; + } + s->d_buf = overlay + s->lit_bufsize/sizeof(ush); + s->l_buf = s->pending_buf + (1+sizeof(ush))*s->lit_bufsize; + + s->level = level; + s->strategy = strategy; + s->method = (Byte)method; + + return deflateReset(strm); +} + +/* ========================================================================= */ +int ZEXPORT deflateSetDictionary (z_streamp strm, const Bytef * dictionary, uInt dictLength) +{ + deflate_state *s; + uInt str, n; + int wrap; + unsigned avail; + z_const unsigned char *next; + + if (strm == Z_NULL || strm->state == Z_NULL || dictionary == Z_NULL) + return Z_STREAM_ERROR; + s = strm->state; + wrap = s->wrap; + if (wrap == 2 || (wrap == 1 && s->status != INIT_STATE) || s->lookahead) + return Z_STREAM_ERROR; + + /* when using zlib wrappers, compute Adler-32 for provided dictionary */ + if (wrap == 1) + strm->adler = adler32(strm->adler, dictionary, dictLength); + s->wrap = 0; /* avoid computing Adler-32 in read_buf */ + + /* if dictionary would fill window, just replace the history */ + if (dictLength >= s->w_size) { + if (wrap == 0) { /* already empty otherwise */ + CLEAR_HASH(s); + s->strstart = 0; + s->block_start = 0L; + s->insert = 0; + } + dictionary += dictLength - s->w_size; /* use the tail */ + dictLength = s->w_size; + } + + /* insert dictionary into window and hash */ + avail = strm->avail_in; + next = strm->next_in; + strm->avail_in = dictLength; + strm->next_in = (z_const Bytef *)dictionary; + fill_window(s); + while (s->lookahead >= MIN_MATCH) { + str = s->strstart; + n = s->lookahead - (MIN_MATCH-1); + do { + UPDATE_HASH(s, s->ins_h, s->window[str + MIN_MATCH-1]); +#ifndef FASTEST + s->prev[str & s->w_mask] = s->head[s->ins_h]; +#endif + s->head[s->ins_h] = (Pos)str; + str++; + } while (--n); + s->strstart = str; + s->lookahead = MIN_MATCH-1; + fill_window(s); + } + s->strstart += s->lookahead; + s->block_start = (long)s->strstart; + s->insert = s->lookahead; + s->lookahead = 0; + s->match_length = s->prev_length = MIN_MATCH-1; + s->match_available = 0; + strm->next_in = next; + strm->avail_in = avail; + s->wrap = wrap; + return Z_OK; +} + +/* ========================================================================= */ +int ZEXPORT deflateResetKeep (z_streamp strm) +{ + deflate_state *s; + + if (strm == Z_NULL || strm->state == Z_NULL || + strm->zalloc == (alloc_func)0 || strm->zfree == (free_func)0) { + return Z_STREAM_ERROR; + } + + strm->total_in = strm->total_out = 0; + strm->msg = Z_NULL; /* use zfree if we ever allocate msg dynamically */ + strm->data_type = Z_UNKNOWN; + + s = (deflate_state *)strm->state; + s->pending = 0; + s->pending_out = s->pending_buf; + + if (s->wrap < 0) { + s->wrap = -s->wrap; /* was made negative by deflate(..., Z_FINISH); */ + } + s->status = s->wrap ? INIT_STATE : BUSY_STATE; + strm->adler = +#ifdef GZIP + s->wrap == 2 ? crc32(0L, Z_NULL, 0) : +#endif + adler32(0L, Z_NULL, 0); + s->last_flush = Z_NO_FLUSH; + + _tr_init(s); + + return Z_OK; +} + +/* ========================================================================= */ +int ZEXPORT deflateReset (z_streamp strm) +{ + int ret; + + ret = deflateResetKeep(strm); + if (ret == Z_OK) + lm_init(strm->state); + return ret; +} + +/* ========================================================================= */ +int ZEXPORT deflateSetHeader (z_streamp strm, gz_headerp head) +{ + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + if (strm->state->wrap != 2) return Z_STREAM_ERROR; + strm->state->gzhead = head; + return Z_OK; +} + +/* ========================================================================= */ +int ZEXPORT deflatePending (z_streamp strm, unsigned *pending, int *bits) +{ + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + if (pending != Z_NULL) + *pending = strm->state->pending; + if (bits != Z_NULL) + *bits = strm->state->bi_valid; + return Z_OK; +} + +/* ========================================================================= */ +int ZEXPORT deflatePrime (z_streamp strm, int bits, int value) +{ + deflate_state *s; + int put; + + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + s = strm->state; + if ((Bytef *)(s->d_buf) < s->pending_out + ((Buf_size + 7) >> 3)) + return Z_BUF_ERROR; + do { + put = Buf_size - s->bi_valid; + if (put > bits) + put = bits; + s->bi_buf |= (ush)((value & ((1 << put) - 1)) << s->bi_valid); + s->bi_valid += put; + _tr_flush_bits(s); + value >>= put; + bits -= put; + } while (bits); + return Z_OK; +} + +/* ========================================================================= */ +int ZEXPORT deflateParams(z_streamp strm, int level, int strategy) +{ + deflate_state *s; + compress_func func; + int err = Z_OK; + + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + s = strm->state; + +#ifdef FASTEST + if (level != 0) level = 1; +#else + if (level == Z_DEFAULT_COMPRESSION) level = 6; +#endif + if (level < 0 || level > 9 || strategy < 0 || strategy > Z_FIXED) { + return Z_STREAM_ERROR; + } + func = configuration_table[s->level].func; + + if ((strategy != s->strategy || func != configuration_table[level].func) && + strm->total_in != 0) { + /* Flush the last buffer: */ + err = deflate(strm, Z_BLOCK); + if (err == Z_BUF_ERROR && s->pending == 0) + err = Z_OK; + } + if (s->level != level) { + s->level = level; + s->max_lazy_match = configuration_table[level].max_lazy; + s->good_match = configuration_table[level].good_length; + s->nice_match = configuration_table[level].nice_length; + s->max_chain_length = configuration_table[level].max_chain; + } + s->strategy = strategy; + return err; +} + +/* ========================================================================= */ +int ZEXPORT deflateTune(z_streamp strm, int good_length, int max_lazy, + int nice_length, int max_chain) +{ + deflate_state *s; + + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + s = strm->state; + s->good_match = good_length; + s->max_lazy_match = max_lazy; + s->nice_match = nice_length; + s->max_chain_length = max_chain; + return Z_OK; +} + +/* ========================================================================= + * For the default windowBits of 15 and memLevel of 8, this function returns + * a close to exact, as well as small, upper bound on the compressed size. + * They are coded as constants here for a reason--if the #define's are + * changed, then this function needs to be changed as well. The return + * value for 15 and 8 only works for those exact settings. + * + * For any setting other than those defaults for windowBits and memLevel, + * the value returned is a conservative worst case for the maximum expansion + * resulting from using fixed blocks instead of stored blocks, which deflate + * can emit on compressed data for some combinations of the parameters. + * + * This function could be more sophisticated to provide closer upper bounds for + * every combination of windowBits and memLevel. But even the conservative + * upper bound of about 14% expansion does not seem onerous for output buffer + * allocation. + */ +uLong ZEXPORT deflateBound(z_streamp strm, uLong sourceLen) +{ + deflate_state *s; + uLong complen, wraplen; + Bytef *str; + + /* conservative upper bound for compressed data */ + complen = sourceLen + + ((sourceLen + 7) >> 3) + ((sourceLen + 63) >> 6) + 5; + + /* if can't get parameters, return conservative bound plus zlib wrapper */ + if (strm == Z_NULL || strm->state == Z_NULL) + return complen + 6; + + /* compute wrapper length */ + s = strm->state; + switch (s->wrap) { + case 0: /* raw deflate */ + wraplen = 0; + break; + case 1: /* zlib wrapper */ + wraplen = 6 + (s->strstart ? 4 : 0); + break; + case 2: /* gzip wrapper */ + wraplen = 18; + if (s->gzhead != Z_NULL) { /* user-supplied gzip header */ + if (s->gzhead->extra != Z_NULL) + wraplen += 2 + s->gzhead->extra_len; + str = s->gzhead->name; + if (str != Z_NULL) + do { + wraplen++; + } while (*str++); + str = s->gzhead->comment; + if (str != Z_NULL) + do { + wraplen++; + } while (*str++); + if (s->gzhead->hcrc) + wraplen += 2; + } + break; + default: /* for compiler happiness */ + wraplen = 6; + } + + /* if not default parameters, return conservative bound */ + if (s->w_bits != 15 || s->hash_bits != 8 + 7) + return complen + wraplen; + + /* default settings: return tight bound for that case */ + return sourceLen + (sourceLen >> 12) + (sourceLen >> 14) + + (sourceLen >> 25) + 13 - 6 + wraplen; +} + +/* ========================================================================= + * Put a short in the pending buffer. The 16-bit value is put in MSB order. + * IN assertion: the stream state is correct and there is enough room in + * pending_buf. + */ +local void putShortMSB (deflate_state *s, uInt b) +{ + put_byte(s, (Byte)(b >> 8)); + put_byte(s, (Byte)(b & 0xff)); +} + +/* ========================================================================= + * Flush as much pending output as possible. All deflate() output goes + * through this function so some applications may wish to modify it + * to avoid allocating a large strm->next_out buffer and copying into it. + * (See also read_buf()). + */ +local void flush_pending(z_streamp strm) +{ + unsigned len; + deflate_state *s = strm->state; + + _tr_flush_bits(s); + len = s->pending; + if (len > strm->avail_out) len = strm->avail_out; + if (len == 0) return; + + zmemcpy(strm->next_out, s->pending_out, len); + strm->next_out += len; + s->pending_out += len; + strm->total_out += len; + strm->avail_out -= len; + s->pending -= len; + if (s->pending == 0) { + s->pending_out = s->pending_buf; + } +} + +/* ========================================================================= */ +int ZEXPORT deflate (z_streamp strm, int flush) +{ + int old_flush; /* value of flush param for previous deflate call */ + deflate_state *s; + + if (strm == Z_NULL || strm->state == Z_NULL || + flush > Z_BLOCK || flush < 0) { + return Z_STREAM_ERROR; + } + s = strm->state; + + if (strm->next_out == Z_NULL || + (strm->next_in == Z_NULL && strm->avail_in != 0) || + (s->status == FINISH_STATE && flush != Z_FINISH)) { + ERR_RETURN(strm, Z_STREAM_ERROR); + } + if (strm->avail_out == 0) ERR_RETURN(strm, Z_BUF_ERROR); + + s->strm = strm; /* just in case */ + old_flush = s->last_flush; + s->last_flush = flush; + + /* Write the header */ + if (s->status == INIT_STATE) { +#ifdef GZIP + if (s->wrap == 2) { + strm->adler = crc32(0L, Z_NULL, 0); + put_byte(s, 31); + put_byte(s, 139); + put_byte(s, 8); + if (s->gzhead == Z_NULL) { + put_byte(s, 0); + put_byte(s, 0); + put_byte(s, 0); + put_byte(s, 0); + put_byte(s, 0); + put_byte(s, s->level == 9 ? 2 : + (s->strategy >= Z_HUFFMAN_ONLY || s->level < 2 ? + 4 : 0)); + put_byte(s, OS_CODE); + s->status = BUSY_STATE; + } + else { + put_byte(s, (s->gzhead->text ? 1 : 0) + + (s->gzhead->hcrc ? 2 : 0) + + (s->gzhead->extra == Z_NULL ? 0 : 4) + + (s->gzhead->name == Z_NULL ? 0 : 8) + + (s->gzhead->comment == Z_NULL ? 0 : 16) + ); + put_byte(s, (Byte)(s->gzhead->time & 0xff)); + put_byte(s, (Byte)((s->gzhead->time >> 8) & 0xff)); + put_byte(s, (Byte)((s->gzhead->time >> 16) & 0xff)); + put_byte(s, (Byte)((s->gzhead->time >> 24) & 0xff)); + put_byte(s, s->level == 9 ? 2 : + (s->strategy >= Z_HUFFMAN_ONLY || s->level < 2 ? + 4 : 0)); + put_byte(s, s->gzhead->os & 0xff); + if (s->gzhead->extra != Z_NULL) { + put_byte(s, s->gzhead->extra_len & 0xff); + put_byte(s, (s->gzhead->extra_len >> 8) & 0xff); + } + if (s->gzhead->hcrc) + strm->adler = crc32(strm->adler, s->pending_buf, + s->pending); + s->gzindex = 0; + s->status = EXTRA_STATE; + } + } + else +#endif + { + uInt header = (Z_DEFLATED + ((s->w_bits-8)<<4)) << 8; + uInt level_flags; + + if (s->strategy >= Z_HUFFMAN_ONLY || s->level < 2) + level_flags = 0; + else if (s->level < 6) + level_flags = 1; + else if (s->level == 6) + level_flags = 2; + else + level_flags = 3; + header |= (level_flags << 6); + if (s->strstart != 0) header |= PRESET_DICT; + header += 31 - (header % 31); + + s->status = BUSY_STATE; + putShortMSB(s, header); + + /* Save the adler32 of the preset dictionary: */ + if (s->strstart != 0) { + putShortMSB(s, (uInt)(strm->adler >> 16)); + putShortMSB(s, (uInt)(strm->adler & 0xffff)); + } + strm->adler = adler32(0L, Z_NULL, 0); + } + } +#ifdef GZIP + if (s->status == EXTRA_STATE) { + if (s->gzhead->extra != Z_NULL) { + uInt beg = s->pending; /* start of bytes to update crc */ + + while (s->gzindex < (s->gzhead->extra_len & 0xffff)) { + if (s->pending == s->pending_buf_size) { + if (s->gzhead->hcrc && s->pending > beg) + strm->adler = crc32(strm->adler, s->pending_buf + beg, + s->pending - beg); + flush_pending(strm); + beg = s->pending; + if (s->pending == s->pending_buf_size) + break; + } + put_byte(s, s->gzhead->extra[s->gzindex]); + s->gzindex++; + } + if (s->gzhead->hcrc && s->pending > beg) + strm->adler = crc32(strm->adler, s->pending_buf + beg, + s->pending - beg); + if (s->gzindex == s->gzhead->extra_len) { + s->gzindex = 0; + s->status = NAME_STATE; + } + } + else + s->status = NAME_STATE; + } + if (s->status == NAME_STATE) { + if (s->gzhead->name != Z_NULL) { + uInt beg = s->pending; /* start of bytes to update crc */ + int val; + + do { + if (s->pending == s->pending_buf_size) { + if (s->gzhead->hcrc && s->pending > beg) + strm->adler = crc32(strm->adler, s->pending_buf + beg, + s->pending - beg); + flush_pending(strm); + beg = s->pending; + if (s->pending == s->pending_buf_size) { + val = 1; + break; + } + } + val = s->gzhead->name[s->gzindex++]; + put_byte(s, val); + } while (val != 0); + if (s->gzhead->hcrc && s->pending > beg) + strm->adler = crc32(strm->adler, s->pending_buf + beg, + s->pending - beg); + if (val == 0) { + s->gzindex = 0; + s->status = COMMENT_STATE; + } + } + else + s->status = COMMENT_STATE; + } + if (s->status == COMMENT_STATE) { + if (s->gzhead->comment != Z_NULL) { + uInt beg = s->pending; /* start of bytes to update crc */ + int val; + + do { + if (s->pending == s->pending_buf_size) { + if (s->gzhead->hcrc && s->pending > beg) + strm->adler = crc32(strm->adler, s->pending_buf + beg, + s->pending - beg); + flush_pending(strm); + beg = s->pending; + if (s->pending == s->pending_buf_size) { + val = 1; + break; + } + } + val = s->gzhead->comment[s->gzindex++]; + put_byte(s, val); + } while (val != 0); + if (s->gzhead->hcrc && s->pending > beg) + strm->adler = crc32(strm->adler, s->pending_buf + beg, + s->pending - beg); + if (val == 0) + s->status = HCRC_STATE; + } + else + s->status = HCRC_STATE; + } + if (s->status == HCRC_STATE) { + if (s->gzhead->hcrc) { + if (s->pending + 2 > s->pending_buf_size) + flush_pending(strm); + if (s->pending + 2 <= s->pending_buf_size) { + put_byte(s, (Byte)(strm->adler & 0xff)); + put_byte(s, (Byte)((strm->adler >> 8) & 0xff)); + strm->adler = crc32(0L, Z_NULL, 0); + s->status = BUSY_STATE; + } + } + else + s->status = BUSY_STATE; + } +#endif + + /* Flush as much pending output as possible */ + if (s->pending != 0) { + flush_pending(strm); + if (strm->avail_out == 0) { + /* Since avail_out is 0, deflate will be called again with + * more output space, but possibly with both pending and + * avail_in equal to zero. There won't be anything to do, + * but this is not an error situation so make sure we + * return OK instead of BUF_ERROR at next call of deflate: + */ + s->last_flush = -1; + return Z_OK; + } + + /* Make sure there is something to do and avoid duplicate consecutive + * flushes. For repeated and useless calls with Z_FINISH, we keep + * returning Z_STREAM_END instead of Z_BUF_ERROR. + */ + } else if (strm->avail_in == 0 && RANK(flush) <= RANK(old_flush) && + flush != Z_FINISH) { + ERR_RETURN(strm, Z_BUF_ERROR); + } + + /* User must not provide more input after the first FINISH: */ + if (s->status == FINISH_STATE && strm->avail_in != 0) { + ERR_RETURN(strm, Z_BUF_ERROR); + } + + /* Start a new block or continue the current one. + */ + if (strm->avail_in != 0 || s->lookahead != 0 || + (flush != Z_NO_FLUSH && s->status != FINISH_STATE)) { + block_state bstate; + + bstate = s->strategy == Z_HUFFMAN_ONLY ? deflate_huff(s, flush) : + (s->strategy == Z_RLE ? deflate_rle(s, flush) : + (*(configuration_table[s->level].func))(s, flush)); + + if (bstate == finish_started || bstate == finish_done) { + s->status = FINISH_STATE; + } + if (bstate == need_more || bstate == finish_started) { + if (strm->avail_out == 0) { + s->last_flush = -1; /* avoid BUF_ERROR next call, see above */ + } + return Z_OK; + /* If flush != Z_NO_FLUSH && avail_out == 0, the next call + * of deflate should use the same flush parameter to make sure + * that the flush is complete. So we don't have to output an + * empty block here, this will be done at next call. This also + * ensures that for a very small output buffer, we emit at most + * one empty block. + */ + } + if (bstate == block_done) { + if (flush == Z_PARTIAL_FLUSH) { + _tr_align(s); + } else if (flush != Z_BLOCK) { /* FULL_FLUSH or SYNC_FLUSH */ + _tr_stored_block(s, (char*)0, 0L, 0); + /* For a full flush, this empty block will be recognized + * as a special marker by inflate_sync(). + */ + if (flush == Z_FULL_FLUSH) { + CLEAR_HASH(s); /* forget history */ + if (s->lookahead == 0) { + s->strstart = 0; + s->block_start = 0L; + s->insert = 0; + } + } + } + flush_pending(strm); + if (strm->avail_out == 0) { + s->last_flush = -1; /* avoid BUF_ERROR at next call, see above */ + return Z_OK; + } + } + } + Assert(strm->avail_out > 0, "bug2"); + + if (flush != Z_FINISH) return Z_OK; + if (s->wrap <= 0) return Z_STREAM_END; + + /* Write the trailer */ +#ifdef GZIP + if (s->wrap == 2) { + put_byte(s, (Byte)(strm->adler & 0xff)); + put_byte(s, (Byte)((strm->adler >> 8) & 0xff)); + put_byte(s, (Byte)((strm->adler >> 16) & 0xff)); + put_byte(s, (Byte)((strm->adler >> 24) & 0xff)); + put_byte(s, (Byte)(strm->total_in & 0xff)); + put_byte(s, (Byte)((strm->total_in >> 8) & 0xff)); + put_byte(s, (Byte)((strm->total_in >> 16) & 0xff)); + put_byte(s, (Byte)((strm->total_in >> 24) & 0xff)); + } + else +#endif + { + putShortMSB(s, (uInt)(strm->adler >> 16)); + putShortMSB(s, (uInt)(strm->adler & 0xffff)); + } + flush_pending(strm); + /* If avail_out is zero, the application will call deflate again + * to flush the rest. + */ + if (s->wrap > 0) s->wrap = -s->wrap; /* write the trailer only once! */ + return s->pending != 0 ? Z_OK : Z_STREAM_END; +} + +/* ========================================================================= */ +int ZEXPORT deflateEnd (z_streamp strm) +{ + int status; + + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + + status = strm->state->status; + if (status != INIT_STATE && + status != EXTRA_STATE && + status != NAME_STATE && + status != COMMENT_STATE && + status != HCRC_STATE && + status != BUSY_STATE && + status != FINISH_STATE) { + return Z_STREAM_ERROR; + } + + /* Deallocate in reverse order of allocations: */ + TRY_FREE(strm, strm->state->pending_buf); + TRY_FREE(strm, strm->state->head); + TRY_FREE(strm, strm->state->prev); + TRY_FREE(strm, strm->state->window); + + ZFREE(strm, strm->state); + strm->state = Z_NULL; + + return status == BUSY_STATE ? Z_DATA_ERROR : Z_OK; +} + +/* ========================================================================= + * Copy the source state to the destination state. + * To simplify the source, this is not supported for 16-bit MSDOS (which + * doesn't have enough memory anyway to duplicate compression states). + */ +int ZEXPORT deflateCopy (z_streamp dest, z_streamp source) +{ +#ifdef MAXSEG_64K + return Z_STREAM_ERROR; +#else + deflate_state *ds; + deflate_state *ss; + ushf *overlay; + + + if (source == Z_NULL || dest == Z_NULL || source->state == Z_NULL) { + return Z_STREAM_ERROR; + } + + ss = source->state; + + zmemcpy((voidpf)dest, (voidpf)source, sizeof(z_stream)); + + ds = (deflate_state *) ZALLOC(dest, 1, sizeof(deflate_state)); + if (ds == Z_NULL) return Z_MEM_ERROR; + dest->state = (struct internal_state FAR *) ds; + zmemcpy((voidpf)ds, (voidpf)ss, sizeof(deflate_state)); + ds->strm = dest; + + ds->window = (Bytef *) ZALLOC(dest, ds->w_size, 2*sizeof(Byte)); + ds->prev = (Posf *) ZALLOC(dest, ds->w_size, sizeof(Pos)); + ds->head = (Posf *) ZALLOC(dest, ds->hash_size, sizeof(Pos)); + overlay = (ushf *) ZALLOC(dest, ds->lit_bufsize, sizeof(ush)+2); + ds->pending_buf = (uchf *) overlay; + + if (ds->window == Z_NULL || ds->prev == Z_NULL || ds->head == Z_NULL || + ds->pending_buf == Z_NULL) { + deflateEnd (dest); + return Z_MEM_ERROR; + } + /* following zmemcpy do not work for 16-bit MSDOS */ + zmemcpy(ds->window, ss->window, ds->w_size * 2 * sizeof(Byte)); + zmemcpy((voidpf)ds->prev, (voidpf)ss->prev, ds->w_size * sizeof(Pos)); + zmemcpy((voidpf)ds->head, (voidpf)ss->head, ds->hash_size * sizeof(Pos)); + zmemcpy(ds->pending_buf, ss->pending_buf, (uInt)ds->pending_buf_size); + + ds->pending_out = ds->pending_buf + (ss->pending_out - ss->pending_buf); + ds->d_buf = overlay + ds->lit_bufsize/sizeof(ush); + ds->l_buf = ds->pending_buf + (1+sizeof(ush))*ds->lit_bufsize; + + ds->l_desc.dyn_tree = ds->dyn_ltree; + ds->d_desc.dyn_tree = ds->dyn_dtree; + ds->bl_desc.dyn_tree = ds->bl_tree; + + return Z_OK; +#endif /* MAXSEG_64K */ +} + +/* =========================================================================== + * Read a new buffer from the current input stream, update the adler32 + * and total number of bytes read. All deflate() input goes through + * this function so some applications may wish to modify it to avoid + * allocating a large strm->next_in buffer and copying from it. + * (See also flush_pending()). + */ +local int read_buf(z_streamp strm, Bytef *buf, unsigned size) +{ + unsigned len = strm->avail_in; + + if (len > size) len = size; + if (len == 0) return 0; + + strm->avail_in -= len; + + zmemcpy(buf, strm->next_in, len); + if (strm->state->wrap == 1) { + strm->adler = adler32(strm->adler, buf, len); + } +#ifdef GZIP + else if (strm->state->wrap == 2) { + strm->adler = crc32(strm->adler, buf, len); + } +#endif + strm->next_in += len; + strm->total_in += len; + + return (int)len; +} + +/* =========================================================================== + * Initialize the "longest match" routines for a new zlib stream + */ +local void lm_init (deflate_state *s) +{ + s->window_size = (ulg)2L*s->w_size; + + CLEAR_HASH(s); + + /* Set the default configuration parameters: + */ + s->max_lazy_match = configuration_table[s->level].max_lazy; + s->good_match = configuration_table[s->level].good_length; + s->nice_match = configuration_table[s->level].nice_length; + s->max_chain_length = configuration_table[s->level].max_chain; + + s->strstart = 0; + s->block_start = 0L; + s->lookahead = 0; + s->insert = 0; + s->match_length = s->prev_length = MIN_MATCH-1; + s->match_available = 0; + s->ins_h = 0; +#ifndef FASTEST +#ifdef ASMV + match_init(); /* initialize the asm code */ +#endif +#endif +} + +#ifndef FASTEST +/* =========================================================================== + * Set match_start to the longest match starting at the given string and + * return its length. Matches shorter or equal to prev_length are discarded, + * in which case the result is equal to prev_length and match_start is + * garbage. + * IN assertions: cur_match is the head of the hash chain for the current + * string (strstart) and its distance is <= MAX_DIST, and prev_length >= 1 + * OUT assertion: the match length is not greater than s->lookahead. + */ +#ifndef ASMV +/* For 80x86 and 680x0, an optimized version will be provided in match.asm or + * match.S. The code will be functionally equivalent. + */ +local uInt longest_match(deflate_state *s, IPos cur_match) +{ + unsigned chain_length = s->max_chain_length;/* max hash chain length */ + register Bytef *scan = s->window + s->strstart; /* current string */ + register Bytef *match; /* matched string */ + register int len; /* length of current match */ + int best_len = s->prev_length; /* best match length so far */ + int nice_match = s->nice_match; /* stop if match long enough */ + IPos limit = s->strstart > (IPos)MAX_DIST(s) ? + s->strstart - (IPos)MAX_DIST(s) : NIL; + /* Stop when cur_match becomes <= limit. To simplify the code, + * we prevent matches with the string of window index 0. + */ + Posf *prev = s->prev; + uInt wmask = s->w_mask; + +#ifdef UNALIGNED_OK + /* Compare two bytes at a time. Note: this is not always beneficial. + * Try with and without -DUNALIGNED_OK to check. + */ + register Bytef *strend = s->window + s->strstart + MAX_MATCH - 1; + register ush scan_start = *(ushf*)scan; + register ush scan_end = *(ushf*)(scan+best_len-1); +#else + register Bytef *strend = s->window + s->strstart + MAX_MATCH; + register Byte scan_end1 = scan[best_len-1]; + register Byte scan_end = scan[best_len]; +#endif + + /* The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16. + * It is easy to get rid of this optimization if necessary. + */ + Assert(s->hash_bits >= 8 && MAX_MATCH == 258, "Code too clever"); + + /* Do not waste too much time if we already have a good match: */ + if (s->prev_length >= s->good_match) { + chain_length >>= 2; + } + /* Do not look for matches beyond the end of the input. This is necessary + * to make deflate deterministic. + */ + if ((uInt)nice_match > s->lookahead) nice_match = s->lookahead; + + Assert((ulg)s->strstart <= s->window_size-MIN_LOOKAHEAD, "need lookahead"); + + do { + Assert(cur_match < s->strstart, "no future"); + match = s->window + cur_match; + + /* Skip to next match if the match length cannot increase + * or if the match length is less than 2. Note that the checks below + * for insufficient lookahead only occur occasionally for performance + * reasons. Therefore uninitialized memory will be accessed, and + * conditional jumps will be made that depend on those values. + * However the length of the match is limited to the lookahead, so + * the output of deflate is not affected by the uninitialized values. + */ +#if (defined(UNALIGNED_OK) && MAX_MATCH == 258) + /* This code assumes sizeof(unsigned short) == 2. Do not use + * UNALIGNED_OK if your compiler uses a different size. + */ + if (*(ushf*)(match+best_len-1) != scan_end || + *(ushf*)match != scan_start) continue; + + /* It is not necessary to compare scan[2] and match[2] since they are + * always equal when the other bytes match, given that the hash keys + * are equal and that HASH_BITS >= 8. Compare 2 bytes at a time at + * strstart+3, +5, ... up to strstart+257. We check for insufficient + * lookahead only every 4th comparison; the 128th check will be made + * at strstart+257. If MAX_MATCH-2 is not a multiple of 8, it is + * necessary to put more guard bytes at the end of the window, or + * to check more often for insufficient lookahead. + */ + Assert(scan[2] == match[2], "scan[2]?"); + scan++, match++; + do { + } while (*(ushf*)(scan+=2) == *(ushf*)(match+=2) && + *(ushf*)(scan+=2) == *(ushf*)(match+=2) && + *(ushf*)(scan+=2) == *(ushf*)(match+=2) && + *(ushf*)(scan+=2) == *(ushf*)(match+=2) && + scan < strend); + /* The funny "do {}" generates better code on most compilers */ + + /* Here, scan <= window+strstart+257 */ + Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan"); + if (*scan == *match) scan++; + + len = (MAX_MATCH - 1) - (int)(strend-scan); + scan = strend - (MAX_MATCH-1); + +#else /* UNALIGNED_OK */ + + if (match[best_len] != scan_end || + match[best_len-1] != scan_end1 || + *match != *scan || + *++match != scan[1]) continue; + + /* The check at best_len-1 can be removed because it will be made + * again later. (This heuristic is not always a win.) + * It is not necessary to compare scan[2] and match[2] since they + * are always equal when the other bytes match, given that + * the hash keys are equal and that HASH_BITS >= 8. + */ + scan += 2, match++; + Assert(*scan == *match, "match[2]?"); + + /* We check for insufficient lookahead only every 8th comparison; + * the 256th check will be made at strstart+258. + */ + do { + } while (*++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + scan < strend); + + Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan"); + + len = MAX_MATCH - (int)(strend - scan); + scan = strend - MAX_MATCH; + +#endif /* UNALIGNED_OK */ + + if (len > best_len) { + s->match_start = cur_match; + best_len = len; + if (len >= nice_match) break; +#ifdef UNALIGNED_OK + scan_end = *(ushf*)(scan+best_len-1); +#else + scan_end1 = scan[best_len-1]; + scan_end = scan[best_len]; +#endif + } + } while ((cur_match = prev[cur_match & wmask]) > limit + && --chain_length != 0); + + if ((uInt)best_len <= s->lookahead) return (uInt)best_len; + return s->lookahead; +} +#endif /* ASMV */ + +#else /* FASTEST */ + +/* --------------------------------------------------------------------------- + * Optimized version for FASTEST only + */ +local uInt longest_match(deflate_state *s, IPos cur_match) +{ + register Bytef *scan = s->window + s->strstart; /* current string */ + register Bytef *match; /* matched string */ + register int len; /* length of current match */ + register Bytef *strend = s->window + s->strstart + MAX_MATCH; + + /* The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16. + * It is easy to get rid of this optimization if necessary. + */ + Assert(s->hash_bits >= 8 && MAX_MATCH == 258, "Code too clever"); + + Assert((ulg)s->strstart <= s->window_size-MIN_LOOKAHEAD, "need lookahead"); + + Assert(cur_match < s->strstart, "no future"); + + match = s->window + cur_match; + + /* Return failure if the match length is less than 2: + */ + if (match[0] != scan[0] || match[1] != scan[1]) return MIN_MATCH-1; + + /* The check at best_len-1 can be removed because it will be made + * again later. (This heuristic is not always a win.) + * It is not necessary to compare scan[2] and match[2] since they + * are always equal when the other bytes match, given that + * the hash keys are equal and that HASH_BITS >= 8. + */ + scan += 2, match += 2; + Assert(*scan == *match, "match[2]?"); + + /* We check for insufficient lookahead only every 8th comparison; + * the 256th check will be made at strstart+258. + */ + do { + } while (*++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + scan < strend); + + Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan"); + + len = MAX_MATCH - (int)(strend - scan); + + if (len < MIN_MATCH) return MIN_MATCH - 1; + + s->match_start = cur_match; + return (uInt)len <= s->lookahead ? (uInt)len : s->lookahead; +} + +#endif /* FASTEST */ + +#ifdef DEBUG +/* =========================================================================== + * Check that the match at match_start is indeed a match. + */ +local void check_match(deflate_state *s, IPos start, IPos match, int length) +{ + /* check that the match is indeed a match */ + if (zmemcmp(s->window + match, + s->window + start, length) != EQUAL) { + fprintf(stderr, " start %u, match %u, length %d\n", + start, match, length); + do { + fprintf(stderr, "%c%c", s->window[match++], s->window[start++]); + } while (--length != 0); + z_error("invalid match"); + } + if (z_verbose > 1) { + fprintf(stderr,"\\[%d,%d]", start-match, length); + do { putc(s->window[start++], stderr); } while (--length != 0); + } +} +#else +# define check_match(s, start, match, length) +#endif /* DEBUG */ + +/* =========================================================================== + * Fill the window when the lookahead becomes insufficient. + * Updates strstart and lookahead. + * + * IN assertion: lookahead < MIN_LOOKAHEAD + * OUT assertions: strstart <= window_size-MIN_LOOKAHEAD + * At least one byte has been read, or avail_in == 0; reads are + * performed for at least two bytes (required for the zip translate_eol + * option -- not supported here). + */ +local void fill_window(deflate_state *s) +{ + register unsigned n, m; + register Posf *p; + unsigned more; /* Amount of free space at the end of the window. */ + uInt wsize = s->w_size; + + Assert(s->lookahead < MIN_LOOKAHEAD, "already enough lookahead"); + + do { + more = (unsigned)(s->window_size -(ulg)s->lookahead -(ulg)s->strstart); + + /* Deal with !@#$% 64K limit: */ + if (sizeof(int) <= 2) { + if (more == 0 && s->strstart == 0 && s->lookahead == 0) { + more = wsize; + + } else if (more == (unsigned)(-1)) { + /* Very unlikely, but possible on 16 bit machine if + * strstart == 0 && lookahead == 1 (input done a byte at time) + */ + more--; + } + } + + /* If the window is almost full and there is insufficient lookahead, + * move the upper half to the lower one to make room in the upper half. + */ + if (s->strstart >= wsize+MAX_DIST(s)) { + + zmemcpy(s->window, s->window+wsize, (unsigned)wsize); + s->match_start -= wsize; + s->strstart -= wsize; /* we now have strstart >= MAX_DIST */ + s->block_start -= (long) wsize; + + /* Slide the hash table (could be avoided with 32 bit values + at the expense of memory usage). We slide even when level == 0 + to keep the hash table consistent if we switch back to level > 0 + later. (Using level 0 permanently is not an optimal usage of + zlib, so we don't care about this pathological case.) + */ + n = s->hash_size; + p = &s->head[n]; + do { + m = *--p; + *p = (Pos)(m >= wsize ? m-wsize : NIL); + } while (--n); + + n = wsize; +#ifndef FASTEST + p = &s->prev[n]; + do { + m = *--p; + *p = (Pos)(m >= wsize ? m-wsize : NIL); + /* If n is not on any hash chain, prev[n] is garbage but + * its value will never be used. + */ + } while (--n); +#endif + more += wsize; + } + if (s->strm->avail_in == 0) break; + + /* If there was no sliding: + * strstart <= WSIZE+MAX_DIST-1 && lookahead <= MIN_LOOKAHEAD - 1 && + * more == window_size - lookahead - strstart + * => more >= window_size - (MIN_LOOKAHEAD-1 + WSIZE + MAX_DIST-1) + * => more >= window_size - 2*WSIZE + 2 + * In the BIG_MEM or MMAP case (not yet supported), + * window_size == input_size + MIN_LOOKAHEAD && + * strstart + s->lookahead <= input_size => more >= MIN_LOOKAHEAD. + * Otherwise, window_size == 2*WSIZE so more >= 2. + * If there was sliding, more >= WSIZE. So in all cases, more >= 2. + */ + Assert(more >= 2, "more < 2"); + + n = read_buf(s->strm, s->window + s->strstart + s->lookahead, more); + s->lookahead += n; + + /* Initialize the hash value now that we have some input: */ + if (s->lookahead + s->insert >= MIN_MATCH) { + uInt str = s->strstart - s->insert; + s->ins_h = s->window[str]; + UPDATE_HASH(s, s->ins_h, s->window[str + 1]); +#if MIN_MATCH != 3 + Call UPDATE_HASH() MIN_MATCH-3 more times +#endif + while (s->insert) { + UPDATE_HASH(s, s->ins_h, s->window[str + MIN_MATCH-1]); +#ifndef FASTEST + s->prev[str & s->w_mask] = s->head[s->ins_h]; +#endif + s->head[s->ins_h] = (Pos)str; + str++; + s->insert--; + if (s->lookahead + s->insert < MIN_MATCH) + break; + } + } + /* If the whole input has less than MIN_MATCH bytes, ins_h is garbage, + * but this is not important since only literal bytes will be emitted. + */ + + } while (s->lookahead < MIN_LOOKAHEAD && s->strm->avail_in != 0); + + /* If the WIN_INIT bytes after the end of the current data have never been + * written, then zero those bytes in order to avoid memory check reports of + * the use of uninitialized (or uninitialised as Julian writes) bytes by + * the longest match routines. Update the high water mark for the next + * time through here. WIN_INIT is set to MAX_MATCH since the longest match + * routines allow scanning to strstart + MAX_MATCH, ignoring lookahead. + */ + if (s->high_water < s->window_size) { + ulg curr = s->strstart + (ulg)(s->lookahead); + ulg init; + + if (s->high_water < curr) { + /* Previous high water mark below current data -- zero WIN_INIT + * bytes or up to end of window, whichever is less. + */ + init = s->window_size - curr; + if (init > WIN_INIT) + init = WIN_INIT; + zmemzero(s->window + curr, (unsigned)init); + s->high_water = curr + init; + } + else if (s->high_water < (ulg)curr + WIN_INIT) { + /* High water mark at or above current data, but below current data + * plus WIN_INIT -- zero out to current data plus WIN_INIT, or up + * to end of window, whichever is less. + */ + init = (ulg)curr + WIN_INIT - s->high_water; + if (init > s->window_size - s->high_water) + init = s->window_size - s->high_water; + zmemzero(s->window + s->high_water, (unsigned)init); + s->high_water += init; + } + } + + Assert((ulg)s->strstart <= s->window_size - MIN_LOOKAHEAD, + "not enough room for search"); +} + +/* =========================================================================== + * Flush the current block, with given end-of-file flag. + * IN assertion: strstart is set to the end of the current match. + */ +#define FLUSH_BLOCK_ONLY(s, last) { \ + _tr_flush_block(s, (s->block_start >= 0L ? \ + (charf *)&s->window[(unsigned)s->block_start] : \ + (charf *)Z_NULL), \ + (ulg)((long)s->strstart - s->block_start), \ + (last)); \ + s->block_start = s->strstart; \ + flush_pending(s->strm); \ + Tracev((stderr,"[FLUSH]")); \ +} + +/* Same but force premature exit if necessary. */ +#define FLUSH_BLOCK(s, last) { \ + FLUSH_BLOCK_ONLY(s, last); \ + if (s->strm->avail_out == 0) return (last) ? finish_started : need_more; \ +} + +/* =========================================================================== + * Copy without compression as much as possible from the input stream, return + * the current block state. + * This function does not insert new strings in the dictionary since + * uncompressible data is probably not useful. This function is used + * only for the level=0 compression option. + * NOTE: this function should be optimized to avoid extra copying from + * window to pending_buf. + */ +local block_state deflate_stored(deflate_state *s, int flush) +{ + /* Stored blocks are limited to 0xffff bytes, pending_buf is limited + * to pending_buf_size, and each stored block has a 5 byte header: + */ + ulg max_block_size = 0xffff; + ulg max_start; + + if (max_block_size > s->pending_buf_size - 5) { + max_block_size = s->pending_buf_size - 5; + } + + /* Copy as much as possible from input to output: */ + for (;;) { + /* Fill the window as much as possible: */ + if (s->lookahead <= 1) { + + Assert(s->strstart < s->w_size+MAX_DIST(s) || + s->block_start >= (long)s->w_size, "slide too late"); + + fill_window(s); + if (s->lookahead == 0 && flush == Z_NO_FLUSH) return need_more; + + if (s->lookahead == 0) break; /* flush the current block */ + } + Assert(s->block_start >= 0L, "block gone"); + + s->strstart += s->lookahead; + s->lookahead = 0; + + /* Emit a stored block if pending_buf will be full: */ + max_start = s->block_start + max_block_size; + if (s->strstart == 0 || (ulg)s->strstart >= max_start) { + /* strstart == 0 is possible when wraparound on 16-bit machine */ + s->lookahead = (uInt)(s->strstart - max_start); + s->strstart = (uInt)max_start; + FLUSH_BLOCK(s, 0); + } + /* Flush if we may have to slide, otherwise block_start may become + * negative and the data will be gone: + */ + if (s->strstart - (uInt)s->block_start >= MAX_DIST(s)) { + FLUSH_BLOCK(s, 0); + } + } + s->insert = 0; + if (flush == Z_FINISH) { + FLUSH_BLOCK(s, 1); + return finish_done; + } + if ((long)s->strstart > s->block_start) + FLUSH_BLOCK(s, 0); + return block_done; +} + +/* =========================================================================== + * Compress as much as possible from the input stream, return the current + * block state. + * This function does not perform lazy evaluation of matches and inserts + * new strings in the dictionary only for unmatched strings or for short + * matches. It is used only for the fast compression options. + */ +local block_state deflate_fast(deflate_state *s, int flush) +{ + IPos hash_head; /* head of the hash chain */ + int bflush; /* set if current block must be flushed */ + + for (;;) { + /* Make sure that we always have enough lookahead, except + * at the end of the input file. We need MAX_MATCH bytes + * for the next match, plus MIN_MATCH bytes to insert the + * string following the next match. + */ + if (s->lookahead < MIN_LOOKAHEAD) { + fill_window(s); + if (s->lookahead < MIN_LOOKAHEAD && flush == Z_NO_FLUSH) { + return need_more; + } + if (s->lookahead == 0) break; /* flush the current block */ + } + + /* Insert the string window[strstart .. strstart+2] in the + * dictionary, and set hash_head to the head of the hash chain: + */ + hash_head = NIL; + if (s->lookahead >= MIN_MATCH) { + INSERT_STRING(s, s->strstart, hash_head); + } + + /* Find the longest match, discarding those <= prev_length. + * At this point we have always match_length < MIN_MATCH + */ + if (hash_head != NIL && s->strstart - hash_head <= MAX_DIST(s)) { + /* To simplify the code, we prevent matches with the string + * of window index 0 (in particular we have to avoid a match + * of the string with itself at the start of the input file). + */ + s->match_length = longest_match (s, hash_head); + /* longest_match() sets match_start */ + } + if (s->match_length >= MIN_MATCH) { + check_match(s, s->strstart, s->match_start, s->match_length); + + _tr_tally_dist(s, s->strstart - s->match_start, + s->match_length - MIN_MATCH, bflush); + + s->lookahead -= s->match_length; + + /* Insert new strings in the hash table only if the match length + * is not too large. This saves time but degrades compression. + */ +#ifndef FASTEST + if (s->match_length <= s->max_insert_length && + s->lookahead >= MIN_MATCH) { + s->match_length--; /* string at strstart already in table */ + do { + s->strstart++; + INSERT_STRING(s, s->strstart, hash_head); + /* strstart never exceeds WSIZE-MAX_MATCH, so there are + * always MIN_MATCH bytes ahead. + */ + } while (--s->match_length != 0); + s->strstart++; + } else +#endif + { + s->strstart += s->match_length; + s->match_length = 0; + s->ins_h = s->window[s->strstart]; + UPDATE_HASH(s, s->ins_h, s->window[s->strstart+1]); +#if MIN_MATCH != 3 + Call UPDATE_HASH() MIN_MATCH-3 more times +#endif + /* If lookahead < MIN_MATCH, ins_h is garbage, but it does not + * matter since it will be recomputed at next deflate call. + */ + } + } else { + /* No match, output a literal byte */ + Tracevv((stderr,"%c", s->window[s->strstart])); + _tr_tally_lit (s, s->window[s->strstart], bflush); + s->lookahead--; + s->strstart++; + } + if (bflush) FLUSH_BLOCK(s, 0); + } + s->insert = s->strstart < MIN_MATCH-1 ? s->strstart : MIN_MATCH-1; + if (flush == Z_FINISH) { + FLUSH_BLOCK(s, 1); + return finish_done; + } + if (s->last_lit) + FLUSH_BLOCK(s, 0); + return block_done; +} + +#ifndef FASTEST +/* =========================================================================== + * Same as above, but achieves better compression. We use a lazy + * evaluation for matches: a match is finally adopted only if there is + * no better match at the next window position. + */ +local block_state deflate_slow(deflate_state *s, int flush) +{ + IPos hash_head; /* head of hash chain */ + int bflush; /* set if current block must be flushed */ + + /* Process the input block. */ + for (;;) { + /* Make sure that we always have enough lookahead, except + * at the end of the input file. We need MAX_MATCH bytes + * for the next match, plus MIN_MATCH bytes to insert the + * string following the next match. + */ + if (s->lookahead < MIN_LOOKAHEAD) { + fill_window(s); + if (s->lookahead < MIN_LOOKAHEAD && flush == Z_NO_FLUSH) { + return need_more; + } + if (s->lookahead == 0) break; /* flush the current block */ + } + + /* Insert the string window[strstart .. strstart+2] in the + * dictionary, and set hash_head to the head of the hash chain: + */ + hash_head = NIL; + if (s->lookahead >= MIN_MATCH) { + INSERT_STRING(s, s->strstart, hash_head); + } + + /* Find the longest match, discarding those <= prev_length. + */ + s->prev_length = s->match_length, s->prev_match = s->match_start; + s->match_length = MIN_MATCH-1; + + if (hash_head != NIL && s->prev_length < s->max_lazy_match && + s->strstart - hash_head <= MAX_DIST(s)) { + /* To simplify the code, we prevent matches with the string + * of window index 0 (in particular we have to avoid a match + * of the string with itself at the start of the input file). + */ + s->match_length = longest_match (s, hash_head); + /* longest_match() sets match_start */ + + if (s->match_length <= 5 && (s->strategy == Z_FILTERED +#if TOO_FAR <= 32767 + || (s->match_length == MIN_MATCH && + s->strstart - s->match_start > TOO_FAR) +#endif + )) { + + /* If prev_match is also MIN_MATCH, match_start is garbage + * but we will ignore the current match anyway. + */ + s->match_length = MIN_MATCH-1; + } + } + /* If there was a match at the previous step and the current + * match is not better, output the previous match: + */ + if (s->prev_length >= MIN_MATCH && s->match_length <= s->prev_length) { + uInt max_insert = s->strstart + s->lookahead - MIN_MATCH; + /* Do not insert strings in hash table beyond this. */ + + check_match(s, s->strstart-1, s->prev_match, s->prev_length); + + _tr_tally_dist(s, s->strstart -1 - s->prev_match, + s->prev_length - MIN_MATCH, bflush); + + /* Insert in hash table all strings up to the end of the match. + * strstart-1 and strstart are already inserted. If there is not + * enough lookahead, the last two strings are not inserted in + * the hash table. + */ + s->lookahead -= s->prev_length-1; + s->prev_length -= 2; + do { + if (++s->strstart <= max_insert) { + INSERT_STRING(s, s->strstart, hash_head); + } + } while (--s->prev_length != 0); + s->match_available = 0; + s->match_length = MIN_MATCH-1; + s->strstart++; + + if (bflush) FLUSH_BLOCK(s, 0); + + } else if (s->match_available) { + /* If there was no match at the previous position, output a + * single literal. If there was a match but the current match + * is longer, truncate the previous match to a single literal. + */ + Tracevv((stderr,"%c", s->window[s->strstart-1])); + _tr_tally_lit(s, s->window[s->strstart-1], bflush); + if (bflush) { + FLUSH_BLOCK_ONLY(s, 0); + } + s->strstart++; + s->lookahead--; + if (s->strm->avail_out == 0) return need_more; + } else { + /* There is no previous match to compare with, wait for + * the next step to decide. + */ + s->match_available = 1; + s->strstart++; + s->lookahead--; + } + } + Assert (flush != Z_NO_FLUSH, "no flush?"); + if (s->match_available) { + Tracevv((stderr,"%c", s->window[s->strstart-1])); + _tr_tally_lit(s, s->window[s->strstart-1], bflush); + s->match_available = 0; + } + s->insert = s->strstart < MIN_MATCH-1 ? s->strstart : MIN_MATCH-1; + if (flush == Z_FINISH) { + FLUSH_BLOCK(s, 1); + return finish_done; + } + if (s->last_lit) + FLUSH_BLOCK(s, 0); + return block_done; +} +#endif /* FASTEST */ + +/* =========================================================================== + * For Z_RLE, simply look for runs of bytes, generate matches only of distance + * one. Do not maintain a hash table. (It will be regenerated if this run of + * deflate switches away from Z_RLE.) + */ +local block_state deflate_rle(deflate_state *s, int flush) +{ + int bflush; /* set if current block must be flushed */ + uInt prev; /* byte at distance one to match */ + Bytef *scan, *strend; /* scan goes up to strend for length of run */ + + for (;;) { + /* Make sure that we always have enough lookahead, except + * at the end of the input file. We need MAX_MATCH bytes + * for the longest run, plus one for the unrolled loop. + */ + if (s->lookahead <= MAX_MATCH) { + fill_window(s); + if (s->lookahead <= MAX_MATCH && flush == Z_NO_FLUSH) { + return need_more; + } + if (s->lookahead == 0) break; /* flush the current block */ + } + + /* See how many times the previous byte repeats */ + s->match_length = 0; + if (s->lookahead >= MIN_MATCH && s->strstart > 0) { + scan = s->window + s->strstart - 1; + prev = *scan; + if (prev == *++scan && prev == *++scan && prev == *++scan) { + strend = s->window + s->strstart + MAX_MATCH; + do { + } while (prev == *++scan && prev == *++scan && + prev == *++scan && prev == *++scan && + prev == *++scan && prev == *++scan && + prev == *++scan && prev == *++scan && + scan < strend); + s->match_length = MAX_MATCH - (int)(strend - scan); + if (s->match_length > s->lookahead) + s->match_length = s->lookahead; + } + Assert(scan <= s->window+(uInt)(s->window_size-1), "wild scan"); + } + + /* Emit match if have run of MIN_MATCH or longer, else emit literal */ + if (s->match_length >= MIN_MATCH) { + check_match(s, s->strstart, s->strstart - 1, s->match_length); + + _tr_tally_dist(s, 1, s->match_length - MIN_MATCH, bflush); + + s->lookahead -= s->match_length; + s->strstart += s->match_length; + s->match_length = 0; + } else { + /* No match, output a literal byte */ + Tracevv((stderr,"%c", s->window[s->strstart])); + _tr_tally_lit (s, s->window[s->strstart], bflush); + s->lookahead--; + s->strstart++; + } + if (bflush) FLUSH_BLOCK(s, 0); + } + s->insert = 0; + if (flush == Z_FINISH) { + FLUSH_BLOCK(s, 1); + return finish_done; + } + if (s->last_lit) + FLUSH_BLOCK(s, 0); + return block_done; +} + +/* =========================================================================== + * For Z_HUFFMAN_ONLY, do not look for matches. Do not maintain a hash table. + * (It will be regenerated if this run of deflate switches away from Huffman.) + */ +local block_state deflate_huff(deflate_state *s, int flush) +{ + int bflush; /* set if current block must be flushed */ + + for (;;) { + /* Make sure that we have a literal to write. */ + if (s->lookahead == 0) { + fill_window(s); + if (s->lookahead == 0) { + if (flush == Z_NO_FLUSH) + return need_more; + break; /* flush the current block */ + } + } + + /* Output a literal byte */ + s->match_length = 0; + Tracevv((stderr,"%c", s->window[s->strstart])); + _tr_tally_lit (s, s->window[s->strstart], bflush); + s->lookahead--; + s->strstart++; + if (bflush) FLUSH_BLOCK(s, 0); + } + s->insert = 0; + if (flush == Z_FINISH) { + FLUSH_BLOCK(s, 1); + return finish_done; + } + if (s->last_lit) + FLUSH_BLOCK(s, 0); + return block_done; +} diff --git a/src/zlib/deflate.h b/src/zlib/deflate.h new file mode 100644 index 00000000..2fa941ef --- /dev/null +++ b/src/zlib/deflate.h @@ -0,0 +1,346 @@ +/* deflate.h -- internal compression state + * Copyright (C) 1995-2012 Jean-loup Gailly + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +/* @(#) $Id: deflate.h,v 1.1 2013/12/16 08:59:56 cfsmp3 Exp $ */ + +#ifndef DEFLATE_H +#define DEFLATE_H + +#include "zutil.h" + +/* define NO_GZIP when compiling if you want to disable gzip header and + trailer creation by deflate(). NO_GZIP would be used to avoid linking in + the crc code when it is not needed. For shared libraries, gzip encoding + should be left enabled. */ +#ifndef NO_GZIP +# define GZIP +#endif + +/* =========================================================================== + * Internal compression state. + */ + +#define LENGTH_CODES 29 +/* number of length codes, not counting the special END_BLOCK code */ + +#define LITERALS 256 +/* number of literal bytes 0..255 */ + +#define L_CODES (LITERALS+1+LENGTH_CODES) +/* number of Literal or Length codes, including the END_BLOCK code */ + +#define D_CODES 30 +/* number of distance codes */ + +#define BL_CODES 19 +/* number of codes used to transfer the bit lengths */ + +#define HEAP_SIZE (2*L_CODES+1) +/* maximum heap size */ + +#define MAX_BITS 15 +/* All codes must not exceed MAX_BITS bits */ + +#define Buf_size 16 +/* size of bit buffer in bi_buf */ + +#define INIT_STATE 42 +#define EXTRA_STATE 69 +#define NAME_STATE 73 +#define COMMENT_STATE 91 +#define HCRC_STATE 103 +#define BUSY_STATE 113 +#define FINISH_STATE 666 +/* Stream status */ + + +/* Data structure describing a single value and its code string. */ +typedef struct ct_data_s { + union { + ush freq; /* frequency count */ + ush code; /* bit string */ + } fc; + union { + ush dad; /* father node in Huffman tree */ + ush len; /* length of bit string */ + } dl; +} FAR ct_data; + +#define Freq fc.freq +#define Code fc.code +#define Dad dl.dad +#define Len dl.len + +typedef struct static_tree_desc_s static_tree_desc; + +typedef struct tree_desc_s { + ct_data *dyn_tree; /* the dynamic tree */ + int max_code; /* largest code with non zero frequency */ + static_tree_desc *stat_desc; /* the corresponding static tree */ +} FAR tree_desc; + +typedef ush Pos; +typedef Pos FAR Posf; +typedef unsigned IPos; + +/* A Pos is an index in the character window. We use short instead of int to + * save space in the various tables. IPos is used only for parameter passing. + */ + +typedef struct internal_state { + z_streamp strm; /* pointer back to this zlib stream */ + int status; /* as the name implies */ + Bytef *pending_buf; /* output still pending */ + ulg pending_buf_size; /* size of pending_buf */ + Bytef *pending_out; /* next pending byte to output to the stream */ + uInt pending; /* nb of bytes in the pending buffer */ + int wrap; /* bit 0 true for zlib, bit 1 true for gzip */ + gz_headerp gzhead; /* gzip header information to write */ + uInt gzindex; /* where in extra, name, or comment */ + Byte method; /* can only be DEFLATED */ + int last_flush; /* value of flush param for previous deflate call */ + + /* used by deflate.c: */ + + uInt w_size; /* LZ77 window size (32K by default) */ + uInt w_bits; /* log2(w_size) (8..16) */ + uInt w_mask; /* w_size - 1 */ + + Bytef *window; + /* Sliding window. Input bytes are read into the second half of the window, + * and move to the first half later to keep a dictionary of at least wSize + * bytes. With this organization, matches are limited to a distance of + * wSize-MAX_MATCH bytes, but this ensures that IO is always + * performed with a length multiple of the block size. Also, it limits + * the window size to 64K, which is quite useful on MSDOS. + * To do: use the user input buffer as sliding window. + */ + + ulg window_size; + /* Actual size of window: 2*wSize, except when the user input buffer + * is directly used as sliding window. + */ + + Posf *prev; + /* Link to older string with same hash index. To limit the size of this + * array to 64K, this link is maintained only for the last 32K strings. + * An index in this array is thus a window index modulo 32K. + */ + + Posf *head; /* Heads of the hash chains or NIL. */ + + uInt ins_h; /* hash index of string to be inserted */ + uInt hash_size; /* number of elements in hash table */ + uInt hash_bits; /* log2(hash_size) */ + uInt hash_mask; /* hash_size-1 */ + + uInt hash_shift; + /* Number of bits by which ins_h must be shifted at each input + * step. It must be such that after MIN_MATCH steps, the oldest + * byte no longer takes part in the hash key, that is: + * hash_shift * MIN_MATCH >= hash_bits + */ + + long block_start; + /* Window position at the beginning of the current output block. Gets + * negative when the window is moved backwards. + */ + + uInt match_length; /* length of best match */ + IPos prev_match; /* previous match */ + int match_available; /* set if previous match exists */ + uInt strstart; /* start of string to insert */ + uInt match_start; /* start of matching string */ + uInt lookahead; /* number of valid bytes ahead in window */ + + uInt prev_length; + /* Length of the best match at previous step. Matches not greater than this + * are discarded. This is used in the lazy match evaluation. + */ + + uInt max_chain_length; + /* To speed up deflation, hash chains are never searched beyond this + * length. A higher limit improves compression ratio but degrades the + * speed. + */ + + uInt max_lazy_match; + /* Attempt to find a better match only when the current match is strictly + * smaller than this value. This mechanism is used only for compression + * levels >= 4. + */ +# define max_insert_length max_lazy_match + /* Insert new strings in the hash table only if the match length is not + * greater than this length. This saves time but degrades compression. + * max_insert_length is used only for compression levels <= 3. + */ + + int level; /* compression level (1..9) */ + int strategy; /* favor or force Huffman coding*/ + + uInt good_match; + /* Use a faster search when the previous match is longer than this */ + + int nice_match; /* Stop searching when current match exceeds this */ + + /* used by trees.c: */ + /* Didn't use ct_data typedef below to suppress compiler warning */ + struct ct_data_s dyn_ltree[HEAP_SIZE]; /* literal and length tree */ + struct ct_data_s dyn_dtree[2*D_CODES+1]; /* distance tree */ + struct ct_data_s bl_tree[2*BL_CODES+1]; /* Huffman tree for bit lengths */ + + struct tree_desc_s l_desc; /* desc. for literal tree */ + struct tree_desc_s d_desc; /* desc. for distance tree */ + struct tree_desc_s bl_desc; /* desc. for bit length tree */ + + ush bl_count[MAX_BITS+1]; + /* number of codes at each bit length for an optimal tree */ + + int heap[2*L_CODES+1]; /* heap used to build the Huffman trees */ + int heap_len; /* number of elements in the heap */ + int heap_max; /* element of largest frequency */ + /* The sons of heap[n] are heap[2*n] and heap[2*n+1]. heap[0] is not used. + * The same heap array is used to build all trees. + */ + + uch depth[2*L_CODES+1]; + /* Depth of each subtree used as tie breaker for trees of equal frequency + */ + + uchf *l_buf; /* buffer for literals or lengths */ + + uInt lit_bufsize; + /* Size of match buffer for literals/lengths. There are 4 reasons for + * limiting lit_bufsize to 64K: + * - frequencies can be kept in 16 bit counters + * - if compression is not successful for the first block, all input + * data is still in the window so we can still emit a stored block even + * when input comes from standard input. (This can also be done for + * all blocks if lit_bufsize is not greater than 32K.) + * - if compression is not successful for a file smaller than 64K, we can + * even emit a stored file instead of a stored block (saving 5 bytes). + * This is applicable only for zip (not gzip or zlib). + * - creating new Huffman trees less frequently may not provide fast + * adaptation to changes in the input data statistics. (Take for + * example a binary file with poorly compressible code followed by + * a highly compressible string table.) Smaller buffer sizes give + * fast adaptation but have of course the overhead of transmitting + * trees more frequently. + * - I can't count above 4 + */ + + uInt last_lit; /* running index in l_buf */ + + ushf *d_buf; + /* Buffer for distances. To simplify the code, d_buf and l_buf have + * the same number of elements. To use different lengths, an extra flag + * array would be necessary. + */ + + ulg opt_len; /* bit length of current block with optimal trees */ + ulg static_len; /* bit length of current block with static trees */ + uInt matches; /* number of string matches in current block */ + uInt insert; /* bytes at end of window left to insert */ + +#ifdef DEBUG + ulg compressed_len; /* total bit length of compressed file mod 2^32 */ + ulg bits_sent; /* bit length of compressed data sent mod 2^32 */ +#endif + + ush bi_buf; + /* Output buffer. bits are inserted starting at the bottom (least + * significant bits). + */ + int bi_valid; + /* Number of valid bits in bi_buf. All bits above the last valid bit + * are always zero. + */ + + ulg high_water; + /* High water mark offset in window for initialized bytes -- bytes above + * this are set to zero in order to avoid memory check warnings when + * longest match routines access bytes past the input. This is then + * updated to the new high water mark. + */ + +} FAR deflate_state; + +/* Output a byte on the stream. + * IN assertion: there is enough room in pending_buf. + */ +#define put_byte(s, c) {s->pending_buf[s->pending++] = (c);} + + +#define MIN_LOOKAHEAD (MAX_MATCH+MIN_MATCH+1) +/* Minimum amount of lookahead, except at the end of the input file. + * See deflate.c for comments about the MIN_MATCH+1. + */ + +#define MAX_DIST(s) ((s)->w_size-MIN_LOOKAHEAD) +/* In order to simplify the code, particularly on 16 bit machines, match + * distances are limited to MAX_DIST instead of WSIZE. + */ + +#define WIN_INIT MAX_MATCH +/* Number of bytes after end of data in window to initialize in order to avoid + memory checker errors from longest match routines */ + + /* in trees.c */ +void ZLIB_INTERNAL _tr_init OF((deflate_state *s)); +int ZLIB_INTERNAL _tr_tally OF((deflate_state *s, unsigned dist, unsigned lc)); +void ZLIB_INTERNAL _tr_flush_block OF((deflate_state *s, charf *buf, + ulg stored_len, int last)); +void ZLIB_INTERNAL _tr_flush_bits OF((deflate_state *s)); +void ZLIB_INTERNAL _tr_align OF((deflate_state *s)); +void ZLIB_INTERNAL _tr_stored_block OF((deflate_state *s, charf *buf, + ulg stored_len, int last)); + +#define d_code(dist) \ + ((dist) < 256 ? _dist_code[dist] : _dist_code[256+((dist)>>7)]) +/* Mapping from a distance to a distance code. dist is the distance - 1 and + * must not have side effects. _dist_code[256] and _dist_code[257] are never + * used. + */ + +#ifndef DEBUG +/* Inline versions of _tr_tally for speed: */ + +#if defined(GEN_TREES_H) || !defined(STDC) + extern uch ZLIB_INTERNAL _length_code[]; + extern uch ZLIB_INTERNAL _dist_code[]; +#else + extern const uch ZLIB_INTERNAL _length_code[]; + extern const uch ZLIB_INTERNAL _dist_code[]; +#endif + +# define _tr_tally_lit(s, c, flush) \ + { uch cc = (c); \ + s->d_buf[s->last_lit] = 0; \ + s->l_buf[s->last_lit++] = cc; \ + s->dyn_ltree[cc].Freq++; \ + flush = (s->last_lit == s->lit_bufsize-1); \ + } +# define _tr_tally_dist(s, distance, length, flush) \ + { uch len = (length); \ + ush dist = (distance); \ + s->d_buf[s->last_lit] = dist; \ + s->l_buf[s->last_lit++] = len; \ + dist--; \ + s->dyn_ltree[_length_code[len]+LITERALS+1].Freq++; \ + s->dyn_dtree[d_code(dist)].Freq++; \ + flush = (s->last_lit == s->lit_bufsize-1); \ + } +#else +# define _tr_tally_lit(s, c, flush) flush = _tr_tally(s, 0, c) +# define _tr_tally_dist(s, distance, length, flush) \ + flush = _tr_tally(s, distance, length) +#endif + +#endif /* DEFLATE_H */ diff --git a/src/zlib/gzguts.h b/src/zlib/gzguts.h new file mode 100644 index 00000000..d87659d0 --- /dev/null +++ b/src/zlib/gzguts.h @@ -0,0 +1,209 @@ +/* gzguts.h -- zlib internal header definitions for gz* operations + * Copyright (C) 2004, 2005, 2010, 2011, 2012, 2013 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +#ifdef _LARGEFILE64_SOURCE +# ifndef _LARGEFILE_SOURCE +# define _LARGEFILE_SOURCE 1 +# endif +# ifdef _FILE_OFFSET_BITS +# undef _FILE_OFFSET_BITS +# endif +#endif + +#ifdef HAVE_HIDDEN +# define ZLIB_INTERNAL __attribute__((visibility ("hidden"))) +#else +# define ZLIB_INTERNAL +#endif + +#include +#include "zlib.h" +#ifdef STDC +# include +# include +# include +#endif +#include + +#ifdef _WIN32 +# include +#endif + +#if defined(__TURBOC__) || defined(_MSC_VER) || defined(_WIN32) +# include +#endif + +#ifdef WINAPI_FAMILY +# define open _open +# define read _read +# define write _write +# define close _close +#endif + +#ifdef NO_DEFLATE /* for compatibility with old definition */ +# define NO_GZCOMPRESS +#endif + +#if defined(STDC99) || (defined(__TURBOC__) && __TURBOC__ >= 0x550) +# ifndef HAVE_VSNPRINTF +# define HAVE_VSNPRINTF +# endif +#endif + +#if defined(__CYGWIN__) +# ifndef HAVE_VSNPRINTF +# define HAVE_VSNPRINTF +# endif +#endif + +#if defined(MSDOS) && defined(__BORLANDC__) && (BORLANDC > 0x410) +# ifndef HAVE_VSNPRINTF +# define HAVE_VSNPRINTF +# endif +#endif + +#ifndef HAVE_VSNPRINTF +# ifdef MSDOS +/* vsnprintf may exist on some MS-DOS compilers (DJGPP?), + but for now we just assume it doesn't. */ +# define NO_vsnprintf +# endif +# ifdef __TURBOC__ +# define NO_vsnprintf +# endif +# ifdef WIN32 +/* In Win32, vsnprintf is available as the "non-ANSI" _vsnprintf. */ +# if !defined(vsnprintf) && !defined(NO_vsnprintf) +# if !defined(_MSC_VER) || ( defined(_MSC_VER) && _MSC_VER < 1500 ) +# define vsnprintf _vsnprintf +# endif +# endif +# endif +# ifdef __SASC +# define NO_vsnprintf +# endif +# ifdef VMS +# define NO_vsnprintf +# endif +# ifdef __OS400__ +# define NO_vsnprintf +# endif +# ifdef __MVS__ +# define NO_vsnprintf +# endif +#endif + +/* unlike snprintf (which is required in C99, yet still not supported by + Microsoft more than a decade later!), _snprintf does not guarantee null + termination of the result -- however this is only used in gzlib.c where + the result is assured to fit in the space provided */ +#ifdef _MSC_VER +# define snprintf _snprintf +#endif + +#ifndef local +# define local static +#endif +/* compile with -Dlocal if your debugger can't find static symbols */ + +/* gz* functions always use library allocation functions */ +#ifndef STDC + extern voidp malloc OF((uInt size)); + extern void free OF((voidpf ptr)); +#endif + +/* get errno and strerror definition */ +#if defined UNDER_CE +# include +# define zstrerror() gz_strwinerror((DWORD)GetLastError()) +#else +# ifndef NO_STRERROR +# include +# define zstrerror() strerror(errno) +# else +# define zstrerror() "stdio error (consult errno)" +# endif +#endif + +/* provide prototypes for these when building zlib without LFS */ +#if !defined(_LARGEFILE64_SOURCE) || _LFS64_LARGEFILE-0 == 0 + ZEXTERN gzFile ZEXPORT gzopen64 OF((const char *, const char *)); + ZEXTERN z_off64_t ZEXPORT gzseek64 OF((gzFile, z_off64_t, int)); + ZEXTERN z_off64_t ZEXPORT gztell64 OF((gzFile)); + ZEXTERN z_off64_t ZEXPORT gzoffset64 OF((gzFile)); +#endif + +/* default memLevel */ +#if MAX_MEM_LEVEL >= 8 +# define DEF_MEM_LEVEL 8 +#else +# define DEF_MEM_LEVEL MAX_MEM_LEVEL +#endif + +/* default i/o buffer size -- double this for output when reading (this and + twice this must be able to fit in an unsigned type) */ +#define GZBUFSIZE 8192 + +/* gzip modes, also provide a little integrity check on the passed structure */ +#define GZ_NONE 0 +#define GZ_READ 7247 +#define GZ_WRITE 31153 +#define GZ_APPEND 1 /* mode set to GZ_WRITE after the file is opened */ + +/* values for gz_state how */ +#define LOOK 0 /* look for a gzip header */ +#define COPY 1 /* copy input directly */ +#define GZIP 2 /* decompress a gzip stream */ + +/* internal gzip file state data structure */ +typedef struct { + /* exposed contents for gzgetc() macro */ + struct gzFile_s x; /* "x" for exposed */ + /* x.have: number of bytes available at x.next */ + /* x.next: next output data to deliver or write */ + /* x.pos: current position in uncompressed data */ + /* used for both reading and writing */ + int mode; /* see gzip modes above */ + int fd; /* file descriptor */ + char *path; /* path or fd for error messages */ + unsigned size; /* buffer size, zero if not allocated yet */ + unsigned want; /* requested buffer size, default is GZBUFSIZE */ + unsigned char *in; /* input buffer */ + unsigned char *out; /* output buffer (double-sized when reading) */ + int direct; /* 0 if processing gzip, 1 if transparent */ + /* just for reading */ + int how; /* 0: get header, 1: copy, 2: decompress */ + z_off64_t start; /* where the gzip data started, for rewinding */ + int eof; /* true if end of input file reached */ + int past; /* true if read requested past end */ + /* just for writing */ + int level; /* compression level */ + int strategy; /* compression strategy */ + /* seek request */ + z_off64_t skip; /* amount to skip (already rewound if backwards) */ + int seek; /* true if seek request pending */ + /* error information */ + int err; /* error code */ + char *msg; /* error message */ + /* zlib inflate or deflate stream */ + z_stream strm; /* stream structure in-place (not a pointer) */ +} gz_state; +typedef gz_state FAR *gz_statep; + +/* shared functions */ +void ZLIB_INTERNAL gz_error OF((gz_statep, int, const char *)); +#if defined UNDER_CE +char ZLIB_INTERNAL *gz_strwinerror OF((DWORD error)); +#endif + +/* GT_OFF(x), where x is an unsigned value, is true if x > maximum z_off64_t + value -- needed when comparing unsigned to z_off64_t, which is signed + (possible z_off64_t types off_t, off64_t, and long are all signed) */ +#ifdef INT_MAX +# define GT_OFF(x) (sizeof(int) == sizeof(z_off64_t) && (x) > INT_MAX) +#else +unsigned ZLIB_INTERNAL gz_intmax OF((void)); +# define GT_OFF(x) (sizeof(int) == sizeof(z_off64_t) && (x) > gz_intmax()) +#endif diff --git a/src/zlib/inffast.c b/src/zlib/inffast.c new file mode 100644 index 00000000..6c1e9de3 --- /dev/null +++ b/src/zlib/inffast.c @@ -0,0 +1,338 @@ +/* inffast.c -- fast decoding + * Copyright (C) 1995-2008, 2010, 2013 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +#include "zutil.h" +#include "inftrees.h" +#include "inflate.h" +#include "inffast.h" + +#ifndef ASMINF + +/* Allow machine dependent optimization for post-increment or pre-increment. + Based on testing to date, + Pre-increment preferred for: + - PowerPC G3 (Adler) + - MIPS R5000 (Randers-Pehrson) + Post-increment preferred for: + - none + No measurable difference: + - Pentium III (Anderson) + - M68060 (Nikl) + */ +#ifdef POSTINC +# define OFF 0 +# define PUP(a) *(a)++ +#else +# define OFF 1 +# define PUP(a) *++(a) +#endif + +/* + Decode literal, length, and distance codes and write out the resulting + literal and match bytes until either not enough input or output is + available, an end-of-block is encountered, or a data error is encountered. + When large enough input and output buffers are supplied to inflate(), for + example, a 16K input buffer and a 64K output buffer, more than 95% of the + inflate execution time is spent in this routine. + + Entry assumptions: + + state->mode == LEN + strm->avail_in >= 6 + strm->avail_out >= 258 + start >= strm->avail_out + state->bits < 8 + + On return, state->mode is one of: + + LEN -- ran out of enough output space or enough available input + TYPE -- reached end of block code, inflate() to interpret next block + BAD -- error in block data + + Notes: + + - The maximum input bits used by a length/distance pair is 15 bits for the + length code, 5 bits for the length extra, 15 bits for the distance code, + and 13 bits for the distance extra. This totals 48 bits, or six bytes. + Therefore if strm->avail_in >= 6, then there is enough input to avoid + checking for available input while decoding. + + - The maximum bytes that a single length/distance pair can output is 258 + bytes, which is the maximum length that can be coded. inflate_fast() + requires strm->avail_out >= 258 for each loop to avoid checking for + output space. + */ +void ZLIB_INTERNAL inflate_fast(z_streamp strm, unsigned start) +{ + struct inflate_state FAR *state; + z_const unsigned char FAR *in; /* local strm->next_in */ + z_const unsigned char FAR *last; /* have enough input while in < last */ + unsigned char FAR *out; /* local strm->next_out */ + unsigned char FAR *beg; /* inflate()'s initial strm->next_out */ + unsigned char FAR *end; /* while out < end, enough space available */ +#ifdef INFLATE_STRICT + unsigned dmax; /* maximum distance from zlib header */ +#endif + unsigned wsize; /* window size or zero if not using window */ + unsigned whave; /* valid bytes in the window */ + unsigned wnext; /* window write index */ + unsigned char FAR *window; /* allocated sliding window, if wsize != 0 */ + unsigned long hold; /* local strm->hold */ + unsigned bits; /* local strm->bits */ + code const FAR *lcode; /* local strm->lencode */ + code const FAR *dcode; /* local strm->distcode */ + unsigned lmask; /* mask for first level of length codes */ + unsigned dmask; /* mask for first level of distance codes */ + code here; /* retrieved table entry */ + unsigned op; /* code bits, operation, extra bits, or */ + /* window position, window bytes to copy */ + unsigned len; /* match length, unused bytes */ + unsigned dist; /* match distance */ + unsigned char FAR *from; /* where to copy match from */ + + /* copy state to local variables */ + state = (struct inflate_state FAR *)strm->state; + in = strm->next_in - OFF; + last = in + (strm->avail_in - 5); + out = strm->next_out - OFF; + beg = out - (start - strm->avail_out); + end = out + (strm->avail_out - 257); +#ifdef INFLATE_STRICT + dmax = state->dmax; +#endif + wsize = state->wsize; + whave = state->whave; + wnext = state->wnext; + window = state->window; + hold = state->hold; + bits = state->bits; + lcode = state->lencode; + dcode = state->distcode; + lmask = (1U << state->lenbits) - 1; + dmask = (1U << state->distbits) - 1; + + /* decode literals and length/distances until end-of-block or not enough + input data or output space */ + do { + if (bits < 15) { + hold += (unsigned long)(PUP(in)) << bits; + bits += 8; + hold += (unsigned long)(PUP(in)) << bits; + bits += 8; + } + here = lcode[hold & lmask]; + dolen: + op = (unsigned)(here.bits); + hold >>= op; + bits -= op; + op = (unsigned)(here.op); + if (op == 0) { /* literal */ + Tracevv((stderr, here.val >= 0x20 && here.val < 0x7f ? + "inflate: literal '%c'\n" : + "inflate: literal 0x%02x\n", here.val)); + PUP(out) = (unsigned char)(here.val); + } + else if (op & 16) { /* length base */ + len = (unsigned)(here.val); + op &= 15; /* number of extra bits */ + if (op) { + if (bits < op) { + hold += (unsigned long)(PUP(in)) << bits; + bits += 8; + } + len += (unsigned)hold & ((1U << op) - 1); + hold >>= op; + bits -= op; + } + Tracevv((stderr, "inflate: length %u\n", len)); + if (bits < 15) { + hold += (unsigned long)(PUP(in)) << bits; + bits += 8; + hold += (unsigned long)(PUP(in)) << bits; + bits += 8; + } + here = dcode[hold & dmask]; + dodist: + op = (unsigned)(here.bits); + hold >>= op; + bits -= op; + op = (unsigned)(here.op); + if (op & 16) { /* distance base */ + dist = (unsigned)(here.val); + op &= 15; /* number of extra bits */ + if (bits < op) { + hold += (unsigned long)(PUP(in)) << bits; + bits += 8; + if (bits < op) { + hold += (unsigned long)(PUP(in)) << bits; + bits += 8; + } + } + dist += (unsigned)hold & ((1U << op) - 1); +#ifdef INFLATE_STRICT + if (dist > dmax) { + strm->msg = (char *)"invalid distance too far back"; + state->mode = BAD; + break; + } +#endif + hold >>= op; + bits -= op; + Tracevv((stderr, "inflate: distance %u\n", dist)); + op = (unsigned)(out - beg); /* max distance in output */ + if (dist > op) { /* see if copy from window */ + op = dist - op; /* distance back in window */ + if (op > whave) { + if (state->sane) { + strm->msg = + (char *)"invalid distance too far back"; + state->mode = BAD; + break; + } +#ifdef INFLATE_ALLOW_INVALID_DISTANCE_TOOFAR_ARRR + if (len <= op - whave) { + do { + PUP(out) = 0; + } while (--len); + continue; + } + len -= op - whave; + do { + PUP(out) = 0; + } while (--op > whave); + if (op == 0) { + from = out - dist; + do { + PUP(out) = PUP(from); + } while (--len); + continue; + } +#endif + } + from = window - OFF; + if (wnext == 0) { /* very common case */ + from += wsize - op; + if (op < len) { /* some from window */ + len -= op; + do { + PUP(out) = PUP(from); + } while (--op); + from = out - dist; /* rest from output */ + } + } + else if (wnext < op) { /* wrap around window */ + from += wsize + wnext - op; + op -= wnext; + if (op < len) { /* some from end of window */ + len -= op; + do { + PUP(out) = PUP(from); + } while (--op); + from = window - OFF; + if (wnext < len) { /* some from start of window */ + op = wnext; + len -= op; + do { + PUP(out) = PUP(from); + } while (--op); + from = out - dist; /* rest from output */ + } + } + } + else { /* contiguous in window */ + from += wnext - op; + if (op < len) { /* some from window */ + len -= op; + do { + PUP(out) = PUP(from); + } while (--op); + from = out - dist; /* rest from output */ + } + } + while (len > 2) { + PUP(out) = PUP(from); + PUP(out) = PUP(from); + PUP(out) = PUP(from); + len -= 3; + } + if (len) { + PUP(out) = PUP(from); + if (len > 1) + PUP(out) = PUP(from); + } + } + else { + from = out - dist; /* copy direct from output */ + do { /* minimum length is three */ + PUP(out) = PUP(from); + PUP(out) = PUP(from); + PUP(out) = PUP(from); + len -= 3; + } while (len > 2); + if (len) { + PUP(out) = PUP(from); + if (len > 1) + PUP(out) = PUP(from); + } + } + } + else if ((op & 64) == 0) { /* 2nd level distance code */ + here = dcode[here.val + (hold & ((1U << op) - 1))]; + goto dodist; + } + else { + strm->msg = (char *)"invalid distance code"; + state->mode = BAD; + break; + } + } + else if ((op & 64) == 0) { /* 2nd level length code */ + here = lcode[here.val + (hold & ((1U << op) - 1))]; + goto dolen; + } + else if (op & 32) { /* end-of-block */ + Tracevv((stderr, "inflate: end of block\n")); + state->mode = TYPE; + break; + } + else { + strm->msg = (char *)"invalid literal/length code"; + state->mode = BAD; + break; + } + } while (in < last && out < end); + + /* return unused bytes (on entry, bits < 8, so in won't go too far back) */ + len = bits >> 3; + in -= len; + bits -= len << 3; + hold &= (1U << bits) - 1; + + /* update state and return */ + strm->next_in = in + OFF; + strm->next_out = out + OFF; + strm->avail_in = (unsigned)(in < last ? 5 + (last - in) : 5 - (in - last)); + strm->avail_out = (unsigned)(out < end ? + 257 + (end - out) : 257 - (out - end)); + state->hold = hold; + state->bits = bits; + return; +} + +/* + inflate_fast() speedups that turned out slower (on a PowerPC G3 750CXe): + - Using bit fields for code structure + - Different op definition to avoid & for extra bits (do & for table bits) + - Three separate decoding do-loops for direct, window, and wnext == 0 + - Special case for distance > 1 copies to do overlapped load and store copy + - Explicit branch predictions (based on measured branch probabilities) + - Deferring match copy and interspersed it with decoding subsequent codes + - Swapping literal/length else + - Swapping window/direct else + - Larger unrolled copy loops (three is about right) + - Moving len -= 3 statement into middle of loop + */ + +#endif /* !ASMINF */ diff --git a/src/zlib/inffast.h b/src/zlib/inffast.h new file mode 100644 index 00000000..e5c1aa4c --- /dev/null +++ b/src/zlib/inffast.h @@ -0,0 +1,11 @@ +/* inffast.h -- header to use inffast.c + * Copyright (C) 1995-2003, 2010 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +void ZLIB_INTERNAL inflate_fast OF((z_streamp strm, unsigned start)); diff --git a/src/zlib/inffixed.h b/src/zlib/inffixed.h new file mode 100644 index 00000000..d6283277 --- /dev/null +++ b/src/zlib/inffixed.h @@ -0,0 +1,94 @@ + /* inffixed.h -- table for decoding fixed codes + * Generated automatically by makefixed(). + */ + + /* WARNING: this file should *not* be used by applications. + It is part of the implementation of this library and is + subject to change. Applications should only use zlib.h. + */ + + static const code lenfix[512] = { + {96,7,0},{0,8,80},{0,8,16},{20,8,115},{18,7,31},{0,8,112},{0,8,48}, + {0,9,192},{16,7,10},{0,8,96},{0,8,32},{0,9,160},{0,8,0},{0,8,128}, + {0,8,64},{0,9,224},{16,7,6},{0,8,88},{0,8,24},{0,9,144},{19,7,59}, + {0,8,120},{0,8,56},{0,9,208},{17,7,17},{0,8,104},{0,8,40},{0,9,176}, + {0,8,8},{0,8,136},{0,8,72},{0,9,240},{16,7,4},{0,8,84},{0,8,20}, + {21,8,227},{19,7,43},{0,8,116},{0,8,52},{0,9,200},{17,7,13},{0,8,100}, + {0,8,36},{0,9,168},{0,8,4},{0,8,132},{0,8,68},{0,9,232},{16,7,8}, + {0,8,92},{0,8,28},{0,9,152},{20,7,83},{0,8,124},{0,8,60},{0,9,216}, + {18,7,23},{0,8,108},{0,8,44},{0,9,184},{0,8,12},{0,8,140},{0,8,76}, + {0,9,248},{16,7,3},{0,8,82},{0,8,18},{21,8,163},{19,7,35},{0,8,114}, + {0,8,50},{0,9,196},{17,7,11},{0,8,98},{0,8,34},{0,9,164},{0,8,2}, + {0,8,130},{0,8,66},{0,9,228},{16,7,7},{0,8,90},{0,8,26},{0,9,148}, + {20,7,67},{0,8,122},{0,8,58},{0,9,212},{18,7,19},{0,8,106},{0,8,42}, + {0,9,180},{0,8,10},{0,8,138},{0,8,74},{0,9,244},{16,7,5},{0,8,86}, + {0,8,22},{64,8,0},{19,7,51},{0,8,118},{0,8,54},{0,9,204},{17,7,15}, + {0,8,102},{0,8,38},{0,9,172},{0,8,6},{0,8,134},{0,8,70},{0,9,236}, + {16,7,9},{0,8,94},{0,8,30},{0,9,156},{20,7,99},{0,8,126},{0,8,62}, + {0,9,220},{18,7,27},{0,8,110},{0,8,46},{0,9,188},{0,8,14},{0,8,142}, + {0,8,78},{0,9,252},{96,7,0},{0,8,81},{0,8,17},{21,8,131},{18,7,31}, + {0,8,113},{0,8,49},{0,9,194},{16,7,10},{0,8,97},{0,8,33},{0,9,162}, + {0,8,1},{0,8,129},{0,8,65},{0,9,226},{16,7,6},{0,8,89},{0,8,25}, + {0,9,146},{19,7,59},{0,8,121},{0,8,57},{0,9,210},{17,7,17},{0,8,105}, + {0,8,41},{0,9,178},{0,8,9},{0,8,137},{0,8,73},{0,9,242},{16,7,4}, + {0,8,85},{0,8,21},{16,8,258},{19,7,43},{0,8,117},{0,8,53},{0,9,202}, + {17,7,13},{0,8,101},{0,8,37},{0,9,170},{0,8,5},{0,8,133},{0,8,69}, + {0,9,234},{16,7,8},{0,8,93},{0,8,29},{0,9,154},{20,7,83},{0,8,125}, + {0,8,61},{0,9,218},{18,7,23},{0,8,109},{0,8,45},{0,9,186},{0,8,13}, + {0,8,141},{0,8,77},{0,9,250},{16,7,3},{0,8,83},{0,8,19},{21,8,195}, + {19,7,35},{0,8,115},{0,8,51},{0,9,198},{17,7,11},{0,8,99},{0,8,35}, + {0,9,166},{0,8,3},{0,8,131},{0,8,67},{0,9,230},{16,7,7},{0,8,91}, + {0,8,27},{0,9,150},{20,7,67},{0,8,123},{0,8,59},{0,9,214},{18,7,19}, + {0,8,107},{0,8,43},{0,9,182},{0,8,11},{0,8,139},{0,8,75},{0,9,246}, + {16,7,5},{0,8,87},{0,8,23},{64,8,0},{19,7,51},{0,8,119},{0,8,55}, + {0,9,206},{17,7,15},{0,8,103},{0,8,39},{0,9,174},{0,8,7},{0,8,135}, + {0,8,71},{0,9,238},{16,7,9},{0,8,95},{0,8,31},{0,9,158},{20,7,99}, + {0,8,127},{0,8,63},{0,9,222},{18,7,27},{0,8,111},{0,8,47},{0,9,190}, + {0,8,15},{0,8,143},{0,8,79},{0,9,254},{96,7,0},{0,8,80},{0,8,16}, + {20,8,115},{18,7,31},{0,8,112},{0,8,48},{0,9,193},{16,7,10},{0,8,96}, + {0,8,32},{0,9,161},{0,8,0},{0,8,128},{0,8,64},{0,9,225},{16,7,6}, + {0,8,88},{0,8,24},{0,9,145},{19,7,59},{0,8,120},{0,8,56},{0,9,209}, + {17,7,17},{0,8,104},{0,8,40},{0,9,177},{0,8,8},{0,8,136},{0,8,72}, + {0,9,241},{16,7,4},{0,8,84},{0,8,20},{21,8,227},{19,7,43},{0,8,116}, + {0,8,52},{0,9,201},{17,7,13},{0,8,100},{0,8,36},{0,9,169},{0,8,4}, + {0,8,132},{0,8,68},{0,9,233},{16,7,8},{0,8,92},{0,8,28},{0,9,153}, + {20,7,83},{0,8,124},{0,8,60},{0,9,217},{18,7,23},{0,8,108},{0,8,44}, + {0,9,185},{0,8,12},{0,8,140},{0,8,76},{0,9,249},{16,7,3},{0,8,82}, + {0,8,18},{21,8,163},{19,7,35},{0,8,114},{0,8,50},{0,9,197},{17,7,11}, + {0,8,98},{0,8,34},{0,9,165},{0,8,2},{0,8,130},{0,8,66},{0,9,229}, + {16,7,7},{0,8,90},{0,8,26},{0,9,149},{20,7,67},{0,8,122},{0,8,58}, + {0,9,213},{18,7,19},{0,8,106},{0,8,42},{0,9,181},{0,8,10},{0,8,138}, + {0,8,74},{0,9,245},{16,7,5},{0,8,86},{0,8,22},{64,8,0},{19,7,51}, + {0,8,118},{0,8,54},{0,9,205},{17,7,15},{0,8,102},{0,8,38},{0,9,173}, + {0,8,6},{0,8,134},{0,8,70},{0,9,237},{16,7,9},{0,8,94},{0,8,30}, + {0,9,157},{20,7,99},{0,8,126},{0,8,62},{0,9,221},{18,7,27},{0,8,110}, + {0,8,46},{0,9,189},{0,8,14},{0,8,142},{0,8,78},{0,9,253},{96,7,0}, + {0,8,81},{0,8,17},{21,8,131},{18,7,31},{0,8,113},{0,8,49},{0,9,195}, + {16,7,10},{0,8,97},{0,8,33},{0,9,163},{0,8,1},{0,8,129},{0,8,65}, + {0,9,227},{16,7,6},{0,8,89},{0,8,25},{0,9,147},{19,7,59},{0,8,121}, + {0,8,57},{0,9,211},{17,7,17},{0,8,105},{0,8,41},{0,9,179},{0,8,9}, + {0,8,137},{0,8,73},{0,9,243},{16,7,4},{0,8,85},{0,8,21},{16,8,258}, + {19,7,43},{0,8,117},{0,8,53},{0,9,203},{17,7,13},{0,8,101},{0,8,37}, + {0,9,171},{0,8,5},{0,8,133},{0,8,69},{0,9,235},{16,7,8},{0,8,93}, + {0,8,29},{0,9,155},{20,7,83},{0,8,125},{0,8,61},{0,9,219},{18,7,23}, + {0,8,109},{0,8,45},{0,9,187},{0,8,13},{0,8,141},{0,8,77},{0,9,251}, + {16,7,3},{0,8,83},{0,8,19},{21,8,195},{19,7,35},{0,8,115},{0,8,51}, + {0,9,199},{17,7,11},{0,8,99},{0,8,35},{0,9,167},{0,8,3},{0,8,131}, + {0,8,67},{0,9,231},{16,7,7},{0,8,91},{0,8,27},{0,9,151},{20,7,67}, + {0,8,123},{0,8,59},{0,9,215},{18,7,19},{0,8,107},{0,8,43},{0,9,183}, + {0,8,11},{0,8,139},{0,8,75},{0,9,247},{16,7,5},{0,8,87},{0,8,23}, + {64,8,0},{19,7,51},{0,8,119},{0,8,55},{0,9,207},{17,7,15},{0,8,103}, + {0,8,39},{0,9,175},{0,8,7},{0,8,135},{0,8,71},{0,9,239},{16,7,9}, + {0,8,95},{0,8,31},{0,9,159},{20,7,99},{0,8,127},{0,8,63},{0,9,223}, + {18,7,27},{0,8,111},{0,8,47},{0,9,191},{0,8,15},{0,8,143},{0,8,79}, + {0,9,255} + }; + + static const code distfix[32] = { + {16,5,1},{23,5,257},{19,5,17},{27,5,4097},{17,5,5},{25,5,1025}, + {21,5,65},{29,5,16385},{16,5,3},{24,5,513},{20,5,33},{28,5,8193}, + {18,5,9},{26,5,2049},{22,5,129},{64,5,0},{16,5,2},{23,5,385}, + {19,5,25},{27,5,6145},{17,5,7},{25,5,1537},{21,5,97},{29,5,24577}, + {16,5,4},{24,5,769},{20,5,49},{28,5,12289},{18,5,13},{26,5,3073}, + {22,5,193},{64,5,0} + }; diff --git a/src/zlib/inflate.c b/src/zlib/inflate.c new file mode 100644 index 00000000..9bc2912b --- /dev/null +++ b/src/zlib/inflate.c @@ -0,0 +1,1475 @@ +/* inflate.c -- zlib decompression + * Copyright (C) 1995-2012 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* + * Change history: + * + * 1.2.beta0 24 Nov 2002 + * - First version -- complete rewrite of inflate to simplify code, avoid + * creation of window when not needed, minimize use of window when it is + * needed, make inffast.c even faster, implement gzip decoding, and to + * improve code readability and style over the previous zlib inflate code + * + * 1.2.beta1 25 Nov 2002 + * - Use pointers for available input and output checking in inffast.c + * - Remove input and output counters in inffast.c + * - Change inffast.c entry and loop from avail_in >= 7 to >= 6 + * - Remove unnecessary second byte pull from length extra in inffast.c + * - Unroll direct copy to three copies per loop in inffast.c + * + * 1.2.beta2 4 Dec 2002 + * - Change external routine names to reduce potential conflicts + * - Correct filename to inffixed.h for fixed tables in inflate.c + * - Make hbuf[] unsigned char to match parameter type in inflate.c + * - Change strm->next_out[-state->offset] to *(strm->next_out - state->offset) + * to avoid negation problem on Alphas (64 bit) in inflate.c + * + * 1.2.beta3 22 Dec 2002 + * - Add comments on state->bits assertion in inffast.c + * - Add comments on op field in inftrees.h + * - Fix bug in reuse of allocated window after inflateReset() + * - Remove bit fields--back to byte structure for speed + * - Remove distance extra == 0 check in inflate_fast()--only helps for lengths + * - Change post-increments to pre-increments in inflate_fast(), PPC biased? + * - Add compile time option, POSTINC, to use post-increments instead (Intel?) + * - Make MATCH copy in inflate() much faster for when inflate_fast() not used + * - Use local copies of stream next and avail values, as well as local bit + * buffer and bit count in inflate()--for speed when inflate_fast() not used + * + * 1.2.beta4 1 Jan 2003 + * - Split ptr - 257 statements in inflate_table() to avoid compiler warnings + * - Move a comment on output buffer sizes from inffast.c to inflate.c + * - Add comments in inffast.c to introduce the inflate_fast() routine + * - Rearrange window copies in inflate_fast() for speed and simplification + * - Unroll last copy for window match in inflate_fast() + * - Use local copies of window variables in inflate_fast() for speed + * - Pull out common wnext == 0 case for speed in inflate_fast() + * - Make op and len in inflate_fast() unsigned for consistency + * - Add FAR to lcode and dcode declarations in inflate_fast() + * - Simplified bad distance check in inflate_fast() + * - Added inflateBackInit(), inflateBack(), and inflateBackEnd() in new + * source file infback.c to provide a call-back interface to inflate for + * programs like gzip and unzip -- uses window as output buffer to avoid + * window copying + * + * 1.2.beta5 1 Jan 2003 + * - Improved inflateBack() interface to allow the caller to provide initial + * input in strm. + * - Fixed stored blocks bug in inflateBack() + * + * 1.2.beta6 4 Jan 2003 + * - Added comments in inffast.c on effectiveness of POSTINC + * - Typecasting all around to reduce compiler warnings + * - Changed loops from while (1) or do {} while (1) to for (;;), again to + * make compilers happy + * - Changed type of window in inflateBackInit() to unsigned char * + * + * 1.2.beta7 27 Jan 2003 + * - Changed many types to unsigned or unsigned short to avoid warnings + * - Added inflateCopy() function + * + * 1.2.0 9 Mar 2003 + * - Changed inflateBack() interface to provide separate opaque descriptors + * for the in() and out() functions + * - Changed inflateBack() argument and in_func typedef to swap the length + * and buffer address return values for the input function + * - Check next_in and next_out for Z_NULL on entry to inflate() + * + * The history for versions after 1.2.0 are in ChangeLog in zlib distribution. + */ + +#include "zutil.h" +#include "inftrees.h" +#include "inflate.h" +#include "inffast.h" + +#ifdef MAKEFIXED +# ifndef BUILDFIXED +# define BUILDFIXED +# endif +#endif + +/* function prototypes */ +local void fixedtables OF((struct inflate_state FAR *state)); +local int updatewindow OF((z_streamp strm, const unsigned char FAR *end, + unsigned copy)); +#ifdef BUILDFIXED + void makefixed OF((void)); +#endif +local unsigned syncsearch OF((unsigned FAR *have, const unsigned char FAR *buf, + unsigned len)); + +int ZEXPORT inflateResetKeep(z_streamp strm) +{ + struct inflate_state FAR *state; + + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + strm->total_in = strm->total_out = state->total = 0; + strm->msg = Z_NULL; + if (state->wrap) /* to support ill-conceived Java test suite */ + strm->adler = state->wrap & 1; + state->mode = HEAD; + state->last = 0; + state->havedict = 0; + state->dmax = 32768U; + state->head = Z_NULL; + state->hold = 0; + state->bits = 0; + state->lencode = state->distcode = state->next = state->codes; + state->sane = 1; + state->back = -1; + Tracev((stderr, "inflate: reset\n")); + return Z_OK; +} + +int ZEXPORT inflateReset(z_streamp strm) +{ + struct inflate_state FAR *state; + + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + state->wsize = 0; + state->whave = 0; + state->wnext = 0; + return inflateResetKeep(strm); +} + +int ZEXPORT inflateReset2(z_streamp strm, int windowBits) +{ + int wrap; + struct inflate_state FAR *state; + + /* get the state */ + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + + /* extract wrap request from windowBits parameter */ + if (windowBits < 0) { + wrap = 0; + windowBits = -windowBits; + } + else { + wrap = (windowBits >> 4) + 1; +#ifdef GUNZIP + if (windowBits < 48) + windowBits &= 15; +#endif + } + + /* set number of window bits, free window if different */ + if (windowBits && (windowBits < 8 || windowBits > 15)) + return Z_STREAM_ERROR; + if (state->window != Z_NULL && state->wbits != (unsigned)windowBits) { + ZFREE(strm, state->window); + state->window = Z_NULL; + } + + /* update state and reset the rest of it */ + state->wrap = wrap; + state->wbits = (unsigned)windowBits; + return inflateReset(strm); +} + +int ZEXPORT inflateInit2_(z_streamp strm, int windowBits, + const char *version, int stream_size) +{ + int ret; + struct inflate_state FAR *state; + + if (version == Z_NULL || version[0] != ZLIB_VERSION[0] || + stream_size != (int)(sizeof(z_stream))) + return Z_VERSION_ERROR; + if (strm == Z_NULL) return Z_STREAM_ERROR; + strm->msg = Z_NULL; /* in case we return an error */ + if (strm->zalloc == (alloc_func)0) { +#ifdef Z_SOLO + return Z_STREAM_ERROR; +#else + strm->zalloc = zcalloc; + strm->opaque = (voidpf)0; +#endif + } + if (strm->zfree == (free_func)0) +#ifdef Z_SOLO + return Z_STREAM_ERROR; +#else + strm->zfree = zcfree; +#endif + state = (struct inflate_state FAR *) + ZALLOC(strm, 1, sizeof(struct inflate_state)); + if (state == Z_NULL) return Z_MEM_ERROR; + Tracev((stderr, "inflate: allocated\n")); + strm->state = (struct internal_state FAR *)state; + state->window = Z_NULL; + ret = inflateReset2(strm, windowBits); + if (ret != Z_OK) { + ZFREE(strm, state); + strm->state = Z_NULL; + } + return ret; +} + +int ZEXPORT inflateInit_(z_streamp strm, const char *version, int stream_size) +{ + return inflateInit2_(strm, DEF_WBITS, version, stream_size); +} + +int ZEXPORT inflatePrime(z_streamp strm, int bits, int value) +{ + struct inflate_state FAR *state; + + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + if (bits < 0) { + state->hold = 0; + state->bits = 0; + return Z_OK; + } + if (bits > 16 || state->bits + bits > 32) return Z_STREAM_ERROR; + value &= (1L << bits) - 1; + state->hold += value << state->bits; + state->bits += bits; + return Z_OK; +} + +/* + Return state with length and distance decoding tables and index sizes set to + fixed code decoding. Normally this returns fixed tables from inffixed.h. + If BUILDFIXED is defined, then instead this routine builds the tables the + first time it's called, and returns those tables the first time and + thereafter. This reduces the size of the code by about 2K bytes, in + exchange for a little execution time. However, BUILDFIXED should not be + used for threaded applications, since the rewriting of the tables and virgin + may not be thread-safe. + */ +local void fixedtables(struct inflate_state FAR *state) +{ +#ifdef BUILDFIXED + static int virgin = 1; + static code *lenfix, *distfix; + static code fixed[544]; + + /* build fixed huffman tables if first call (may not be thread safe) */ + if (virgin) { + unsigned sym, bits; + static code *next; + + /* literal/length table */ + sym = 0; + while (sym < 144) state->lens[sym++] = 8; + while (sym < 256) state->lens[sym++] = 9; + while (sym < 280) state->lens[sym++] = 7; + while (sym < 288) state->lens[sym++] = 8; + next = fixed; + lenfix = next; + bits = 9; + inflate_table(LENS, state->lens, 288, &(next), &(bits), state->work); + + /* distance table */ + sym = 0; + while (sym < 32) state->lens[sym++] = 5; + distfix = next; + bits = 5; + inflate_table(DISTS, state->lens, 32, &(next), &(bits), state->work); + + /* do this just once */ + virgin = 0; + } +#else /* !BUILDFIXED */ +# include "inffixed.h" +#endif /* BUILDFIXED */ + state->lencode = lenfix; + state->lenbits = 9; + state->distcode = distfix; + state->distbits = 5; +} + +#ifdef MAKEFIXED +#include + +/* + Write out the inffixed.h that is #include'd above. Defining MAKEFIXED also + defines BUILDFIXED, so the tables are built on the fly. makefixed() writes + those tables to stdout, which would be piped to inffixed.h. A small program + can simply call makefixed to do this: + + void makefixed(void); + + int main(void) + { + makefixed(); + return 0; + } + + Then that can be linked with zlib built with MAKEFIXED defined and run: + + a.out > inffixed.h + */ +void makefixed() +{ + unsigned low, size; + struct inflate_state state; + + fixedtables(&state); + puts(" /* inffixed.h -- table for decoding fixed codes"); + puts(" * Generated automatically by makefixed()."); + puts(" */"); + puts(""); + puts(" /* WARNING: this file should *not* be used by applications."); + puts(" It is part of the implementation of this library and is"); + puts(" subject to change. Applications should only use zlib.h."); + puts(" */"); + puts(""); + size = 1U << 9; + printf(" static const code lenfix[%u] = {", size); + low = 0; + for (;;) { + if ((low % 7) == 0) printf("\n "); + printf("{%u,%u,%d}", (low & 127) == 99 ? 64 : state.lencode[low].op, + state.lencode[low].bits, state.lencode[low].val); + if (++low == size) break; + putchar(','); + } + puts("\n };"); + size = 1U << 5; + printf("\n static const code distfix[%u] = {", size); + low = 0; + for (;;) { + if ((low % 6) == 0) printf("\n "); + printf("{%u,%u,%d}", state.distcode[low].op, state.distcode[low].bits, + state.distcode[low].val); + if (++low == size) break; + putchar(','); + } + puts("\n };"); +} +#endif /* MAKEFIXED */ + +/* + Update the window with the last wsize (normally 32K) bytes written before + returning. If window does not exist yet, create it. This is only called + when a window is already in use, or when output has been written during this + inflate call, but the end of the deflate stream has not been reached yet. + It is also called to create a window for dictionary data when a dictionary + is loaded. + + Providing output buffers larger than 32K to inflate() should provide a speed + advantage, since only the last 32K of output is copied to the sliding window + upon return from inflate(), and since all distances after the first 32K of + output will fall in the output data, making match copies simpler and faster. + The advantage may be dependent on the size of the processor's data caches. + */ +local int updatewindow(z_streamp strm, const Bytef *end, unsigned copy) +{ + struct inflate_state FAR *state; + unsigned dist; + + state = (struct inflate_state FAR *)strm->state; + + /* if it hasn't been done already, allocate space for the window */ + if (state->window == Z_NULL) { + state->window = (unsigned char FAR *) + ZALLOC(strm, 1U << state->wbits, + sizeof(unsigned char)); + if (state->window == Z_NULL) return 1; + } + + /* if window not in use yet, initialize */ + if (state->wsize == 0) { + state->wsize = 1U << state->wbits; + state->wnext = 0; + state->whave = 0; + } + + /* copy state->wsize or less output bytes into the circular window */ + if (copy >= state->wsize) { + zmemcpy(state->window, end - state->wsize, state->wsize); + state->wnext = 0; + state->whave = state->wsize; + } + else { + dist = state->wsize - state->wnext; + if (dist > copy) dist = copy; + zmemcpy(state->window + state->wnext, end - copy, dist); + copy -= dist; + if (copy) { + zmemcpy(state->window, end - copy, copy); + state->wnext = copy; + state->whave = state->wsize; + } + else { + state->wnext += dist; + if (state->wnext == state->wsize) state->wnext = 0; + if (state->whave < state->wsize) state->whave += dist; + } + } + return 0; +} + +/* Macros for inflate(): */ + +/* check function to use adler32() for zlib or crc32() for gzip */ +#ifdef GUNZIP +# define UPDATE(check, buf, len) \ + (state->flags ? crc32(check, buf, len) : adler32(check, buf, len)) +#else +# define UPDATE(check, buf, len) adler32(check, buf, len) +#endif + +/* check macros for header crc */ +#ifdef GUNZIP +# define CRC2(check, word) \ + do { \ + hbuf[0] = (unsigned char)(word); \ + hbuf[1] = (unsigned char)((word) >> 8); \ + check = crc32(check, hbuf, 2); \ + } while (0) + +# define CRC4(check, word) \ + do { \ + hbuf[0] = (unsigned char)(word); \ + hbuf[1] = (unsigned char)((word) >> 8); \ + hbuf[2] = (unsigned char)((word) >> 16); \ + hbuf[3] = (unsigned char)((word) >> 24); \ + check = crc32(check, hbuf, 4); \ + } while (0) +#endif + +/* Load registers with state in inflate() for speed */ +#define LOAD() \ + do { \ + put = strm->next_out; \ + left = strm->avail_out; \ + next = strm->next_in; \ + have = strm->avail_in; \ + hold = state->hold; \ + bits = state->bits; \ + } while (0) + +/* Restore state from registers in inflate() */ +#define RESTORE() \ + do { \ + strm->next_out = put; \ + strm->avail_out = left; \ + strm->next_in = next; \ + strm->avail_in = have; \ + state->hold = hold; \ + state->bits = bits; \ + } while (0) + +/* Clear the input bit accumulator */ +#define INITBITS() \ + do { \ + hold = 0; \ + bits = 0; \ + } while (0) + +/* Get a byte of input into the bit accumulator, or return from inflate() + if there is no input available. */ +#define PULLBYTE() \ + do { \ + if (have == 0) goto inf_leave; \ + have--; \ + hold += (unsigned long)(*next++) << bits; \ + bits += 8; \ + } while (0) + +/* Assure that there are at least n bits in the bit accumulator. If there is + not enough available input to do that, then return from inflate(). */ +#define NEEDBITS(n) \ + do { \ + while (bits < (unsigned)(n)) \ + PULLBYTE(); \ + } while (0) + +/* Return the low n bits of the bit accumulator (n < 16) */ +#define BITS(n) \ + ((unsigned)hold & ((1U << (n)) - 1)) + +/* Remove n bits from the bit accumulator */ +#define DROPBITS(n) \ + do { \ + hold >>= (n); \ + bits -= (unsigned)(n); \ + } while (0) + +/* Remove zero to seven bits as needed to go to a byte boundary */ +#define BYTEBITS() \ + do { \ + hold >>= bits & 7; \ + bits -= bits & 7; \ + } while (0) + +/* + inflate() uses a state machine to process as much input data and generate as + much output data as possible before returning. The state machine is + structured roughly as follows: + + for (;;) switch (state) { + ... + case STATEn: + if (not enough input data or output space to make progress) + return; + ... make progress ... + state = STATEm; + break; + ... + } + + so when inflate() is called again, the same case is attempted again, and + if the appropriate resources are provided, the machine proceeds to the + next state. The NEEDBITS() macro is usually the way the state evaluates + whether it can proceed or should return. NEEDBITS() does the return if + the requested bits are not available. The typical use of the BITS macros + is: + + NEEDBITS(n); + ... do something with BITS(n) ... + DROPBITS(n); + + where NEEDBITS(n) either returns from inflate() if there isn't enough + input left to load n bits into the accumulator, or it continues. BITS(n) + gives the low n bits in the accumulator. When done, DROPBITS(n) drops + the low n bits off the accumulator. INITBITS() clears the accumulator + and sets the number of available bits to zero. BYTEBITS() discards just + enough bits to put the accumulator on a byte boundary. After BYTEBITS() + and a NEEDBITS(8), then BITS(8) would return the next byte in the stream. + + NEEDBITS(n) uses PULLBYTE() to get an available byte of input, or to return + if there is no input available. The decoding of variable length codes uses + PULLBYTE() directly in order to pull just enough bytes to decode the next + code, and no more. + + Some states loop until they get enough input, making sure that enough + state information is maintained to continue the loop where it left off + if NEEDBITS() returns in the loop. For example, want, need, and keep + would all have to actually be part of the saved state in case NEEDBITS() + returns: + + case STATEw: + while (want < need) { + NEEDBITS(n); + keep[want++] = BITS(n); + DROPBITS(n); + } + state = STATEx; + case STATEx: + + As shown above, if the next state is also the next case, then the break + is omitted. + + A state may also return if there is not enough output space available to + complete that state. Those states are copying stored data, writing a + literal byte, and copying a matching string. + + When returning, a "goto inf_leave" is used to update the total counters, + update the check value, and determine whether any progress has been made + during that inflate() call in order to return the proper return code. + Progress is defined as a change in either strm->avail_in or strm->avail_out. + When there is a window, goto inf_leave will update the window with the last + output written. If a goto inf_leave occurs in the middle of decompression + and there is no window currently, goto inf_leave will create one and copy + output to the window for the next call of inflate(). + + In this implementation, the flush parameter of inflate() only affects the + return code (per zlib.h). inflate() always writes as much as possible to + strm->next_out, given the space available and the provided input--the effect + documented in zlib.h of Z_SYNC_FLUSH. Furthermore, inflate() always defers + the allocation of and copying into a sliding window until necessary, which + provides the effect documented in zlib.h for Z_FINISH when the entire input + stream available. So the only thing the flush parameter actually does is: + when flush is set to Z_FINISH, inflate() cannot return Z_OK. Instead it + will return Z_BUF_ERROR if it has not reached the end of the stream. + */ + +int ZEXPORT inflate(z_streamp strm, int flush) +{ + struct inflate_state FAR *state; + z_const unsigned char FAR *next; /* next input */ + unsigned char FAR *put; /* next output */ + unsigned have, left; /* available input and output */ + unsigned long hold; /* bit buffer */ + unsigned bits; /* bits in bit buffer */ + unsigned in, out; /* save starting available input and output */ + unsigned copy; /* number of stored or match bytes to copy */ + unsigned char FAR *from; /* where to copy match bytes from */ + code here; /* current decoding table entry */ + code last; /* parent table entry */ + unsigned len; /* length to copy for repeats, bits to drop */ + int ret; /* return code */ +#ifdef GUNZIP + unsigned char hbuf[4]; /* buffer for gzip header crc calculation */ +#endif + static const unsigned short order[19] = /* permutation of code lengths */ + {16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}; + + if (strm == Z_NULL || strm->state == Z_NULL || strm->next_out == Z_NULL || + (strm->next_in == Z_NULL && strm->avail_in != 0)) + return Z_STREAM_ERROR; + + state = (struct inflate_state FAR *)strm->state; + if (state->mode == TYPE) state->mode = TYPEDO; /* skip check */ + LOAD(); + in = have; + out = left; + ret = Z_OK; + for (;;) + switch (state->mode) { + case HEAD: + if (state->wrap == 0) { + state->mode = TYPEDO; + break; + } + NEEDBITS(16); +#ifdef GUNZIP + if ((state->wrap & 2) && hold == 0x8b1f) { /* gzip header */ + state->check = crc32(0L, Z_NULL, 0); + CRC2(state->check, hold); + INITBITS(); + state->mode = FLAGS; + break; + } + state->flags = 0; /* expect zlib header */ + if (state->head != Z_NULL) + state->head->done = -1; + if (!(state->wrap & 1) || /* check if zlib header allowed */ +#else + if ( +#endif + ((BITS(8) << 8) + (hold >> 8)) % 31) { + strm->msg = (char *)"incorrect header check"; + state->mode = BAD; + break; + } + if (BITS(4) != Z_DEFLATED) { + strm->msg = (char *)"unknown compression method"; + state->mode = BAD; + break; + } + DROPBITS(4); + len = BITS(4) + 8; + if (state->wbits == 0) + state->wbits = len; + else if (len > state->wbits) { + strm->msg = (char *)"invalid window size"; + state->mode = BAD; + break; + } + state->dmax = 1U << len; + Tracev((stderr, "inflate: zlib header ok\n")); + strm->adler = state->check = adler32(0L, Z_NULL, 0); + state->mode = hold & 0x200 ? DICTID : TYPE; + INITBITS(); + break; +#ifdef GUNZIP + case FLAGS: + NEEDBITS(16); + state->flags = (int)(hold); + if ((state->flags & 0xff) != Z_DEFLATED) { + strm->msg = (char *)"unknown compression method"; + state->mode = BAD; + break; + } + if (state->flags & 0xe000) { + strm->msg = (char *)"unknown header flags set"; + state->mode = BAD; + break; + } + if (state->head != Z_NULL) + state->head->text = (int)((hold >> 8) & 1); + if (state->flags & 0x0200) CRC2(state->check, hold); + INITBITS(); + state->mode = TIME; + case TIME: + NEEDBITS(32); + if (state->head != Z_NULL) + state->head->time = hold; + if (state->flags & 0x0200) CRC4(state->check, hold); + INITBITS(); + state->mode = OS; + case OS: + NEEDBITS(16); + if (state->head != Z_NULL) { + state->head->xflags = (int)(hold & 0xff); + state->head->os = (int)(hold >> 8); + } + if (state->flags & 0x0200) CRC2(state->check, hold); + INITBITS(); + state->mode = EXLEN; + case EXLEN: + if (state->flags & 0x0400) { + NEEDBITS(16); + state->length = (unsigned)(hold); + if (state->head != Z_NULL) + state->head->extra_len = (unsigned)hold; + if (state->flags & 0x0200) CRC2(state->check, hold); + INITBITS(); + } + else if (state->head != Z_NULL) + state->head->extra = Z_NULL; + state->mode = EXTRA; + case EXTRA: + if (state->flags & 0x0400) { + copy = state->length; + if (copy > have) copy = have; + if (copy) { + if (state->head != Z_NULL && + state->head->extra != Z_NULL) { + len = state->head->extra_len - state->length; + zmemcpy(state->head->extra + len, next, + len + copy > state->head->extra_max ? + state->head->extra_max - len : copy); + } + if (state->flags & 0x0200) + state->check = crc32(state->check, next, copy); + have -= copy; + next += copy; + state->length -= copy; + } + if (state->length) goto inf_leave; + } + state->length = 0; + state->mode = NAME; + case NAME: + if (state->flags & 0x0800) { + if (have == 0) goto inf_leave; + copy = 0; + do { + len = (unsigned)(next[copy++]); + if (state->head != Z_NULL && + state->head->name != Z_NULL && + state->length < state->head->name_max) + state->head->name[state->length++] = len; + } while (len && copy < have); + if (state->flags & 0x0200) + state->check = crc32(state->check, next, copy); + have -= copy; + next += copy; + if (len) goto inf_leave; + } + else if (state->head != Z_NULL) + state->head->name = Z_NULL; + state->length = 0; + state->mode = COMMENT; + case COMMENT: + if (state->flags & 0x1000) { + if (have == 0) goto inf_leave; + copy = 0; + do { + len = (unsigned)(next[copy++]); + if (state->head != Z_NULL && + state->head->comment != Z_NULL && + state->length < state->head->comm_max) + state->head->comment[state->length++] = len; + } while (len && copy < have); + if (state->flags & 0x0200) + state->check = crc32(state->check, next, copy); + have -= copy; + next += copy; + if (len) goto inf_leave; + } + else if (state->head != Z_NULL) + state->head->comment = Z_NULL; + state->mode = HCRC; + case HCRC: + if (state->flags & 0x0200) { + NEEDBITS(16); + if (hold != (state->check & 0xffff)) { + strm->msg = (char *)"header crc mismatch"; + state->mode = BAD; + break; + } + INITBITS(); + } + if (state->head != Z_NULL) { + state->head->hcrc = (int)((state->flags >> 9) & 1); + state->head->done = 1; + } + strm->adler = state->check = crc32(0L, Z_NULL, 0); + state->mode = TYPE; + break; +#endif + case DICTID: + NEEDBITS(32); + strm->adler = state->check = ZSWAP32(hold); + INITBITS(); + state->mode = DICT; + case DICT: + if (state->havedict == 0) { + RESTORE(); + return Z_NEED_DICT; + } + strm->adler = state->check = adler32(0L, Z_NULL, 0); + state->mode = TYPE; + case TYPE: + if (flush == Z_BLOCK || flush == Z_TREES) goto inf_leave; + case TYPEDO: + if (state->last) { + BYTEBITS(); + state->mode = CHECK; + break; + } + NEEDBITS(3); + state->last = BITS(1); + DROPBITS(1); + switch (BITS(2)) { + case 0: /* stored block */ + Tracev((stderr, "inflate: stored block%s\n", + state->last ? " (last)" : "")); + state->mode = STORED; + break; + case 1: /* fixed block */ + fixedtables(state); + Tracev((stderr, "inflate: fixed codes block%s\n", + state->last ? " (last)" : "")); + state->mode = LEN_; /* decode codes */ + if (flush == Z_TREES) { + DROPBITS(2); + goto inf_leave; + } + break; + case 2: /* dynamic block */ + Tracev((stderr, "inflate: dynamic codes block%s\n", + state->last ? " (last)" : "")); + state->mode = TABLE; + break; + case 3: + strm->msg = (char *)"invalid block type"; + state->mode = BAD; + } + DROPBITS(2); + break; + case STORED: + BYTEBITS(); /* go to byte boundary */ + NEEDBITS(32); + if ((hold & 0xffff) != ((hold >> 16) ^ 0xffff)) { + strm->msg = (char *)"invalid stored block lengths"; + state->mode = BAD; + break; + } + state->length = (unsigned)hold & 0xffff; + Tracev((stderr, "inflate: stored length %u\n", + state->length)); + INITBITS(); + state->mode = COPY_; + if (flush == Z_TREES) goto inf_leave; + case COPY_: + state->mode = COPY; + case COPY: + copy = state->length; + if (copy) { + if (copy > have) copy = have; + if (copy > left) copy = left; + if (copy == 0) goto inf_leave; + zmemcpy(put, next, copy); + have -= copy; + next += copy; + left -= copy; + put += copy; + state->length -= copy; + break; + } + Tracev((stderr, "inflate: stored end\n")); + state->mode = TYPE; + break; + case TABLE: + NEEDBITS(14); + state->nlen = BITS(5) + 257; + DROPBITS(5); + state->ndist = BITS(5) + 1; + DROPBITS(5); + state->ncode = BITS(4) + 4; + DROPBITS(4); +#ifndef PKZIP_BUG_WORKAROUND + if (state->nlen > 286 || state->ndist > 30) { + strm->msg = (char *)"too many length or distance symbols"; + state->mode = BAD; + break; + } +#endif + Tracev((stderr, "inflate: table sizes ok\n")); + state->have = 0; + state->mode = LENLENS; + case LENLENS: + while (state->have < state->ncode) { + NEEDBITS(3); + state->lens[order[state->have++]] = (unsigned short)BITS(3); + DROPBITS(3); + } + while (state->have < 19) + state->lens[order[state->have++]] = 0; + state->next = state->codes; + state->lencode = (const code FAR *)(state->next); + state->lenbits = 7; + ret = inflate_table(CODES, state->lens, 19, &(state->next), + &(state->lenbits), state->work); + if (ret) { + strm->msg = (char *)"invalid code lengths set"; + state->mode = BAD; + break; + } + Tracev((stderr, "inflate: code lengths ok\n")); + state->have = 0; + state->mode = CODELENS; + case CODELENS: + while (state->have < state->nlen + state->ndist) { + for (;;) { + here = state->lencode[BITS(state->lenbits)]; + if ((unsigned)(here.bits) <= bits) break; + PULLBYTE(); + } + if (here.val < 16) { + DROPBITS(here.bits); + state->lens[state->have++] = here.val; + } + else { + if (here.val == 16) { + NEEDBITS(here.bits + 2); + DROPBITS(here.bits); + if (state->have == 0) { + strm->msg = (char *)"invalid bit length repeat"; + state->mode = BAD; + break; + } + len = state->lens[state->have - 1]; + copy = 3 + BITS(2); + DROPBITS(2); + } + else if (here.val == 17) { + NEEDBITS(here.bits + 3); + DROPBITS(here.bits); + len = 0; + copy = 3 + BITS(3); + DROPBITS(3); + } + else { + NEEDBITS(here.bits + 7); + DROPBITS(here.bits); + len = 0; + copy = 11 + BITS(7); + DROPBITS(7); + } + if (state->have + copy > state->nlen + state->ndist) { + strm->msg = (char *)"invalid bit length repeat"; + state->mode = BAD; + break; + } + while (copy--) + state->lens[state->have++] = (unsigned short)len; + } + } + + /* handle error breaks in while */ + if (state->mode == BAD) break; + + /* check for end-of-block code (better have one) */ + if (state->lens[256] == 0) { + strm->msg = (char *)"invalid code -- missing end-of-block"; + state->mode = BAD; + break; + } + + /* build code tables -- note: do not change the lenbits or distbits + values here (9 and 6) without reading the comments in inftrees.h + concerning the ENOUGH constants, which depend on those values */ + state->next = state->codes; + state->lencode = (const code FAR *)(state->next); + state->lenbits = 9; + ret = inflate_table(LENS, state->lens, state->nlen, &(state->next), + &(state->lenbits), state->work); + if (ret) { + strm->msg = (char *)"invalid literal/lengths set"; + state->mode = BAD; + break; + } + state->distcode = (const code FAR *)(state->next); + state->distbits = 6; + ret = inflate_table(DISTS, state->lens + state->nlen, state->ndist, + &(state->next), &(state->distbits), state->work); + if (ret) { + strm->msg = (char *)"invalid distances set"; + state->mode = BAD; + break; + } + Tracev((stderr, "inflate: codes ok\n")); + state->mode = LEN_; + if (flush == Z_TREES) goto inf_leave; + case LEN_: + state->mode = LEN; + case LEN: + if (have >= 6 && left >= 258) { + RESTORE(); + inflate_fast(strm, out); + LOAD(); + if (state->mode == TYPE) + state->back = -1; + break; + } + state->back = 0; + for (;;) { + here = state->lencode[BITS(state->lenbits)]; + if ((unsigned)(here.bits) <= bits) break; + PULLBYTE(); + } + if (here.op && (here.op & 0xf0) == 0) { + last = here; + for (;;) { + here = state->lencode[last.val + + (BITS(last.bits + last.op) >> last.bits)]; + if ((unsigned)(last.bits + here.bits) <= bits) break; + PULLBYTE(); + } + DROPBITS(last.bits); + state->back += last.bits; + } + DROPBITS(here.bits); + state->back += here.bits; + state->length = (unsigned)here.val; + if ((int)(here.op) == 0) { + Tracevv((stderr, here.val >= 0x20 && here.val < 0x7f ? + "inflate: literal '%c'\n" : + "inflate: literal 0x%02x\n", here.val)); + state->mode = LIT; + break; + } + if (here.op & 32) { + Tracevv((stderr, "inflate: end of block\n")); + state->back = -1; + state->mode = TYPE; + break; + } + if (here.op & 64) { + strm->msg = (char *)"invalid literal/length code"; + state->mode = BAD; + break; + } + state->extra = (unsigned)(here.op) & 15; + state->mode = LENEXT; + case LENEXT: + if (state->extra) { + NEEDBITS(state->extra); + state->length += BITS(state->extra); + DROPBITS(state->extra); + state->back += state->extra; + } + Tracevv((stderr, "inflate: length %u\n", state->length)); + state->was = state->length; + state->mode = DIST; + case DIST: + for (;;) { + here = state->distcode[BITS(state->distbits)]; + if ((unsigned)(here.bits) <= bits) break; + PULLBYTE(); + } + if ((here.op & 0xf0) == 0) { + last = here; + for (;;) { + here = state->distcode[last.val + + (BITS(last.bits + last.op) >> last.bits)]; + if ((unsigned)(last.bits + here.bits) <= bits) break; + PULLBYTE(); + } + DROPBITS(last.bits); + state->back += last.bits; + } + DROPBITS(here.bits); + state->back += here.bits; + if (here.op & 64) { + strm->msg = (char *)"invalid distance code"; + state->mode = BAD; + break; + } + state->offset = (unsigned)here.val; + state->extra = (unsigned)(here.op) & 15; + state->mode = DISTEXT; + case DISTEXT: + if (state->extra) { + NEEDBITS(state->extra); + state->offset += BITS(state->extra); + DROPBITS(state->extra); + state->back += state->extra; + } +#ifdef INFLATE_STRICT + if (state->offset > state->dmax) { + strm->msg = (char *)"invalid distance too far back"; + state->mode = BAD; + break; + } +#endif + Tracevv((stderr, "inflate: distance %u\n", state->offset)); + state->mode = MATCH; + case MATCH: + if (left == 0) goto inf_leave; + copy = out - left; + if (state->offset > copy) { /* copy from window */ + copy = state->offset - copy; + if (copy > state->whave) { + if (state->sane) { + strm->msg = (char *)"invalid distance too far back"; + state->mode = BAD; + break; + } +#ifdef INFLATE_ALLOW_INVALID_DISTANCE_TOOFAR_ARRR + Trace((stderr, "inflate.c too far\n")); + copy -= state->whave; + if (copy > state->length) copy = state->length; + if (copy > left) copy = left; + left -= copy; + state->length -= copy; + do { + *put++ = 0; + } while (--copy); + if (state->length == 0) state->mode = LEN; + break; +#endif + } + if (copy > state->wnext) { + copy -= state->wnext; + from = state->window + (state->wsize - copy); + } + else + from = state->window + (state->wnext - copy); + if (copy > state->length) copy = state->length; + } + else { /* copy from output */ + from = put - state->offset; + copy = state->length; + } + if (copy > left) copy = left; + left -= copy; + state->length -= copy; + do { + *put++ = *from++; + } while (--copy); + if (state->length == 0) state->mode = LEN; + break; + case LIT: + if (left == 0) goto inf_leave; + *put++ = (unsigned char)(state->length); + left--; + state->mode = LEN; + break; + case CHECK: + if (state->wrap) { + NEEDBITS(32); + out -= left; + strm->total_out += out; + state->total += out; + if (out) + strm->adler = state->check = + UPDATE(state->check, put - out, out); + out = left; + if (( +#ifdef GUNZIP + state->flags ? hold : +#endif + ZSWAP32(hold)) != state->check) { + strm->msg = (char *)"incorrect data check"; + state->mode = BAD; + break; + } + INITBITS(); + Tracev((stderr, "inflate: check matches trailer\n")); + } +#ifdef GUNZIP + state->mode = LENGTH; + case LENGTH: + if (state->wrap && state->flags) { + NEEDBITS(32); + if (hold != (state->total & 0xffffffffUL)) { + strm->msg = (char *)"incorrect length check"; + state->mode = BAD; + break; + } + INITBITS(); + Tracev((stderr, "inflate: length matches trailer\n")); + } +#endif + state->mode = DONE; + case DONE: + ret = Z_STREAM_END; + goto inf_leave; + case BAD: + ret = Z_DATA_ERROR; + goto inf_leave; + case MEM: + return Z_MEM_ERROR; + case SYNC: + default: + return Z_STREAM_ERROR; + } + + /* + Return from inflate(), updating the total counts and the check value. + If there was no progress during the inflate() call, return a buffer + error. Call updatewindow() to create and/or update the window state. + Note: a memory error from inflate() is non-recoverable. + */ + inf_leave: + RESTORE(); + if (state->wsize || (out != strm->avail_out && state->mode < BAD && + (state->mode < CHECK || flush != Z_FINISH))) + if (updatewindow(strm, strm->next_out, out - strm->avail_out)) { + state->mode = MEM; + return Z_MEM_ERROR; + } + in -= strm->avail_in; + out -= strm->avail_out; + strm->total_in += in; + strm->total_out += out; + state->total += out; + if (state->wrap && out) + strm->adler = state->check = + UPDATE(state->check, strm->next_out - out, out); + strm->data_type = state->bits + (state->last ? 64 : 0) + + (state->mode == TYPE ? 128 : 0) + + (state->mode == LEN_ || state->mode == COPY_ ? 256 : 0); + if (((in == 0 && out == 0) || flush == Z_FINISH) && ret == Z_OK) + ret = Z_BUF_ERROR; + return ret; +} + +int ZEXPORT inflateEnd(z_streamp strm) +{ + struct inflate_state FAR *state; + if (strm == Z_NULL || strm->state == Z_NULL || strm->zfree == (free_func)0) + return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + if (state->window != Z_NULL) ZFREE(strm, state->window); + ZFREE(strm, strm->state); + strm->state = Z_NULL; + Tracev((stderr, "inflate: end\n")); + return Z_OK; +} + +int ZEXPORT inflateGetDictionary(z_streamp strm, Bytef *dictionary, uInt *dictLength) +{ + struct inflate_state FAR *state; + + /* check state */ + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + + /* copy dictionary */ + if (state->whave && dictionary != Z_NULL) { + zmemcpy(dictionary, state->window + state->wnext, + state->whave - state->wnext); + zmemcpy(dictionary + state->whave - state->wnext, + state->window, state->wnext); + } + if (dictLength != Z_NULL) + *dictLength = state->whave; + return Z_OK; +} + +int ZEXPORT inflateSetDictionary(z_streamp strm, const Bytef *dictionary, uInt dictLength) +{ + struct inflate_state FAR *state; + unsigned long dictid; + int ret; + + /* check state */ + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + if (state->wrap != 0 && state->mode != DICT) + return Z_STREAM_ERROR; + + /* check for correct dictionary identifier */ + if (state->mode == DICT) { + dictid = adler32(0L, Z_NULL, 0); + dictid = adler32(dictid, dictionary, dictLength); + if (dictid != state->check) + return Z_DATA_ERROR; + } + + /* copy dictionary to window using updatewindow(), which will amend the + existing dictionary if appropriate */ + ret = updatewindow(strm, dictionary + dictLength, dictLength); + if (ret) { + state->mode = MEM; + return Z_MEM_ERROR; + } + state->havedict = 1; + Tracev((stderr, "inflate: dictionary set\n")); + return Z_OK; +} + +int ZEXPORT inflateGetHeader(z_streamp strm, gz_headerp head) +{ + struct inflate_state FAR *state; + + /* check state */ + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + if ((state->wrap & 2) == 0) return Z_STREAM_ERROR; + + /* save header structure */ + state->head = head; + head->done = 0; + return Z_OK; +} + +/* + Search buf[0..len-1] for the pattern: 0, 0, 0xff, 0xff. Return when found + or when out of input. When called, *have is the number of pattern bytes + found in order so far, in 0..3. On return *have is updated to the new + state. If on return *have equals four, then the pattern was found and the + return value is how many bytes were read including the last byte of the + pattern. If *have is less than four, then the pattern has not been found + yet and the return value is len. In the latter case, syncsearch() can be + called again with more data and the *have state. *have is initialized to + zero for the first call. + */ +local unsigned syncsearch(unsigned FAR *have, + const unsigned char FAR *buf, unsigned len) +{ + unsigned got; + unsigned next; + + got = *have; + next = 0; + while (next < len && got < 4) { + if ((int)(buf[next]) == (got < 2 ? 0 : 0xff)) + got++; + else if (buf[next]) + got = 0; + else + got = 4 - got; + next++; + } + *have = got; + return next; +} + +int ZEXPORT inflateSync(z_streamp strm) +{ + unsigned len; /* number of bytes to look at or looked at */ + unsigned long in, out; /* temporary to save total_in and total_out */ + unsigned char buf[4]; /* to restore bit buffer to byte string */ + struct inflate_state FAR *state; + + /* check parameters */ + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + if (strm->avail_in == 0 && state->bits < 8) return Z_BUF_ERROR; + + /* if first time, start search in bit buffer */ + if (state->mode != SYNC) { + state->mode = SYNC; + state->hold <<= state->bits & 7; + state->bits -= state->bits & 7; + len = 0; + while (state->bits >= 8) { + buf[len++] = (unsigned char)(state->hold); + state->hold >>= 8; + state->bits -= 8; + } + state->have = 0; + syncsearch(&(state->have), buf, len); + } + + /* search available input */ + len = syncsearch(&(state->have), strm->next_in, strm->avail_in); + strm->avail_in -= len; + strm->next_in += len; + strm->total_in += len; + + /* return no joy or set up to restart inflate() on a new block */ + if (state->have != 4) return Z_DATA_ERROR; + in = strm->total_in; out = strm->total_out; + inflateReset(strm); + strm->total_in = in; strm->total_out = out; + state->mode = TYPE; + return Z_OK; +} + +/* + Returns true if inflate is currently at the end of a block generated by + Z_SYNC_FLUSH or Z_FULL_FLUSH. This function is used by one PPP + implementation to provide an additional safety check. PPP uses + Z_SYNC_FLUSH but removes the length bytes of the resulting empty stored + block. When decompressing, PPP checks that at the end of input packet, + inflate is waiting for these length bytes. + */ +int ZEXPORT inflateSyncPoint(z_streamp strm) +{ + struct inflate_state FAR *state; + + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + return state->mode == STORED && state->bits == 0; +} + +int ZEXPORT inflateCopy(z_streamp dest, z_streamp source) +{ + struct inflate_state FAR *state; + struct inflate_state FAR *copy; + unsigned char FAR *window; + unsigned wsize; + + /* check input */ + if (dest == Z_NULL || source == Z_NULL || source->state == Z_NULL || + source->zalloc == (alloc_func)0 || source->zfree == (free_func)0) + return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)source->state; + + /* allocate space */ + copy = (struct inflate_state FAR *) + ZALLOC(source, 1, sizeof(struct inflate_state)); + if (copy == Z_NULL) return Z_MEM_ERROR; + window = Z_NULL; + if (state->window != Z_NULL) { + window = (unsigned char FAR *) + ZALLOC(source, 1U << state->wbits, sizeof(unsigned char)); + if (window == Z_NULL) { + ZFREE(source, copy); + return Z_MEM_ERROR; + } + } + + /* copy state */ + zmemcpy((voidpf)dest, (voidpf)source, sizeof(z_stream)); + zmemcpy((voidpf)copy, (voidpf)state, sizeof(struct inflate_state)); + if (state->lencode >= state->codes && + state->lencode <= state->codes + ENOUGH - 1) { + copy->lencode = copy->codes + (state->lencode - state->codes); + copy->distcode = copy->codes + (state->distcode - state->codes); + } + copy->next = copy->codes + (state->next - state->codes); + if (window != Z_NULL) { + wsize = 1U << state->wbits; + zmemcpy(window, state->window, wsize); + } + copy->window = window; + dest->state = (struct internal_state FAR *)copy; + return Z_OK; +} + +int ZEXPORT inflateUndermine(z_streamp strm, int subvert) +{ + struct inflate_state FAR *state; + + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + state->sane = !subvert; +#ifdef INFLATE_ALLOW_INVALID_DISTANCE_TOOFAR_ARRR + return Z_OK; +#else + state->sane = 1; + return Z_DATA_ERROR; +#endif +} + +long ZEXPORT inflateMark(z_streamp strm) +{ + struct inflate_state FAR *state; + + if (strm == Z_NULL || strm->state == Z_NULL) return -1L << 16; + state = (struct inflate_state FAR *)strm->state; + return ((long)(state->back) << 16) + + (state->mode == COPY ? state->length : + (state->mode == MATCH ? state->was - state->length : 0)); +} diff --git a/src/zlib/inflate.h b/src/zlib/inflate.h new file mode 100644 index 00000000..95f4986d --- /dev/null +++ b/src/zlib/inflate.h @@ -0,0 +1,122 @@ +/* inflate.h -- internal inflate state definition + * Copyright (C) 1995-2009 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +/* define NO_GZIP when compiling if you want to disable gzip header and + trailer decoding by inflate(). NO_GZIP would be used to avoid linking in + the crc code when it is not needed. For shared libraries, gzip decoding + should be left enabled. */ +#ifndef NO_GZIP +# define GUNZIP +#endif + +/* Possible inflate modes between inflate() calls */ +typedef enum { + HEAD, /* i: waiting for magic header */ + FLAGS, /* i: waiting for method and flags (gzip) */ + TIME, /* i: waiting for modification time (gzip) */ + OS, /* i: waiting for extra flags and operating system (gzip) */ + EXLEN, /* i: waiting for extra length (gzip) */ + EXTRA, /* i: waiting for extra bytes (gzip) */ + NAME, /* i: waiting for end of file name (gzip) */ + COMMENT, /* i: waiting for end of comment (gzip) */ + HCRC, /* i: waiting for header crc (gzip) */ + DICTID, /* i: waiting for dictionary check value */ + DICT, /* waiting for inflateSetDictionary() call */ + TYPE, /* i: waiting for type bits, including last-flag bit */ + TYPEDO, /* i: same, but skip check to exit inflate on new block */ + STORED, /* i: waiting for stored size (length and complement) */ + COPY_, /* i/o: same as COPY below, but only first time in */ + COPY, /* i/o: waiting for input or output to copy stored block */ + TABLE, /* i: waiting for dynamic block table lengths */ + LENLENS, /* i: waiting for code length code lengths */ + CODELENS, /* i: waiting for length/lit and distance code lengths */ + LEN_, /* i: same as LEN below, but only first time in */ + LEN, /* i: waiting for length/lit/eob code */ + LENEXT, /* i: waiting for length extra bits */ + DIST, /* i: waiting for distance code */ + DISTEXT, /* i: waiting for distance extra bits */ + MATCH, /* o: waiting for output space to copy string */ + LIT, /* o: waiting for output space to write literal */ + CHECK, /* i: waiting for 32-bit check value */ + LENGTH, /* i: waiting for 32-bit length (gzip) */ + DONE, /* finished check, done -- remain here until reset */ + BAD, /* got a data error -- remain here until reset */ + MEM, /* got an inflate() memory error -- remain here until reset */ + SYNC /* looking for synchronization bytes to restart inflate() */ +} inflate_mode; + +/* + State transitions between above modes - + + (most modes can go to BAD or MEM on error -- not shown for clarity) + + Process header: + HEAD -> (gzip) or (zlib) or (raw) + (gzip) -> FLAGS -> TIME -> OS -> EXLEN -> EXTRA -> NAME -> COMMENT -> + HCRC -> TYPE + (zlib) -> DICTID or TYPE + DICTID -> DICT -> TYPE + (raw) -> TYPEDO + Read deflate blocks: + TYPE -> TYPEDO -> STORED or TABLE or LEN_ or CHECK + STORED -> COPY_ -> COPY -> TYPE + TABLE -> LENLENS -> CODELENS -> LEN_ + LEN_ -> LEN + Read deflate codes in fixed or dynamic block: + LEN -> LENEXT or LIT or TYPE + LENEXT -> DIST -> DISTEXT -> MATCH -> LEN + LIT -> LEN + Process trailer: + CHECK -> LENGTH -> DONE + */ + +/* state maintained between inflate() calls. Approximately 10K bytes. */ +struct inflate_state { + inflate_mode mode; /* current inflate mode */ + int last; /* true if processing last block */ + int wrap; /* bit 0 true for zlib, bit 1 true for gzip */ + int havedict; /* true if dictionary provided */ + int flags; /* gzip header method and flags (0 if zlib) */ + unsigned dmax; /* zlib header max distance (INFLATE_STRICT) */ + unsigned long check; /* protected copy of check value */ + unsigned long total; /* protected copy of output count */ + gz_headerp head; /* where to save gzip header information */ + /* sliding window */ + unsigned wbits; /* log base 2 of requested window size */ + unsigned wsize; /* window size or zero if not using window */ + unsigned whave; /* valid bytes in the window */ + unsigned wnext; /* window write index */ + unsigned char FAR *window; /* allocated sliding window, if needed */ + /* bit accumulator */ + unsigned long hold; /* input bit accumulator */ + unsigned bits; /* number of bits in "in" */ + /* for string and stored block copying */ + unsigned length; /* literal or length of data to copy */ + unsigned offset; /* distance back to copy string from */ + /* for table and code decoding */ + unsigned extra; /* extra bits needed */ + /* fixed and dynamic code tables */ + code const FAR *lencode; /* starting table for length/literal codes */ + code const FAR *distcode; /* starting table for distance codes */ + unsigned lenbits; /* index bits for lencode */ + unsigned distbits; /* index bits for distcode */ + /* dynamic table building */ + unsigned ncode; /* number of code length code lengths */ + unsigned nlen; /* number of length code lengths */ + unsigned ndist; /* number of distance code lengths */ + unsigned have; /* number of code lengths in lens[] */ + code FAR *next; /* next available space in codes[] */ + unsigned short lens[320]; /* temporary storage for code lengths */ + unsigned short work[288]; /* work area for code table building */ + code codes[ENOUGH]; /* space for code tables */ + int sane; /* if false, allow invalid distance too far */ + int back; /* bits back of last unprocessed length/lit */ + unsigned was; /* initial length of match */ +}; diff --git a/src/zlib/inftrees.c b/src/zlib/inftrees.c new file mode 100644 index 00000000..0be5c019 --- /dev/null +++ b/src/zlib/inftrees.c @@ -0,0 +1,305 @@ +/* inftrees.c -- generate Huffman trees for efficient decoding + * Copyright (C) 1995-2013 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +#include "zutil.h" +#include "inftrees.h" + +#define MAXBITS 15 + +const char inflate_copyright[] = + " inflate 1.2.8 Copyright 1995-2013 Mark Adler "; +/* + If you use the zlib library in a product, an acknowledgment is welcome + in the documentation of your product. If for some reason you cannot + include such an acknowledgment, I would appreciate that you keep this + copyright string in the executable of your product. + */ + +/* + Build a set of tables to decode the provided canonical Huffman code. + The code lengths are lens[0..codes-1]. The result starts at *table, + whose indices are 0..2^bits-1. work is a writable array of at least + lens shorts, which is used as a work area. type is the type of code + to be generated, CODES, LENS, or DISTS. On return, zero is success, + -1 is an invalid code, and +1 means that ENOUGH isn't enough. table + on return points to the next available entry's address. bits is the + requested root table index bits, and on return it is the actual root + table index bits. It will differ if the request is greater than the + longest code or if it is less than the shortest code. + */ +int ZLIB_INTERNAL inflate_table(codetype type, + unsigned short FAR *lens, + unsigned codes, + code FAR * FAR *table, + unsigned FAR *bits, + unsigned short FAR *work) +{ + unsigned len; /* a code's length in bits */ + unsigned sym; /* index of code symbols */ + unsigned min, max; /* minimum and maximum code lengths */ + unsigned root; /* number of index bits for root table */ + unsigned curr; /* number of index bits for current table */ + unsigned drop; /* code bits to drop for sub-table */ + int left; /* number of prefix codes available */ + unsigned used; /* code entries in table used */ + unsigned huff; /* Huffman code */ + unsigned incr; /* for incrementing code, index */ + unsigned fill; /* index for replicating entries */ + unsigned low; /* low bits for current root entry */ + unsigned mask; /* mask for low root bits */ + code here; /* table entry for duplication */ + code FAR *next; /* next available space in table */ + const unsigned short FAR *base; /* base value table to use */ + const unsigned short FAR *extra; /* extra bits table to use */ + int end; /* use base and extra for symbol > end */ + unsigned short count[MAXBITS+1]; /* number of codes of each length */ + unsigned short offs[MAXBITS+1]; /* offsets in table for each length */ + static const unsigned short lbase[31] = { /* Length codes 257..285 base */ + 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, + 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0}; + static const unsigned short lext[31] = { /* Length codes 257..285 extra */ + 16, 16, 16, 16, 16, 16, 16, 16, 17, 17, 17, 17, 18, 18, 18, 18, + 19, 19, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 16, 72, 78}; + static const unsigned short dbase[32] = { /* Distance codes 0..29 base */ + 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, + 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, + 8193, 12289, 16385, 24577, 0, 0}; + static const unsigned short dext[32] = { /* Distance codes 0..29 extra */ + 16, 16, 16, 16, 17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22, + 23, 23, 24, 24, 25, 25, 26, 26, 27, 27, + 28, 28, 29, 29, 64, 64}; + + /* + Process a set of code lengths to create a canonical Huffman code. The + code lengths are lens[0..codes-1]. Each length corresponds to the + symbols 0..codes-1. The Huffman code is generated by first sorting the + symbols by length from short to long, and retaining the symbol order + for codes with equal lengths. Then the code starts with all zero bits + for the first code of the shortest length, and the codes are integer + increments for the same length, and zeros are appended as the length + increases. For the deflate format, these bits are stored backwards + from their more natural integer increment ordering, and so when the + decoding tables are built in the large loop below, the integer codes + are incremented backwards. + + This routine assumes, but does not check, that all of the entries in + lens[] are in the range 0..MAXBITS. The caller must assure this. + 1..MAXBITS is interpreted as that code length. zero means that that + symbol does not occur in this code. + + The codes are sorted by computing a count of codes for each length, + creating from that a table of starting indices for each length in the + sorted table, and then entering the symbols in order in the sorted + table. The sorted table is work[], with that space being provided by + the caller. + + The length counts are used for other purposes as well, i.e. finding + the minimum and maximum length codes, determining if there are any + codes at all, checking for a valid set of lengths, and looking ahead + at length counts to determine sub-table sizes when building the + decoding tables. + */ + + /* accumulate lengths for codes (assumes lens[] all in 0..MAXBITS) */ + for (len = 0; len <= MAXBITS; len++) + count[len] = 0; + for (sym = 0; sym < codes; sym++) + count[lens[sym]]++; + + /* bound code lengths, force root to be within code lengths */ + root = *bits; + for (max = MAXBITS; max >= 1; max--) + if (count[max] != 0) break; + if (root > max) root = max; + if (max == 0) { /* no symbols to code at all */ + here.op = (unsigned char)64; /* invalid code marker */ + here.bits = (unsigned char)1; + here.val = (unsigned short)0; + *(*table)++ = here; /* make a table to force an error */ + *(*table)++ = here; + *bits = 1; + return 0; /* no symbols, but wait for decoding to report error */ + } + for (min = 1; min < max; min++) + if (count[min] != 0) break; + if (root < min) root = min; + + /* check for an over-subscribed or incomplete set of lengths */ + left = 1; + for (len = 1; len <= MAXBITS; len++) { + left <<= 1; + left -= count[len]; + if (left < 0) return -1; /* over-subscribed */ + } + if (left > 0 && (type == CODES || max != 1)) + return -1; /* incomplete set */ + + /* generate offsets into symbol table for each length for sorting */ + offs[1] = 0; + for (len = 1; len < MAXBITS; len++) + offs[len + 1] = offs[len] + count[len]; + + /* sort symbols by length, by symbol order within each length */ + for (sym = 0; sym < codes; sym++) + if (lens[sym] != 0) work[offs[lens[sym]]++] = (unsigned short)sym; + + /* + Create and fill in decoding tables. In this loop, the table being + filled is at next and has curr index bits. The code being used is huff + with length len. That code is converted to an index by dropping drop + bits off of the bottom. For codes where len is less than drop + curr, + those top drop + curr - len bits are incremented through all values to + fill the table with replicated entries. + + root is the number of index bits for the root table. When len exceeds + root, sub-tables are created pointed to by the root entry with an index + of the low root bits of huff. This is saved in low to check for when a + new sub-table should be started. drop is zero when the root table is + being filled, and drop is root when sub-tables are being filled. + + When a new sub-table is needed, it is necessary to look ahead in the + code lengths to determine what size sub-table is needed. The length + counts are used for this, and so count[] is decremented as codes are + entered in the tables. + + used keeps track of how many table entries have been allocated from the + provided *table space. It is checked for LENS and DIST tables against + the constants ENOUGH_LENS and ENOUGH_DISTS to guard against changes in + the initial root table size constants. See the comments in inftrees.h + for more information. + + sym increments through all symbols, and the loop terminates when + all codes of length max, i.e. all codes, have been processed. This + routine permits incomplete codes, so another loop after this one fills + in the rest of the decoding tables with invalid code markers. + */ + + /* set up for code type */ + switch (type) { + case CODES: + base = extra = work; /* dummy value--not used */ + end = 19; + break; + case LENS: + base = lbase; + base -= 257; + extra = lext; + extra -= 257; + end = 256; + break; + default: /* DISTS */ + base = dbase; + extra = dext; + end = -1; + } + + /* initialize state for loop */ + huff = 0; /* starting code */ + sym = 0; /* starting code symbol */ + len = min; /* starting code length */ + next = *table; /* current table to fill in */ + curr = root; /* current table index bits */ + drop = 0; /* current bits to drop from code for index */ + low = (unsigned)(-1); /* trigger new sub-table when len > root */ + used = 1U << root; /* use root table entries */ + mask = used - 1; /* mask for comparing low */ + + /* check available table space */ + if ((type == LENS && used > ENOUGH_LENS) || + (type == DISTS && used > ENOUGH_DISTS)) + return 1; + + /* process all codes and make table entries */ + for (;;) { + /* create table entry */ + here.bits = (unsigned char)(len - drop); + if ((int)(work[sym]) < end) { + here.op = (unsigned char)0; + here.val = work[sym]; + } + else if ((int)(work[sym]) > end) { + here.op = (unsigned char)(extra[work[sym]]); + here.val = base[work[sym]]; + } + else { + here.op = (unsigned char)(32 + 64); /* end of block */ + here.val = 0; + } + + /* replicate for those indices with low len bits equal to huff */ + incr = 1U << (len - drop); + fill = 1U << curr; + min = fill; /* save offset to next table */ + do { + fill -= incr; + next[(huff >> drop) + fill] = here; + } while (fill != 0); + + /* backwards increment the len-bit code huff */ + incr = 1U << (len - 1); + while (huff & incr) + incr >>= 1; + if (incr != 0) { + huff &= incr - 1; + huff += incr; + } + else + huff = 0; + + /* go to next symbol, update count, len */ + sym++; + if (--(count[len]) == 0) { + if (len == max) break; + len = lens[work[sym]]; + } + + /* create new sub-table if needed */ + if (len > root && (huff & mask) != low) { + /* if first time, transition to sub-tables */ + if (drop == 0) + drop = root; + + /* increment past last table */ + next += min; /* here min is 1 << curr */ + + /* determine length of next table */ + curr = len - drop; + left = (int)(1 << curr); + while (curr + drop < max) { + left -= count[curr + drop]; + if (left <= 0) break; + curr++; + left <<= 1; + } + + /* check for enough space */ + used += 1U << curr; + if ((type == LENS && used > ENOUGH_LENS) || + (type == DISTS && used > ENOUGH_DISTS)) + return 1; + + /* point entry in root table to sub-table */ + low = huff & mask; + (*table)[low].op = (unsigned char)curr; + (*table)[low].bits = (unsigned char)root; + (*table)[low].val = (unsigned short)(next - *table); + } + } + + /* fill in remaining table entry if code is incomplete (guaranteed to have + at most one remaining entry, since if the code is incomplete, the + maximum code length that was allowed to get this far is one bit) */ + if (huff != 0) { + here.op = (unsigned char)64; /* invalid code marker */ + here.bits = (unsigned char)(len - drop); + here.val = (unsigned short)0; + next[huff] = here; + } + + /* set return parameters */ + *table += used; + *bits = root; + return 0; +} diff --git a/src/zlib/inftrees.h b/src/zlib/inftrees.h new file mode 100644 index 00000000..baa53a0b --- /dev/null +++ b/src/zlib/inftrees.h @@ -0,0 +1,62 @@ +/* inftrees.h -- header to use inftrees.c + * Copyright (C) 1995-2005, 2010 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +/* Structure for decoding tables. Each entry provides either the + information needed to do the operation requested by the code that + indexed that table entry, or it provides a pointer to another + table that indexes more bits of the code. op indicates whether + the entry is a pointer to another table, a literal, a length or + distance, an end-of-block, or an invalid code. For a table + pointer, the low four bits of op is the number of index bits of + that table. For a length or distance, the low four bits of op + is the number of extra bits to get after the code. bits is + the number of bits in this code or part of the code to drop off + of the bit buffer. val is the actual byte to output in the case + of a literal, the base length or distance, or the offset from + the current table to the next table. Each entry is four bytes. */ +typedef struct { + unsigned char op; /* operation, extra bits, table bits */ + unsigned char bits; /* bits in this part of the code */ + unsigned short val; /* offset in table or code value */ +} code; + +/* op values as set by inflate_table(): + 00000000 - literal + 0000tttt - table link, tttt != 0 is the number of table index bits + 0001eeee - length or distance, eeee is the number of extra bits + 01100000 - end of block + 01000000 - invalid code + */ + +/* Maximum size of the dynamic table. The maximum number of code structures is + 1444, which is the sum of 852 for literal/length codes and 592 for distance + codes. These values were found by exhaustive searches using the program + examples/enough.c found in the zlib distribtution. The arguments to that + program are the number of symbols, the initial root table size, and the + maximum bit length of a code. "enough 286 9 15" for literal/length codes + returns returns 852, and "enough 30 6 15" for distance codes returns 592. + The initial root table size (9 or 6) is found in the fifth argument of the + inflate_table() calls in inflate.c and infback.c. If the root table size is + changed, then these maximum sizes would be need to be recalculated and + updated. */ +#define ENOUGH_LENS 852 +#define ENOUGH_DISTS 592 +#define ENOUGH (ENOUGH_LENS+ENOUGH_DISTS) + +/* Type of code to build for inflate_table() */ +typedef enum { + CODES, + LENS, + DISTS +} codetype; + +int ZLIB_INTERNAL inflate_table OF((codetype type, unsigned short FAR *lens, + unsigned codes, code FAR * FAR *table, + unsigned FAR *bits, unsigned short FAR *work)); diff --git a/src/zlib/trees.c b/src/zlib/trees.c new file mode 100644 index 00000000..4aa3c870 --- /dev/null +++ b/src/zlib/trees.c @@ -0,0 +1,1186 @@ +/* trees.c -- output deflated data using Huffman coding + * Copyright (C) 1995-2012 Jean-loup Gailly + * detect_data_type() function provided freely by Cosmin Truta, 2006 + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* + * ALGORITHM + * + * The "deflation" process uses several Huffman trees. The more + * common source values are represented by shorter bit sequences. + * + * Each code tree is stored in a compressed form which is itself + * a Huffman encoding of the lengths of all the code strings (in + * ascending order by source values). The actual code strings are + * reconstructed from the lengths in the inflate process, as described + * in the deflate specification. + * + * REFERENCES + * + * Deutsch, L.P.,"'Deflate' Compressed Data Format Specification". + * Available in ftp.uu.net:/pub/archiving/zip/doc/deflate-1.1.doc + * + * Storer, James A. + * Data Compression: Methods and Theory, pp. 49-50. + * Computer Science Press, 1988. ISBN 0-7167-8156-5. + * + * Sedgewick, R. + * Algorithms, p290. + * Addison-Wesley, 1983. ISBN 0-201-06672-6. + */ + +/* @(#) $Id: trees.c,v 1.2 2013/12/18 09:48:32 cfsmp3 Exp $ */ + +/* #define GEN_TREES_H */ + +#include "deflate.h" + +#ifdef DEBUG +# include +#endif + +/* =========================================================================== + * Constants + */ + +#define MAX_BL_BITS 7 +/* Bit length codes must not exceed MAX_BL_BITS bits */ + +#define END_BLOCK 256 +/* end of block literal code */ + +#define REP_3_6 16 +/* repeat previous bit length 3-6 times (2 bits of repeat count) */ + +#define REPZ_3_10 17 +/* repeat a zero length 3-10 times (3 bits of repeat count) */ + +#define REPZ_11_138 18 +/* repeat a zero length 11-138 times (7 bits of repeat count) */ + +local const int extra_lbits[LENGTH_CODES] /* extra bits for each length code */ + = {0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0}; + +local const int extra_dbits[D_CODES] /* extra bits for each distance code */ + = {0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13}; + +local const int extra_blbits[BL_CODES]/* extra bits for each bit length code */ + = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,3,7}; + +local const uch bl_order[BL_CODES] + = {16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15}; +/* The lengths of the bit length codes are sent in order of decreasing + * probability, to avoid transmitting the lengths for unused bit length codes. + */ + +/* =========================================================================== + * Local data. These are initialized only once. + */ + +#define DIST_CODE_LEN 512 /* see definition of array dist_code below */ + +#if defined(GEN_TREES_H) || !defined(STDC) +/* non ANSI compilers may not accept trees.h */ + +local ct_data static_ltree[L_CODES+2]; +/* The static literal tree. Since the bit lengths are imposed, there is no + * need for the L_CODES extra codes used during heap construction. However + * The codes 286 and 287 are needed to build a canonical tree (see _tr_init + * below). + */ + +local ct_data static_dtree[D_CODES]; +/* The static distance tree. (Actually a trivial tree since all codes use + * 5 bits.) + */ + +uch _dist_code[DIST_CODE_LEN]; +/* Distance codes. The first 256 values correspond to the distances + * 3 .. 258, the last 256 values correspond to the top 8 bits of + * the 15 bit distances. + */ + +uch _length_code[MAX_MATCH-MIN_MATCH+1]; +/* length code for each normalized match length (0 == MIN_MATCH) */ + +local int base_length[LENGTH_CODES]; +/* First normalized length for each code (0 = MIN_MATCH) */ + +local int base_dist[D_CODES]; +/* First normalized distance for each code (0 = distance of 1) */ + +#else +# include "trees.h" +#endif /* GEN_TREES_H */ + +struct static_tree_desc_s { + const ct_data *static_tree; /* static tree or NULL */ + const intf *extra_bits; /* extra bits for each code or NULL */ + int extra_base; /* base index for extra_bits */ + int elems; /* max number of elements in the tree */ + int max_length; /* max bit length for the codes */ +}; + +local static_tree_desc static_l_desc = +{static_ltree, extra_lbits, LITERALS+1, L_CODES, MAX_BITS}; + +local static_tree_desc static_d_desc = +{static_dtree, extra_dbits, 0, D_CODES, MAX_BITS}; + +local static_tree_desc static_bl_desc = +{(const ct_data *)0, extra_blbits, 0, BL_CODES, MAX_BL_BITS}; + +/* =========================================================================== + * Local (static) routines in this file. + */ + +local void tr_static_init OF((void)); +local void init_block OF((deflate_state *s)); +local void pqdownheap OF((deflate_state *s, ct_data *tree, int k)); +local void gen_bitlen OF((deflate_state *s, tree_desc *desc)); +local void gen_codes OF((ct_data *tree, int max_code, ushf *bl_count)); +local void build_tree OF((deflate_state *s, tree_desc *desc)); +local void scan_tree OF((deflate_state *s, ct_data *tree, int max_code)); +local void send_tree OF((deflate_state *s, ct_data *tree, int max_code)); +local int build_bl_tree OF((deflate_state *s)); +local void send_all_trees OF((deflate_state *s, int lcodes, int dcodes, + int blcodes)); +local void compress_block OF((deflate_state *s, const ct_data *ltree, + const ct_data *dtree)); +local int detect_data_type OF((deflate_state *s)); +local unsigned bi_reverse OF((unsigned value, int length)); +local void bi_windup OF((deflate_state *s)); +local void bi_flush OF((deflate_state *s)); +local void copy_block OF((deflate_state *s, charf *buf, unsigned len, + int header)); + +#ifdef GEN_TREES_H +local void gen_trees_header OF((void)); +#endif + +#ifndef DEBUG +# define send_code(s, c, tree) send_bits(s, tree[c].Code, tree[c].Len) + /* Send a code of the given tree. c and tree must not have side effects */ + +#else /* DEBUG */ +# define send_code(s, c, tree) \ + { if (z_verbose>2) fprintf(stderr,"\ncd %3d ",(c)); \ + send_bits(s, tree[c].Code, tree[c].Len); } +#endif + +/* =========================================================================== + * Output a short LSB first on the stream. + * IN assertion: there is enough room in pendingBuf. + */ +#define put_short(s, w) { \ + put_byte(s, (uch)((w) & 0xff)); \ + put_byte(s, (uch)((ush)(w) >> 8)); \ +} + +/* =========================================================================== + * Send a value on a given number of bits. + * IN assertion: length <= 16 and value fits in length bits. + */ +#ifdef DEBUG +local void send_bits OF((deflate_state *s, int value, int length)); + +local void send_bits(deflate_state *s, int value, int length) +{ + Tracevv((stderr," l %2d v %4x ", length, value)); + Assert(length > 0 && length <= 15, "invalid length"); + s->bits_sent += (ulg)length; + + /* If not enough room in bi_buf, use (valid) bits from bi_buf and + * (16 - bi_valid) bits from value, leaving (width - (16-bi_valid)) + * unused bits in value. + */ + if (s->bi_valid > (int)Buf_size - length) { + s->bi_buf |= (ush)value << s->bi_valid; + put_short(s, s->bi_buf); + s->bi_buf = (ush)value >> (Buf_size - s->bi_valid); + s->bi_valid += length - Buf_size; + } else { + s->bi_buf |= (ush)value << s->bi_valid; + s->bi_valid += length; + } +} +#else /* !DEBUG */ + +#define send_bits(s, value, length) \ +{ int len = length;\ + if (s->bi_valid > (int)Buf_size - len) {\ + int val = value;\ + s->bi_buf |= (ush)val << s->bi_valid;\ + put_short(s, s->bi_buf);\ + s->bi_buf = (ush)val >> (Buf_size - s->bi_valid);\ + s->bi_valid += len - Buf_size;\ + } else {\ + s->bi_buf |= (ush)(value) << s->bi_valid;\ + s->bi_valid += len;\ + }\ +} +#endif /* DEBUG */ + + +/* the arguments must not have side effects */ + +/* =========================================================================== + * Initialize the various 'constant' tables. + */ +local void tr_static_init() +{ +#if defined(GEN_TREES_H) || !defined(STDC) + static int static_init_done = 0; + int n; /* iterates over tree elements */ + int bits; /* bit counter */ + int length; /* length value */ + int code; /* code value */ + int dist; /* distance index */ + ush bl_count[MAX_BITS+1]; + /* number of codes at each bit length for an optimal tree */ + + if (static_init_done) return; + + /* For some embedded targets, global variables are not initialized: */ +#ifdef NO_INIT_GLOBAL_POINTERS + static_l_desc.static_tree = static_ltree; + static_l_desc.extra_bits = extra_lbits; + static_d_desc.static_tree = static_dtree; + static_d_desc.extra_bits = extra_dbits; + static_bl_desc.extra_bits = extra_blbits; +#endif + + /* Initialize the mapping length (0..255) -> length code (0..28) */ + length = 0; + for (code = 0; code < LENGTH_CODES-1; code++) { + base_length[code] = length; + for (n = 0; n < (1< dist code (0..29) */ + dist = 0; + for (code = 0 ; code < 16; code++) { + base_dist[code] = dist; + for (n = 0; n < (1<>= 7; /* from now on, all distances are divided by 128 */ + for ( ; code < D_CODES; code++) { + base_dist[code] = dist << 7; + for (n = 0; n < (1<<(extra_dbits[code]-7)); n++) { + _dist_code[256 + dist++] = (uch)code; + } + } + Assert (dist == 256, "tr_static_init: 256+dist != 512"); + + /* Construct the codes of the static literal tree */ + for (bits = 0; bits <= MAX_BITS; bits++) bl_count[bits] = 0; + n = 0; + while (n <= 143) static_ltree[n++].Len = 8, bl_count[8]++; + while (n <= 255) static_ltree[n++].Len = 9, bl_count[9]++; + while (n <= 279) static_ltree[n++].Len = 7, bl_count[7]++; + while (n <= 287) static_ltree[n++].Len = 8, bl_count[8]++; + /* Codes 286 and 287 do not exist, but we must include them in the + * tree construction to get a canonical Huffman tree (longest code + * all ones) + */ + gen_codes((ct_data *)static_ltree, L_CODES+1, bl_count); + + /* The static distance tree is trivial: */ + for (n = 0; n < D_CODES; n++) { + static_dtree[n].Len = 5; + static_dtree[n].Code = bi_reverse((unsigned)n, 5); + } + static_init_done = 1; + +# ifdef GEN_TREES_H + gen_trees_header(); +# endif +#endif /* defined(GEN_TREES_H) || !defined(STDC) */ +} + +/* =========================================================================== + * Genererate the file trees.h describing the static trees. + */ +#ifdef GEN_TREES_H +# ifndef DEBUG +# include +# endif + +# define SEPARATOR(i, last, width) \ + ((i) == (last)? "\n};\n\n" : \ + ((i) % (width) == (width)-1 ? ",\n" : ", ")) + +void gen_trees_header() +{ + FILE *header = fopen("trees.h", "w"); + int i; + + Assert (header != NULL, "Can't open trees.h"); + fprintf(header, + "/* header created automatically with -DGEN_TREES_H */\n\n"); + + fprintf(header, "local const ct_data static_ltree[L_CODES+2] = {\n"); + for (i = 0; i < L_CODES+2; i++) { + fprintf(header, "{{%3u},{%3u}}%s", static_ltree[i].Code, + static_ltree[i].Len, SEPARATOR(i, L_CODES+1, 5)); + } + + fprintf(header, "local const ct_data static_dtree[D_CODES] = {\n"); + for (i = 0; i < D_CODES; i++) { + fprintf(header, "{{%2u},{%2u}}%s", static_dtree[i].Code, + static_dtree[i].Len, SEPARATOR(i, D_CODES-1, 5)); + } + + fprintf(header, "const uch ZLIB_INTERNAL _dist_code[DIST_CODE_LEN] = {\n"); + for (i = 0; i < DIST_CODE_LEN; i++) { + fprintf(header, "%2u%s", _dist_code[i], + SEPARATOR(i, DIST_CODE_LEN-1, 20)); + } + + fprintf(header, + "const uch ZLIB_INTERNAL _length_code[MAX_MATCH-MIN_MATCH+1]= {\n"); + for (i = 0; i < MAX_MATCH-MIN_MATCH+1; i++) { + fprintf(header, "%2u%s", _length_code[i], + SEPARATOR(i, MAX_MATCH-MIN_MATCH, 20)); + } + + fprintf(header, "local const int base_length[LENGTH_CODES] = {\n"); + for (i = 0; i < LENGTH_CODES; i++) { + fprintf(header, "%1u%s", base_length[i], + SEPARATOR(i, LENGTH_CODES-1, 20)); + } + + fprintf(header, "local const int base_dist[D_CODES] = {\n"); + for (i = 0; i < D_CODES; i++) { + fprintf(header, "%5u%s", base_dist[i], + SEPARATOR(i, D_CODES-1, 10)); + } + + fclose(header); +} +#endif /* GEN_TREES_H */ + +/* =========================================================================== + * Initialize the tree data structures for a new zlib stream. + */ +void ZLIB_INTERNAL _tr_init(deflate_state *s) +{ + tr_static_init(); + + s->l_desc.dyn_tree = s->dyn_ltree; + s->l_desc.stat_desc = &static_l_desc; + + s->d_desc.dyn_tree = s->dyn_dtree; + s->d_desc.stat_desc = &static_d_desc; + + s->bl_desc.dyn_tree = s->bl_tree; + s->bl_desc.stat_desc = &static_bl_desc; + + s->bi_buf = 0; + s->bi_valid = 0; +#ifdef DEBUG + s->compressed_len = 0L; + s->bits_sent = 0L; +#endif + + /* Initialize the first block of the first file: */ + init_block(s); +} + +/* =========================================================================== + * Initialize a new block. + */ +local void init_block(deflate_state *s) +{ + int n; /* iterates over tree elements */ + + /* Initialize the trees. */ + for (n = 0; n < L_CODES; n++) s->dyn_ltree[n].Freq = 0; + for (n = 0; n < D_CODES; n++) s->dyn_dtree[n].Freq = 0; + for (n = 0; n < BL_CODES; n++) s->bl_tree[n].Freq = 0; + + s->dyn_ltree[END_BLOCK].Freq = 1; + s->opt_len = s->static_len = 0L; + s->last_lit = s->matches = 0; +} + +#define SMALLEST 1 +/* Index within the heap array of least frequent node in the Huffman tree */ + + +/* =========================================================================== + * Remove the smallest element from the heap and recreate the heap with + * one less element. Updates heap and heap_len. + */ +#define pqremove(s, tree, top) \ +{\ + top = s->heap[SMALLEST]; \ + s->heap[SMALLEST] = s->heap[s->heap_len--]; \ + pqdownheap(s, tree, SMALLEST); \ +} + +/* =========================================================================== + * Compares to subtrees, using the tree depth as tie breaker when + * the subtrees have equal frequency. This minimizes the worst case length. + */ +#define smaller(tree, n, m, depth) \ + (tree[n].Freq < tree[m].Freq || \ + (tree[n].Freq == tree[m].Freq && depth[n] <= depth[m])) + +/* =========================================================================== + * Restore the heap property by moving down the tree starting at node k, + * exchanging a node with the smallest of its two sons if necessary, stopping + * when the heap property is re-established (each father smaller than its + * two sons). + */ +local void pqdownheap(deflate_state *s, + ct_data *tree, /* the tree to restore */ + int k) /* node to move down */ +{ + int v = s->heap[k]; + int j = k << 1; /* left son of k */ + while (j <= s->heap_len) { + /* Set j to the smallest of the two sons: */ + if (j < s->heap_len && + smaller(tree, s->heap[j+1], s->heap[j], s->depth)) { + j++; + } + /* Exit if v is smaller than both sons */ + if (smaller(tree, v, s->heap[j], s->depth)) break; + + /* Exchange v with the smallest son */ + s->heap[k] = s->heap[j]; k = j; + + /* And continue down the tree, setting j to the left son of k */ + j <<= 1; + } + s->heap[k] = v; +} + +/* =========================================================================== + * Compute the optimal bit lengths for a tree and update the total bit length + * for the current block. + * IN assertion: the fields freq and dad are set, heap[heap_max] and + * above are the tree nodes sorted by increasing frequency. + * OUT assertions: the field len is set to the optimal bit length, the + * array bl_count contains the frequencies for each bit length. + * The length opt_len is updated; static_len is also updated if stree is + * not null. + */ +local void gen_bitlen(deflate_state *s, + tree_desc *desc) /* the tree descriptor */ +{ + ct_data *tree = desc->dyn_tree; + int max_code = desc->max_code; + const ct_data *stree = desc->stat_desc->static_tree; + const intf *extra = desc->stat_desc->extra_bits; + int base = desc->stat_desc->extra_base; + int max_length = desc->stat_desc->max_length; + int h; /* heap index */ + int n, m; /* iterate over the tree elements */ + int bits; /* bit length */ + int xbits; /* extra bits */ + ush f; /* frequency */ + int overflow = 0; /* number of elements with bit length too large */ + + for (bits = 0; bits <= MAX_BITS; bits++) s->bl_count[bits] = 0; + + /* In a first pass, compute the optimal bit lengths (which may + * overflow in the case of the bit length tree). + */ + tree[s->heap[s->heap_max]].Len = 0; /* root of the heap */ + + for (h = s->heap_max+1; h < HEAP_SIZE; h++) { + n = s->heap[h]; + bits = tree[tree[n].Dad].Len + 1; + if (bits > max_length) bits = max_length, overflow++; + tree[n].Len = (ush)bits; + /* We overwrite tree[n].Dad which is no longer needed */ + + if (n > max_code) continue; /* not a leaf node */ + + s->bl_count[bits]++; + xbits = 0; + if (n >= base) xbits = extra[n-base]; + f = tree[n].Freq; + s->opt_len += (ulg)f * (bits + xbits); + if (stree) s->static_len += (ulg)f * (stree[n].Len + xbits); + } + if (overflow == 0) return; + + Trace((stderr,"\nbit length overflow\n")); + /* This happens for example on obj2 and pic of the Calgary corpus */ + + /* Find the first bit length which could increase: */ + do { + bits = max_length-1; + while (s->bl_count[bits] == 0) bits--; + s->bl_count[bits]--; /* move one leaf down the tree */ + s->bl_count[bits+1] += 2; /* move one overflow item as its brother */ + s->bl_count[max_length]--; + /* The brother of the overflow item also moves one step up, + * but this does not affect bl_count[max_length] + */ + overflow -= 2; + } while (overflow > 0); + + /* Now recompute all bit lengths, scanning in increasing frequency. + * h is still equal to HEAP_SIZE. (It is simpler to reconstruct all + * lengths instead of fixing only the wrong ones. This idea is taken + * from 'ar' written by Haruhiko Okumura.) + */ + for (bits = max_length; bits != 0; bits--) { + n = s->bl_count[bits]; + while (n != 0) { + m = s->heap[--h]; + if (m > max_code) continue; + if ((unsigned) tree[m].Len != (unsigned) bits) { + Trace((stderr,"code %d bits %d->%d\n", m, tree[m].Len, bits)); + s->opt_len += ((long)bits - (long)tree[m].Len) + *(long)tree[m].Freq; + tree[m].Len = (ush)bits; + } + n--; + } + } +} + +/* =========================================================================== + * Generate the codes for a given tree and bit counts (which need not be + * optimal). + * IN assertion: the array bl_count contains the bit length statistics for + * the given tree and the field len is set for all tree elements. + * OUT assertion: the field code is set for all tree elements of non + * zero code length. + */ +local void gen_codes (ct_data *tree, /* the tree to decorate */ + int max_code, /* largest code with non zero frequency */ + ushf *bl_count) /* number of codes at each bit length */ +{ + ush next_code[MAX_BITS+1]; /* next code value for each bit length */ + ush code = 0; /* running code value */ + int bits; /* bit index */ + int n; /* code index */ + + /* The distribution counts are first used to generate the code values + * without bit reversal. + */ + for (bits = 1; bits <= MAX_BITS; bits++) { + next_code[bits] = code = (code + bl_count[bits-1]) << 1; + } + /* Check that the bit counts in bl_count are consistent. The last code + * must be all ones. + */ + Assert (code + bl_count[MAX_BITS]-1 == (1<dyn_tree; + const ct_data *stree = desc->stat_desc->static_tree; + int elems = desc->stat_desc->elems; + int n, m; /* iterate over heap elements */ + int max_code = -1; /* largest code with non zero frequency */ + int node; /* new node being created */ + + /* Construct the initial heap, with least frequent element in + * heap[SMALLEST]. The sons of heap[n] are heap[2*n] and heap[2*n+1]. + * heap[0] is not used. + */ + s->heap_len = 0, s->heap_max = HEAP_SIZE; + + for (n = 0; n < elems; n++) { + if (tree[n].Freq != 0) { + s->heap[++(s->heap_len)] = max_code = n; + s->depth[n] = 0; + } else { + tree[n].Len = 0; + } + } + + /* The pkzip format requires that at least one distance code exists, + * and that at least one bit should be sent even if there is only one + * possible code. So to avoid special checks later on we force at least + * two codes of non zero frequency. + */ + while (s->heap_len < 2) { + node = s->heap[++(s->heap_len)] = (max_code < 2 ? ++max_code : 0); + tree[node].Freq = 1; + s->depth[node] = 0; + s->opt_len--; if (stree) s->static_len -= stree[node].Len; + /* node is 0 or 1 so it does not have extra bits */ + } + desc->max_code = max_code; + + /* The elements heap[heap_len/2+1 .. heap_len] are leaves of the tree, + * establish sub-heaps of increasing lengths: + */ + for (n = s->heap_len/2; n >= 1; n--) pqdownheap(s, tree, n); + + /* Construct the Huffman tree by repeatedly combining the least two + * frequent nodes. + */ + node = elems; /* next internal node of the tree */ + do { + pqremove(s, tree, n); /* n = node of least frequency */ + m = s->heap[SMALLEST]; /* m = node of next least frequency */ + + s->heap[--(s->heap_max)] = n; /* keep the nodes sorted by frequency */ + s->heap[--(s->heap_max)] = m; + + /* Create a new node father of n and m */ + tree[node].Freq = tree[n].Freq + tree[m].Freq; + s->depth[node] = (uch)((s->depth[n] >= s->depth[m] ? + s->depth[n] : s->depth[m]) + 1); + tree[n].Dad = tree[m].Dad = (ush)node; +#ifdef DUMP_BL_TREE + if (tree == s->bl_tree) { + fprintf(stderr,"\nnode %d(%d), sons %d(%d) %d(%d)", + node, tree[node].Freq, n, tree[n].Freq, m, tree[m].Freq); + } +#endif + /* and insert the new node in the heap */ + s->heap[SMALLEST] = node++; + pqdownheap(s, tree, SMALLEST); + + } while (s->heap_len >= 2); + + s->heap[--(s->heap_max)] = s->heap[SMALLEST]; + + /* At this point, the fields freq and dad are set. We can now + * generate the bit lengths. + */ + gen_bitlen(s, (tree_desc *)desc); + + /* The field len is now set, we can generate the bit codes */ + gen_codes ((ct_data *)tree, max_code, s->bl_count); +} + +/* =========================================================================== + * Scan a literal or distance tree to determine the frequencies of the codes + * in the bit length tree. + */ +local void scan_tree (deflate_state *s, ct_data *tree, int max_code) +{ + int n; /* iterates over all tree elements */ + int prevlen = -1; /* last emitted length */ + int curlen; /* length of current code */ + int nextlen = tree[0].Len; /* length of next code */ + int count = 0; /* repeat count of the current code */ + int max_count = 7; /* max repeat count */ + int min_count = 4; /* min repeat count */ + + if (nextlen == 0) max_count = 138, min_count = 3; + tree[max_code+1].Len = (ush)0xffff; /* guard */ + + for (n = 0; n <= max_code; n++) { + curlen = nextlen; nextlen = tree[n+1].Len; + if (++count < max_count && curlen == nextlen) { + continue; + } else if (count < min_count) { + s->bl_tree[curlen].Freq += count; + } else if (curlen != 0) { + if (curlen != prevlen) s->bl_tree[curlen].Freq++; + s->bl_tree[REP_3_6].Freq++; + } else if (count <= 10) { + s->bl_tree[REPZ_3_10].Freq++; + } else { + s->bl_tree[REPZ_11_138].Freq++; + } + count = 0; prevlen = curlen; + if (nextlen == 0) { + max_count = 138, min_count = 3; + } else if (curlen == nextlen) { + max_count = 6, min_count = 3; + } else { + max_count = 7, min_count = 4; + } + } +} + +/* =========================================================================== + * Send a literal or distance tree in compressed form, using the codes in + * bl_tree. + */ +local void send_tree (deflate_state *s, ct_data *tree, int max_code) +{ + int n; /* iterates over all tree elements */ + int prevlen = -1; /* last emitted length */ + int curlen; /* length of current code */ + int nextlen = tree[0].Len; /* length of next code */ + int count = 0; /* repeat count of the current code */ + int max_count = 7; /* max repeat count */ + int min_count = 4; /* min repeat count */ + + /* tree[max_code+1].Len = -1; */ /* guard already set */ + if (nextlen == 0) max_count = 138, min_count = 3; + + for (n = 0; n <= max_code; n++) { + curlen = nextlen; nextlen = tree[n+1].Len; + if (++count < max_count && curlen == nextlen) { + continue; + } else if (count < min_count) { + do { send_code(s, curlen, s->bl_tree); } while (--count != 0); + + } else if (curlen != 0) { + if (curlen != prevlen) { + send_code(s, curlen, s->bl_tree); count--; + } + Assert(count >= 3 && count <= 6, " 3_6?"); + send_code(s, REP_3_6, s->bl_tree); send_bits(s, count-3, 2); + + } else if (count <= 10) { + send_code(s, REPZ_3_10, s->bl_tree); send_bits(s, count-3, 3); + + } else { + send_code(s, REPZ_11_138, s->bl_tree); send_bits(s, count-11, 7); + } + count = 0; prevlen = curlen; + if (nextlen == 0) { + max_count = 138, min_count = 3; + } else if (curlen == nextlen) { + max_count = 6, min_count = 3; + } else { + max_count = 7, min_count = 4; + } + } +} + +/* =========================================================================== + * Construct the Huffman tree for the bit lengths and return the index in + * bl_order of the last bit length code to send. + */ +local int build_bl_tree(deflate_state *s) +{ + int max_blindex; /* index of last bit length code of non zero freq */ + + /* Determine the bit length frequencies for literal and distance trees */ + scan_tree(s, (ct_data *)s->dyn_ltree, s->l_desc.max_code); + scan_tree(s, (ct_data *)s->dyn_dtree, s->d_desc.max_code); + + /* Build the bit length tree: */ + build_tree(s, (tree_desc *)(&(s->bl_desc))); + /* opt_len now includes the length of the tree representations, except + * the lengths of the bit lengths codes and the 5+5+4 bits for the counts. + */ + + /* Determine the number of bit length codes to send. The pkzip format + * requires that at least 4 bit length codes be sent. (appnote.txt says + * 3 but the actual value used is 4.) + */ + for (max_blindex = BL_CODES-1; max_blindex >= 3; max_blindex--) { + if (s->bl_tree[bl_order[max_blindex]].Len != 0) break; + } + /* Update opt_len to include the bit length tree and counts */ + s->opt_len += 3*(max_blindex+1) + 5+5+4; + Tracev((stderr, "\ndyn trees: dyn %ld, stat %ld", + s->opt_len, s->static_len)); + + return max_blindex; +} + +/* =========================================================================== + * Send the header for a block using dynamic Huffman trees: the counts, the + * lengths of the bit length codes, the literal tree and the distance tree. + * IN assertion: lcodes >= 257, dcodes >= 1, blcodes >= 4. + */ +local void send_all_trees(deflate_state *s, int lcodes, int dcodes, int blcodes) +{ + int rank; /* index in bl_order */ + + Assert (lcodes >= 257 && dcodes >= 1 && blcodes >= 4, "not enough codes"); + Assert (lcodes <= L_CODES && dcodes <= D_CODES && blcodes <= BL_CODES, + "too many codes"); + Tracev((stderr, "\nbl counts: ")); + send_bits(s, lcodes-257, 5); /* not +255 as stated in appnote.txt */ + send_bits(s, dcodes-1, 5); + send_bits(s, blcodes-4, 4); /* not -3 as stated in appnote.txt */ + for (rank = 0; rank < blcodes; rank++) { + Tracev((stderr, "\nbl code %2d ", bl_order[rank])); + send_bits(s, s->bl_tree[bl_order[rank]].Len, 3); + } + Tracev((stderr, "\nbl tree: sent %ld", s->bits_sent)); + + send_tree(s, (ct_data *)s->dyn_ltree, lcodes-1); /* literal tree */ + Tracev((stderr, "\nlit tree: sent %ld", s->bits_sent)); + + send_tree(s, (ct_data *)s->dyn_dtree, dcodes-1); /* distance tree */ + Tracev((stderr, "\ndist tree: sent %ld", s->bits_sent)); +} + +/* =========================================================================== + * Send a stored block + */ +void ZLIB_INTERNAL _tr_stored_block(deflate_state *s, charf *buf, + ulg stored_len, int last) +{ + send_bits(s, (STORED_BLOCK<<1)+last, 3); /* send block type */ +#ifdef DEBUG + s->compressed_len = (s->compressed_len + 3 + 7) & (ulg)~7L; + s->compressed_len += (stored_len + 4) << 3; +#endif + copy_block(s, buf, (unsigned)stored_len, 1); /* with header */ +} + +/* =========================================================================== + * Flush the bits in the bit buffer to pending output (leaves at most 7 bits) + */ +void ZLIB_INTERNAL _tr_flush_bits(deflate_state *s) +{ + bi_flush(s); +} + +/* =========================================================================== + * Send one empty static block to give enough lookahead for inflate. + * This takes 10 bits, of which 7 may remain in the bit buffer. + */ +void ZLIB_INTERNAL _tr_align(deflate_state *s) +{ + send_bits(s, STATIC_TREES<<1, 3); + send_code(s, END_BLOCK, static_ltree); +#ifdef DEBUG + s->compressed_len += 10L; /* 3 for block type, 7 for EOB */ +#endif + bi_flush(s); +} + +/* =========================================================================== + * Determine the best encoding for the current block: dynamic trees, static + * trees or store, and output the encoded block to the zip file. + */ +void ZLIB_INTERNAL _tr_flush_block(deflate_state *s, + charf *buf, /* input block, or NULL if too old */ + ulg stored_len, /* length of input block */ + int last) /* one if this is the last block for a file */ +{ + ulg opt_lenb, static_lenb; /* opt_len and static_len in bytes */ + int max_blindex = 0; /* index of last bit length code of non zero freq */ + + /* Build the Huffman trees unless a stored block is forced */ + if (s->level > 0) { + + /* Check if the file is binary or text */ + if (s->strm->data_type == Z_UNKNOWN) + s->strm->data_type = detect_data_type(s); + + /* Construct the literal and distance trees */ + build_tree(s, (tree_desc *)(&(s->l_desc))); + Tracev((stderr, "\nlit data: dyn %ld, stat %ld", s->opt_len, + s->static_len)); + + build_tree(s, (tree_desc *)(&(s->d_desc))); + Tracev((stderr, "\ndist data: dyn %ld, stat %ld", s->opt_len, + s->static_len)); + /* At this point, opt_len and static_len are the total bit lengths of + * the compressed block data, excluding the tree representations. + */ + + /* Build the bit length tree for the above two trees, and get the index + * in bl_order of the last bit length code to send. + */ + max_blindex = build_bl_tree(s); + + /* Determine the best encoding. Compute the block lengths in bytes. */ + opt_lenb = (s->opt_len+3+7)>>3; + static_lenb = (s->static_len+3+7)>>3; + + Tracev((stderr, "\nopt %lu(%lu) stat %lu(%lu) stored %lu lit %u ", + opt_lenb, s->opt_len, static_lenb, s->static_len, stored_len, + s->last_lit)); + + if (static_lenb <= opt_lenb) opt_lenb = static_lenb; + + } else { + Assert(buf != (char*)0, "lost buf"); + opt_lenb = static_lenb = stored_len + 5; /* force a stored block */ + } + +#ifdef FORCE_STORED + if (buf != (char*)0) { /* force stored block */ +#else + if (stored_len+4 <= opt_lenb && buf != (char*)0) { + /* 4: two words for the lengths */ +#endif + /* The test buf != NULL is only necessary if LIT_BUFSIZE > WSIZE. + * Otherwise we can't have processed more than WSIZE input bytes since + * the last block flush, because compression would have been + * successful. If LIT_BUFSIZE <= WSIZE, it is never too late to + * transform a block into a stored block. + */ + _tr_stored_block(s, buf, stored_len, last); + +#ifdef FORCE_STATIC + } else if (static_lenb >= 0) { /* force static trees */ +#else + } else if (s->strategy == Z_FIXED || static_lenb == opt_lenb) { +#endif + send_bits(s, (STATIC_TREES<<1)+last, 3); + compress_block(s, (const ct_data *)static_ltree, + (const ct_data *)static_dtree); +#ifdef DEBUG + s->compressed_len += 3 + s->static_len; +#endif + } else { + send_bits(s, (DYN_TREES<<1)+last, 3); + send_all_trees(s, s->l_desc.max_code+1, s->d_desc.max_code+1, + max_blindex+1); + compress_block(s, (const ct_data *)s->dyn_ltree, + (const ct_data *)s->dyn_dtree); +#ifdef DEBUG + s->compressed_len += 3 + s->opt_len; +#endif + } + Assert (s->compressed_len == s->bits_sent, "bad compressed size"); + /* The above check is made mod 2^32, for files larger than 512 MB + * and uLong implemented on 32 bits. + */ + init_block(s); + + if (last) { + bi_windup(s); +#ifdef DEBUG + s->compressed_len += 7; /* align on byte boundary */ +#endif + } + Tracev((stderr,"\ncomprlen %lu(%lu) ", s->compressed_len>>3, + s->compressed_len-7*last)); +} + +/* =========================================================================== + * Save the match info and tally the frequency counts. Return true if + * the current block must be flushed. + */ +int ZLIB_INTERNAL _tr_tally (deflate_state *s, unsigned dist, unsigned lc) +{ + s->d_buf[s->last_lit] = (ush)dist; + s->l_buf[s->last_lit++] = (uch)lc; + if (dist == 0) { + /* lc is the unmatched char */ + s->dyn_ltree[lc].Freq++; + } else { + s->matches++; + /* Here, lc is the match length - MIN_MATCH */ + dist--; /* dist = match distance - 1 */ + Assert((ush)dist < (ush)MAX_DIST(s) && + (ush)lc <= (ush)(MAX_MATCH-MIN_MATCH) && + (ush)d_code(dist) < (ush)D_CODES, "_tr_tally: bad match"); + + s->dyn_ltree[_length_code[lc]+LITERALS+1].Freq++; + s->dyn_dtree[d_code(dist)].Freq++; + } + +#ifdef TRUNCATE_BLOCK + /* Try to guess if it is profitable to stop the current block here */ + if ((s->last_lit & 0x1fff) == 0 && s->level > 2) { + /* Compute an upper bound for the compressed length */ + ulg out_length = (ulg)s->last_lit*8L; + ulg in_length = (ulg)((long)s->strstart - s->block_start); + int dcode; + for (dcode = 0; dcode < D_CODES; dcode++) { + out_length += (ulg)s->dyn_dtree[dcode].Freq * + (5L+extra_dbits[dcode]); + } + out_length >>= 3; + Tracev((stderr,"\nlast_lit %u, in %ld, out ~%ld(%ld%%) ", + s->last_lit, in_length, out_length, + 100L - out_length*100L/in_length)); + if (s->matches < s->last_lit/2 && out_length < in_length/2) return 1; + } +#endif + return (s->last_lit == s->lit_bufsize-1); + /* We avoid equality with lit_bufsize because of wraparound at 64K + * on 16 bit machines and because stored blocks are restricted to + * 64K-1 bytes. + */ +} + +/* =========================================================================== + * Send the block data compressed using the given Huffman trees + */ +local void compress_block(deflate_state *s, const ct_data *ltree, const ct_data *dtree) +{ + unsigned dist; /* distance of matched string */ + int lc; /* match length or unmatched char (if dist == 0) */ + unsigned lx = 0; /* running index in l_buf */ + unsigned code; /* the code to send */ + int extra; /* number of extra bits to send */ + + if (s->last_lit != 0) do { + dist = s->d_buf[lx]; + lc = s->l_buf[lx++]; + if (dist == 0) { + send_code(s, lc, ltree); /* send a literal byte */ + Tracecv(isgraph(lc), (stderr," '%c' ", lc)); + } else { + /* Here, lc is the match length - MIN_MATCH */ + code = _length_code[lc]; + send_code(s, code+LITERALS+1, ltree); /* send the length code */ + extra = extra_lbits[code]; + if (extra != 0) { + lc -= base_length[code]; + send_bits(s, lc, extra); /* send the extra length bits */ + } + dist--; /* dist is now the match distance - 1 */ + code = d_code(dist); + Assert (code < D_CODES, "bad d_code"); + + send_code(s, code, dtree); /* send the distance code */ + extra = extra_dbits[code]; + if (extra != 0) { + dist -= base_dist[code]; + send_bits(s, dist, extra); /* send the extra distance bits */ + } + } /* literal or match pair ? */ + + /* Check that the overlay between pending_buf and d_buf+l_buf is ok: */ + Assert((uInt)(s->pending) < s->lit_bufsize + 2*lx, + "pendingBuf overflow"); + + } while (lx < s->last_lit); + + send_code(s, END_BLOCK, ltree); +} + +/* =========================================================================== + * Check if the data type is TEXT or BINARY, using the following algorithm: + * - TEXT if the two conditions below are satisfied: + * a) There are no non-portable control characters belonging to the + * "black list" (0..6, 14..25, 28..31). + * b) There is at least one printable character belonging to the + * "white list" (9 {TAB}, 10 {LF}, 13 {CR}, 32..255). + * - BINARY otherwise. + * - The following partially-portable control characters form a + * "gray list" that is ignored in this detection algorithm: + * (7 {BEL}, 8 {BS}, 11 {VT}, 12 {FF}, 26 {SUB}, 27 {ESC}). + * IN assertion: the fields Freq of dyn_ltree are set. + */ +local int detect_data_type(deflate_state *s) +{ + /* black_mask is the bit mask of black-listed bytes + * set bits 0..6, 14..25, and 28..31 + * 0xf3ffc07f = binary 11110011111111111100000001111111 + */ + unsigned long black_mask = 0xf3ffc07fUL; + int n; + + /* Check for non-textual ("black-listed") bytes. */ + for (n = 0; n <= 31; n++, black_mask >>= 1) + if ((black_mask & 1) && (s->dyn_ltree[n].Freq != 0)) + return Z_BINARY; + + /* Check for textual ("white-listed") bytes. */ + if (s->dyn_ltree[9].Freq != 0 || s->dyn_ltree[10].Freq != 0 + || s->dyn_ltree[13].Freq != 0) + return Z_TEXT; + for (n = 32; n < LITERALS; n++) + if (s->dyn_ltree[n].Freq != 0) + return Z_TEXT; + + /* There are no "black-listed" or "white-listed" bytes: + * this stream either is empty or has tolerated ("gray-listed") bytes only. + */ + return Z_BINARY; +} + +/* =========================================================================== + * Reverse the first len bits of a code, using straightforward code (a faster + * method would use a table) + * IN assertion: 1 <= len <= 15 + */ +local unsigned bi_reverse(unsigned code, int len) +{ + register unsigned res = 0; + do { + res |= code & 1; + code >>= 1, res <<= 1; + } while (--len > 0); + return res >> 1; +} + +/* =========================================================================== + * Flush the bit buffer, keeping at most 7 bits in it. + */ +local void bi_flush(deflate_state *s) +{ + if (s->bi_valid == 16) { + put_short(s, s->bi_buf); + s->bi_buf = 0; + s->bi_valid = 0; + } else if (s->bi_valid >= 8) { + put_byte(s, (Byte)s->bi_buf); + s->bi_buf >>= 8; + s->bi_valid -= 8; + } +} + +/* =========================================================================== + * Flush the bit buffer and align the output on a byte boundary + */ +local void bi_windup(deflate_state *s) +{ + if (s->bi_valid > 8) { + put_short(s, s->bi_buf); + } else if (s->bi_valid > 0) { + put_byte(s, (Byte)s->bi_buf); + } + s->bi_buf = 0; + s->bi_valid = 0; +#ifdef DEBUG + s->bits_sent = (s->bits_sent+7) & ~7; +#endif +} + +/* =========================================================================== + * Copy a stored block, storing first the length and its + * one's complement if requested. + */ +local void copy_block(deflate_state *s, charf *buf, unsigned len, int header) +{ + bi_windup(s); /* align on byte boundary */ + + if (header) { + put_short(s, (ush)len); + put_short(s, (ush)~len); +#ifdef DEBUG + s->bits_sent += 2*16; +#endif + } +#ifdef DEBUG + s->bits_sent += (ulg)len<<3; +#endif + while (len--) { + put_byte(s, *buf++); + } +} diff --git a/src/zlib/trees.h b/src/zlib/trees.h new file mode 100644 index 00000000..d35639d8 --- /dev/null +++ b/src/zlib/trees.h @@ -0,0 +1,128 @@ +/* header created automatically with -DGEN_TREES_H */ + +local const ct_data static_ltree[L_CODES+2] = { +{{ 12},{ 8}}, {{140},{ 8}}, {{ 76},{ 8}}, {{204},{ 8}}, {{ 44},{ 8}}, +{{172},{ 8}}, {{108},{ 8}}, {{236},{ 8}}, {{ 28},{ 8}}, {{156},{ 8}}, +{{ 92},{ 8}}, {{220},{ 8}}, {{ 60},{ 8}}, {{188},{ 8}}, {{124},{ 8}}, +{{252},{ 8}}, {{ 2},{ 8}}, {{130},{ 8}}, {{ 66},{ 8}}, {{194},{ 8}}, +{{ 34},{ 8}}, {{162},{ 8}}, {{ 98},{ 8}}, {{226},{ 8}}, {{ 18},{ 8}}, +{{146},{ 8}}, {{ 82},{ 8}}, {{210},{ 8}}, {{ 50},{ 8}}, {{178},{ 8}}, +{{114},{ 8}}, {{242},{ 8}}, {{ 10},{ 8}}, {{138},{ 8}}, {{ 74},{ 8}}, +{{202},{ 8}}, {{ 42},{ 8}}, {{170},{ 8}}, {{106},{ 8}}, {{234},{ 8}}, +{{ 26},{ 8}}, {{154},{ 8}}, {{ 90},{ 8}}, {{218},{ 8}}, {{ 58},{ 8}}, +{{186},{ 8}}, {{122},{ 8}}, {{250},{ 8}}, {{ 6},{ 8}}, {{134},{ 8}}, +{{ 70},{ 8}}, {{198},{ 8}}, {{ 38},{ 8}}, {{166},{ 8}}, {{102},{ 8}}, +{{230},{ 8}}, {{ 22},{ 8}}, {{150},{ 8}}, {{ 86},{ 8}}, {{214},{ 8}}, +{{ 54},{ 8}}, {{182},{ 8}}, {{118},{ 8}}, {{246},{ 8}}, {{ 14},{ 8}}, +{{142},{ 8}}, {{ 78},{ 8}}, {{206},{ 8}}, {{ 46},{ 8}}, {{174},{ 8}}, +{{110},{ 8}}, {{238},{ 8}}, {{ 30},{ 8}}, {{158},{ 8}}, {{ 94},{ 8}}, +{{222},{ 8}}, {{ 62},{ 8}}, {{190},{ 8}}, {{126},{ 8}}, {{254},{ 8}}, +{{ 1},{ 8}}, {{129},{ 8}}, {{ 65},{ 8}}, {{193},{ 8}}, {{ 33},{ 8}}, +{{161},{ 8}}, {{ 97},{ 8}}, {{225},{ 8}}, {{ 17},{ 8}}, {{145},{ 8}}, +{{ 81},{ 8}}, {{209},{ 8}}, {{ 49},{ 8}}, {{177},{ 8}}, {{113},{ 8}}, +{{241},{ 8}}, {{ 9},{ 8}}, {{137},{ 8}}, {{ 73},{ 8}}, {{201},{ 8}}, +{{ 41},{ 8}}, {{169},{ 8}}, {{105},{ 8}}, {{233},{ 8}}, {{ 25},{ 8}}, +{{153},{ 8}}, {{ 89},{ 8}}, {{217},{ 8}}, {{ 57},{ 8}}, {{185},{ 8}}, +{{121},{ 8}}, {{249},{ 8}}, {{ 5},{ 8}}, {{133},{ 8}}, {{ 69},{ 8}}, +{{197},{ 8}}, {{ 37},{ 8}}, {{165},{ 8}}, {{101},{ 8}}, {{229},{ 8}}, +{{ 21},{ 8}}, {{149},{ 8}}, {{ 85},{ 8}}, {{213},{ 8}}, {{ 53},{ 8}}, +{{181},{ 8}}, {{117},{ 8}}, {{245},{ 8}}, {{ 13},{ 8}}, {{141},{ 8}}, +{{ 77},{ 8}}, {{205},{ 8}}, {{ 45},{ 8}}, {{173},{ 8}}, {{109},{ 8}}, +{{237},{ 8}}, {{ 29},{ 8}}, {{157},{ 8}}, {{ 93},{ 8}}, {{221},{ 8}}, +{{ 61},{ 8}}, {{189},{ 8}}, {{125},{ 8}}, {{253},{ 8}}, {{ 19},{ 9}}, +{{275},{ 9}}, {{147},{ 9}}, {{403},{ 9}}, {{ 83},{ 9}}, {{339},{ 9}}, +{{211},{ 9}}, {{467},{ 9}}, {{ 51},{ 9}}, {{307},{ 9}}, {{179},{ 9}}, +{{435},{ 9}}, {{115},{ 9}}, {{371},{ 9}}, {{243},{ 9}}, {{499},{ 9}}, +{{ 11},{ 9}}, {{267},{ 9}}, {{139},{ 9}}, {{395},{ 9}}, {{ 75},{ 9}}, +{{331},{ 9}}, {{203},{ 9}}, {{459},{ 9}}, {{ 43},{ 9}}, {{299},{ 9}}, +{{171},{ 9}}, {{427},{ 9}}, {{107},{ 9}}, {{363},{ 9}}, {{235},{ 9}}, +{{491},{ 9}}, {{ 27},{ 9}}, {{283},{ 9}}, {{155},{ 9}}, {{411},{ 9}}, +{{ 91},{ 9}}, {{347},{ 9}}, {{219},{ 9}}, {{475},{ 9}}, {{ 59},{ 9}}, +{{315},{ 9}}, {{187},{ 9}}, {{443},{ 9}}, {{123},{ 9}}, {{379},{ 9}}, +{{251},{ 9}}, {{507},{ 9}}, {{ 7},{ 9}}, {{263},{ 9}}, {{135},{ 9}}, +{{391},{ 9}}, {{ 71},{ 9}}, {{327},{ 9}}, {{199},{ 9}}, {{455},{ 9}}, +{{ 39},{ 9}}, {{295},{ 9}}, {{167},{ 9}}, {{423},{ 9}}, {{103},{ 9}}, +{{359},{ 9}}, {{231},{ 9}}, {{487},{ 9}}, {{ 23},{ 9}}, {{279},{ 9}}, +{{151},{ 9}}, {{407},{ 9}}, {{ 87},{ 9}}, {{343},{ 9}}, {{215},{ 9}}, +{{471},{ 9}}, {{ 55},{ 9}}, {{311},{ 9}}, {{183},{ 9}}, {{439},{ 9}}, +{{119},{ 9}}, {{375},{ 9}}, {{247},{ 9}}, {{503},{ 9}}, {{ 15},{ 9}}, +{{271},{ 9}}, {{143},{ 9}}, {{399},{ 9}}, {{ 79},{ 9}}, {{335},{ 9}}, +{{207},{ 9}}, {{463},{ 9}}, {{ 47},{ 9}}, {{303},{ 9}}, {{175},{ 9}}, +{{431},{ 9}}, {{111},{ 9}}, {{367},{ 9}}, {{239},{ 9}}, {{495},{ 9}}, +{{ 31},{ 9}}, {{287},{ 9}}, {{159},{ 9}}, {{415},{ 9}}, {{ 95},{ 9}}, +{{351},{ 9}}, {{223},{ 9}}, {{479},{ 9}}, {{ 63},{ 9}}, {{319},{ 9}}, +{{191},{ 9}}, {{447},{ 9}}, {{127},{ 9}}, {{383},{ 9}}, {{255},{ 9}}, +{{511},{ 9}}, {{ 0},{ 7}}, {{ 64},{ 7}}, {{ 32},{ 7}}, {{ 96},{ 7}}, +{{ 16},{ 7}}, {{ 80},{ 7}}, {{ 48},{ 7}}, {{112},{ 7}}, {{ 8},{ 7}}, +{{ 72},{ 7}}, {{ 40},{ 7}}, {{104},{ 7}}, {{ 24},{ 7}}, {{ 88},{ 7}}, +{{ 56},{ 7}}, {{120},{ 7}}, {{ 4},{ 7}}, {{ 68},{ 7}}, {{ 36},{ 7}}, +{{100},{ 7}}, {{ 20},{ 7}}, {{ 84},{ 7}}, {{ 52},{ 7}}, {{116},{ 7}}, +{{ 3},{ 8}}, {{131},{ 8}}, {{ 67},{ 8}}, {{195},{ 8}}, {{ 35},{ 8}}, +{{163},{ 8}}, {{ 99},{ 8}}, {{227},{ 8}} +}; + +local const ct_data static_dtree[D_CODES] = { +{{ 0},{ 5}}, {{16},{ 5}}, {{ 8},{ 5}}, {{24},{ 5}}, {{ 4},{ 5}}, +{{20},{ 5}}, {{12},{ 5}}, {{28},{ 5}}, {{ 2},{ 5}}, {{18},{ 5}}, +{{10},{ 5}}, {{26},{ 5}}, {{ 6},{ 5}}, {{22},{ 5}}, {{14},{ 5}}, +{{30},{ 5}}, {{ 1},{ 5}}, {{17},{ 5}}, {{ 9},{ 5}}, {{25},{ 5}}, +{{ 5},{ 5}}, {{21},{ 5}}, {{13},{ 5}}, {{29},{ 5}}, {{ 3},{ 5}}, +{{19},{ 5}}, {{11},{ 5}}, {{27},{ 5}}, {{ 7},{ 5}}, {{23},{ 5}} +}; + +const uch ZLIB_INTERNAL _dist_code[DIST_CODE_LEN] = { + 0, 1, 2, 3, 4, 4, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 8, + 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, +10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, +11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, +12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 13, 13, 13, 13, +13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, +13, 13, 13, 13, 13, 13, 13, 13, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, +14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, +14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, +14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 15, 15, 15, 15, 15, 15, 15, 15, +15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, +15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, +15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 0, 0, 16, 17, +18, 18, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22, 22, 22, +23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, +24, 24, 24, 24, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, +26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, +26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, +27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, +27, 27, 27, 27, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, +28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, +28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, +28, 28, 28, 28, 28, 28, 28, 28, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, +29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, +29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, +29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29 +}; + +const uch ZLIB_INTERNAL _length_code[MAX_MATCH-MIN_MATCH+1]= { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 12, 12, +13, 13, 13, 13, 14, 14, 14, 14, 15, 15, 15, 15, 16, 16, 16, 16, 16, 16, 16, 16, +17, 17, 17, 17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 18, 18, 18, 19, 19, 19, 19, +19, 19, 19, 19, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, +21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 22, 22, 22, 22, +22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 23, 23, 23, 23, 23, 23, 23, 23, +23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, +24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, +25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, +25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 26, 26, 26, 26, 26, 26, 26, 26, +26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, +26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, +27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 28 +}; + +local const int base_length[LENGTH_CODES] = { +0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 14, 16, 20, 24, 28, 32, 40, 48, 56, +64, 80, 96, 112, 128, 160, 192, 224, 0 +}; + +local const int base_dist[D_CODES] = { + 0, 1, 2, 3, 4, 6, 8, 12, 16, 24, + 32, 48, 64, 96, 128, 192, 256, 384, 512, 768, + 1024, 1536, 2048, 3072, 4096, 6144, 8192, 12288, 16384, 24576 +}; + diff --git a/src/zlib/zconf.h b/src/zlib/zconf.h new file mode 100644 index 00000000..e12ab3f3 --- /dev/null +++ b/src/zlib/zconf.h @@ -0,0 +1,511 @@ +/* zconf.h -- configuration of the zlib compression library + * Copyright (C) 1995-2013 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* @(#) $Id: zconf.h,v 1.1 2013/12/16 08:59:57 cfsmp3 Exp $ */ + +#ifndef ZCONF_H +#define ZCONF_H + +/* + * If you *really* need a unique prefix for all types and library functions, + * compile with -DZ_PREFIX. The "standard" zlib should be compiled without it. + * Even better than compiling with -DZ_PREFIX would be to use configure to set + * this permanently in zconf.h using "./configure --zprefix". + */ +#ifdef Z_PREFIX /* may be set to #if 1 by ./configure */ +# define Z_PREFIX_SET + +/* all linked symbols */ +# define _dist_code z__dist_code +# define _length_code z__length_code +# define _tr_align z__tr_align +# define _tr_flush_bits z__tr_flush_bits +# define _tr_flush_block z__tr_flush_block +# define _tr_init z__tr_init +# define _tr_stored_block z__tr_stored_block +# define _tr_tally z__tr_tally +# define adler32 z_adler32 +# define adler32_combine z_adler32_combine +# define adler32_combine64 z_adler32_combine64 +# ifndef Z_SOLO +# define compress z_compress +# define compress2 z_compress2 +# define compressBound z_compressBound +# endif +# define crc32 z_crc32 +# define crc32_combine z_crc32_combine +# define crc32_combine64 z_crc32_combine64 +# define deflate z_deflate +# define deflateBound z_deflateBound +# define deflateCopy z_deflateCopy +# define deflateEnd z_deflateEnd +# define deflateInit2_ z_deflateInit2_ +# define deflateInit_ z_deflateInit_ +# define deflateParams z_deflateParams +# define deflatePending z_deflatePending +# define deflatePrime z_deflatePrime +# define deflateReset z_deflateReset +# define deflateResetKeep z_deflateResetKeep +# define deflateSetDictionary z_deflateSetDictionary +# define deflateSetHeader z_deflateSetHeader +# define deflateTune z_deflateTune +# define deflate_copyright z_deflate_copyright +# define get_crc_table z_get_crc_table +# ifndef Z_SOLO +# define gz_error z_gz_error +# define gz_intmax z_gz_intmax +# define gz_strwinerror z_gz_strwinerror +# define gzbuffer z_gzbuffer +# define gzclearerr z_gzclearerr +# define gzclose z_gzclose +# define gzclose_r z_gzclose_r +# define gzclose_w z_gzclose_w +# define gzdirect z_gzdirect +# define gzdopen z_gzdopen +# define gzeof z_gzeof +# define gzerror z_gzerror +# define gzflush z_gzflush +# define gzgetc z_gzgetc +# define gzgetc_ z_gzgetc_ +# define gzgets z_gzgets +# define gzoffset z_gzoffset +# define gzoffset64 z_gzoffset64 +# define gzopen z_gzopen +# define gzopen64 z_gzopen64 +# ifdef _WIN32 +# define gzopen_w z_gzopen_w +# endif +# define gzprintf z_gzprintf +# define gzvprintf z_gzvprintf +# define gzputc z_gzputc +# define gzputs z_gzputs +# define gzread z_gzread +# define gzrewind z_gzrewind +# define gzseek z_gzseek +# define gzseek64 z_gzseek64 +# define gzsetparams z_gzsetparams +# define gztell z_gztell +# define gztell64 z_gztell64 +# define gzungetc z_gzungetc +# define gzwrite z_gzwrite +# endif +# define inflate z_inflate +# define inflateBack z_inflateBack +# define inflateBackEnd z_inflateBackEnd +# define inflateBackInit_ z_inflateBackInit_ +# define inflateCopy z_inflateCopy +# define inflateEnd z_inflateEnd +# define inflateGetHeader z_inflateGetHeader +# define inflateInit2_ z_inflateInit2_ +# define inflateInit_ z_inflateInit_ +# define inflateMark z_inflateMark +# define inflatePrime z_inflatePrime +# define inflateReset z_inflateReset +# define inflateReset2 z_inflateReset2 +# define inflateSetDictionary z_inflateSetDictionary +# define inflateGetDictionary z_inflateGetDictionary +# define inflateSync z_inflateSync +# define inflateSyncPoint z_inflateSyncPoint +# define inflateUndermine z_inflateUndermine +# define inflateResetKeep z_inflateResetKeep +# define inflate_copyright z_inflate_copyright +# define inflate_fast z_inflate_fast +# define inflate_table z_inflate_table +# ifndef Z_SOLO +# define uncompress z_uncompress +# endif +# define zError z_zError +# ifndef Z_SOLO +# define zcalloc z_zcalloc +# define zcfree z_zcfree +# endif +# define zlibCompileFlags z_zlibCompileFlags +# define zlibVersion z_zlibVersion + +/* all zlib typedefs in zlib.h and zconf.h */ +# define Byte z_Byte +# define Bytef z_Bytef +# define alloc_func z_alloc_func +# define charf z_charf +# define free_func z_free_func +# ifndef Z_SOLO +# define gzFile z_gzFile +# endif +# define gz_header z_gz_header +# define gz_headerp z_gz_headerp +# define in_func z_in_func +# define intf z_intf +# define out_func z_out_func +# define uInt z_uInt +# define uIntf z_uIntf +# define uLong z_uLong +# define uLongf z_uLongf +# define voidp z_voidp +# define voidpc z_voidpc +# define voidpf z_voidpf + +/* all zlib structs in zlib.h and zconf.h */ +# define gz_header_s z_gz_header_s +# define internal_state z_internal_state + +#endif + +#if defined(__MSDOS__) && !defined(MSDOS) +# define MSDOS +#endif +#if (defined(OS_2) || defined(__OS2__)) && !defined(OS2) +# define OS2 +#endif +#if defined(_WINDOWS) && !defined(WINDOWS) +# define WINDOWS +#endif +#if defined(_WIN32) || defined(_WIN32_WCE) || defined(__WIN32__) +# ifndef WIN32 +# define WIN32 +# endif +#endif +#if (defined(MSDOS) || defined(OS2) || defined(WINDOWS)) && !defined(WIN32) +# if !defined(__GNUC__) && !defined(__FLAT__) && !defined(__386__) +# ifndef SYS16BIT +# define SYS16BIT +# endif +# endif +#endif + +/* + * Compile with -DMAXSEG_64K if the alloc function cannot allocate more + * than 64k bytes at a time (needed on systems with 16-bit int). + */ +#ifdef SYS16BIT +# define MAXSEG_64K +#endif +#ifdef MSDOS +# define UNALIGNED_OK +#endif + +#ifdef __STDC_VERSION__ +# ifndef STDC +# define STDC +# endif +# if __STDC_VERSION__ >= 199901L +# ifndef STDC99 +# define STDC99 +# endif +# endif +#endif +#if !defined(STDC) && (defined(__STDC__) || defined(__cplusplus)) +# define STDC +#endif +#if !defined(STDC) && (defined(__GNUC__) || defined(__BORLANDC__)) +# define STDC +#endif +#if !defined(STDC) && (defined(MSDOS) || defined(WINDOWS) || defined(WIN32)) +# define STDC +#endif +#if !defined(STDC) && (defined(OS2) || defined(__HOS_AIX__)) +# define STDC +#endif + +#if defined(__OS400__) && !defined(STDC) /* iSeries (formerly AS/400). */ +# define STDC +#endif + +#ifndef STDC +# ifndef const /* cannot use !defined(STDC) && !defined(const) on Mac */ +# define const /* note: need a more gentle solution here */ +# endif +#endif + +#if defined(ZLIB_CONST) && !defined(z_const) +# define z_const const +#else +# define z_const +#endif + +/* Some Mac compilers merge all .h files incorrectly: */ +#if defined(__MWERKS__)||defined(applec)||defined(THINK_C)||defined(__SC__) +# define NO_DUMMY_DECL +#endif + +/* Maximum value for memLevel in deflateInit2 */ +#ifndef MAX_MEM_LEVEL +# ifdef MAXSEG_64K +# define MAX_MEM_LEVEL 8 +# else +# define MAX_MEM_LEVEL 9 +# endif +#endif + +/* Maximum value for windowBits in deflateInit2 and inflateInit2. + * WARNING: reducing MAX_WBITS makes minigzip unable to extract .gz files + * created by gzip. (Files created by minigzip can still be extracted by + * gzip.) + */ +#ifndef MAX_WBITS +# define MAX_WBITS 15 /* 32K LZ77 window */ +#endif + +/* The memory requirements for deflate are (in bytes): + (1 << (windowBits+2)) + (1 << (memLevel+9)) + that is: 128K for windowBits=15 + 128K for memLevel = 8 (default values) + plus a few kilobytes for small objects. For example, if you want to reduce + the default memory requirements from 256K to 128K, compile with + make CFLAGS="-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7" + Of course this will generally degrade compression (there's no free lunch). + + The memory requirements for inflate are (in bytes) 1 << windowBits + that is, 32K for windowBits=15 (default value) plus a few kilobytes + for small objects. +*/ + + /* Type declarations */ + +#ifndef OF /* function prototypes */ +# ifdef STDC +# define OF(args) args +# else +# define OF(args) () +# endif +#endif + +#ifndef Z_ARG /* function prototypes for stdarg */ +# if defined(STDC) || defined(Z_HAVE_STDARG_H) +# define Z_ARG(args) args +# else +# define Z_ARG(args) () +# endif +#endif + +/* The following definitions for FAR are needed only for MSDOS mixed + * model programming (small or medium model with some far allocations). + * This was tested only with MSC; for other MSDOS compilers you may have + * to define NO_MEMCPY in zutil.h. If you don't need the mixed model, + * just define FAR to be empty. + */ +#ifdef SYS16BIT +# if defined(M_I86SM) || defined(M_I86MM) + /* MSC small or medium model */ +# define SMALL_MEDIUM +# ifdef _MSC_VER +# define FAR _far +# else +# define FAR far +# endif +# endif +# if (defined(__SMALL__) || defined(__MEDIUM__)) + /* Turbo C small or medium model */ +# define SMALL_MEDIUM +# ifdef __BORLANDC__ +# define FAR _far +# else +# define FAR far +# endif +# endif +#endif + +#if defined(WINDOWS) || defined(WIN32) + /* If building or using zlib as a DLL, define ZLIB_DLL. + * This is not mandatory, but it offers a little performance increase. + */ +# ifdef ZLIB_DLL +# if defined(WIN32) && (!defined(__BORLANDC__) || (__BORLANDC__ >= 0x500)) +# ifdef ZLIB_INTERNAL +# define ZEXTERN extern __declspec(dllexport) +# else +# define ZEXTERN extern __declspec(dllimport) +# endif +# endif +# endif /* ZLIB_DLL */ + /* If building or using zlib with the WINAPI/WINAPIV calling convention, + * define ZLIB_WINAPI. + * Caution: the standard ZLIB1.DLL is NOT compiled using ZLIB_WINAPI. + */ +# ifdef ZLIB_WINAPI +# ifdef FAR +# undef FAR +# endif +# include + /* No need for _export, use ZLIB.DEF instead. */ + /* For complete Windows compatibility, use WINAPI, not __stdcall. */ +# define ZEXPORT WINAPI +# ifdef WIN32 +# define ZEXPORTVA WINAPIV +# else +# define ZEXPORTVA FAR CDECL +# endif +# endif +#endif + +#if defined (__BEOS__) +# ifdef ZLIB_DLL +# ifdef ZLIB_INTERNAL +# define ZEXPORT __declspec(dllexport) +# define ZEXPORTVA __declspec(dllexport) +# else +# define ZEXPORT __declspec(dllimport) +# define ZEXPORTVA __declspec(dllimport) +# endif +# endif +#endif + +#ifndef ZEXTERN +# define ZEXTERN extern +#endif +#ifndef ZEXPORT +# define ZEXPORT +#endif +#ifndef ZEXPORTVA +# define ZEXPORTVA +#endif + +#ifndef FAR +# define FAR +#endif + +#if !defined(__MACTYPES__) +typedef unsigned char Byte; /* 8 bits */ +#endif +typedef unsigned int uInt; /* 16 bits or more */ +typedef unsigned long uLong; /* 32 bits or more */ + +#ifdef SMALL_MEDIUM + /* Borland C/C++ and some old MSC versions ignore FAR inside typedef */ +# define Bytef Byte FAR +#else + typedef Byte FAR Bytef; +#endif +typedef char FAR charf; +typedef int FAR intf; +typedef uInt FAR uIntf; +typedef uLong FAR uLongf; + +#ifdef STDC + typedef void const *voidpc; + typedef void FAR *voidpf; + typedef void *voidp; +#else + typedef Byte const *voidpc; + typedef Byte FAR *voidpf; + typedef Byte *voidp; +#endif + +#if !defined(Z_U4) && !defined(Z_SOLO) && defined(STDC) +# include +# if (UINT_MAX == 0xffffffffUL) +# define Z_U4 unsigned +# elif (ULONG_MAX == 0xffffffffUL) +# define Z_U4 unsigned long +# elif (USHRT_MAX == 0xffffffffUL) +# define Z_U4 unsigned short +# endif +#endif + +#ifdef Z_U4 + typedef Z_U4 z_crc_t; +#else + typedef unsigned long z_crc_t; +#endif + +#ifdef HAVE_UNISTD_H /* may be set to #if 1 by ./configure */ +# define Z_HAVE_UNISTD_H +#endif + +#ifdef HAVE_STDARG_H /* may be set to #if 1 by ./configure */ +# define Z_HAVE_STDARG_H +#endif + +#ifdef STDC +# ifndef Z_SOLO +# include /* for off_t */ +# endif +#endif + +#if defined(STDC) || defined(Z_HAVE_STDARG_H) +# ifndef Z_SOLO +# include /* for va_list */ +# endif +#endif + +#ifdef _WIN32 +# ifndef Z_SOLO +# include /* for wchar_t */ +# endif +#endif + +/* a little trick to accommodate both "#define _LARGEFILE64_SOURCE" and + * "#define _LARGEFILE64_SOURCE 1" as requesting 64-bit operations, (even + * though the former does not conform to the LFS document), but considering + * both "#undef _LARGEFILE64_SOURCE" and "#define _LARGEFILE64_SOURCE 0" as + * equivalently requesting no 64-bit operations + */ +#if defined(_LARGEFILE64_SOURCE) && -_LARGEFILE64_SOURCE - -1 == 1 +# undef _LARGEFILE64_SOURCE +#endif + +#if defined(__WATCOMC__) && !defined(Z_HAVE_UNISTD_H) +# define Z_HAVE_UNISTD_H +#endif +#ifndef Z_SOLO +# if defined(Z_HAVE_UNISTD_H) || defined(_LARGEFILE64_SOURCE) +# include /* for SEEK_*, off_t, and _LFS64_LARGEFILE */ +# ifdef VMS +# include /* for off_t */ +# endif +# ifndef z_off_t +# define z_off_t off_t +# endif +# endif +#endif + +#if defined(_LFS64_LARGEFILE) && _LFS64_LARGEFILE-0 +# define Z_LFS64 +#endif + +#if defined(_LARGEFILE64_SOURCE) && defined(Z_LFS64) +# define Z_LARGE64 +#endif + +#if defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS-0 == 64 && defined(Z_LFS64) +# define Z_WANT64 +#endif + +#if !defined(SEEK_SET) && !defined(Z_SOLO) +# define SEEK_SET 0 /* Seek from beginning of file. */ +# define SEEK_CUR 1 /* Seek from current position. */ +# define SEEK_END 2 /* Set file pointer to EOF plus "offset" */ +#endif + +#ifndef z_off_t +# define z_off_t long +#endif + +#if !defined(_WIN32) && defined(Z_LARGE64) +# define z_off64_t off64_t +#else +# if defined(_WIN32) && !defined(__GNUC__) && !defined(Z_SOLO) +# define z_off64_t __int64 +# else +# define z_off64_t z_off_t +# endif +#endif + +/* MVS linker does not support external names larger than 8 bytes */ +#if defined(__MVS__) + #pragma map(deflateInit_,"DEIN") + #pragma map(deflateInit2_,"DEIN2") + #pragma map(deflateEnd,"DEEND") + #pragma map(deflateBound,"DEBND") + #pragma map(inflateInit_,"ININ") + #pragma map(inflateInit2_,"ININ2") + #pragma map(inflateEnd,"INEND") + #pragma map(inflateSync,"INSY") + #pragma map(inflateSetDictionary,"INSEDI") + #pragma map(compressBound,"CMBND") + #pragma map(inflate_table,"INTABL") + #pragma map(inflate_fast,"INFA") + #pragma map(inflate_copyright,"INCOPY") +#endif + +#endif /* ZCONF_H */ diff --git a/src/zlib/zlib.h b/src/zlib/zlib.h new file mode 100644 index 00000000..3e0c7672 --- /dev/null +++ b/src/zlib/zlib.h @@ -0,0 +1,1768 @@ +/* zlib.h -- interface of the 'zlib' general purpose compression library + version 1.2.8, April 28th, 2013 + + Copyright (C) 1995-2013 Jean-loup Gailly and Mark Adler + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Jean-loup Gailly Mark Adler + jloup@gzip.org madler@alumni.caltech.edu + + + The data format used by the zlib library is described by RFCs (Request for + Comments) 1950 to 1952 in the files http://tools.ietf.org/html/rfc1950 + (zlib format), rfc1951 (deflate format) and rfc1952 (gzip format). +*/ + +#ifndef ZLIB_H +#define ZLIB_H + +#include "zconf.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define ZLIB_VERSION "1.2.8" +#define ZLIB_VERNUM 0x1280 +#define ZLIB_VER_MAJOR 1 +#define ZLIB_VER_MINOR 2 +#define ZLIB_VER_REVISION 8 +#define ZLIB_VER_SUBREVISION 0 + +/* + The 'zlib' compression library provides in-memory compression and + decompression functions, including integrity checks of the uncompressed data. + This version of the library supports only one compression method (deflation) + but other algorithms will be added later and will have the same stream + interface. + + Compression can be done in a single step if the buffers are large enough, + or can be done by repeated calls of the compression function. In the latter + case, the application must provide more input and/or consume the output + (providing more output space) before each call. + + The compressed data format used by default by the in-memory functions is + the zlib format, which is a zlib wrapper documented in RFC 1950, wrapped + around a deflate stream, which is itself documented in RFC 1951. + + The library also supports reading and writing files in gzip (.gz) format + with an interface similar to that of stdio using the functions that start + with "gz". The gzip format is different from the zlib format. gzip is a + gzip wrapper, documented in RFC 1952, wrapped around a deflate stream. + + This library can optionally read and write gzip streams in memory as well. + + The zlib format was designed to be compact and fast for use in memory + and on communications channels. The gzip format was designed for single- + file compression on file systems, has a larger header than zlib to maintain + directory information, and uses a different, slower check method than zlib. + + The library does not install any signal handler. The decoder checks + the consistency of the compressed data, so the library should never crash + even in case of corrupted input. +*/ + +typedef voidpf (*alloc_func) OF((voidpf opaque, uInt items, uInt size)); +typedef void (*free_func) OF((voidpf opaque, voidpf address)); + +struct internal_state; + +typedef struct z_stream_s { + z_const Bytef *next_in; /* next input byte */ + uInt avail_in; /* number of bytes available at next_in */ + uLong total_in; /* total number of input bytes read so far */ + + Bytef *next_out; /* next output byte should be put there */ + uInt avail_out; /* remaining free space at next_out */ + uLong total_out; /* total number of bytes output so far */ + + z_const char *msg; /* last error message, NULL if no error */ + struct internal_state FAR *state; /* not visible by applications */ + + alloc_func zalloc; /* used to allocate the internal state */ + free_func zfree; /* used to free the internal state */ + voidpf opaque; /* private data object passed to zalloc and zfree */ + + int data_type; /* best guess about the data type: binary or text */ + uLong adler; /* adler32 value of the uncompressed data */ + uLong reserved; /* reserved for future use */ +} z_stream; + +typedef z_stream FAR *z_streamp; + +/* + gzip header information passed to and from zlib routines. See RFC 1952 + for more details on the meanings of these fields. +*/ +typedef struct gz_header_s { + int text; /* true if compressed data believed to be text */ + uLong time; /* modification time */ + int xflags; /* extra flags (not used when writing a gzip file) */ + int os; /* operating system */ + Bytef *extra; /* pointer to extra field or Z_NULL if none */ + uInt extra_len; /* extra field length (valid if extra != Z_NULL) */ + uInt extra_max; /* space at extra (only when reading header) */ + Bytef *name; /* pointer to zero-terminated file name or Z_NULL */ + uInt name_max; /* space at name (only when reading header) */ + Bytef *comment; /* pointer to zero-terminated comment or Z_NULL */ + uInt comm_max; /* space at comment (only when reading header) */ + int hcrc; /* true if there was or will be a header crc */ + int done; /* true when done reading gzip header (not used + when writing a gzip file) */ +} gz_header; + +typedef gz_header FAR *gz_headerp; + +/* + The application must update next_in and avail_in when avail_in has dropped + to zero. It must update next_out and avail_out when avail_out has dropped + to zero. The application must initialize zalloc, zfree and opaque before + calling the init function. All other fields are set by the compression + library and must not be updated by the application. + + The opaque value provided by the application will be passed as the first + parameter for calls of zalloc and zfree. This can be useful for custom + memory management. The compression library attaches no meaning to the + opaque value. + + zalloc must return Z_NULL if there is not enough memory for the object. + If zlib is used in a multi-threaded application, zalloc and zfree must be + thread safe. + + On 16-bit systems, the functions zalloc and zfree must be able to allocate + exactly 65536 bytes, but will not be required to allocate more than this if + the symbol MAXSEG_64K is defined (see zconf.h). WARNING: On MSDOS, pointers + returned by zalloc for objects of exactly 65536 bytes *must* have their + offset normalized to zero. The default allocation function provided by this + library ensures this (see zutil.c). To reduce memory requirements and avoid + any allocation of 64K objects, at the expense of compression ratio, compile + the library with -DMAX_WBITS=14 (see zconf.h). + + The fields total_in and total_out can be used for statistics or progress + reports. After compression, total_in holds the total size of the + uncompressed data and may be saved for use in the decompressor (particularly + if the decompressor wants to decompress everything in a single step). +*/ + + /* constants */ + +#define Z_NO_FLUSH 0 +#define Z_PARTIAL_FLUSH 1 +#define Z_SYNC_FLUSH 2 +#define Z_FULL_FLUSH 3 +#define Z_FINISH 4 +#define Z_BLOCK 5 +#define Z_TREES 6 +/* Allowed flush values; see deflate() and inflate() below for details */ + +#define Z_OK 0 +#define Z_STREAM_END 1 +#define Z_NEED_DICT 2 +#define Z_ERRNO (-1) +#define Z_STREAM_ERROR (-2) +#define Z_DATA_ERROR (-3) +#define Z_MEM_ERROR (-4) +#define Z_BUF_ERROR (-5) +#define Z_VERSION_ERROR (-6) +/* Return codes for the compression/decompression functions. Negative values + * are errors, positive values are used for special but normal events. + */ + +#define Z_NO_COMPRESSION 0 +#define Z_BEST_SPEED 1 +#define Z_BEST_COMPRESSION 9 +#define Z_DEFAULT_COMPRESSION (-1) +/* compression levels */ + +#define Z_FILTERED 1 +#define Z_HUFFMAN_ONLY 2 +#define Z_RLE 3 +#define Z_FIXED 4 +#define Z_DEFAULT_STRATEGY 0 +/* compression strategy; see deflateInit2() below for details */ + +#define Z_BINARY 0 +#define Z_TEXT 1 +#define Z_ASCII Z_TEXT /* for compatibility with 1.2.2 and earlier */ +#define Z_UNKNOWN 2 +/* Possible values of the data_type field (though see inflate()) */ + +#define Z_DEFLATED 8 +/* The deflate compression method (the only one supported in this version) */ + +#define Z_NULL 0 /* for initializing zalloc, zfree, opaque */ + +#define zlib_version zlibVersion() +/* for compatibility with versions < 1.0.2 */ + + + /* basic functions */ + +ZEXTERN const char * ZEXPORT zlibVersion OF((void)); +/* The application can compare zlibVersion and ZLIB_VERSION for consistency. + If the first character differs, the library code actually used is not + compatible with the zlib.h header file used by the application. This check + is automatically made by deflateInit and inflateInit. + */ + +/* +ZEXTERN int ZEXPORT deflateInit OF((z_streamp strm, int level)); + + Initializes the internal stream state for compression. The fields + zalloc, zfree and opaque must be initialized before by the caller. If + zalloc and zfree are set to Z_NULL, deflateInit updates them to use default + allocation functions. + + The compression level must be Z_DEFAULT_COMPRESSION, or between 0 and 9: + 1 gives best speed, 9 gives best compression, 0 gives no compression at all + (the input data is simply copied a block at a time). Z_DEFAULT_COMPRESSION + requests a default compromise between speed and compression (currently + equivalent to level 6). + + deflateInit returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_STREAM_ERROR if level is not a valid compression level, or + Z_VERSION_ERROR if the zlib library version (zlib_version) is incompatible + with the version assumed by the caller (ZLIB_VERSION). msg is set to null + if there is no error message. deflateInit does not perform any compression: + this will be done by deflate(). +*/ + + +ZEXTERN int ZEXPORT deflate OF((z_streamp strm, int flush)); +/* + deflate compresses as much data as possible, and stops when the input + buffer becomes empty or the output buffer becomes full. It may introduce + some output latency (reading input without producing any output) except when + forced to flush. + + The detailed semantics are as follows. deflate performs one or both of the + following actions: + + - Compress more input starting at next_in and update next_in and avail_in + accordingly. If not all input can be processed (because there is not + enough room in the output buffer), next_in and avail_in are updated and + processing will resume at this point for the next call of deflate(). + + - Provide more output starting at next_out and update next_out and avail_out + accordingly. This action is forced if the parameter flush is non zero. + Forcing flush frequently degrades the compression ratio, so this parameter + should be set only when necessary (in interactive applications). Some + output may be provided even if flush is not set. + + Before the call of deflate(), the application should ensure that at least + one of the actions is possible, by providing more input and/or consuming more + output, and updating avail_in or avail_out accordingly; avail_out should + never be zero before the call. The application can consume the compressed + output when it wants, for example when the output buffer is full (avail_out + == 0), or after each call of deflate(). If deflate returns Z_OK and with + zero avail_out, it must be called again after making room in the output + buffer because there might be more output pending. + + Normally the parameter flush is set to Z_NO_FLUSH, which allows deflate to + decide how much data to accumulate before producing output, in order to + maximize compression. + + If the parameter flush is set to Z_SYNC_FLUSH, all pending output is + flushed to the output buffer and the output is aligned on a byte boundary, so + that the decompressor can get all input data available so far. (In + particular avail_in is zero after the call if enough output space has been + provided before the call.) Flushing may degrade compression for some + compression algorithms and so it should be used only when necessary. This + completes the current deflate block and follows it with an empty stored block + that is three bits plus filler bits to the next byte, followed by four bytes + (00 00 ff ff). + + If flush is set to Z_PARTIAL_FLUSH, all pending output is flushed to the + output buffer, but the output is not aligned to a byte boundary. All of the + input data so far will be available to the decompressor, as for Z_SYNC_FLUSH. + This completes the current deflate block and follows it with an empty fixed + codes block that is 10 bits long. This assures that enough bytes are output + in order for the decompressor to finish the block before the empty fixed code + block. + + If flush is set to Z_BLOCK, a deflate block is completed and emitted, as + for Z_SYNC_FLUSH, but the output is not aligned on a byte boundary, and up to + seven bits of the current block are held to be written as the next byte after + the next deflate block is completed. In this case, the decompressor may not + be provided enough bits at this point in order to complete decompression of + the data provided so far to the compressor. It may need to wait for the next + block to be emitted. This is for advanced applications that need to control + the emission of deflate blocks. + + If flush is set to Z_FULL_FLUSH, all output is flushed as with + Z_SYNC_FLUSH, and the compression state is reset so that decompression can + restart from this point if previous compressed data has been damaged or if + random access is desired. Using Z_FULL_FLUSH too often can seriously degrade + compression. + + If deflate returns with avail_out == 0, this function must be called again + with the same value of the flush parameter and more output space (updated + avail_out), until the flush is complete (deflate returns with non-zero + avail_out). In the case of a Z_FULL_FLUSH or Z_SYNC_FLUSH, make sure that + avail_out is greater than six to avoid repeated flush markers due to + avail_out == 0 on return. + + If the parameter flush is set to Z_FINISH, pending input is processed, + pending output is flushed and deflate returns with Z_STREAM_END if there was + enough output space; if deflate returns with Z_OK, this function must be + called again with Z_FINISH and more output space (updated avail_out) but no + more input data, until it returns with Z_STREAM_END or an error. After + deflate has returned Z_STREAM_END, the only possible operations on the stream + are deflateReset or deflateEnd. + + Z_FINISH can be used immediately after deflateInit if all the compression + is to be done in a single step. In this case, avail_out must be at least the + value returned by deflateBound (see below). Then deflate is guaranteed to + return Z_STREAM_END. If not enough output space is provided, deflate will + not return Z_STREAM_END, and it must be called again as described above. + + deflate() sets strm->adler to the adler32 checksum of all input read + so far (that is, total_in bytes). + + deflate() may update strm->data_type if it can make a good guess about + the input data type (Z_BINARY or Z_TEXT). In doubt, the data is considered + binary. This field is only for information purposes and does not affect the + compression algorithm in any manner. + + deflate() returns Z_OK if some progress has been made (more input + processed or more output produced), Z_STREAM_END if all input has been + consumed and all output has been produced (only when flush is set to + Z_FINISH), Z_STREAM_ERROR if the stream state was inconsistent (for example + if next_in or next_out was Z_NULL), Z_BUF_ERROR if no progress is possible + (for example avail_in or avail_out was zero). Note that Z_BUF_ERROR is not + fatal, and deflate() can be called again with more input and more output + space to continue compressing. +*/ + + +ZEXTERN int ZEXPORT deflateEnd OF((z_streamp strm)); +/* + All dynamically allocated data structures for this stream are freed. + This function discards any unprocessed input and does not flush any pending + output. + + deflateEnd returns Z_OK if success, Z_STREAM_ERROR if the + stream state was inconsistent, Z_DATA_ERROR if the stream was freed + prematurely (some input or output was discarded). In the error case, msg + may be set but then points to a static string (which must not be + deallocated). +*/ + + +/* +ZEXTERN int ZEXPORT inflateInit OF((z_streamp strm)); + + Initializes the internal stream state for decompression. The fields + next_in, avail_in, zalloc, zfree and opaque must be initialized before by + the caller. If next_in is not Z_NULL and avail_in is large enough (the + exact value depends on the compression method), inflateInit determines the + compression method from the zlib header and allocates all data structures + accordingly; otherwise the allocation will be deferred to the first call of + inflate. If zalloc and zfree are set to Z_NULL, inflateInit updates them to + use default allocation functions. + + inflateInit returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_VERSION_ERROR if the zlib library version is incompatible with the + version assumed by the caller, or Z_STREAM_ERROR if the parameters are + invalid, such as a null pointer to the structure. msg is set to null if + there is no error message. inflateInit does not perform any decompression + apart from possibly reading the zlib header if present: actual decompression + will be done by inflate(). (So next_in and avail_in may be modified, but + next_out and avail_out are unused and unchanged.) The current implementation + of inflateInit() does not process any header information -- that is deferred + until inflate() is called. +*/ + + +ZEXTERN int ZEXPORT inflate OF((z_streamp strm, int flush)); +/* + inflate decompresses as much data as possible, and stops when the input + buffer becomes empty or the output buffer becomes full. It may introduce + some output latency (reading input without producing any output) except when + forced to flush. + + The detailed semantics are as follows. inflate performs one or both of the + following actions: + + - Decompress more input starting at next_in and update next_in and avail_in + accordingly. If not all input can be processed (because there is not + enough room in the output buffer), next_in is updated and processing will + resume at this point for the next call of inflate(). + + - Provide more output starting at next_out and update next_out and avail_out + accordingly. inflate() provides as much output as possible, until there is + no more input data or no more space in the output buffer (see below about + the flush parameter). + + Before the call of inflate(), the application should ensure that at least + one of the actions is possible, by providing more input and/or consuming more + output, and updating the next_* and avail_* values accordingly. The + application can consume the uncompressed output when it wants, for example + when the output buffer is full (avail_out == 0), or after each call of + inflate(). If inflate returns Z_OK and with zero avail_out, it must be + called again after making room in the output buffer because there might be + more output pending. + + The flush parameter of inflate() can be Z_NO_FLUSH, Z_SYNC_FLUSH, Z_FINISH, + Z_BLOCK, or Z_TREES. Z_SYNC_FLUSH requests that inflate() flush as much + output as possible to the output buffer. Z_BLOCK requests that inflate() + stop if and when it gets to the next deflate block boundary. When decoding + the zlib or gzip format, this will cause inflate() to return immediately + after the header and before the first block. When doing a raw inflate, + inflate() will go ahead and process the first block, and will return when it + gets to the end of that block, or when it runs out of data. + + The Z_BLOCK option assists in appending to or combining deflate streams. + Also to assist in this, on return inflate() will set strm->data_type to the + number of unused bits in the last byte taken from strm->next_in, plus 64 if + inflate() is currently decoding the last block in the deflate stream, plus + 128 if inflate() returned immediately after decoding an end-of-block code or + decoding the complete header up to just before the first byte of the deflate + stream. The end-of-block will not be indicated until all of the uncompressed + data from that block has been written to strm->next_out. The number of + unused bits may in general be greater than seven, except when bit 7 of + data_type is set, in which case the number of unused bits will be less than + eight. data_type is set as noted here every time inflate() returns for all + flush options, and so can be used to determine the amount of currently + consumed input in bits. + + The Z_TREES option behaves as Z_BLOCK does, but it also returns when the + end of each deflate block header is reached, before any actual data in that + block is decoded. This allows the caller to determine the length of the + deflate block header for later use in random access within a deflate block. + 256 is added to the value of strm->data_type when inflate() returns + immediately after reaching the end of the deflate block header. + + inflate() should normally be called until it returns Z_STREAM_END or an + error. However if all decompression is to be performed in a single step (a + single call of inflate), the parameter flush should be set to Z_FINISH. In + this case all pending input is processed and all pending output is flushed; + avail_out must be large enough to hold all of the uncompressed data for the + operation to complete. (The size of the uncompressed data may have been + saved by the compressor for this purpose.) The use of Z_FINISH is not + required to perform an inflation in one step. However it may be used to + inform inflate that a faster approach can be used for the single inflate() + call. Z_FINISH also informs inflate to not maintain a sliding window if the + stream completes, which reduces inflate's memory footprint. If the stream + does not complete, either because not all of the stream is provided or not + enough output space is provided, then a sliding window will be allocated and + inflate() can be called again to continue the operation as if Z_NO_FLUSH had + been used. + + In this implementation, inflate() always flushes as much output as + possible to the output buffer, and always uses the faster approach on the + first call. So the effects of the flush parameter in this implementation are + on the return value of inflate() as noted below, when inflate() returns early + when Z_BLOCK or Z_TREES is used, and when inflate() avoids the allocation of + memory for a sliding window when Z_FINISH is used. + + If a preset dictionary is needed after this call (see inflateSetDictionary + below), inflate sets strm->adler to the Adler-32 checksum of the dictionary + chosen by the compressor and returns Z_NEED_DICT; otherwise it sets + strm->adler to the Adler-32 checksum of all output produced so far (that is, + total_out bytes) and returns Z_OK, Z_STREAM_END or an error code as described + below. At the end of the stream, inflate() checks that its computed adler32 + checksum is equal to that saved by the compressor and returns Z_STREAM_END + only if the checksum is correct. + + inflate() can decompress and check either zlib-wrapped or gzip-wrapped + deflate data. The header type is detected automatically, if requested when + initializing with inflateInit2(). Any information contained in the gzip + header is not retained, so applications that need that information should + instead use raw inflate, see inflateInit2() below, or inflateBack() and + perform their own processing of the gzip header and trailer. When processing + gzip-wrapped deflate data, strm->adler32 is set to the CRC-32 of the output + producted so far. The CRC-32 is checked against the gzip trailer. + + inflate() returns Z_OK if some progress has been made (more input processed + or more output produced), Z_STREAM_END if the end of the compressed data has + been reached and all uncompressed output has been produced, Z_NEED_DICT if a + preset dictionary is needed at this point, Z_DATA_ERROR if the input data was + corrupted (input stream not conforming to the zlib format or incorrect check + value), Z_STREAM_ERROR if the stream structure was inconsistent (for example + next_in or next_out was Z_NULL), Z_MEM_ERROR if there was not enough memory, + Z_BUF_ERROR if no progress is possible or if there was not enough room in the + output buffer when Z_FINISH is used. Note that Z_BUF_ERROR is not fatal, and + inflate() can be called again with more input and more output space to + continue decompressing. If Z_DATA_ERROR is returned, the application may + then call inflateSync() to look for a good compression block if a partial + recovery of the data is desired. +*/ + + +ZEXTERN int ZEXPORT inflateEnd OF((z_streamp strm)); +/* + All dynamically allocated data structures for this stream are freed. + This function discards any unprocessed input and does not flush any pending + output. + + inflateEnd returns Z_OK if success, Z_STREAM_ERROR if the stream state + was inconsistent. In the error case, msg may be set but then points to a + static string (which must not be deallocated). +*/ + + + /* Advanced functions */ + +/* + The following functions are needed only in some special applications. +*/ + +/* +ZEXTERN int ZEXPORT deflateInit2 OF((z_streamp strm, + int level, + int method, + int windowBits, + int memLevel, + int strategy)); + + This is another version of deflateInit with more compression options. The + fields next_in, zalloc, zfree and opaque must be initialized before by the + caller. + + The method parameter is the compression method. It must be Z_DEFLATED in + this version of the library. + + The windowBits parameter is the base two logarithm of the window size + (the size of the history buffer). It should be in the range 8..15 for this + version of the library. Larger values of this parameter result in better + compression at the expense of memory usage. The default value is 15 if + deflateInit is used instead. + + windowBits can also be -8..-15 for raw deflate. In this case, -windowBits + determines the window size. deflate() will then generate raw deflate data + with no zlib header or trailer, and will not compute an adler32 check value. + + windowBits can also be greater than 15 for optional gzip encoding. Add + 16 to windowBits to write a simple gzip header and trailer around the + compressed data instead of a zlib wrapper. The gzip header will have no + file name, no extra data, no comment, no modification time (set to zero), no + header crc, and the operating system will be set to 255 (unknown). If a + gzip stream is being written, strm->adler is a crc32 instead of an adler32. + + The memLevel parameter specifies how much memory should be allocated + for the internal compression state. memLevel=1 uses minimum memory but is + slow and reduces compression ratio; memLevel=9 uses maximum memory for + optimal speed. The default value is 8. See zconf.h for total memory usage + as a function of windowBits and memLevel. + + The strategy parameter is used to tune the compression algorithm. Use the + value Z_DEFAULT_STRATEGY for normal data, Z_FILTERED for data produced by a + filter (or predictor), Z_HUFFMAN_ONLY to force Huffman encoding only (no + string match), or Z_RLE to limit match distances to one (run-length + encoding). Filtered data consists mostly of small values with a somewhat + random distribution. In this case, the compression algorithm is tuned to + compress them better. The effect of Z_FILTERED is to force more Huffman + coding and less string matching; it is somewhat intermediate between + Z_DEFAULT_STRATEGY and Z_HUFFMAN_ONLY. Z_RLE is designed to be almost as + fast as Z_HUFFMAN_ONLY, but give better compression for PNG image data. The + strategy parameter only affects the compression ratio but not the + correctness of the compressed output even if it is not set appropriately. + Z_FIXED prevents the use of dynamic Huffman codes, allowing for a simpler + decoder for special applications. + + deflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_STREAM_ERROR if any parameter is invalid (such as an invalid + method), or Z_VERSION_ERROR if the zlib library version (zlib_version) is + incompatible with the version assumed by the caller (ZLIB_VERSION). msg is + set to null if there is no error message. deflateInit2 does not perform any + compression: this will be done by deflate(). +*/ + +ZEXTERN int ZEXPORT deflateSetDictionary OF((z_streamp strm, + const Bytef *dictionary, + uInt dictLength)); +/* + Initializes the compression dictionary from the given byte sequence + without producing any compressed output. When using the zlib format, this + function must be called immediately after deflateInit, deflateInit2 or + deflateReset, and before any call of deflate. When doing raw deflate, this + function must be called either before any call of deflate, or immediately + after the completion of a deflate block, i.e. after all input has been + consumed and all output has been delivered when using any of the flush + options Z_BLOCK, Z_PARTIAL_FLUSH, Z_SYNC_FLUSH, or Z_FULL_FLUSH. The + compressor and decompressor must use exactly the same dictionary (see + inflateSetDictionary). + + The dictionary should consist of strings (byte sequences) that are likely + to be encountered later in the data to be compressed, with the most commonly + used strings preferably put towards the end of the dictionary. Using a + dictionary is most useful when the data to be compressed is short and can be + predicted with good accuracy; the data can then be compressed better than + with the default empty dictionary. + + Depending on the size of the compression data structures selected by + deflateInit or deflateInit2, a part of the dictionary may in effect be + discarded, for example if the dictionary is larger than the window size + provided in deflateInit or deflateInit2. Thus the strings most likely to be + useful should be put at the end of the dictionary, not at the front. In + addition, the current implementation of deflate will use at most the window + size minus 262 bytes of the provided dictionary. + + Upon return of this function, strm->adler is set to the adler32 value + of the dictionary; the decompressor may later use this value to determine + which dictionary has been used by the compressor. (The adler32 value + applies to the whole dictionary even if only a subset of the dictionary is + actually used by the compressor.) If a raw deflate was requested, then the + adler32 value is not computed and strm->adler is not set. + + deflateSetDictionary returns Z_OK if success, or Z_STREAM_ERROR if a + parameter is invalid (e.g. dictionary being Z_NULL) or the stream state is + inconsistent (for example if deflate has already been called for this stream + or if not at a block boundary for raw deflate). deflateSetDictionary does + not perform any compression: this will be done by deflate(). +*/ + +ZEXTERN int ZEXPORT deflateCopy OF((z_streamp dest, + z_streamp source)); +/* + Sets the destination stream as a complete copy of the source stream. + + This function can be useful when several compression strategies will be + tried, for example when there are several ways of pre-processing the input + data with a filter. The streams that will be discarded should then be freed + by calling deflateEnd. Note that deflateCopy duplicates the internal + compression state which can be quite large, so this strategy is slow and can + consume lots of memory. + + deflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_STREAM_ERROR if the source stream state was inconsistent + (such as zalloc being Z_NULL). msg is left unchanged in both source and + destination. +*/ + +ZEXTERN int ZEXPORT deflateReset OF((z_streamp strm)); +/* + This function is equivalent to deflateEnd followed by deflateInit, + but does not free and reallocate all the internal compression state. The + stream will keep the same compression level and any other attributes that + may have been set by deflateInit2. + + deflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent (such as zalloc or state being Z_NULL). +*/ + +ZEXTERN int ZEXPORT deflateParams OF((z_streamp strm, + int level, + int strategy)); +/* + Dynamically update the compression level and compression strategy. The + interpretation of level and strategy is as in deflateInit2. This can be + used to switch between compression and straight copy of the input data, or + to switch to a different kind of input data requiring a different strategy. + If the compression level is changed, the input available so far is + compressed with the old level (and may be flushed); the new level will take + effect only at the next call of deflate(). + + Before the call of deflateParams, the stream state must be set as for + a call of deflate(), since the currently available input may have to be + compressed and flushed. In particular, strm->avail_out must be non-zero. + + deflateParams returns Z_OK if success, Z_STREAM_ERROR if the source + stream state was inconsistent or if a parameter was invalid, Z_BUF_ERROR if + strm->avail_out was zero. +*/ + +ZEXTERN int ZEXPORT deflateTune OF((z_streamp strm, + int good_length, + int max_lazy, + int nice_length, + int max_chain)); +/* + Fine tune deflate's internal compression parameters. This should only be + used by someone who understands the algorithm used by zlib's deflate for + searching for the best matching string, and even then only by the most + fanatic optimizer trying to squeeze out the last compressed bit for their + specific input data. Read the deflate.c source code for the meaning of the + max_lazy, good_length, nice_length, and max_chain parameters. + + deflateTune() can be called after deflateInit() or deflateInit2(), and + returns Z_OK on success, or Z_STREAM_ERROR for an invalid deflate stream. + */ + +ZEXTERN uLong ZEXPORT deflateBound OF((z_streamp strm, + uLong sourceLen)); +/* + deflateBound() returns an upper bound on the compressed size after + deflation of sourceLen bytes. It must be called after deflateInit() or + deflateInit2(), and after deflateSetHeader(), if used. This would be used + to allocate an output buffer for deflation in a single pass, and so would be + called before deflate(). If that first deflate() call is provided the + sourceLen input bytes, an output buffer allocated to the size returned by + deflateBound(), and the flush value Z_FINISH, then deflate() is guaranteed + to return Z_STREAM_END. Note that it is possible for the compressed size to + be larger than the value returned by deflateBound() if flush options other + than Z_FINISH or Z_NO_FLUSH are used. +*/ + +ZEXTERN int ZEXPORT deflatePending OF((z_streamp strm, + unsigned *pending, + int *bits)); +/* + deflatePending() returns the number of bytes and bits of output that have + been generated, but not yet provided in the available output. The bytes not + provided would be due to the available output space having being consumed. + The number of bits of output not provided are between 0 and 7, where they + await more bits to join them in order to fill out a full byte. If pending + or bits are Z_NULL, then those values are not set. + + deflatePending returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent. + */ + +ZEXTERN int ZEXPORT deflatePrime OF((z_streamp strm, + int bits, + int value)); +/* + deflatePrime() inserts bits in the deflate output stream. The intent + is that this function is used to start off the deflate output with the bits + leftover from a previous deflate stream when appending to it. As such, this + function can only be used for raw deflate, and must be used before the first + deflate() call after a deflateInit2() or deflateReset(). bits must be less + than or equal to 16, and that many of the least significant bits of value + will be inserted in the output. + + deflatePrime returns Z_OK if success, Z_BUF_ERROR if there was not enough + room in the internal buffer to insert the bits, or Z_STREAM_ERROR if the + source stream state was inconsistent. +*/ + +ZEXTERN int ZEXPORT deflateSetHeader OF((z_streamp strm, + gz_headerp head)); +/* + deflateSetHeader() provides gzip header information for when a gzip + stream is requested by deflateInit2(). deflateSetHeader() may be called + after deflateInit2() or deflateReset() and before the first call of + deflate(). The text, time, os, extra field, name, and comment information + in the provided gz_header structure are written to the gzip header (xflag is + ignored -- the extra flags are set according to the compression level). The + caller must assure that, if not Z_NULL, name and comment are terminated with + a zero byte, and that if extra is not Z_NULL, that extra_len bytes are + available there. If hcrc is true, a gzip header crc is included. Note that + the current versions of the command-line version of gzip (up through version + 1.3.x) do not support header crc's, and will report that it is a "multi-part + gzip file" and give up. + + If deflateSetHeader is not used, the default gzip header has text false, + the time set to zero, and os set to 255, with no extra, name, or comment + fields. The gzip header is returned to the default state by deflateReset(). + + deflateSetHeader returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent. +*/ + +/* +ZEXTERN int ZEXPORT inflateInit2 OF((z_streamp strm, + int windowBits)); + + This is another version of inflateInit with an extra parameter. The + fields next_in, avail_in, zalloc, zfree and opaque must be initialized + before by the caller. + + The windowBits parameter is the base two logarithm of the maximum window + size (the size of the history buffer). It should be in the range 8..15 for + this version of the library. The default value is 15 if inflateInit is used + instead. windowBits must be greater than or equal to the windowBits value + provided to deflateInit2() while compressing, or it must be equal to 15 if + deflateInit2() was not used. If a compressed stream with a larger window + size is given as input, inflate() will return with the error code + Z_DATA_ERROR instead of trying to allocate a larger window. + + windowBits can also be zero to request that inflate use the window size in + the zlib header of the compressed stream. + + windowBits can also be -8..-15 for raw inflate. In this case, -windowBits + determines the window size. inflate() will then process raw deflate data, + not looking for a zlib or gzip header, not generating a check value, and not + looking for any check values for comparison at the end of the stream. This + is for use with other formats that use the deflate compressed data format + such as zip. Those formats provide their own check values. If a custom + format is developed using the raw deflate format for compressed data, it is + recommended that a check value such as an adler32 or a crc32 be applied to + the uncompressed data as is done in the zlib, gzip, and zip formats. For + most applications, the zlib format should be used as is. Note that comments + above on the use in deflateInit2() applies to the magnitude of windowBits. + + windowBits can also be greater than 15 for optional gzip decoding. Add + 32 to windowBits to enable zlib and gzip decoding with automatic header + detection, or add 16 to decode only the gzip format (the zlib format will + return a Z_DATA_ERROR). If a gzip stream is being decoded, strm->adler is a + crc32 instead of an adler32. + + inflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_VERSION_ERROR if the zlib library version is incompatible with the + version assumed by the caller, or Z_STREAM_ERROR if the parameters are + invalid, such as a null pointer to the structure. msg is set to null if + there is no error message. inflateInit2 does not perform any decompression + apart from possibly reading the zlib header if present: actual decompression + will be done by inflate(). (So next_in and avail_in may be modified, but + next_out and avail_out are unused and unchanged.) The current implementation + of inflateInit2() does not process any header information -- that is + deferred until inflate() is called. +*/ + +ZEXTERN int ZEXPORT inflateSetDictionary OF((z_streamp strm, + const Bytef *dictionary, + uInt dictLength)); +/* + Initializes the decompression dictionary from the given uncompressed byte + sequence. This function must be called immediately after a call of inflate, + if that call returned Z_NEED_DICT. The dictionary chosen by the compressor + can be determined from the adler32 value returned by that call of inflate. + The compressor and decompressor must use exactly the same dictionary (see + deflateSetDictionary). For raw inflate, this function can be called at any + time to set the dictionary. If the provided dictionary is smaller than the + window and there is already data in the window, then the provided dictionary + will amend what's there. The application must insure that the dictionary + that was used for compression is provided. + + inflateSetDictionary returns Z_OK if success, Z_STREAM_ERROR if a + parameter is invalid (e.g. dictionary being Z_NULL) or the stream state is + inconsistent, Z_DATA_ERROR if the given dictionary doesn't match the + expected one (incorrect adler32 value). inflateSetDictionary does not + perform any decompression: this will be done by subsequent calls of + inflate(). +*/ + +ZEXTERN int ZEXPORT inflateGetDictionary OF((z_streamp strm, + Bytef *dictionary, + uInt *dictLength)); +/* + Returns the sliding dictionary being maintained by inflate. dictLength is + set to the number of bytes in the dictionary, and that many bytes are copied + to dictionary. dictionary must have enough space, where 32768 bytes is + always enough. If inflateGetDictionary() is called with dictionary equal to + Z_NULL, then only the dictionary length is returned, and nothing is copied. + Similary, if dictLength is Z_NULL, then it is not set. + + inflateGetDictionary returns Z_OK on success, or Z_STREAM_ERROR if the + stream state is inconsistent. +*/ + +ZEXTERN int ZEXPORT inflateSync OF((z_streamp strm)); +/* + Skips invalid compressed data until a possible full flush point (see above + for the description of deflate with Z_FULL_FLUSH) can be found, or until all + available input is skipped. No output is provided. + + inflateSync searches for a 00 00 FF FF pattern in the compressed data. + All full flush points have this pattern, but not all occurrences of this + pattern are full flush points. + + inflateSync returns Z_OK if a possible full flush point has been found, + Z_BUF_ERROR if no more input was provided, Z_DATA_ERROR if no flush point + has been found, or Z_STREAM_ERROR if the stream structure was inconsistent. + In the success case, the application may save the current current value of + total_in which indicates where valid compressed data was found. In the + error case, the application may repeatedly call inflateSync, providing more + input each time, until success or end of the input data. +*/ + +ZEXTERN int ZEXPORT inflateCopy OF((z_streamp dest, + z_streamp source)); +/* + Sets the destination stream as a complete copy of the source stream. + + This function can be useful when randomly accessing a large stream. The + first pass through the stream can periodically record the inflate state, + allowing restarting inflate at those points when randomly accessing the + stream. + + inflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_STREAM_ERROR if the source stream state was inconsistent + (such as zalloc being Z_NULL). msg is left unchanged in both source and + destination. +*/ + +ZEXTERN int ZEXPORT inflateReset OF((z_streamp strm)); +/* + This function is equivalent to inflateEnd followed by inflateInit, + but does not free and reallocate all the internal decompression state. The + stream will keep attributes that may have been set by inflateInit2. + + inflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent (such as zalloc or state being Z_NULL). +*/ + +ZEXTERN int ZEXPORT inflateReset2 OF((z_streamp strm, + int windowBits)); +/* + This function is the same as inflateReset, but it also permits changing + the wrap and window size requests. The windowBits parameter is interpreted + the same as it is for inflateInit2. + + inflateReset2 returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent (such as zalloc or state being Z_NULL), or if + the windowBits parameter is invalid. +*/ + +ZEXTERN int ZEXPORT inflatePrime OF((z_streamp strm, + int bits, + int value)); +/* + This function inserts bits in the inflate input stream. The intent is + that this function is used to start inflating at a bit position in the + middle of a byte. The provided bits will be used before any bytes are used + from next_in. This function should only be used with raw inflate, and + should be used before the first inflate() call after inflateInit2() or + inflateReset(). bits must be less than or equal to 16, and that many of the + least significant bits of value will be inserted in the input. + + If bits is negative, then the input stream bit buffer is emptied. Then + inflatePrime() can be called again to put bits in the buffer. This is used + to clear out bits leftover after feeding inflate a block description prior + to feeding inflate codes. + + inflatePrime returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent. +*/ + +ZEXTERN long ZEXPORT inflateMark OF((z_streamp strm)); +/* + This function returns two values, one in the lower 16 bits of the return + value, and the other in the remaining upper bits, obtained by shifting the + return value down 16 bits. If the upper value is -1 and the lower value is + zero, then inflate() is currently decoding information outside of a block. + If the upper value is -1 and the lower value is non-zero, then inflate is in + the middle of a stored block, with the lower value equaling the number of + bytes from the input remaining to copy. If the upper value is not -1, then + it is the number of bits back from the current bit position in the input of + the code (literal or length/distance pair) currently being processed. In + that case the lower value is the number of bytes already emitted for that + code. + + A code is being processed if inflate is waiting for more input to complete + decoding of the code, or if it has completed decoding but is waiting for + more output space to write the literal or match data. + + inflateMark() is used to mark locations in the input data for random + access, which may be at bit positions, and to note those cases where the + output of a code may span boundaries of random access blocks. The current + location in the input stream can be determined from avail_in and data_type + as noted in the description for the Z_BLOCK flush parameter for inflate. + + inflateMark returns the value noted above or -1 << 16 if the provided + source stream state was inconsistent. +*/ + +ZEXTERN int ZEXPORT inflateGetHeader OF((z_streamp strm, + gz_headerp head)); +/* + inflateGetHeader() requests that gzip header information be stored in the + provided gz_header structure. inflateGetHeader() may be called after + inflateInit2() or inflateReset(), and before the first call of inflate(). + As inflate() processes the gzip stream, head->done is zero until the header + is completed, at which time head->done is set to one. If a zlib stream is + being decoded, then head->done is set to -1 to indicate that there will be + no gzip header information forthcoming. Note that Z_BLOCK or Z_TREES can be + used to force inflate() to return immediately after header processing is + complete and before any actual data is decompressed. + + The text, time, xflags, and os fields are filled in with the gzip header + contents. hcrc is set to true if there is a header CRC. (The header CRC + was valid if done is set to one.) If extra is not Z_NULL, then extra_max + contains the maximum number of bytes to write to extra. Once done is true, + extra_len contains the actual extra field length, and extra contains the + extra field, or that field truncated if extra_max is less than extra_len. + If name is not Z_NULL, then up to name_max characters are written there, + terminated with a zero unless the length is greater than name_max. If + comment is not Z_NULL, then up to comm_max characters are written there, + terminated with a zero unless the length is greater than comm_max. When any + of extra, name, or comment are not Z_NULL and the respective field is not + present in the header, then that field is set to Z_NULL to signal its + absence. This allows the use of deflateSetHeader() with the returned + structure to duplicate the header. However if those fields are set to + allocated memory, then the application will need to save those pointers + elsewhere so that they can be eventually freed. + + If inflateGetHeader is not used, then the header information is simply + discarded. The header is always checked for validity, including the header + CRC if present. inflateReset() will reset the process to discard the header + information. The application would need to call inflateGetHeader() again to + retrieve the header from the next gzip stream. + + inflateGetHeader returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent. +*/ + +/* +ZEXTERN int ZEXPORT inflateBackInit OF((z_streamp strm, int windowBits, + unsigned char FAR *window)); + + Initialize the internal stream state for decompression using inflateBack() + calls. The fields zalloc, zfree and opaque in strm must be initialized + before the call. If zalloc and zfree are Z_NULL, then the default library- + derived memory allocation routines are used. windowBits is the base two + logarithm of the window size, in the range 8..15. window is a caller + supplied buffer of that size. Except for special applications where it is + assured that deflate was used with small window sizes, windowBits must be 15 + and a 32K byte window must be supplied to be able to decompress general + deflate streams. + + See inflateBack() for the usage of these routines. + + inflateBackInit will return Z_OK on success, Z_STREAM_ERROR if any of + the parameters are invalid, Z_MEM_ERROR if the internal state could not be + allocated, or Z_VERSION_ERROR if the version of the library does not match + the version of the header file. +*/ + +typedef unsigned (*in_func) OF((void FAR *, + z_const unsigned char FAR * FAR *)); +typedef int (*out_func) OF((void FAR *, unsigned char FAR *, unsigned)); + +ZEXTERN int ZEXPORT inflateBack OF((z_streamp strm, + in_func in, void FAR *in_desc, + out_func out, void FAR *out_desc)); +/* + inflateBack() does a raw inflate with a single call using a call-back + interface for input and output. This is potentially more efficient than + inflate() for file i/o applications, in that it avoids copying between the + output and the sliding window by simply making the window itself the output + buffer. inflate() can be faster on modern CPUs when used with large + buffers. inflateBack() trusts the application to not change the output + buffer passed by the output function, at least until inflateBack() returns. + + inflateBackInit() must be called first to allocate the internal state + and to initialize the state with the user-provided window buffer. + inflateBack() may then be used multiple times to inflate a complete, raw + deflate stream with each call. inflateBackEnd() is then called to free the + allocated state. + + A raw deflate stream is one with no zlib or gzip header or trailer. + This routine would normally be used in a utility that reads zip or gzip + files and writes out uncompressed files. The utility would decode the + header and process the trailer on its own, hence this routine expects only + the raw deflate stream to decompress. This is different from the normal + behavior of inflate(), which expects either a zlib or gzip header and + trailer around the deflate stream. + + inflateBack() uses two subroutines supplied by the caller that are then + called by inflateBack() for input and output. inflateBack() calls those + routines until it reads a complete deflate stream and writes out all of the + uncompressed data, or until it encounters an error. The function's + parameters and return types are defined above in the in_func and out_func + typedefs. inflateBack() will call in(in_desc, &buf) which should return the + number of bytes of provided input, and a pointer to that input in buf. If + there is no input available, in() must return zero--buf is ignored in that + case--and inflateBack() will return a buffer error. inflateBack() will call + out(out_desc, buf, len) to write the uncompressed data buf[0..len-1]. out() + should return zero on success, or non-zero on failure. If out() returns + non-zero, inflateBack() will return with an error. Neither in() nor out() + are permitted to change the contents of the window provided to + inflateBackInit(), which is also the buffer that out() uses to write from. + The length written by out() will be at most the window size. Any non-zero + amount of input may be provided by in(). + + For convenience, inflateBack() can be provided input on the first call by + setting strm->next_in and strm->avail_in. If that input is exhausted, then + in() will be called. Therefore strm->next_in must be initialized before + calling inflateBack(). If strm->next_in is Z_NULL, then in() will be called + immediately for input. If strm->next_in is not Z_NULL, then strm->avail_in + must also be initialized, and then if strm->avail_in is not zero, input will + initially be taken from strm->next_in[0 .. strm->avail_in - 1]. + + The in_desc and out_desc parameters of inflateBack() is passed as the + first parameter of in() and out() respectively when they are called. These + descriptors can be optionally used to pass any information that the caller- + supplied in() and out() functions need to do their job. + + On return, inflateBack() will set strm->next_in and strm->avail_in to + pass back any unused input that was provided by the last in() call. The + return values of inflateBack() can be Z_STREAM_END on success, Z_BUF_ERROR + if in() or out() returned an error, Z_DATA_ERROR if there was a format error + in the deflate stream (in which case strm->msg is set to indicate the nature + of the error), or Z_STREAM_ERROR if the stream was not properly initialized. + In the case of Z_BUF_ERROR, an input or output error can be distinguished + using strm->next_in which will be Z_NULL only if in() returned an error. If + strm->next_in is not Z_NULL, then the Z_BUF_ERROR was due to out() returning + non-zero. (in() will always be called before out(), so strm->next_in is + assured to be defined if out() returns non-zero.) Note that inflateBack() + cannot return Z_OK. +*/ + +ZEXTERN int ZEXPORT inflateBackEnd OF((z_streamp strm)); +/* + All memory allocated by inflateBackInit() is freed. + + inflateBackEnd() returns Z_OK on success, or Z_STREAM_ERROR if the stream + state was inconsistent. +*/ + +ZEXTERN uLong ZEXPORT zlibCompileFlags OF((void)); +/* Return flags indicating compile-time options. + + Type sizes, two bits each, 00 = 16 bits, 01 = 32, 10 = 64, 11 = other: + 1.0: size of uInt + 3.2: size of uLong + 5.4: size of voidpf (pointer) + 7.6: size of z_off_t + + Compiler, assembler, and debug options: + 8: DEBUG + 9: ASMV or ASMINF -- use ASM code + 10: ZLIB_WINAPI -- exported functions use the WINAPI calling convention + 11: 0 (reserved) + + One-time table building (smaller code, but not thread-safe if true): + 12: BUILDFIXED -- build static block decoding tables when needed + 13: DYNAMIC_CRC_TABLE -- build CRC calculation tables when needed + 14,15: 0 (reserved) + + Library content (indicates missing functionality): + 16: NO_GZCOMPRESS -- gz* functions cannot compress (to avoid linking + deflate code when not needed) + 17: NO_GZIP -- deflate can't write gzip streams, and inflate can't detect + and decode gzip streams (to avoid linking crc code) + 18-19: 0 (reserved) + + Operation variations (changes in library functionality): + 20: PKZIP_BUG_WORKAROUND -- slightly more permissive inflate + 21: FASTEST -- deflate algorithm with only one, lowest compression level + 22,23: 0 (reserved) + + The sprintf variant used by gzprintf (zero is best): + 24: 0 = vs*, 1 = s* -- 1 means limited to 20 arguments after the format + 25: 0 = *nprintf, 1 = *printf -- 1 means gzprintf() not secure! + 26: 0 = returns value, 1 = void -- 1 means inferred string length returned + + Remainder: + 27-31: 0 (reserved) + */ + +#ifndef Z_SOLO + + /* utility functions */ + +/* + The following utility functions are implemented on top of the basic + stream-oriented functions. To simplify the interface, some default options + are assumed (compression level and memory usage, standard memory allocation + functions). The source code of these utility functions can be modified if + you need special options. +*/ + +ZEXTERN int ZEXPORT compress OF((Bytef *dest, uLongf *destLen, + const Bytef *source, uLong sourceLen)); +/* + Compresses the source buffer into the destination buffer. sourceLen is + the byte length of the source buffer. Upon entry, destLen is the total size + of the destination buffer, which must be at least the value returned by + compressBound(sourceLen). Upon exit, destLen is the actual size of the + compressed buffer. + + compress returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_BUF_ERROR if there was not enough room in the output + buffer. +*/ + +ZEXTERN int ZEXPORT compress2 OF((Bytef *dest, uLongf *destLen, + const Bytef *source, uLong sourceLen, + int level)); +/* + Compresses the source buffer into the destination buffer. The level + parameter has the same meaning as in deflateInit. sourceLen is the byte + length of the source buffer. Upon entry, destLen is the total size of the + destination buffer, which must be at least the value returned by + compressBound(sourceLen). Upon exit, destLen is the actual size of the + compressed buffer. + + compress2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_BUF_ERROR if there was not enough room in the output buffer, + Z_STREAM_ERROR if the level parameter is invalid. +*/ + +ZEXTERN uLong ZEXPORT compressBound OF((uLong sourceLen)); +/* + compressBound() returns an upper bound on the compressed size after + compress() or compress2() on sourceLen bytes. It would be used before a + compress() or compress2() call to allocate the destination buffer. +*/ + +ZEXTERN int ZEXPORT uncompress OF((Bytef *dest, uLongf *destLen, + const Bytef *source, uLong sourceLen)); +/* + Decompresses the source buffer into the destination buffer. sourceLen is + the byte length of the source buffer. Upon entry, destLen is the total size + of the destination buffer, which must be large enough to hold the entire + uncompressed data. (The size of the uncompressed data must have been saved + previously by the compressor and transmitted to the decompressor by some + mechanism outside the scope of this compression library.) Upon exit, destLen + is the actual size of the uncompressed buffer. + + uncompress returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_BUF_ERROR if there was not enough room in the output + buffer, or Z_DATA_ERROR if the input data was corrupted or incomplete. In + the case where there is not enough room, uncompress() will fill the output + buffer with the uncompressed data up to that point. +*/ + + /* gzip file access functions */ + +/* + This library supports reading and writing files in gzip (.gz) format with + an interface similar to that of stdio, using the functions that start with + "gz". The gzip format is different from the zlib format. gzip is a gzip + wrapper, documented in RFC 1952, wrapped around a deflate stream. +*/ + +typedef struct gzFile_s *gzFile; /* semi-opaque gzip file descriptor */ + +/* +ZEXTERN gzFile ZEXPORT gzopen OF((const char *path, const char *mode)); + + Opens a gzip (.gz) file for reading or writing. The mode parameter is as + in fopen ("rb" or "wb") but can also include a compression level ("wb9") or + a strategy: 'f' for filtered data as in "wb6f", 'h' for Huffman-only + compression as in "wb1h", 'R' for run-length encoding as in "wb1R", or 'F' + for fixed code compression as in "wb9F". (See the description of + deflateInit2 for more information about the strategy parameter.) 'T' will + request transparent writing or appending with no compression and not using + the gzip format. + + "a" can be used instead of "w" to request that the gzip stream that will + be written be appended to the file. "+" will result in an error, since + reading and writing to the same gzip file is not supported. The addition of + "x" when writing will create the file exclusively, which fails if the file + already exists. On systems that support it, the addition of "e" when + reading or writing will set the flag to close the file on an execve() call. + + These functions, as well as gzip, will read and decode a sequence of gzip + streams in a file. The append function of gzopen() can be used to create + such a file. (Also see gzflush() for another way to do this.) When + appending, gzopen does not test whether the file begins with a gzip stream, + nor does it look for the end of the gzip streams to begin appending. gzopen + will simply append a gzip stream to the existing file. + + gzopen can be used to read a file which is not in gzip format; in this + case gzread will directly read from the file without decompression. When + reading, this will be detected automatically by looking for the magic two- + byte gzip header. + + gzopen returns NULL if the file could not be opened, if there was + insufficient memory to allocate the gzFile state, or if an invalid mode was + specified (an 'r', 'w', or 'a' was not provided, or '+' was provided). + errno can be checked to determine if the reason gzopen failed was that the + file could not be opened. +*/ + +ZEXTERN gzFile ZEXPORT gzdopen OF((int fd, const char *mode)); +/* + gzdopen associates a gzFile with the file descriptor fd. File descriptors + are obtained from calls like open, dup, creat, pipe or fileno (if the file + has been previously opened with fopen). The mode parameter is as in gzopen. + + The next call of gzclose on the returned gzFile will also close the file + descriptor fd, just like fclose(fdopen(fd, mode)) closes the file descriptor + fd. If you want to keep fd open, use fd = dup(fd_keep); gz = gzdopen(fd, + mode);. The duplicated descriptor should be saved to avoid a leak, since + gzdopen does not close fd if it fails. If you are using fileno() to get the + file descriptor from a FILE *, then you will have to use dup() to avoid + double-close()ing the file descriptor. Both gzclose() and fclose() will + close the associated file descriptor, so they need to have different file + descriptors. + + gzdopen returns NULL if there was insufficient memory to allocate the + gzFile state, if an invalid mode was specified (an 'r', 'w', or 'a' was not + provided, or '+' was provided), or if fd is -1. The file descriptor is not + used until the next gz* read, write, seek, or close operation, so gzdopen + will not detect if fd is invalid (unless fd is -1). +*/ + +ZEXTERN int ZEXPORT gzbuffer OF((gzFile file, unsigned size)); +/* + Set the internal buffer size used by this library's functions. The + default buffer size is 8192 bytes. This function must be called after + gzopen() or gzdopen(), and before any other calls that read or write the + file. The buffer memory allocation is always deferred to the first read or + write. Two buffers are allocated, either both of the specified size when + writing, or one of the specified size and the other twice that size when + reading. A larger buffer size of, for example, 64K or 128K bytes will + noticeably increase the speed of decompression (reading). + + The new buffer size also affects the maximum length for gzprintf(). + + gzbuffer() returns 0 on success, or -1 on failure, such as being called + too late. +*/ + +ZEXTERN int ZEXPORT gzsetparams OF((gzFile file, int level, int strategy)); +/* + Dynamically update the compression level or strategy. See the description + of deflateInit2 for the meaning of these parameters. + + gzsetparams returns Z_OK if success, or Z_STREAM_ERROR if the file was not + opened for writing. +*/ + +ZEXTERN int ZEXPORT gzread OF((gzFile file, voidp buf, unsigned len)); +/* + Reads the given number of uncompressed bytes from the compressed file. If + the input file is not in gzip format, gzread copies the given number of + bytes into the buffer directly from the file. + + After reaching the end of a gzip stream in the input, gzread will continue + to read, looking for another gzip stream. Any number of gzip streams may be + concatenated in the input file, and will all be decompressed by gzread(). + If something other than a gzip stream is encountered after a gzip stream, + that remaining trailing garbage is ignored (and no error is returned). + + gzread can be used to read a gzip file that is being concurrently written. + Upon reaching the end of the input, gzread will return with the available + data. If the error code returned by gzerror is Z_OK or Z_BUF_ERROR, then + gzclearerr can be used to clear the end of file indicator in order to permit + gzread to be tried again. Z_OK indicates that a gzip stream was completed + on the last gzread. Z_BUF_ERROR indicates that the input file ended in the + middle of a gzip stream. Note that gzread does not return -1 in the event + of an incomplete gzip stream. This error is deferred until gzclose(), which + will return Z_BUF_ERROR if the last gzread ended in the middle of a gzip + stream. Alternatively, gzerror can be used before gzclose to detect this + case. + + gzread returns the number of uncompressed bytes actually read, less than + len for end of file, or -1 for error. +*/ + +ZEXTERN int ZEXPORT gzwrite OF((gzFile file, + voidpc buf, unsigned len)); +/* + Writes the given number of uncompressed bytes into the compressed file. + gzwrite returns the number of uncompressed bytes written or 0 in case of + error. +*/ + +ZEXTERN int ZEXPORTVA gzprintf Z_ARG((gzFile file, const char *format, ...)); +/* + Converts, formats, and writes the arguments to the compressed file under + control of the format string, as in fprintf. gzprintf returns the number of + uncompressed bytes actually written, or 0 in case of error. The number of + uncompressed bytes written is limited to 8191, or one less than the buffer + size given to gzbuffer(). The caller should assure that this limit is not + exceeded. If it is exceeded, then gzprintf() will return an error (0) with + nothing written. In this case, there may also be a buffer overflow with + unpredictable consequences, which is possible only if zlib was compiled with + the insecure functions sprintf() or vsprintf() because the secure snprintf() + or vsnprintf() functions were not available. This can be determined using + zlibCompileFlags(). +*/ + +ZEXTERN int ZEXPORT gzputs OF((gzFile file, const char *s)); +/* + Writes the given null-terminated string to the compressed file, excluding + the terminating null character. + + gzputs returns the number of characters written, or -1 in case of error. +*/ + +ZEXTERN char * ZEXPORT gzgets OF((gzFile file, char *buf, int len)); +/* + Reads bytes from the compressed file until len-1 characters are read, or a + newline character is read and transferred to buf, or an end-of-file + condition is encountered. If any characters are read or if len == 1, the + string is terminated with a null character. If no characters are read due + to an end-of-file or len < 1, then the buffer is left untouched. + + gzgets returns buf which is a null-terminated string, or it returns NULL + for end-of-file or in case of error. If there was an error, the contents at + buf are indeterminate. +*/ + +ZEXTERN int ZEXPORT gzputc OF((gzFile file, int c)); +/* + Writes c, converted to an unsigned char, into the compressed file. gzputc + returns the value that was written, or -1 in case of error. +*/ + +ZEXTERN int ZEXPORT gzgetc OF((gzFile file)); +/* + Reads one byte from the compressed file. gzgetc returns this byte or -1 + in case of end of file or error. This is implemented as a macro for speed. + As such, it does not do all of the checking the other functions do. I.e. + it does not check to see if file is NULL, nor whether the structure file + points to has been clobbered or not. +*/ + +ZEXTERN int ZEXPORT gzungetc OF((int c, gzFile file)); +/* + Push one character back onto the stream to be read as the first character + on the next read. At least one character of push-back is allowed. + gzungetc() returns the character pushed, or -1 on failure. gzungetc() will + fail if c is -1, and may fail if a character has been pushed but not read + yet. If gzungetc is used immediately after gzopen or gzdopen, at least the + output buffer size of pushed characters is allowed. (See gzbuffer above.) + The pushed character will be discarded if the stream is repositioned with + gzseek() or gzrewind(). +*/ + +ZEXTERN int ZEXPORT gzflush OF((gzFile file, int flush)); +/* + Flushes all pending output into the compressed file. The parameter flush + is as in the deflate() function. The return value is the zlib error number + (see function gzerror below). gzflush is only permitted when writing. + + If the flush parameter is Z_FINISH, the remaining data is written and the + gzip stream is completed in the output. If gzwrite() is called again, a new + gzip stream will be started in the output. gzread() is able to read such + concatented gzip streams. + + gzflush should be called only when strictly necessary because it will + degrade compression if called too often. +*/ + +/* +ZEXTERN z_off_t ZEXPORT gzseek OF((gzFile file, + z_off_t offset, int whence)); + + Sets the starting position for the next gzread or gzwrite on the given + compressed file. The offset represents a number of bytes in the + uncompressed data stream. The whence parameter is defined as in lseek(2); + the value SEEK_END is not supported. + + If the file is opened for reading, this function is emulated but can be + extremely slow. If the file is opened for writing, only forward seeks are + supported; gzseek then compresses a sequence of zeroes up to the new + starting position. + + gzseek returns the resulting offset location as measured in bytes from + the beginning of the uncompressed stream, or -1 in case of error, in + particular if the file is opened for writing and the new starting position + would be before the current position. +*/ + +ZEXTERN int ZEXPORT gzrewind OF((gzFile file)); +/* + Rewinds the given file. This function is supported only for reading. + + gzrewind(file) is equivalent to (int)gzseek(file, 0L, SEEK_SET) +*/ + +/* +ZEXTERN z_off_t ZEXPORT gztell OF((gzFile file)); + + Returns the starting position for the next gzread or gzwrite on the given + compressed file. This position represents a number of bytes in the + uncompressed data stream, and is zero when starting, even if appending or + reading a gzip stream from the middle of a file using gzdopen(). + + gztell(file) is equivalent to gzseek(file, 0L, SEEK_CUR) +*/ + +/* +ZEXTERN z_off_t ZEXPORT gzoffset OF((gzFile file)); + + Returns the current offset in the file being read or written. This offset + includes the count of bytes that precede the gzip stream, for example when + appending or when using gzdopen() for reading. When reading, the offset + does not include as yet unused buffered input. This information can be used + for a progress indicator. On error, gzoffset() returns -1. +*/ + +ZEXTERN int ZEXPORT gzeof OF((gzFile file)); +/* + Returns true (1) if the end-of-file indicator has been set while reading, + false (0) otherwise. Note that the end-of-file indicator is set only if the + read tried to go past the end of the input, but came up short. Therefore, + just like feof(), gzeof() may return false even if there is no more data to + read, in the event that the last read request was for the exact number of + bytes remaining in the input file. This will happen if the input file size + is an exact multiple of the buffer size. + + If gzeof() returns true, then the read functions will return no more data, + unless the end-of-file indicator is reset by gzclearerr() and the input file + has grown since the previous end of file was detected. +*/ + +ZEXTERN int ZEXPORT gzdirect OF((gzFile file)); +/* + Returns true (1) if file is being copied directly while reading, or false + (0) if file is a gzip stream being decompressed. + + If the input file is empty, gzdirect() will return true, since the input + does not contain a gzip stream. + + If gzdirect() is used immediately after gzopen() or gzdopen() it will + cause buffers to be allocated to allow reading the file to determine if it + is a gzip file. Therefore if gzbuffer() is used, it should be called before + gzdirect(). + + When writing, gzdirect() returns true (1) if transparent writing was + requested ("wT" for the gzopen() mode), or false (0) otherwise. (Note: + gzdirect() is not needed when writing. Transparent writing must be + explicitly requested, so the application already knows the answer. When + linking statically, using gzdirect() will include all of the zlib code for + gzip file reading and decompression, which may not be desired.) +*/ + +ZEXTERN int ZEXPORT gzclose OF((gzFile file)); +/* + Flushes all pending output if necessary, closes the compressed file and + deallocates the (de)compression state. Note that once file is closed, you + cannot call gzerror with file, since its structures have been deallocated. + gzclose must not be called more than once on the same file, just as free + must not be called more than once on the same allocation. + + gzclose will return Z_STREAM_ERROR if file is not valid, Z_ERRNO on a + file operation error, Z_MEM_ERROR if out of memory, Z_BUF_ERROR if the + last read ended in the middle of a gzip stream, or Z_OK on success. +*/ + +ZEXTERN int ZEXPORT gzclose_r OF((gzFile file)); +ZEXTERN int ZEXPORT gzclose_w OF((gzFile file)); +/* + Same as gzclose(), but gzclose_r() is only for use when reading, and + gzclose_w() is only for use when writing or appending. The advantage to + using these instead of gzclose() is that they avoid linking in zlib + compression or decompression code that is not used when only reading or only + writing respectively. If gzclose() is used, then both compression and + decompression code will be included the application when linking to a static + zlib library. +*/ + +ZEXTERN const char * ZEXPORT gzerror OF((gzFile file, int *errnum)); +/* + Returns the error message for the last error which occurred on the given + compressed file. errnum is set to zlib error number. If an error occurred + in the file system and not in the compression library, errnum is set to + Z_ERRNO and the application may consult errno to get the exact error code. + + The application must not modify the returned string. Future calls to + this function may invalidate the previously returned string. If file is + closed, then the string previously returned by gzerror will no longer be + available. + + gzerror() should be used to distinguish errors from end-of-file for those + functions above that do not distinguish those cases in their return values. +*/ + +ZEXTERN void ZEXPORT gzclearerr OF((gzFile file)); +/* + Clears the error and end-of-file flags for file. This is analogous to the + clearerr() function in stdio. This is useful for continuing to read a gzip + file that is being written concurrently. +*/ + +#endif /* !Z_SOLO */ + + /* checksum functions */ + +/* + These functions are not related to compression but are exported + anyway because they might be useful in applications using the compression + library. +*/ + +ZEXTERN uLong ZEXPORT adler32 OF((uLong adler, const Bytef *buf, uInt len)); +/* + Update a running Adler-32 checksum with the bytes buf[0..len-1] and + return the updated checksum. If buf is Z_NULL, this function returns the + required initial value for the checksum. + + An Adler-32 checksum is almost as reliable as a CRC32 but can be computed + much faster. + + Usage example: + + uLong adler = adler32(0L, Z_NULL, 0); + + while (read_buffer(buffer, length) != EOF) { + adler = adler32(adler, buffer, length); + } + if (adler != original_adler) error(); +*/ + +/* +ZEXTERN uLong ZEXPORT adler32_combine OF((uLong adler1, uLong adler2, + z_off_t len2)); + + Combine two Adler-32 checksums into one. For two sequences of bytes, seq1 + and seq2 with lengths len1 and len2, Adler-32 checksums were calculated for + each, adler1 and adler2. adler32_combine() returns the Adler-32 checksum of + seq1 and seq2 concatenated, requiring only adler1, adler2, and len2. Note + that the z_off_t type (like off_t) is a signed integer. If len2 is + negative, the result has no meaning or utility. +*/ + +ZEXTERN uLong ZEXPORT crc32 OF((uLong crc, const Bytef *buf, uInt len)); +/* + Update a running CRC-32 with the bytes buf[0..len-1] and return the + updated CRC-32. If buf is Z_NULL, this function returns the required + initial value for the crc. Pre- and post-conditioning (one's complement) is + performed within this function so it shouldn't be done by the application. + + Usage example: + + uLong crc = crc32(0L, Z_NULL, 0); + + while (read_buffer(buffer, length) != EOF) { + crc = crc32(crc, buffer, length); + } + if (crc != original_crc) error(); +*/ + +/* +ZEXTERN uLong ZEXPORT crc32_combine OF((uLong crc1, uLong crc2, z_off_t len2)); + + Combine two CRC-32 check values into one. For two sequences of bytes, + seq1 and seq2 with lengths len1 and len2, CRC-32 check values were + calculated for each, crc1 and crc2. crc32_combine() returns the CRC-32 + check value of seq1 and seq2 concatenated, requiring only crc1, crc2, and + len2. +*/ + + + /* various hacks, don't look :) */ + +/* deflateInit and inflateInit are macros to allow checking the zlib version + * and the compiler's view of z_stream: + */ +ZEXTERN int ZEXPORT deflateInit_ OF((z_streamp strm, int level, + const char *version, int stream_size)); +ZEXTERN int ZEXPORT inflateInit_ OF((z_streamp strm, + const char *version, int stream_size)); +ZEXTERN int ZEXPORT deflateInit2_ OF((z_streamp strm, int level, int method, + int windowBits, int memLevel, + int strategy, const char *version, + int stream_size)); +ZEXTERN int ZEXPORT inflateInit2_ OF((z_streamp strm, int windowBits, + const char *version, int stream_size)); +ZEXTERN int ZEXPORT inflateBackInit_ OF((z_streamp strm, int windowBits, + unsigned char FAR *window, + const char *version, + int stream_size)); +#define deflateInit(strm, level) \ + deflateInit_((strm), (level), ZLIB_VERSION, (int)sizeof(z_stream)) +#define inflateInit(strm) \ + inflateInit_((strm), ZLIB_VERSION, (int)sizeof(z_stream)) +#define deflateInit2(strm, level, method, windowBits, memLevel, strategy) \ + deflateInit2_((strm),(level),(method),(windowBits),(memLevel),\ + (strategy), ZLIB_VERSION, (int)sizeof(z_stream)) +#define inflateInit2(strm, windowBits) \ + inflateInit2_((strm), (windowBits), ZLIB_VERSION, \ + (int)sizeof(z_stream)) +#define inflateBackInit(strm, windowBits, window) \ + inflateBackInit_((strm), (windowBits), (window), \ + ZLIB_VERSION, (int)sizeof(z_stream)) + +#ifndef Z_SOLO + +/* gzgetc() macro and its supporting function and exposed data structure. Note + * that the real internal state is much larger than the exposed structure. + * This abbreviated structure exposes just enough for the gzgetc() macro. The + * user should not mess with these exposed elements, since their names or + * behavior could change in the future, perhaps even capriciously. They can + * only be used by the gzgetc() macro. You have been warned. + */ +struct gzFile_s { + unsigned have; + unsigned char *next; + z_off64_t pos; +}; +ZEXTERN int ZEXPORT gzgetc_ OF((gzFile file)); /* backward compatibility */ +#ifdef Z_PREFIX_SET +# undef z_gzgetc +# define z_gzgetc(g) \ + ((g)->have ? ((g)->have--, (g)->pos++, *((g)->next)++) : gzgetc(g)) +#else +# define gzgetc(g) \ + ((g)->have ? ((g)->have--, (g)->pos++, *((g)->next)++) : gzgetc(g)) +#endif + +/* provide 64-bit offset functions if _LARGEFILE64_SOURCE defined, and/or + * change the regular functions to 64 bits if _FILE_OFFSET_BITS is 64 (if + * both are true, the application gets the *64 functions, and the regular + * functions are changed to 64 bits) -- in case these are set on systems + * without large file support, _LFS64_LARGEFILE must also be true + */ +#ifdef Z_LARGE64 + ZEXTERN gzFile ZEXPORT gzopen64 OF((const char *, const char *)); + ZEXTERN z_off64_t ZEXPORT gzseek64 OF((gzFile, z_off64_t, int)); + ZEXTERN z_off64_t ZEXPORT gztell64 OF((gzFile)); + ZEXTERN z_off64_t ZEXPORT gzoffset64 OF((gzFile)); + ZEXTERN uLong ZEXPORT adler32_combine64 OF((uLong, uLong, z_off64_t)); + ZEXTERN uLong ZEXPORT crc32_combine64 OF((uLong, uLong, z_off64_t)); +#endif + +#if !defined(ZLIB_INTERNAL) && defined(Z_WANT64) +# ifdef Z_PREFIX_SET +# define z_gzopen z_gzopen64 +# define z_gzseek z_gzseek64 +# define z_gztell z_gztell64 +# define z_gzoffset z_gzoffset64 +# define z_adler32_combine z_adler32_combine64 +# define z_crc32_combine z_crc32_combine64 +# else +# define gzopen gzopen64 +# define gzseek gzseek64 +# define gztell gztell64 +# define gzoffset gzoffset64 +# define adler32_combine adler32_combine64 +# define crc32_combine crc32_combine64 +# endif +# ifndef Z_LARGE64 + ZEXTERN gzFile ZEXPORT gzopen64 OF((const char *, const char *)); + ZEXTERN z_off_t ZEXPORT gzseek64 OF((gzFile, z_off_t, int)); + ZEXTERN z_off_t ZEXPORT gztell64 OF((gzFile)); + ZEXTERN z_off_t ZEXPORT gzoffset64 OF((gzFile)); + ZEXTERN uLong ZEXPORT adler32_combine64 OF((uLong, uLong, z_off_t)); + ZEXTERN uLong ZEXPORT crc32_combine64 OF((uLong, uLong, z_off_t)); +# endif +#else + ZEXTERN gzFile ZEXPORT gzopen OF((const char *, const char *)); + ZEXTERN z_off_t ZEXPORT gzseek OF((gzFile, z_off_t, int)); + ZEXTERN z_off_t ZEXPORT gztell OF((gzFile)); + ZEXTERN z_off_t ZEXPORT gzoffset OF((gzFile)); + ZEXTERN uLong ZEXPORT adler32_combine OF((uLong, uLong, z_off_t)); + ZEXTERN uLong ZEXPORT crc32_combine OF((uLong, uLong, z_off_t)); +#endif + +#else /* Z_SOLO */ + + ZEXTERN uLong ZEXPORT adler32_combine OF((uLong, uLong, z_off_t)); + ZEXTERN uLong ZEXPORT crc32_combine OF((uLong, uLong, z_off_t)); + +#endif /* !Z_SOLO */ + +/* hack for buggy compilers */ +#if !defined(ZUTIL_H) && !defined(NO_DUMMY_DECL) + struct internal_state {int dummy;}; +#endif + +/* undocumented functions */ +ZEXTERN const char * ZEXPORT zError OF((int)); +ZEXTERN int ZEXPORT inflateSyncPoint OF((z_streamp)); +ZEXTERN const z_crc_t FAR * ZEXPORT get_crc_table OF((void)); +ZEXTERN int ZEXPORT inflateUndermine OF((z_streamp, int)); +ZEXTERN int ZEXPORT inflateResetKeep OF((z_streamp)); +ZEXTERN int ZEXPORT deflateResetKeep OF((z_streamp)); +#if defined(_WIN32) && !defined(Z_SOLO) +ZEXTERN gzFile ZEXPORT gzopen_w OF((const wchar_t *path, + const char *mode)); +#endif +#if defined(STDC) || defined(Z_HAVE_STDARG_H) +# ifndef Z_SOLO +ZEXTERN int ZEXPORTVA gzvprintf Z_ARG((gzFile file, + const char *format, + va_list va)); +# endif +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* ZLIB_H */ diff --git a/src/zlib/zutil.c b/src/zlib/zutil.c new file mode 100644 index 00000000..662ebf45 --- /dev/null +++ b/src/zlib/zutil.c @@ -0,0 +1,309 @@ +/* zutil.c -- target dependent utility functions for the compression library + * Copyright (C) 1995-2005, 2010, 2011, 2012 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* @(#) $Id: zutil.c,v 1.2 2013/12/18 09:48:32 cfsmp3 Exp $ */ + +#include "zutil.h" +#ifndef Z_SOLO +# include "gzguts.h" +#endif + +#ifndef NO_DUMMY_DECL +struct internal_state {int dummy;}; /* for buggy compilers */ +#endif + +z_const char * const z_errmsg[10] = { +"need dictionary", /* Z_NEED_DICT 2 */ +"stream end", /* Z_STREAM_END 1 */ +"", /* Z_OK 0 */ +"file error", /* Z_ERRNO (-1) */ +"stream error", /* Z_STREAM_ERROR (-2) */ +"data error", /* Z_DATA_ERROR (-3) */ +"insufficient memory", /* Z_MEM_ERROR (-4) */ +"buffer error", /* Z_BUF_ERROR (-5) */ +"incompatible version",/* Z_VERSION_ERROR (-6) */ +""}; + + +const char * ZEXPORT zlibVersion() +{ + return ZLIB_VERSION; +} + +uLong ZEXPORT zlibCompileFlags() +{ + uLong flags; + + flags = 0; + switch ((int)(sizeof(uInt))) { + case 2: break; + case 4: flags += 1; break; + case 8: flags += 2; break; + default: flags += 3; + } + switch ((int)(sizeof(uLong))) { + case 2: break; + case 4: flags += 1 << 2; break; + case 8: flags += 2 << 2; break; + default: flags += 3 << 2; + } + switch ((int)(sizeof(voidpf))) { + case 2: break; + case 4: flags += 1 << 4; break; + case 8: flags += 2 << 4; break; + default: flags += 3 << 4; + } + switch ((int)(sizeof(z_off_t))) { + case 2: break; + case 4: flags += 1 << 6; break; + case 8: flags += 2 << 6; break; + default: flags += 3 << 6; + } +#ifdef DEBUG + flags += 1 << 8; +#endif +#if defined(ASMV) || defined(ASMINF) + flags += 1 << 9; +#endif +#ifdef ZLIB_WINAPI + flags += 1 << 10; +#endif +#ifdef BUILDFIXED + flags += 1 << 12; +#endif +#ifdef DYNAMIC_CRC_TABLE + flags += 1 << 13; +#endif +#ifdef NO_GZCOMPRESS + flags += 1L << 16; +#endif +#ifdef NO_GZIP + flags += 1L << 17; +#endif +#ifdef PKZIP_BUG_WORKAROUND + flags += 1L << 20; +#endif +#ifdef FASTEST + flags += 1L << 21; +#endif +#if defined(STDC) || defined(Z_HAVE_STDARG_H) +# ifdef NO_vsnprintf + flags += 1L << 25; +# ifdef HAS_vsprintf_void + flags += 1L << 26; +# endif +# else +# ifdef HAS_vsnprintf_void + flags += 1L << 26; +# endif +# endif +#else + flags += 1L << 24; +# ifdef NO_snprintf + flags += 1L << 25; +# ifdef HAS_sprintf_void + flags += 1L << 26; +# endif +# else +# ifdef HAS_snprintf_void + flags += 1L << 26; +# endif +# endif +#endif + return flags; +} + +#ifdef DEBUG + +# ifndef verbose +# define verbose 0 +# endif +int ZLIB_INTERNAL z_verbose = verbose; + +void ZLIB_INTERNAL z_error (char *m) +{ + fprintf(stderr, "%s\n", m); + exit(1); +} +#endif + +/* exported to allow conversion of error code to string for compress() and + * uncompress() + */ +const char * ZEXPORT zError(int err) +{ + return ERR_MSG(err); +} + +#if defined(_WIN32_WCE) + /* The Microsoft C Run-Time Library for Windows CE doesn't have + * errno. We define it as a global variable to simplify porting. + * Its value is always 0 and should not be used. + */ + int errno = 0; +#endif + +#ifndef HAVE_MEMCPY + +void ZLIB_INTERNAL zmemcpy(Bytef *dest, const Bytef *source, uInt len) +{ + if (len == 0) return; + do { + *dest++ = *source++; /* ??? to be unrolled */ + } while (--len != 0); +} + +int ZLIB_INTERNAL zmemcmp( const Bytef *s1, const Bytef *s2, uInt len) +{ + uInt j; + + for (j = 0; j < len; j++) { + if (s1[j] != s2[j]) return 2*(s1[j] > s2[j])-1; + } + return 0; +} + +void ZLIB_INTERNAL zmemzero(Bytef *dest, uInt len) +{ + if (len == 0) return; + do { + *dest++ = 0; /* ??? to be unrolled */ + } while (--len != 0); +} +#endif + +#ifndef Z_SOLO + +#ifdef SYS16BIT + +#ifdef __TURBOC__ +/* Turbo C in 16-bit mode */ + +# define MY_ZCALLOC + +/* Turbo C malloc() does not allow dynamic allocation of 64K bytes + * and farmalloc(64K) returns a pointer with an offset of 8, so we + * must fix the pointer. Warning: the pointer must be put back to its + * original form in order to free it, use zcfree(). + */ + +#define MAX_PTR 10 +/* 10*64K = 640K */ + +local int next_ptr = 0; + +typedef struct ptr_table_s { + voidpf org_ptr; + voidpf new_ptr; +} ptr_table; + +local ptr_table table[MAX_PTR]; +/* This table is used to remember the original form of pointers + * to large buffers (64K). Such pointers are normalized with a zero offset. + * Since MSDOS is not a preemptive multitasking OS, this table is not + * protected from concurrent access. This hack doesn't work anyway on + * a protected system like OS/2. Use Microsoft C instead. + */ + +voidpf ZLIB_INTERNAL zcalloc (voidpf opaque, unsigned items, unsigned size) +{ + voidpf buf = opaque; /* just to make some compilers happy */ + ulg bsize = (ulg)items*size; + + /* If we allocate less than 65520 bytes, we assume that farmalloc + * will return a usable pointer which doesn't have to be normalized. + */ + if (bsize < 65520L) { + buf = farmalloc(bsize); + if (*(ush*)&buf != 0) return buf; + } else { + buf = farmalloc(bsize + 16L); + } + if (buf == NULL || next_ptr >= MAX_PTR) return NULL; + table[next_ptr].org_ptr = buf; + + /* Normalize the pointer to seg:0 */ + *((ush*)&buf+1) += ((ush)((uch*)buf-0) + 15) >> 4; + *(ush*)&buf = 0; + table[next_ptr++].new_ptr = buf; + return buf; +} + +void ZLIB_INTERNAL zcfree (voidpf opaque, voidpf ptr) +{ + int n; + if (*(ush*)&ptr != 0) { /* object < 64K */ + farfree(ptr); + return; + } + /* Find the original pointer */ + for (n = 0; n < next_ptr; n++) { + if (ptr != table[n].new_ptr) continue; + + farfree(table[n].org_ptr); + while (++n < next_ptr) { + table[n-1] = table[n]; + } + next_ptr--; + return; + } + ptr = opaque; /* just to make some compilers happy */ + Assert(0, "zcfree: ptr not found"); +} + +#endif /* __TURBOC__ */ + + +#ifdef M_I86 +/* Microsoft C in 16-bit mode */ + +# define MY_ZCALLOC + +#if (!defined(_MSC_VER) || (_MSC_VER <= 600)) +# define _halloc halloc +# define _hfree hfree +#endif + +voidpf ZLIB_INTERNAL zcalloc (voidpf opaque, uInt items, uInt size) +{ + if (opaque) opaque = 0; /* to make compiler happy */ + return _halloc((long)items, size); +} + +void ZLIB_INTERNAL zcfree (voidpf opaque, voidpf ptr) +{ + if (opaque) opaque = 0; /* to make compiler happy */ + _hfree(ptr); +} + +#endif /* M_I86 */ + +#endif /* SYS16BIT */ + + +#ifndef MY_ZCALLOC /* Any system without a special alloc function */ + +#ifndef STDC +extern voidp malloc OF((uInt size)); +extern voidp calloc OF((uInt items, uInt size)); +extern void free OF((voidpf ptr)); +#endif + +voidpf ZLIB_INTERNAL zcalloc (voidpf opaque, unsigned items, unsigned size) +{ + if (opaque) items += size - size; /* make compiler happy */ + return sizeof(uInt) > 2 ? (voidpf)malloc(items * size) : + (voidpf)calloc(items, size); +} + +void ZLIB_INTERNAL zcfree (voidpf opaque, voidpf ptr) +{ + free(ptr); + if (opaque) return; /* make compiler happy */ +} + +#endif /* MY_ZCALLOC */ + +#endif /* !Z_SOLO */ diff --git a/src/zlib/zutil.h b/src/zlib/zutil.h new file mode 100644 index 00000000..b5e4923e --- /dev/null +++ b/src/zlib/zutil.h @@ -0,0 +1,253 @@ +/* zutil.h -- internal interface and configuration of the compression library + * Copyright (C) 1995-2013 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +/* @(#) $Id: zutil.h,v 1.1 2013/12/16 08:59:57 cfsmp3 Exp $ */ + +#ifndef ZUTIL_H +#define ZUTIL_H + +#ifdef HAVE_HIDDEN +# define ZLIB_INTERNAL __attribute__((visibility ("hidden"))) +#else +# define ZLIB_INTERNAL +#endif + +#include "zlib.h" + +#if defined(STDC) && !defined(Z_SOLO) +# if !(defined(_WIN32_WCE) && defined(_MSC_VER)) +# include +# endif +# include +# include +#endif + +#ifdef Z_SOLO + typedef long ptrdiff_t; /* guess -- will be caught if guess is wrong */ +#endif + +#ifndef local +# define local static +#endif +/* compile with -Dlocal if your debugger can't find static symbols */ + +typedef unsigned char uch; +typedef uch FAR uchf; +typedef unsigned short ush; +typedef ush FAR ushf; +typedef unsigned long ulg; + +extern z_const char * const z_errmsg[10]; /* indexed by 2-zlib_error */ +/* (size given to avoid silly warnings with Visual C++) */ + +#define ERR_MSG(err) z_errmsg[Z_NEED_DICT-(err)] + +#define ERR_RETURN(strm,err) \ + return (strm->msg = ERR_MSG(err), (err)) +/* To be used only when the state is known to be valid */ + + /* common constants */ + +#ifndef DEF_WBITS +# define DEF_WBITS MAX_WBITS +#endif +/* default windowBits for decompression. MAX_WBITS is for compression only */ + +#if MAX_MEM_LEVEL >= 8 +# define DEF_MEM_LEVEL 8 +#else +# define DEF_MEM_LEVEL MAX_MEM_LEVEL +#endif +/* default memLevel */ + +#define STORED_BLOCK 0 +#define STATIC_TREES 1 +#define DYN_TREES 2 +/* The three kinds of block type */ + +#define MIN_MATCH 3 +#define MAX_MATCH 258 +/* The minimum and maximum match lengths */ + +#define PRESET_DICT 0x20 /* preset dictionary flag in zlib header */ + + /* target dependencies */ + +#if defined(MSDOS) || (defined(WINDOWS) && !defined(WIN32)) +# define OS_CODE 0x00 +# ifndef Z_SOLO +# if defined(__TURBOC__) || defined(__BORLANDC__) +# if (__STDC__ == 1) && (defined(__LARGE__) || defined(__COMPACT__)) + /* Allow compilation with ANSI keywords only enabled */ + void _Cdecl farfree( void *block ); + void *_Cdecl farmalloc( unsigned long nbytes ); +# else +# include +# endif +# else /* MSC or DJGPP */ +# include +# endif +# endif +#endif + +#ifdef AMIGA +# define OS_CODE 0x01 +#endif + +#if defined(VAXC) || defined(VMS) +# define OS_CODE 0x02 +# define F_OPEN(name, mode) \ + fopen((name), (mode), "mbc=60", "ctx=stm", "rfm=fix", "mrs=512") +#endif + +#if defined(ATARI) || defined(atarist) +# define OS_CODE 0x05 +#endif + +#ifdef OS2 +# define OS_CODE 0x06 +# if defined(M_I86) && !defined(Z_SOLO) +# include +# endif +#endif + +#if defined(MACOS) || defined(TARGET_OS_MAC) +# define OS_CODE 0x07 +# ifndef Z_SOLO +# if defined(__MWERKS__) && __dest_os != __be_os && __dest_os != __win32_os +# include /* for fdopen */ +# else +# ifndef fdopen +# define fdopen(fd,mode) NULL /* No fdopen() */ +# endif +# endif +# endif +#endif + +#ifdef TOPS20 +# define OS_CODE 0x0a +#endif + +#ifdef WIN32 +# ifndef __CYGWIN__ /* Cygwin is Unix, not Win32 */ +# define OS_CODE 0x0b +# endif +#endif + +#ifdef __50SERIES /* Prime/PRIMOS */ +# define OS_CODE 0x0f +#endif + +#if defined(_BEOS_) || defined(RISCOS) +# define fdopen(fd,mode) NULL /* No fdopen() */ +#endif + +#if (defined(_MSC_VER) && (_MSC_VER > 600)) && !defined __INTERIX +# if defined(_WIN32_WCE) +# define fdopen(fd,mode) NULL /* No fdopen() */ +# ifndef _PTRDIFF_T_DEFINED + typedef int ptrdiff_t; +# define _PTRDIFF_T_DEFINED +# endif +# else +# define fdopen(fd,type) _fdopen(fd,type) +# endif +#endif + +#if defined(__BORLANDC__) && !defined(MSDOS) + #pragma warn -8004 + #pragma warn -8008 + #pragma warn -8066 +#endif + +/* provide prototypes for these when building zlib without LFS */ +#if !defined(_WIN32) && \ + (!defined(_LARGEFILE64_SOURCE) || _LFS64_LARGEFILE-0 == 0) + ZEXTERN uLong ZEXPORT adler32_combine64 OF((uLong, uLong, z_off_t)); + ZEXTERN uLong ZEXPORT crc32_combine64 OF((uLong, uLong, z_off_t)); +#endif + + /* common defaults */ + +#ifndef OS_CODE +# define OS_CODE 0x03 /* assume Unix */ +#endif + +#ifndef F_OPEN +# define F_OPEN(name, mode) fopen((name), (mode)) +#endif + + /* functions */ + +#if defined(pyr) || defined(Z_SOLO) +# define NO_MEMCPY +#endif +#if defined(SMALL_MEDIUM) && !defined(_MSC_VER) && !defined(__SC__) + /* Use our own functions for small and medium model with MSC <= 5.0. + * You may have to use the same strategy for Borland C (untested). + * The __SC__ check is for Symantec. + */ +# define NO_MEMCPY +#endif +#if defined(STDC) && !defined(HAVE_MEMCPY) && !defined(NO_MEMCPY) +# define HAVE_MEMCPY +#endif +#ifdef HAVE_MEMCPY +# ifdef SMALL_MEDIUM /* MSDOS small or medium model */ +# define zmemcpy _fmemcpy +# define zmemcmp _fmemcmp +# define zmemzero(dest, len) _fmemset(dest, 0, len) +# else +# define zmemcpy memcpy +# define zmemcmp memcmp +# define zmemzero(dest, len) memset(dest, 0, len) +# endif +#else + void ZLIB_INTERNAL zmemcpy OF((Bytef* dest, const Bytef* source, uInt len)); + int ZLIB_INTERNAL zmemcmp OF((const Bytef* s1, const Bytef* s2, uInt len)); + void ZLIB_INTERNAL zmemzero OF((Bytef* dest, uInt len)); +#endif + +/* Diagnostic functions */ +#ifdef DEBUG +# include + extern int ZLIB_INTERNAL z_verbose; + extern void ZLIB_INTERNAL z_error OF((char *m)); +# define Assert(cond,msg) {if(!(cond)) z_error(msg);} +# define Trace(x) {if (z_verbose>=0) fprintf x ;} +# define Tracev(x) {if (z_verbose>0) fprintf x ;} +# define Tracevv(x) {if (z_verbose>1) fprintf x ;} +# define Tracec(c,x) {if (z_verbose>0 && (c)) fprintf x ;} +# define Tracecv(c,x) {if (z_verbose>1 && (c)) fprintf x ;} +#else +# define Assert(cond,msg) +# define Trace(x) +# define Tracev(x) +# define Tracevv(x) +# define Tracec(c,x) +# define Tracecv(c,x) +#endif + +#ifndef Z_SOLO + voidpf ZLIB_INTERNAL zcalloc OF((voidpf opaque, unsigned items, + unsigned size)); + void ZLIB_INTERNAL zcfree OF((voidpf opaque, voidpf ptr)); +#endif + +#define ZALLOC(strm, items, size) \ + (*((strm)->zalloc))((strm)->opaque, (items), (size)) +#define ZFREE(strm, addr) (*((strm)->zfree))((strm)->opaque, (voidpf)(addr)) +#define TRY_FREE(s, p) {if (p) ZFREE(s, p);} + +/* Reverse the bytes in a 32-bit value */ +#define ZSWAP32(q) ((((q) >> 24) & 0xff) + (((q) >> 8) & 0xff00) + \ + (((q) & 0xff00) << 8) + (((q) & 0xff) << 24)) + +#endif /* ZUTIL_H */ diff --git a/windows/README.TXT b/windows/README.TXT new file mode 100644 index 00000000..4881674a --- /dev/null +++ b/windows/README.TXT @@ -0,0 +1,6 @@ +The Windows binaries are now distributed separately, please visit + +http://ccextractor.sourceforge.net + +For a link to the current version. + diff --git a/windows/ccextractor.sln b/windows/ccextractor.sln new file mode 100644 index 00000000..803ce28a --- /dev/null +++ b/windows/ccextractor.sln @@ -0,0 +1,20 @@ + +Microsoft Visual Studio Solution File, Format Version 10.00 +# Visual C++ Express 2008 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ccextractor", "ccextractor.vcproj", "{0F0063C4-BCBC-4379-A6D5-84A5669C940A}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {0F0063C4-BCBC-4379-A6D5-84A5669C940A}.Debug|Win32.ActiveCfg = Debug|Win32 + {0F0063C4-BCBC-4379-A6D5-84A5669C940A}.Debug|Win32.Build.0 = Debug|Win32 + {0F0063C4-BCBC-4379-A6D5-84A5669C940A}.Release|Win32.ActiveCfg = Release|Win32 + {0F0063C4-BCBC-4379-A6D5-84A5669C940A}.Release|Win32.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/windows/ccextractor.vcproj b/windows/ccextractor.vcproj new file mode 100644 index 00000000..948da6c0 --- /dev/null +++ b/windows/ccextractor.vcproj @@ -0,0 +1,744 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/windows/cygming.mak b/windows/cygming.mak new file mode 100644 index 00000000..e55a0e6e --- /dev/null +++ b/windows/cygming.mak @@ -0,0 +1,266 @@ +# Makefile for MinGW using the cygwin environment + +# Run make in the src directory with the following command: +# make -f ../windows/cygming.mak + +CXX=g++ +#CXXFLAGS=-g -Wall +CXXFLAGS=-g -mno-cygwin -Wall +#CXXFLAGS=-O2 -mno-cygwin -Wall + +objects = 608.o \ + 608_helpers.o \ + 608_sami.o \ + 608_srt.o \ + 708.o \ + 708_encoding.o \ + activity.o \ + asf_functions.o \ + avc_functions.o \ + bitstream.o \ + ccextractor.o \ + encoding.o \ + es_functions.o \ + es_userdata.o \ + file_functions.o \ + general_loop.o \ + mp4_bridge2bento4.o \ + myth.o \ + output.o \ + params.o \ + params_dump.o \ + sequencing.o \ + stream_functions.o \ + timing.o \ + ts_functions.o + +bentoobjects = bento4/Ap48bdlAtom.o \ + bento4/Ap4AdtsParser.o \ + bento4/Ap4AesBlockCipher.o \ + bento4/Ap4Atom.o \ + bento4/Ap4AtomFactory.o \ + bento4/Ap4AtomSampleTable.o \ + bento4/Ap4AvcParser.o \ + bento4/Ap4AvccAtom.o \ + bento4/Ap4BitStream.o \ + bento4/Ap4ByteStream.o \ + bento4/Ap4Co64Atom.o \ + bento4/Ap4Command.o \ + bento4/Ap4CommandFactory.o \ + bento4/Ap4ContainerAtom.o \ + bento4/Ap4CttsAtom.o \ + bento4/Ap4DataBuffer.o \ + bento4/Ap4Debug.o \ + bento4/Ap4DecoderConfigDescriptor.o \ + bento4/Ap4DecoderSpecificInfoDescriptor.o \ + bento4/Ap4Descriptor.o \ + bento4/Ap4DescriptorFactory.o \ + bento4/Ap4DrefAtom.o \ + bento4/Ap4ElstAtom.o \ + bento4/Ap4EsDescriptor.o \ + bento4/Ap4EsdsAtom.o \ + bento4/Ap4Expandable.o \ + bento4/Ap4File.o \ + bento4/Ap4FileCopier.o \ + bento4/Ap4FileWriter.o \ + bento4/Ap4FragmentSampleTable.o \ + bento4/Ap4FrmaAtom.o \ + bento4/Ap4FtypAtom.o \ + bento4/Ap4GrpiAtom.o \ + bento4/Ap4HdlrAtom.o \ + bento4/Ap4HintTrackReader.o \ + bento4/Ap4Hmac.o \ + bento4/Ap4HmhdAtom.o \ + bento4/Ap4IkmsAtom.o \ + bento4/Ap4IodsAtom.o \ + bento4/Ap4Ipmp.o \ + bento4/Ap4IproAtom.o \ + bento4/Ap4IsfmAtom.o \ + bento4/Ap4IsltAtom.o \ + bento4/Ap4IsmaCryp.o \ + bento4/Ap4KeyWrap.o \ + bento4/Ap4LinearReader.o \ + bento4/Ap4Marlin.o \ + bento4/Ap4MdhdAtom.o \ + bento4/Ap4MehdAtom.o \ + bento4/Ap4MetaData.o \ + bento4/Ap4MfhdAtom.o \ + bento4/Ap4MfroAtom.o \ + bento4/Ap4MoovAtom.o \ + bento4/Ap4Movie.o \ + bento4/Ap4MovieFragment.o \ + bento4/Ap4Mp4AudioInfo.o \ + bento4/Ap4Mpeg2Ts.o \ + bento4/Ap4MvhdAtom.o \ + bento4/Ap4NmhdAtom.o \ + bento4/Ap4ObjectDescriptor.o \ + bento4/Ap4OdafAtom.o \ + bento4/Ap4OddaAtom.o \ + bento4/Ap4OdheAtom.o \ + bento4/Ap4OhdrAtom.o \ + bento4/Ap4OmaDcf.o \ + bento4/Ap4Piff.o \ + bento4/Ap4Processor.o \ + bento4/Ap4Protection.o \ + bento4/Ap4Results.o \ + bento4/Ap4RtpAtom.o \ + bento4/Ap4RtpHint.o \ + bento4/Ap4SLConfigDescriptor.o \ + bento4/Ap4Sample.o \ + bento4/Ap4SampleDescription.o \ + bento4/Ap4SampleEntry.o \ + bento4/Ap4SampleSource.o \ + bento4/Ap4SampleTable.o \ + bento4/Ap4SchmAtom.o \ + bento4/Ap4SdpAtom.o \ + bento4/Ap4SmhdAtom.o \ + bento4/Ap4StcoAtom.o \ + bento4/Ap4StdCFileByteStream.o \ + bento4/Ap4StreamCipher.o \ + bento4/Ap4String.o \ + bento4/Ap4StscAtom.o \ + bento4/Ap4StsdAtom.o \ + bento4/Ap4StssAtom.o \ + bento4/Ap4StszAtom.o \ + bento4/Ap4SttsAtom.o \ + bento4/Ap4SyntheticSampleTable.o \ + bento4/Ap4TfhdAtom.o \ + bento4/Ap4TfraAtom.o \ + bento4/Ap4TimsAtom.o \ + bento4/Ap4TkhdAtom.o \ + bento4/Ap4Track.o \ + bento4/Ap4TrakAtom.o \ + bento4/Ap4TrefTypeAtom.o \ + bento4/Ap4TrexAtom.o \ + bento4/Ap4TrunAtom.o \ + bento4/Ap4UrlAtom.o \ + bento4/Ap4Utils.o \ + bento4/Ap4UuidAtom.o \ + bento4/Ap4VmhdAtom.o \ + bento4/LinearReaderTest.o + +bentoheaders = bento4/Ap4.h \ + bento4/Ap48bdlAtom.h \ + bento4/Ap4AdtsParser.h \ + bento4/Ap4AesBlockCipher.h \ + bento4/Ap4Array.h \ + bento4/Ap4Atom.h \ + bento4/Ap4AtomFactory.h \ + bento4/Ap4AtomSampleTable.h \ + bento4/Ap4AvcParser.h \ + bento4/Ap4AvccAtom.h \ + bento4/Ap4BitStream.h \ + bento4/Ap4ByteStream.h \ + bento4/Ap4Co64Atom.h \ + bento4/Ap4Command.h \ + bento4/Ap4CommandFactory.h \ + bento4/Ap4Config.h \ + bento4/Ap4Constants.h \ + bento4/Ap4ContainerAtom.h \ + bento4/Ap4CttsAtom.h \ + bento4/Ap4DataBuffer.h \ + bento4/Ap4Debug.h \ + bento4/Ap4DecoderConfigDescriptor.h \ + bento4/Ap4DecoderSpecificInfoDescriptor.h \ + bento4/Ap4Descriptor.h \ + bento4/Ap4DescriptorFactory.h \ + bento4/Ap4DrefAtom.h \ + bento4/Ap4DynamicCast.h \ + bento4/Ap4ElstAtom.h \ + bento4/Ap4EsDescriptor.h \ + bento4/Ap4EsdsAtom.h \ + bento4/Ap4Expandable.h \ + bento4/Ap4File.h \ + bento4/Ap4FileByteStream.h \ + bento4/Ap4FileCopier.h \ + bento4/Ap4FileWriter.h \ + bento4/Ap4FragmentSampleTable.h \ + bento4/Ap4FrmaAtom.h \ + bento4/Ap4FtypAtom.h \ + bento4/Ap4GrpiAtom.h \ + bento4/Ap4HdlrAtom.h \ + bento4/Ap4HintTrackReader.h \ + bento4/Ap4Hmac.h \ + bento4/Ap4HmhdAtom.h \ + bento4/Ap4IkmsAtom.h \ + bento4/Ap4Interfaces.h \ + bento4/Ap4IodsAtom.h \ + bento4/Ap4Ipmp.h \ + bento4/Ap4IproAtom.h \ + bento4/Ap4IsfmAtom.h \ + bento4/Ap4IsltAtom.h \ + bento4/Ap4IsmaCryp.h \ + bento4/Ap4KeyWrap.h \ + bento4/Ap4LinearReader.h \ + bento4/Ap4List.h \ + bento4/Ap4Marlin.h \ + bento4/Ap4MdhdAtom.h \ + bento4/Ap4MehdAtom.h \ + bento4/Ap4MetaData.h \ + bento4/Ap4MfhdAtom.h \ + bento4/Ap4MfroAtom.h \ + bento4/Ap4MoovAtom.h \ + bento4/Ap4Movie.h \ + bento4/Ap4MovieFragment.h \ + bento4/Ap4Mp4AudioInfo.h \ + bento4/Ap4Mpeg2Ts.h \ + bento4/Ap4MvhdAtom.h \ + bento4/Ap4NmhdAtom.h \ + bento4/Ap4ObjectDescriptor.h \ + bento4/Ap4OdafAtom.h \ + bento4/Ap4OddaAtom.h \ + bento4/Ap4OdheAtom.h \ + bento4/Ap4OhdrAtom.h \ + bento4/Ap4OmaDcf.h \ + bento4/Ap4Piff.h \ + bento4/Ap4Processor.h \ + bento4/Ap4Protection.h \ + bento4/Ap4Results.h \ + bento4/Ap4RtpAtom.h \ + bento4/Ap4RtpHint.h \ + bento4/Ap4SLConfigDescriptor.h \ + bento4/Ap4Sample.h \ + bento4/Ap4SampleDescription.h \ + bento4/Ap4SampleEntry.h \ + bento4/Ap4SampleSource.h \ + bento4/Ap4SampleTable.h \ + bento4/Ap4SchmAtom.h \ + bento4/Ap4SdpAtom.h \ + bento4/Ap4SmhdAtom.h \ + bento4/Ap4StcoAtom.h \ + bento4/Ap4StreamCipher.h \ + bento4/Ap4String.h \ + bento4/Ap4StscAtom.h \ + bento4/Ap4StsdAtom.h \ + bento4/Ap4StssAtom.h \ + bento4/Ap4StszAtom.h \ + bento4/Ap4SttsAtom.h \ + bento4/Ap4SyntheticSampleTable.h \ + bento4/Ap4TfhdAtom.h \ + bento4/Ap4TfraAtom.h \ + bento4/Ap4TimsAtom.h \ + bento4/Ap4TkhdAtom.h \ + bento4/Ap4Track.h \ + bento4/Ap4TrakAtom.h \ + bento4/Ap4TrefTypeAtom.h \ + bento4/Ap4TrexAtom.h \ + bento4/Ap4TrunAtom.h \ + bento4/Ap4Types.h \ + bento4/Ap4UrlAtom.h \ + bento4/Ap4Utils.h \ + bento4/Ap4UuidAtom.h \ + bento4/Ap4Version.h \ + bento4/Ap4VmhdAtom.h \ + +ccextractor : $(objects) $(bentoobjects) + $(CXX) $(CXXFLAGS) -o $@ $(objects) $(bentoobjects) + strip $@ + +$(objects) : ccextractor.h 608.h 708.h bitstream.h + +# Lazy solution. +$(bentoobjects) : $(bentoheaders) + +.PHONY : clean +clean : + rm ccextractor.exe $(objects) $(bentoobjects)